@penvhq/cli 0.9.5 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +429 -62
- package/dist/bin.cjs.map +1 -1
- package/dist/chunk-JJY4RLDJ.js +318 -0
- package/dist/chunk-JJY4RLDJ.js.map +1 -0
- package/dist/index.cjs +73 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -47
- package/dist/index.d.ts +1 -47
- package/dist/index.js +296 -536
- package/dist/index.js.map +1 -1
- package/dist/install.cjs +349 -0
- package/dist/install.cjs.map +1 -0
- package/dist/install.d.cts +92 -0
- package/dist/install.d.ts +92 -0
- package/dist/install.js +23 -0
- package/dist/install.js.map +1 -0
- package/package.json +12 -7
package/dist/index.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectPackageManager,
|
|
3
|
+
engineVersion,
|
|
4
|
+
installWithPackageManager,
|
|
5
|
+
noCommand,
|
|
6
|
+
planInstall,
|
|
7
|
+
renderInstallPlan,
|
|
8
|
+
startChild
|
|
9
|
+
} from "./chunk-JJY4RLDJ.js";
|
|
10
|
+
|
|
1
11
|
// src/index.ts
|
|
2
12
|
import { setKeychain } from "@penvhq/core";
|
|
3
13
|
import { runMain as cittyRunMain, defineCommand as defineCommand22 } from "citty";
|
|
4
14
|
|
|
5
15
|
// src/commands/artifact.ts
|
|
6
16
|
import { mkdirSync, writeFileSync } from "fs";
|
|
7
|
-
import { dirname as dirname3, isAbsolute
|
|
17
|
+
import { dirname as dirname3, isAbsolute, relative, resolve as resolve3 } from "path";
|
|
8
18
|
import {
|
|
9
19
|
ARTIFACT_FORMAT,
|
|
10
20
|
candidatesFor as candidatesFor2,
|
|
@@ -12,7 +22,7 @@ import {
|
|
|
12
22
|
formatValueFile as formatValueFile2,
|
|
13
23
|
isSecret as isSecret2,
|
|
14
24
|
keySourceIdentifier,
|
|
15
|
-
PenvError as
|
|
25
|
+
PenvError as PenvError7,
|
|
16
26
|
parameterId as parameterId3,
|
|
17
27
|
serializeArtifact,
|
|
18
28
|
variableName as variableName3
|
|
@@ -20,313 +30,9 @@ import {
|
|
|
20
30
|
import { assertDeliverableNames, declaredRefs as declaredRefs2 } from "@penvhq/runtime";
|
|
21
31
|
import { defineCommand as defineCommand4 } from "citty";
|
|
22
32
|
|
|
23
|
-
// src/install.ts
|
|
24
|
-
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
25
|
-
import { join as join2 } from "path";
|
|
26
|
-
import { PenvError as PenvError2 } from "@penvhq/core";
|
|
27
|
-
|
|
28
|
-
// src/child.ts
|
|
29
|
-
import { spawn } from "child_process";
|
|
30
|
-
import { existsSync, statSync } from "fs";
|
|
31
|
-
import { delimiter, isAbsolute, join, win32 } from "path";
|
|
32
|
-
import { PenvError } from "@penvhq/core";
|
|
33
|
-
var FORWARDED = ["SIGINT", "SIGTERM", "SIGHUP", "SIGBREAK"];
|
|
34
|
-
var startChild = (invocation) => {
|
|
35
|
-
const [executable, ...args] = invocation.command;
|
|
36
|
-
if (executable === void 0) {
|
|
37
|
-
throw noCommand();
|
|
38
|
-
}
|
|
39
|
-
const target = resolveTarget(executable, args, invocation.env);
|
|
40
|
-
const child = spawn(target.file, target.args, {
|
|
41
|
-
cwd: invocation.cwd,
|
|
42
|
-
env: invocation.env,
|
|
43
|
-
stdio: "inherit",
|
|
44
|
-
...target.verbatim ? { windowsVerbatimArguments: true } : {}
|
|
45
|
-
});
|
|
46
|
-
const forward = /* @__PURE__ */ new Map();
|
|
47
|
-
for (const signal of FORWARDED) {
|
|
48
|
-
const handler = () => {
|
|
49
|
-
child.kill(signal);
|
|
50
|
-
};
|
|
51
|
-
forward.set(signal, handler);
|
|
52
|
-
process.on(signal, handler);
|
|
53
|
-
}
|
|
54
|
-
const release = () => {
|
|
55
|
-
for (const [signal, handler] of forward) {
|
|
56
|
-
process.off(signal, handler);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
const ended = new Promise((resolve9, reject) => {
|
|
60
|
-
child.on("error", (cause) => {
|
|
61
|
-
release();
|
|
62
|
-
reject(cannotStart(executable, cause, invocation.purpose));
|
|
63
|
-
});
|
|
64
|
-
child.on("exit", (code, signal) => {
|
|
65
|
-
release();
|
|
66
|
-
resolve9({ exitCode: code ?? 1, signal });
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
return {
|
|
70
|
-
ended,
|
|
71
|
-
kill(signal) {
|
|
72
|
-
child.kill(signal);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
};
|
|
76
|
-
function noCommand() {
|
|
77
|
-
return new PenvError(
|
|
78
|
-
"RUN_NO_COMMAND",
|
|
79
|
-
"`penv run` was given no command to start",
|
|
80
|
-
"Put the command after `--`, e.g. `penv run -- pnpm dev`."
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
function cannotStart(executable, cause, purpose) {
|
|
84
|
-
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
85
|
-
if (purpose !== void 0) {
|
|
86
|
-
return new PenvError(
|
|
87
|
-
"PENV_COMMAND_NOT_STARTED",
|
|
88
|
-
`penv could not start \`${executable}\` to ${purpose}: ${detail}`,
|
|
89
|
-
`Check that \`${executable}\` runs on its own \u2014 penv starts it the way your shell does, so it has to be on PATH. Nothing was changed.`
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
return new PenvError(
|
|
93
|
-
"RUN_COMMAND_NOT_STARTED",
|
|
94
|
-
`\`${executable}\` could not be started: ${detail}`,
|
|
95
|
-
`Check the command after \`--\` runs on its own \u2014 \`${executable}\` has to be on PATH, exactly as it is spelled here.`
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
function resolveTarget(executable, args, env) {
|
|
99
|
-
if (process.platform !== "win32") {
|
|
100
|
-
return { file: executable, args, verbatim: false };
|
|
101
|
-
}
|
|
102
|
-
const resolved = findExecutable(executable, env);
|
|
103
|
-
if (resolved === void 0 || !/\.(cmd|bat)$/i.test(resolved)) {
|
|
104
|
-
return { file: resolved ?? executable, args, verbatim: false };
|
|
105
|
-
}
|
|
106
|
-
return {
|
|
107
|
-
file: env.ComSpec ?? "cmd.exe",
|
|
108
|
-
args: ["/d", "/s", "/c", `"${cmdCommandLine(resolved, args)}"`],
|
|
109
|
-
verbatim: true
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
var SHIM = /(?:^|\\)node_modules\\\.bin\\[^\\]+\.cmd$/i;
|
|
113
|
-
function cmdCommandLine(resolved, args) {
|
|
114
|
-
const command = win32.normalize(resolved);
|
|
115
|
-
const shim = SHIM.test(command);
|
|
116
|
-
return [escapeCommand(command), ...args.map((argument) => escapeArgument(argument, shim))].join(
|
|
117
|
-
" "
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
function extensions(env, platform) {
|
|
121
|
-
if (platform !== "win32") {
|
|
122
|
-
return [""];
|
|
123
|
-
}
|
|
124
|
-
const declared = env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
|
|
125
|
-
return [...declared.split(";").filter((extension) => extension.length > 0), ""];
|
|
126
|
-
}
|
|
127
|
-
function findExecutable(executable, env, platform = process.platform) {
|
|
128
|
-
const candidates = extensions(env, platform);
|
|
129
|
-
const isFile = (path2) => existsSync(path2) && statSync(path2).isFile();
|
|
130
|
-
if (executable.includes("/") || executable.includes("\\") || isAbsolute(executable)) {
|
|
131
|
-
return candidates.map((extension) => executable + extension).find(isFile);
|
|
132
|
-
}
|
|
133
|
-
const path = env.PATH ?? env.Path ?? "";
|
|
134
|
-
for (const directory of path.split(delimiter).filter((entry) => entry.length > 0)) {
|
|
135
|
-
const hit = candidates.map((extension) => join(directory, executable + extension)).find(isFile);
|
|
136
|
-
if (hit !== void 0) {
|
|
137
|
-
return hit;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return void 0;
|
|
141
|
-
}
|
|
142
|
-
var CMD_METACHARACTERS = /([()\][%!^"`<>&|;, *?])/g;
|
|
143
|
-
function escapeCommand(command) {
|
|
144
|
-
return command.replace(CMD_METACHARACTERS, "^$1");
|
|
145
|
-
}
|
|
146
|
-
function escapeArgument(argument, doubleEscape) {
|
|
147
|
-
const quoted = `"${argument.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\*)$/, "$1$1")}"`;
|
|
148
|
-
const escaped = quoted.replace(CMD_METACHARACTERS, "^$1");
|
|
149
|
-
return doubleEscape ? escaped.replace(CMD_METACHARACTERS, "^$1") : escaped;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// src/install.ts
|
|
153
|
-
var RUNTIME_PACKAGE = "@penvhq/penv";
|
|
154
|
-
var SCHEMA_PACKAGE = "zod";
|
|
155
|
-
var LOCKFILES = [
|
|
156
|
-
["pnpm", "pnpm-lock.yaml"],
|
|
157
|
-
["yarn", "yarn.lock"],
|
|
158
|
-
["bun", "bun.lock"],
|
|
159
|
-
["bun", "bun.lockb"],
|
|
160
|
-
["npm", "package-lock.json"]
|
|
161
|
-
];
|
|
162
|
-
var ADD = {
|
|
163
|
-
pnpm: ["pnpm", "add", "--save-exact"],
|
|
164
|
-
npm: ["npm", "install", "--save-exact"],
|
|
165
|
-
yarn: ["yarn", "add", "--exact"],
|
|
166
|
-
bun: ["bun", "add", "--exact"]
|
|
167
|
-
};
|
|
168
|
-
function engineVersion() {
|
|
169
|
-
const version = ownManifest()?.version;
|
|
170
|
-
if (typeof version === "string" && version.length > 0) {
|
|
171
|
-
return version;
|
|
172
|
-
}
|
|
173
|
-
throw new PenvError2(
|
|
174
|
-
"ENGINE_VERSION_UNREADABLE",
|
|
175
|
-
"penv could not read its own version, so it cannot say which `@penvhq/penv` this project needs",
|
|
176
|
-
`Reinstall penv, then run \`penv init\` again.`
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
function schemaPackageVersion() {
|
|
180
|
-
const peers = ownManifest()?.peerDependencies;
|
|
181
|
-
const declared = peers !== null && typeof peers === "object" && !Array.isArray(peers) ? peers[SCHEMA_PACKAGE] : void 0;
|
|
182
|
-
const floor = typeof declared === "string" ? declared.replace(/^[\^~>=\s]+/, "").trim() : "";
|
|
183
|
-
if (floor.length > 0) {
|
|
184
|
-
return floor;
|
|
185
|
-
}
|
|
186
|
-
throw new PenvError2(
|
|
187
|
-
"ENGINE_PEER_UNREADABLE",
|
|
188
|
-
`penv could not read its own \`${SCHEMA_PACKAGE}\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,
|
|
189
|
-
`Reinstall penv, then run \`penv init\` again.`
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
function ownManifest() {
|
|
193
|
-
try {
|
|
194
|
-
const parsed = JSON.parse(
|
|
195
|
-
readFileSync(new URL("../package.json", import.meta.url), "utf8")
|
|
196
|
-
);
|
|
197
|
-
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
198
|
-
} catch {
|
|
199
|
-
return void 0;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function detectPackageManager(root) {
|
|
203
|
-
for (const [manager, lockfile] of LOCKFILES) {
|
|
204
|
-
if (existsSync2(join2(root, lockfile))) {
|
|
205
|
-
return manager;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return declaredManager(root) ?? "npm";
|
|
209
|
-
}
|
|
210
|
-
function declaredManager(root) {
|
|
211
|
-
const declared = manifestOf(root)?.packageManager;
|
|
212
|
-
if (typeof declared !== "string") {
|
|
213
|
-
return void 0;
|
|
214
|
-
}
|
|
215
|
-
const name = declared.split("@")[0];
|
|
216
|
-
return name === "pnpm" || name === "npm" || name === "yarn" || name === "bun" ? name : void 0;
|
|
217
|
-
}
|
|
218
|
-
function manifestOf(root) {
|
|
219
|
-
const file = join2(root, "package.json");
|
|
220
|
-
if (!existsSync2(file)) {
|
|
221
|
-
return void 0;
|
|
222
|
-
}
|
|
223
|
-
try {
|
|
224
|
-
const manifest = JSON.parse(readFileSync(file, "utf8"));
|
|
225
|
-
return manifest !== null && typeof manifest === "object" && !Array.isArray(manifest) ? manifest : void 0;
|
|
226
|
-
} catch {
|
|
227
|
-
return void 0;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
function declaredVersion(root, name) {
|
|
231
|
-
const manifest = manifestOf(root);
|
|
232
|
-
for (const field of ["dependencies", "devDependencies"]) {
|
|
233
|
-
const block = manifest?.[field];
|
|
234
|
-
if (block !== null && typeof block === "object" && !Array.isArray(block)) {
|
|
235
|
-
const version = block[name];
|
|
236
|
-
if (typeof version === "string") {
|
|
237
|
-
return version;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return void 0;
|
|
242
|
-
}
|
|
243
|
-
function planInstall(root, version = engineVersion()) {
|
|
244
|
-
const manager = detectPackageManager(root);
|
|
245
|
-
const lockfile = LOCKFILES.find(
|
|
246
|
-
([name, file]) => name === manager && existsSync2(join2(root, file))
|
|
247
|
-
)?.[1];
|
|
248
|
-
const runtimeDeclared = declaredVersion(root, RUNTIME_PACKAGE);
|
|
249
|
-
const zodDeclared = declaredVersion(root, SCHEMA_PACKAGE);
|
|
250
|
-
const packages = [
|
|
251
|
-
{
|
|
252
|
-
name: RUNTIME_PACKAGE,
|
|
253
|
-
version,
|
|
254
|
-
...runtimeDeclared === void 0 ? {} : { declared: runtimeDeclared },
|
|
255
|
-
satisfied: runtimeDeclared === version
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
name: SCHEMA_PACKAGE,
|
|
259
|
-
version: schemaPackageVersion(),
|
|
260
|
-
...zodDeclared === void 0 ? {} : { declared: zodDeclared },
|
|
261
|
-
// Any declared zod counts: which zod a project uses is the project's
|
|
262
|
-
// decision, and penv is here to make sure there is one, not to move it.
|
|
263
|
-
satisfied: zodDeclared !== void 0
|
|
264
|
-
}
|
|
265
|
-
];
|
|
266
|
-
const pending = packages.filter((entry) => !entry.satisfied);
|
|
267
|
-
const specs = (pending.length === 0 ? packages : pending).map(
|
|
268
|
-
(entry) => `${entry.name}@${entry.version}`
|
|
269
|
-
);
|
|
270
|
-
return {
|
|
271
|
-
root,
|
|
272
|
-
manager,
|
|
273
|
-
packages,
|
|
274
|
-
command: [...ADD[manager], ...specs],
|
|
275
|
-
...lockfile === void 0 ? {} : { lockfile },
|
|
276
|
-
satisfied: pending.length === 0
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
function describe(entry) {
|
|
280
|
-
return `${entry.name} ${entry.version}`;
|
|
281
|
-
}
|
|
282
|
-
function renderInstallPlan(plan2) {
|
|
283
|
-
if (plan2.satisfied) {
|
|
284
|
-
return [
|
|
285
|
-
`package.json already has ${plan2.packages.map(describe).join(" and ")} \u2014 nothing to install.`
|
|
286
|
-
];
|
|
287
|
-
}
|
|
288
|
-
const pending = plan2.packages.filter((entry) => !entry.satisfied);
|
|
289
|
-
const added = pending.filter((entry) => entry.declared === void 0);
|
|
290
|
-
const replaced = pending.filter((entry) => entry.declared !== void 0);
|
|
291
|
-
return [
|
|
292
|
-
"package.json",
|
|
293
|
-
...added.length === 0 ? [] : [
|
|
294
|
-
' + "dependencies": {',
|
|
295
|
-
...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
|
|
296
|
-
" + }"
|
|
297
|
-
],
|
|
298
|
-
...replaced.flatMap((entry) => [
|
|
299
|
-
` - "${entry.name}": "${entry.declared}"`,
|
|
300
|
-
` + "${entry.name}": "${entry.version}"`
|
|
301
|
-
]),
|
|
302
|
-
...plan2.lockfile === void 0 ? [] : [plan2.lockfile, ...pending.map((entry) => ` + ${entry.name}@${entry.version}`)],
|
|
303
|
-
"",
|
|
304
|
-
`Run with: ${plan2.command.join(" ")}`
|
|
305
|
-
];
|
|
306
|
-
}
|
|
307
|
-
var installWithPackageManager = async (plan2) => {
|
|
308
|
-
const child = startChild({
|
|
309
|
-
command: plan2.command,
|
|
310
|
-
env: process.env,
|
|
311
|
-
cwd: plan2.root,
|
|
312
|
-
purpose: `install ${plan2.packages.map(describe).join(" and ")}`
|
|
313
|
-
});
|
|
314
|
-
const ended = await child.ended;
|
|
315
|
-
if (ended.exitCode !== 0 || ended.signal !== null) {
|
|
316
|
-
throw installFailed(plan2);
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
function installFailed(plan2) {
|
|
320
|
-
return new PenvError2(
|
|
321
|
-
"INIT_INSTALL_FAILED",
|
|
322
|
-
`${plan2.command.join(" ")} did not finish, so penv migrated nothing`,
|
|
323
|
-
`Run \`${plan2.command.join(" ")}\` yourself, then start this command again. Your dotenv files are exactly where they were.`
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
33
|
// src/project.ts
|
|
328
|
-
import { existsSync
|
|
329
|
-
import { dirname, join as
|
|
34
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
35
|
+
import { dirname, join as join2 } from "path";
|
|
330
36
|
import {
|
|
331
37
|
assertMigrated,
|
|
332
38
|
candidatesFor,
|
|
@@ -335,7 +41,7 @@ import {
|
|
|
335
41
|
isReservedToken,
|
|
336
42
|
loadConfig,
|
|
337
43
|
openValue,
|
|
338
|
-
PenvError as
|
|
44
|
+
PenvError as PenvError3,
|
|
339
45
|
parameterId,
|
|
340
46
|
ReservedTokenError,
|
|
341
47
|
recordsDir as recordsDir2,
|
|
@@ -348,7 +54,7 @@ import {
|
|
|
348
54
|
import { FilesystemProvider } from "@penvhq/provider-filesystem";
|
|
349
55
|
|
|
350
56
|
// src/env-flags.ts
|
|
351
|
-
import { PenvError
|
|
57
|
+
import { PenvError } from "@penvhq/core";
|
|
352
58
|
var BASE_RESERVED = ["help", "version", "h", "v", "_", "--"];
|
|
353
59
|
var COMMAND_FLAGS = [
|
|
354
60
|
...BASE_RESERVED,
|
|
@@ -381,14 +87,14 @@ function environmentFromShorthand(config, candidates, explicit) {
|
|
|
381
87
|
const hits = candidates.filter((candidate) => config.environments.includes(candidate));
|
|
382
88
|
const strangers = candidates.filter((candidate) => !config.environments.includes(candidate));
|
|
383
89
|
if (strangers.length > 0) {
|
|
384
|
-
throw new
|
|
90
|
+
throw new PenvError(
|
|
385
91
|
"UNKNOWN_FLAG",
|
|
386
92
|
`${quoteList(strangers)} ${strangers.length === 1 ? "is not a flag" : "are not flags"} this command takes, and ${strangers.length === 1 ? "names" : "name"} no declared environment`,
|
|
387
93
|
`Declared environments work as bare flags: ${quoteList(config.environments)}. Anything else needs \`--env <name>\`.`
|
|
388
94
|
);
|
|
389
95
|
}
|
|
390
96
|
if (hits.length > 1) {
|
|
391
|
-
throw new
|
|
97
|
+
throw new PenvError(
|
|
392
98
|
"ENVIRONMENT_FLAG_AMBIGUOUS",
|
|
393
99
|
`${quoteList(hits)} name two environments at once, and a command acts on exactly one`,
|
|
394
100
|
"Pass a single environment flag, or the canonical `--env <name>`."
|
|
@@ -396,7 +102,7 @@ function environmentFromShorthand(config, candidates, explicit) {
|
|
|
396
102
|
}
|
|
397
103
|
const hit = hits[0];
|
|
398
104
|
if (hit !== void 0 && explicit !== void 0 && explicit !== hit) {
|
|
399
|
-
throw new
|
|
105
|
+
throw new PenvError(
|
|
400
106
|
"ENVIRONMENT_FLAG_AMBIGUOUS",
|
|
401
107
|
`\`--env ${explicit}\` and \`--${hit}\` name two environments at once`,
|
|
402
108
|
"Drop one of them \u2014 `--env` is the canonical spelling."
|
|
@@ -406,17 +112,22 @@ function environmentFromShorthand(config, candidates, explicit) {
|
|
|
406
112
|
}
|
|
407
113
|
|
|
408
114
|
// src/registry.ts
|
|
409
|
-
import { readFileSync
|
|
115
|
+
import { readFileSync } from "fs";
|
|
410
116
|
import { createRequire } from "module";
|
|
411
|
-
import { resolve } from "path";
|
|
117
|
+
import { join, resolve } from "path";
|
|
412
118
|
import { pathToFileURL } from "url";
|
|
413
119
|
import {
|
|
414
120
|
holdsProjection,
|
|
415
121
|
LOCAL_EXTENSIONS_PATH,
|
|
416
122
|
localExtensionsFile,
|
|
123
|
+
MANIFEST_PATH,
|
|
417
124
|
PENV_DIR,
|
|
418
|
-
PenvError as
|
|
125
|
+
PenvError as PenvError2,
|
|
126
|
+
packageDir,
|
|
127
|
+
packageEntry,
|
|
419
128
|
parseLocalExtensions,
|
|
129
|
+
parseManifest,
|
|
130
|
+
penvHome,
|
|
420
131
|
recordsDir
|
|
421
132
|
} from "@penvhq/core";
|
|
422
133
|
import { createFilesystemProvider } from "@penvhq/provider-filesystem";
|
|
@@ -461,23 +172,16 @@ async function createSourceProvider(type, context) {
|
|
|
461
172
|
return loadPluginProvider(type, context);
|
|
462
173
|
}
|
|
463
174
|
async function loadPluginProvider(type, context) {
|
|
464
|
-
const
|
|
465
|
-
if (resolved === void 0) {
|
|
466
|
-
throw unknownProvider(type, context.environment);
|
|
467
|
-
}
|
|
175
|
+
const path = resolveExtension(type, context.root, context.environment);
|
|
468
176
|
let mod;
|
|
469
177
|
try {
|
|
470
|
-
mod = await importPlugin(
|
|
471
|
-
} catch {
|
|
472
|
-
throw
|
|
473
|
-
"PROVIDER_PLUGIN_LOAD",
|
|
474
|
-
`The provider package \`${type}\` failed to load`,
|
|
475
|
-
"It resolved but threw while importing. Check it builds and its dependencies are installed."
|
|
476
|
-
);
|
|
178
|
+
mod = await importPlugin(path);
|
|
179
|
+
} catch (cause) {
|
|
180
|
+
throw providerLoadFailed(type, path, cause);
|
|
477
181
|
}
|
|
478
182
|
const factory = mod[PLUGIN_FACTORY_EXPORT];
|
|
479
183
|
if (typeof factory !== "function") {
|
|
480
|
-
throw new
|
|
184
|
+
throw new PenvError2(
|
|
481
185
|
"PROVIDER_PLUGIN_INVALID",
|
|
482
186
|
`\`${type}\` does not export \`${PLUGIN_FACTORY_EXPORT}\``,
|
|
483
187
|
`A penv provider package must export \`${PLUGIN_FACTORY_EXPORT}(context) => Provider\`.`
|
|
@@ -497,15 +201,47 @@ function assertProvidersRegistered(config, projectRoot, options) {
|
|
|
497
201
|
if (ci && local.includes(provider.type)) {
|
|
498
202
|
throw localExtensionInCi(provider.type, environment);
|
|
499
203
|
}
|
|
500
|
-
|
|
501
|
-
|
|
204
|
+
resolveExtension(provider.type, projectRoot, environment, local);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function resolveExtension(type, projectRoot, environment, local = localExtensions(projectRoot)) {
|
|
208
|
+
const fromProject = resolvePlugin(type, projectRoot);
|
|
209
|
+
if (local.includes(type)) {
|
|
210
|
+
if (fromProject === void 0) {
|
|
211
|
+
throw localExtensionUnresolved(type, projectRoot, environment);
|
|
502
212
|
}
|
|
213
|
+
return fromProject;
|
|
214
|
+
}
|
|
215
|
+
if (fromProject !== void 0) {
|
|
216
|
+
return fromProject;
|
|
217
|
+
}
|
|
218
|
+
const version = pinnedVersion(type, projectRoot);
|
|
219
|
+
if (version === void 0) {
|
|
220
|
+
throw unknownProvider(type, environment);
|
|
221
|
+
}
|
|
222
|
+
const stored = storedExtension(type, version);
|
|
223
|
+
if (stored === void 0) {
|
|
224
|
+
throw extensionNotInstalled(type, version, environment);
|
|
225
|
+
}
|
|
226
|
+
return stored;
|
|
227
|
+
}
|
|
228
|
+
function pinnedVersion(type, projectRoot) {
|
|
229
|
+
let manifest;
|
|
230
|
+
try {
|
|
231
|
+
manifest = parseManifest(readFileSync(join(projectRoot, ...MANIFEST_PATH.split("/")), "utf8"));
|
|
232
|
+
} catch {
|
|
233
|
+
return void 0;
|
|
503
234
|
}
|
|
235
|
+
return manifest.extensions[type]?.version;
|
|
236
|
+
}
|
|
237
|
+
function storedExtension(type, version) {
|
|
238
|
+
const entry = packageEntry(packageDir(penvHome(process.env), "extensions", type, version));
|
|
239
|
+
return entry?.file;
|
|
504
240
|
}
|
|
505
241
|
function localExtensions(projectRoot) {
|
|
506
242
|
let text;
|
|
507
243
|
try {
|
|
508
|
-
text =
|
|
244
|
+
text = readFileSync(localExtensionsFile(projectRoot), "utf8");
|
|
509
245
|
} catch {
|
|
510
246
|
return [];
|
|
511
247
|
}
|
|
@@ -515,7 +251,7 @@ function isCi(value) {
|
|
|
515
251
|
return value !== void 0 && value !== "" && value !== "0" && value.toLowerCase() !== "false";
|
|
516
252
|
}
|
|
517
253
|
function localExtensionInCi(type, environment) {
|
|
518
|
-
return new
|
|
254
|
+
return new PenvError2(
|
|
519
255
|
"LOCAL_EXTENSION_IN_CI",
|
|
520
256
|
`The provider \`${type}\` for environment ${environment} is a local extension, and this is CI`,
|
|
521
257
|
`${LOCAL_EXTENSIONS_PATH} records it as a package this project develops, so nothing pins the bytes CI would run. Publish it and run \`penv add ${type}\` to pin a release.`
|
|
@@ -544,7 +280,7 @@ function assertSatisfiesContract(provider, specifier) {
|
|
|
544
280
|
const contract = projection ? "the @penvhq/core ProjectionProvider contract its declared capabilities select" : "the @penvhq/core Provider contract that the filesystem provider defines";
|
|
545
281
|
for (const method of methods) {
|
|
546
282
|
if (typeof provider[method] !== "function") {
|
|
547
|
-
throw new
|
|
283
|
+
throw new PenvError2(
|
|
548
284
|
"PROVIDER_PLUGIN_INVALID",
|
|
549
285
|
`The provider from \`${specifier}\` is missing \`${method}()\``,
|
|
550
286
|
`It must satisfy ${contract}.`
|
|
@@ -552,13 +288,37 @@ function assertSatisfiesContract(provider, specifier) {
|
|
|
552
288
|
}
|
|
553
289
|
}
|
|
554
290
|
}
|
|
291
|
+
function where(environment) {
|
|
292
|
+
return environment === void 0 ? "" : ` for environment ${environment}`;
|
|
293
|
+
}
|
|
555
294
|
function unknownProvider(type, environment) {
|
|
556
295
|
const preinstalled = [...REGISTRY.keys()].map((name) => `\`${name}\``).join(", ");
|
|
557
|
-
|
|
558
|
-
return new PenvError4(
|
|
296
|
+
return new PenvError2(
|
|
559
297
|
"UNKNOWN_PROVIDER",
|
|
560
|
-
`The provider \`${type}\`${where} in penv.config.ts is
|
|
561
|
-
`
|
|
298
|
+
`The provider \`${type}\`${where(environment)} in penv.config.ts is nowhere penv looks for it`,
|
|
299
|
+
`Run \`penv add ${type}\` to pin a release, or \`penv add --local ${type}\` if this repository is the one that builds it. penv looks in ${LOCAL_EXTENSIONS_PATH}, this project's node_modules, and then $PENV_HOME at the version ${MANIFEST_PATH} pins. The CLI ships ${preinstalled} pre-installed.`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
function extensionNotInstalled(type, version, environment) {
|
|
303
|
+
return new PenvError2(
|
|
304
|
+
"EXTENSION_NOT_INSTALLED",
|
|
305
|
+
`${MANIFEST_PATH} pins \`${type}\` ${version}${where(environment)}, and it is not installed in ${penvHome(process.env)}`,
|
|
306
|
+
"Run `penv install` \u2014 it downloads and verifies every version the manifest pins, extensions included."
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
function localExtensionUnresolved(type, projectRoot, environment) {
|
|
310
|
+
return new PenvError2(
|
|
311
|
+
"LOCAL_EXTENSION_UNRESOLVED",
|
|
312
|
+
`${LOCAL_EXTENSIONS_PATH} records \`${type}\`${where(environment)} as a package this project develops, and it does not resolve from ${projectRoot}`,
|
|
313
|
+
`Add it as a dependency of the root (a workspace link is one) and run \`pnpm install\`, or drop the name from ${LOCAL_EXTENSIONS_PATH} if this project no longer builds it.`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
function providerLoadFailed(type, path, cause) {
|
|
317
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
318
|
+
return new PenvError2(
|
|
319
|
+
"PROVIDER_PLUGIN_LOAD",
|
|
320
|
+
`The provider package \`${type}\` failed to load from ${path}: ${detail}`,
|
|
321
|
+
"penv imports that file exactly as it stands, with no transform, so it has to be built JavaScript with its dependencies installed."
|
|
562
322
|
);
|
|
563
323
|
}
|
|
564
324
|
|
|
@@ -578,7 +338,7 @@ function openProject(cwd) {
|
|
|
578
338
|
}
|
|
579
339
|
function localTree(project) {
|
|
580
340
|
if (!(project.provider instanceof FilesystemProvider)) {
|
|
581
|
-
throw new
|
|
341
|
+
throw new PenvError3(
|
|
582
342
|
"PROVIDER_NOT_LOCAL",
|
|
583
343
|
`This command reads the local .penv tree synchronously, which the \`${project.provider.type}\` provider is not`,
|
|
584
344
|
"Run this against a filesystem-backed project, or use a command that speaks the async provider contract."
|
|
@@ -587,11 +347,11 @@ function localTree(project) {
|
|
|
587
347
|
return project.provider;
|
|
588
348
|
}
|
|
589
349
|
function selfContainedSchemaModule(root, schemaFile) {
|
|
590
|
-
const file =
|
|
591
|
-
if (!
|
|
350
|
+
const file = join2(root, ...schemaFile.split("/"));
|
|
351
|
+
if (!existsSync(file)) {
|
|
592
352
|
return void 0;
|
|
593
353
|
}
|
|
594
|
-
const source =
|
|
354
|
+
const source = readFileSync2(file, "utf8");
|
|
595
355
|
return source.includes("PenvSchemaShape") || source.includes("z.object") ? schemaFile : void 0;
|
|
596
356
|
}
|
|
597
357
|
function schemaShapeFileOf(project) {
|
|
@@ -620,7 +380,7 @@ function refFromKey(key, config) {
|
|
|
620
380
|
const segments = key.split(KEY_SEPARATOR).filter((segment) => segment.length > 0);
|
|
621
381
|
const name = segments[segments.length - 1];
|
|
622
382
|
if (name === void 0) {
|
|
623
|
-
throw new
|
|
383
|
+
throw new PenvError3(
|
|
624
384
|
"PARAMETER_KEY",
|
|
625
385
|
`\`${key}\` names no parameter`,
|
|
626
386
|
"A key is `<namespace>/<name>` or `<namespace>.<name>`, e.g. `redis/password`."
|
|
@@ -639,13 +399,13 @@ function assertWritableKey(key) {
|
|
|
639
399
|
const ref = refFromAccessPath(segments);
|
|
640
400
|
if (ref !== void 0) {
|
|
641
401
|
const suggestion = [...ref.namespace, ref.name].join("/");
|
|
642
|
-
throw new
|
|
402
|
+
throw new PenvError3(
|
|
643
403
|
"PARAMETER_KEY_CASING",
|
|
644
404
|
`\`${key}\` is not a canonical parameter name`,
|
|
645
405
|
`Parameter files are lower-case and hyphenated. Did you mean \`${suggestion}\`? That is the file that backs the \`${key}\` key in your schema.`
|
|
646
406
|
);
|
|
647
407
|
}
|
|
648
|
-
throw new
|
|
408
|
+
throw new PenvError3(
|
|
649
409
|
"PARAMETER_KEY_UNREACHABLE",
|
|
650
410
|
`No value file can be named that reaches \`${key}\``,
|
|
651
411
|
"Parameter files are lower-case and hyphenated, and this key maps to no such file \u2014 a run of capitals like `apiURL` cannot be reached (use `api-url`, which the schema reads as `apiUrl`). Run `penv validate` or `penv fill` to see the names penv expects."
|
|
@@ -693,7 +453,7 @@ function refsFrom(files) {
|
|
|
693
453
|
}
|
|
694
454
|
|
|
695
455
|
// src/ui.ts
|
|
696
|
-
import { PenvError as
|
|
456
|
+
import { PenvError as PenvError4 } from "@penvhq/core";
|
|
697
457
|
|
|
698
458
|
// src/style.ts
|
|
699
459
|
function supportsColor(stream) {
|
|
@@ -826,7 +586,7 @@ function writeError(lines) {
|
|
|
826
586
|
}
|
|
827
587
|
}
|
|
828
588
|
function reportError(error) {
|
|
829
|
-
if (error instanceof
|
|
589
|
+
if (error instanceof PenvError4) {
|
|
830
590
|
process.stderr.write(`${err.red(CROSS)} ${error.summary}
|
|
831
591
|
`);
|
|
832
592
|
if (error.remedy !== void 0) {
|
|
@@ -857,9 +617,9 @@ async function guard(run) {
|
|
|
857
617
|
}
|
|
858
618
|
|
|
859
619
|
// src/commands/run.ts
|
|
860
|
-
import { existsSync as
|
|
620
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, watch } from "fs";
|
|
861
621
|
import { createRequire as createRequire2 } from "module";
|
|
862
|
-
import { basename, dirname as dirname2, join as
|
|
622
|
+
import { basename, dirname as dirname2, join as join3, resolve as resolve2 } from "path";
|
|
863
623
|
import {
|
|
864
624
|
ARTIFACT_BUILD_COMMAND,
|
|
865
625
|
assertArtifactFor,
|
|
@@ -869,7 +629,7 @@ import {
|
|
|
869
629
|
MissingMaterializationError,
|
|
870
630
|
openSealed,
|
|
871
631
|
own,
|
|
872
|
-
PenvError as
|
|
632
|
+
PenvError as PenvError6,
|
|
873
633
|
parseArtifact,
|
|
874
634
|
RECORDS_PATH,
|
|
875
635
|
UndecryptableValueError,
|
|
@@ -1118,7 +878,7 @@ import {
|
|
|
1118
878
|
checkNameCollisions,
|
|
1119
879
|
jitiFor,
|
|
1120
880
|
NameCollisionError,
|
|
1121
|
-
PenvError as
|
|
881
|
+
PenvError as PenvError5,
|
|
1122
882
|
ReservedTokenError as ReservedTokenError2,
|
|
1123
883
|
resolveAll,
|
|
1124
884
|
SCHEMA_HARVEST_ENV,
|
|
@@ -1467,7 +1227,7 @@ async function checkEnvironment(project, environment) {
|
|
|
1467
1227
|
try {
|
|
1468
1228
|
files = await project.provider.list();
|
|
1469
1229
|
} catch (error) {
|
|
1470
|
-
if (!(error instanceof
|
|
1230
|
+
if (!(error instanceof PenvError5)) {
|
|
1471
1231
|
throw error;
|
|
1472
1232
|
}
|
|
1473
1233
|
return {
|
|
@@ -1619,7 +1379,7 @@ function assertNotNested(host, inner) {
|
|
|
1619
1379
|
if (outer === void 0) {
|
|
1620
1380
|
return;
|
|
1621
1381
|
}
|
|
1622
|
-
throw new
|
|
1382
|
+
throw new PenvError6(
|
|
1623
1383
|
"RUN_NESTED",
|
|
1624
1384
|
`\`${inner}\` is starting inside \`${outer}\`, and two penv environments cannot own one process`,
|
|
1625
1385
|
`Drop one of the two wrappers \u2014 the inner one is in a package.json script \u2014 then run \`${outer}\` again.`
|
|
@@ -1629,7 +1389,7 @@ function assertSource(source, inner) {
|
|
|
1629
1389
|
if (source === "project" || source === "snapshot") {
|
|
1630
1390
|
return source;
|
|
1631
1391
|
}
|
|
1632
|
-
throw new
|
|
1392
|
+
throw new PenvError6(
|
|
1633
1393
|
"RUN_SOURCE_UNKNOWN",
|
|
1634
1394
|
`\`--source ${source}\` names no source penv reads`,
|
|
1635
1395
|
`A run reads ${SOURCES.map((name) => `\`${name}\``).join(" or ")}, and \`project\` is the default \u2014 so \`${inner}\` reads the local tree.`
|
|
@@ -1650,7 +1410,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
|
|
|
1650
1410
|
}
|
|
1651
1411
|
const parameter = [...ref.namespace, ref.name].join("/");
|
|
1652
1412
|
const prefix = prefixes.find((candidate) => variable.startsWith(candidate));
|
|
1653
|
-
throw new
|
|
1413
|
+
throw new PenvError6(
|
|
1654
1414
|
"RUN_PUBLIC_SECRET",
|
|
1655
1415
|
`The secret ${parameter} maps to ${variable}, which the \`${prefix}\` prefix publishes to the browser`,
|
|
1656
1416
|
`Rename the parameter, or drop \`secret\` from its meta if it is not one \u2014 then \`${retry}\`.`
|
|
@@ -1659,7 +1419,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
|
|
|
1659
1419
|
}
|
|
1660
1420
|
function invalidConfiguration(result2, inner) {
|
|
1661
1421
|
const lines = result2.issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
|
|
1662
|
-
return new
|
|
1422
|
+
return new PenvError6(
|
|
1663
1423
|
"RUN_INVALID_CONFIGURATION",
|
|
1664
1424
|
`Configuration for environment ${result2.environment} is not valid, so nothing was started:
|
|
1665
1425
|
${lines}`,
|
|
@@ -1681,7 +1441,7 @@ function assertNoActiveDotenv(project) {
|
|
|
1681
1441
|
if (first === void 0) {
|
|
1682
1442
|
return;
|
|
1683
1443
|
}
|
|
1684
|
-
throw new
|
|
1444
|
+
throw new PenvError6(
|
|
1685
1445
|
"RUN_DOTENV_ACTIVE",
|
|
1686
1446
|
`${first.name} is active configuration again, and your framework would read it beside penv's records`,
|
|
1687
1447
|
`Adopt it with \`penv init\`, or delete ${first.name} \u2014 its values belong in ${RECORDS_PATH}/.`
|
|
@@ -1699,10 +1459,10 @@ function packageManifest(specifier, root) {
|
|
|
1699
1459
|
return void 0;
|
|
1700
1460
|
}
|
|
1701
1461
|
for (; ; ) {
|
|
1702
|
-
const file =
|
|
1703
|
-
if (
|
|
1462
|
+
const file = join3(directory, "package.json");
|
|
1463
|
+
if (existsSync2(file)) {
|
|
1704
1464
|
try {
|
|
1705
|
-
const parsed = JSON.parse(
|
|
1465
|
+
const parsed = JSON.parse(readFileSync3(file, "utf8"));
|
|
1706
1466
|
if (isPlainObject(parsed) && parsed.name === specifier) {
|
|
1707
1467
|
return parsed;
|
|
1708
1468
|
}
|
|
@@ -1729,7 +1489,7 @@ function declaredCredentials(config, root) {
|
|
|
1729
1489
|
continue;
|
|
1730
1490
|
}
|
|
1731
1491
|
if (!Array.isArray(credentials) || credentials.some((name) => typeof name !== "string" || !VARIABLE.test(name))) {
|
|
1732
|
-
throw new
|
|
1492
|
+
throw new PenvError6(
|
|
1733
1493
|
"PROVIDER_CREDENTIALS_INVALID",
|
|
1734
1494
|
`\`${provider.type}\` declares \`penv.credentials\`, and it is not a list of variable names`,
|
|
1735
1495
|
`Its package.json should read \`"penv": { "credentials": ["ACME_TOKEN"] }\` \u2014 penv strips exactly what an extension declares, so it will not guess at a declaration it cannot read.`
|
|
@@ -1765,7 +1525,7 @@ async function prepare(project, environment, host, inner) {
|
|
|
1765
1525
|
function snapshotPath(host) {
|
|
1766
1526
|
const path = host[SNAPSHOT_VARIABLE]?.trim();
|
|
1767
1527
|
if (path === void 0 || path.length === 0) {
|
|
1768
|
-
throw new
|
|
1528
|
+
throw new PenvError6(
|
|
1769
1529
|
"RUN_SNAPSHOT_UNSET",
|
|
1770
1530
|
`\`--source snapshot\` reads the sealed artifact ${SNAPSHOT_VARIABLE} names, and ${SNAPSHOT_VARIABLE} is not set`,
|
|
1771
1531
|
`Build one with \`${ARTIFACT_BUILD_COMMAND}\` and point ${SNAPSHOT_VARIABLE} at it.`
|
|
@@ -1775,9 +1535,9 @@ function snapshotPath(host) {
|
|
|
1775
1535
|
}
|
|
1776
1536
|
function readSnapshot(path) {
|
|
1777
1537
|
try {
|
|
1778
|
-
return
|
|
1538
|
+
return readFileSync3(path, "utf8");
|
|
1779
1539
|
} catch {
|
|
1780
|
-
throw new
|
|
1540
|
+
throw new PenvError6(
|
|
1781
1541
|
"RUN_SNAPSHOT_MISSING",
|
|
1782
1542
|
`${SNAPSHOT_VARIABLE} names ${path}, and penv cannot read a sealed artifact there`,
|
|
1783
1543
|
`Point ${SNAPSHOT_VARIABLE} at the artifact your release mounted \u2014 \`${ARTIFACT_BUILD_COMMAND}\` writes one.`
|
|
@@ -1854,7 +1614,7 @@ function watchProject(project, onChange) {
|
|
|
1854
1614
|
timer = setTimeout(onChange, DEBOUNCE_MS);
|
|
1855
1615
|
};
|
|
1856
1616
|
const add = (target, recursive, only) => {
|
|
1857
|
-
if (!
|
|
1617
|
+
if (!existsSync2(target)) {
|
|
1858
1618
|
return;
|
|
1859
1619
|
}
|
|
1860
1620
|
try {
|
|
@@ -1893,7 +1653,7 @@ async function runRun(options) {
|
|
|
1893
1653
|
}
|
|
1894
1654
|
if (source === "snapshot") {
|
|
1895
1655
|
if (options.watch === true) {
|
|
1896
|
-
throw new
|
|
1656
|
+
throw new PenvError6(
|
|
1897
1657
|
"RUN_SNAPSHOT_WATCH",
|
|
1898
1658
|
"`--watch` re-syncs the project tree, and a run from a sealed artifact has no tree to watch",
|
|
1899
1659
|
"Drop `--watch` \u2014 an artifact is built once and read unchanged. Watch the project instead: `penv run --watch -- <command>`."
|
|
@@ -2037,7 +1797,7 @@ function buildCommand(environment, out2) {
|
|
|
2037
1797
|
function targetOf(options) {
|
|
2038
1798
|
const environment = options.environment?.trim();
|
|
2039
1799
|
if (environment === void 0 || environment.length === 0) {
|
|
2040
|
-
throw new
|
|
1800
|
+
throw new PenvError7(
|
|
2041
1801
|
"ARTIFACT_ENV_REQUIRED",
|
|
2042
1802
|
"`penv artifact build` names the environment it builds for, and `--env` was not given",
|
|
2043
1803
|
`Name it: \`${buildCommand(void 0, options.out)}\`. An artifact carries one environment, and penv will not pick which.`
|
|
@@ -2048,13 +1808,13 @@ function targetOf(options) {
|
|
|
2048
1808
|
function outputOf(options, environment) {
|
|
2049
1809
|
const out2 = options.out?.trim();
|
|
2050
1810
|
if (out2 === void 0 || out2.length === 0) {
|
|
2051
|
-
throw new
|
|
1811
|
+
throw new PenvError7(
|
|
2052
1812
|
"ARTIFACT_OUT_REQUIRED",
|
|
2053
1813
|
"`penv artifact build` writes where it is told, and `--out` was not given",
|
|
2054
1814
|
`Name the path: \`${buildCommand(environment, void 0)}\`. The artifact belongs outside the repository, so there is no default worth having.`
|
|
2055
1815
|
);
|
|
2056
1816
|
}
|
|
2057
|
-
return
|
|
1817
|
+
return isAbsolute(out2) ? out2 : resolve3(options.cwd, out2);
|
|
2058
1818
|
}
|
|
2059
1819
|
async function winnerOf(project, ref, environment) {
|
|
2060
1820
|
for (const file of candidatesFor2(ref, environment, true)) {
|
|
@@ -2066,7 +1826,7 @@ async function winnerOf(project, ref, environment) {
|
|
|
2066
1826
|
return void 0;
|
|
2067
1827
|
}
|
|
2068
1828
|
function plaintextSecret(parameter, location, environment) {
|
|
2069
|
-
return new
|
|
1829
|
+
return new PenvError7(
|
|
2070
1830
|
"ARTIFACT_PLAINTEXT_SECRET",
|
|
2071
1831
|
`${parameter} is a secret for environment ${environment}, and its value comes from ${location}, which is not sealed`,
|
|
2072
1832
|
`Seal it with \`penv encrypt ${parameter} --env ${environment}\` \u2014 an artifact carries ciphertext or nothing.`
|
|
@@ -2080,7 +1840,7 @@ async function runArtifactBuild(options) {
|
|
|
2080
1840
|
const { schema, issues } = await loadSchema(project, environment);
|
|
2081
1841
|
if (schema === void 0) {
|
|
2082
1842
|
const lines = issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
|
|
2083
|
-
throw new
|
|
1843
|
+
throw new PenvError7(
|
|
2084
1844
|
"ARTIFACT_NO_SCHEMA",
|
|
2085
1845
|
`The schema did not load, so penv cannot tell what this artifact should deliver:
|
|
2086
1846
|
${lines}`,
|
|
@@ -2132,7 +1892,7 @@ ${lines}`,
|
|
|
2132
1892
|
values: present,
|
|
2133
1893
|
sealed,
|
|
2134
1894
|
absent: refs.length - present,
|
|
2135
|
-
insideRepo: inside !== "" && !inside.startsWith("..") && !
|
|
1895
|
+
insideRepo: inside !== "" && !inside.startsWith("..") && !isAbsolute(inside)
|
|
2136
1896
|
};
|
|
2137
1897
|
}
|
|
2138
1898
|
function displayPath(cwd, file) {
|
|
@@ -2193,25 +1953,25 @@ import { defineCommand as defineCommand5 } from "citty";
|
|
|
2193
1953
|
|
|
2194
1954
|
// src/cutover.ts
|
|
2195
1955
|
import {
|
|
2196
|
-
existsSync as
|
|
1956
|
+
existsSync as existsSync3,
|
|
2197
1957
|
mkdirSync as mkdirSync2,
|
|
2198
1958
|
readdirSync as readdirSync2,
|
|
2199
|
-
readFileSync as
|
|
1959
|
+
readFileSync as readFileSync4,
|
|
2200
1960
|
renameSync,
|
|
2201
1961
|
rmSync,
|
|
2202
1962
|
writeFileSync as writeFileSync2
|
|
2203
1963
|
} from "fs";
|
|
2204
|
-
import { dirname as dirname4, join as
|
|
1964
|
+
import { dirname as dirname4, join as join4, resolve as resolve4 } from "path";
|
|
2205
1965
|
import {
|
|
2206
1966
|
CUTOVER_PATH,
|
|
2207
1967
|
findConfigFile,
|
|
2208
|
-
PenvError as
|
|
1968
|
+
PenvError as PenvError8,
|
|
2209
1969
|
ROLLBACK_DOTENV_PATH,
|
|
2210
1970
|
ROLLBACK_PATH
|
|
2211
1971
|
} from "@penvhq/core";
|
|
2212
1972
|
var CUTOVER_FORMAT = 1;
|
|
2213
1973
|
function fileFor(root, relativePosix) {
|
|
2214
|
-
return
|
|
1974
|
+
return join4(root, ...relativePosix.split("/"));
|
|
2215
1975
|
}
|
|
2216
1976
|
function bundleDir(root) {
|
|
2217
1977
|
return fileFor(root, ROLLBACK_DOTENV_PATH);
|
|
@@ -2235,12 +1995,12 @@ function bundleUnresolved(root) {
|
|
|
2235
1995
|
}
|
|
2236
1996
|
function readCutover(root) {
|
|
2237
1997
|
const file = cutoverFile(root);
|
|
2238
|
-
if (!
|
|
1998
|
+
if (!existsSync3(file)) {
|
|
2239
1999
|
return void 0;
|
|
2240
2000
|
}
|
|
2241
2001
|
let parsed;
|
|
2242
2002
|
try {
|
|
2243
|
-
parsed = JSON.parse(
|
|
2003
|
+
parsed = JSON.parse(readFileSync4(file, "utf8"));
|
|
2244
2004
|
} catch {
|
|
2245
2005
|
throw unreadable("it is not JSON");
|
|
2246
2006
|
}
|
|
@@ -2266,7 +2026,7 @@ function readCutover(root) {
|
|
|
2266
2026
|
};
|
|
2267
2027
|
}
|
|
2268
2028
|
function unreadable(what) {
|
|
2269
|
-
return new
|
|
2029
|
+
return new PenvError8(
|
|
2270
2030
|
"CUTOVER_UNREADABLE",
|
|
2271
2031
|
`${CUTOVER_PATH} records the last dotenv cutover, and ${what}`,
|
|
2272
2032
|
`Run \`penv cleanup\` to drop that record and the rollback bundle it names.`
|
|
@@ -2298,7 +2058,7 @@ function bundleDotenvFiles(root, files, environments, now = /* @__PURE__ */ new
|
|
|
2298
2058
|
const bundle = bundleDir(root);
|
|
2299
2059
|
mkdirSync2(bundle, { recursive: true });
|
|
2300
2060
|
for (const name of files) {
|
|
2301
|
-
renameSync(
|
|
2061
|
+
renameSync(join4(root, name), join4(bundle, name));
|
|
2302
2062
|
}
|
|
2303
2063
|
return cutover;
|
|
2304
2064
|
}
|
|
@@ -2307,7 +2067,7 @@ function runUndo(options) {
|
|
|
2307
2067
|
const cutover = readCutover(root);
|
|
2308
2068
|
const bundled = bundledFiles(root);
|
|
2309
2069
|
if (cutover === void 0 && bundled.length === 0) {
|
|
2310
|
-
throw new
|
|
2070
|
+
throw new PenvError8(
|
|
2311
2071
|
"INIT_UNDO_NOTHING",
|
|
2312
2072
|
"There is no dotenv cutover to undo in this project",
|
|
2313
2073
|
"Run `penv init` to adopt your dotenv files; undo puts them back afterwards."
|
|
@@ -2317,11 +2077,11 @@ function runUndo(options) {
|
|
|
2317
2077
|
const names = [...recorded, ...bundled.filter((name) => !recorded.includes(name))];
|
|
2318
2078
|
const bundle = bundleDir(root);
|
|
2319
2079
|
const held = new Set(bundled);
|
|
2320
|
-
const occupied2 = names.filter((name) => held.has(name) &&
|
|
2080
|
+
const occupied2 = names.filter((name) => held.has(name) && existsSync3(join4(root, name)));
|
|
2321
2081
|
if (occupied2.length > 0) {
|
|
2322
2082
|
const listed = occupied2.join(", ");
|
|
2323
2083
|
const many = occupied2.length > 1;
|
|
2324
|
-
throw new
|
|
2084
|
+
throw new PenvError8(
|
|
2325
2085
|
"INIT_UNDO_OCCUPIED",
|
|
2326
2086
|
`${listed} ${many ? "exist" : "exists"} again, and restoring what penv moved aside would write over ${many ? "them" : "it"}`,
|
|
2327
2087
|
`Move ${listed} out of the way, or run \`penv cleanup\` to keep ${many ? "them" : "it"} and drop the bundle. Nothing was restored.`
|
|
@@ -2332,9 +2092,9 @@ function runUndo(options) {
|
|
|
2332
2092
|
const missing = [];
|
|
2333
2093
|
for (const name of names) {
|
|
2334
2094
|
if (held.has(name)) {
|
|
2335
|
-
renameSync(
|
|
2095
|
+
renameSync(join4(bundle, name), join4(root, name));
|
|
2336
2096
|
restored.push(name);
|
|
2337
|
-
} else if (
|
|
2097
|
+
} else if (existsSync3(join4(root, name))) {
|
|
2338
2098
|
alreadyBack.push(name);
|
|
2339
2099
|
} else {
|
|
2340
2100
|
missing.push(name);
|
|
@@ -2346,7 +2106,7 @@ function runUndo(options) {
|
|
|
2346
2106
|
function runCleanup(options) {
|
|
2347
2107
|
const root = cutoverRoot(options.cwd);
|
|
2348
2108
|
const held = bundledFiles(root);
|
|
2349
|
-
const cleaned = held.length > 0 ||
|
|
2109
|
+
const cleaned = held.length > 0 || existsSync3(cutoverFile(root)) || existsSync3(fileFor(root, ROLLBACK_PATH));
|
|
2350
2110
|
removeBundle(root);
|
|
2351
2111
|
return { root, removed: held, cleaned };
|
|
2352
2112
|
}
|
|
@@ -2387,8 +2147,8 @@ var cleanupCommand = defineCommand5({
|
|
|
2387
2147
|
});
|
|
2388
2148
|
|
|
2389
2149
|
// src/commands/doctor.ts
|
|
2390
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
2391
|
-
import { join as
|
|
2150
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync } from "fs";
|
|
2151
|
+
import { join as join5, relative as relative2 } from "path";
|
|
2392
2152
|
import {
|
|
2393
2153
|
ARTIFACT_BUILD_COMMAND as ARTIFACT_BUILD_COMMAND2,
|
|
2394
2154
|
accessPath as accessPath3,
|
|
@@ -2414,7 +2174,7 @@ import { defineCommand as defineCommand7 } from "citty";
|
|
|
2414
2174
|
import {
|
|
2415
2175
|
checkNameCollisions as checkNameCollisions2,
|
|
2416
2176
|
holdsProjection as holdsProjection3,
|
|
2417
|
-
PenvError as
|
|
2177
|
+
PenvError as PenvError9,
|
|
2418
2178
|
recordPath,
|
|
2419
2179
|
requireValue,
|
|
2420
2180
|
variableName as variableName4
|
|
@@ -2482,7 +2242,7 @@ async function destinationFor(project, environment, options) {
|
|
|
2482
2242
|
const declared = project.config.providers[environment];
|
|
2483
2243
|
const location = declared?.location;
|
|
2484
2244
|
if (declared === void 0 || declared.type === LOCAL_TREE_TYPE) {
|
|
2485
|
-
throw new
|
|
2245
|
+
throw new PenvError9(
|
|
2486
2246
|
"NO_DESTINATION",
|
|
2487
2247
|
`Environment ${environment}'s provider is the local records tree itself, so penv has nowhere to push`,
|
|
2488
2248
|
`Declare a provider for it in penv.config.ts \u2014 e.g. \`${environment}: { type: "@penvhq/provider-github", location: "owner/repo" }\` \u2014 or push somewhere once with \`penv push --env ${environment} --destination <package> --location <place>\`.`
|
|
@@ -2509,7 +2269,7 @@ function plan(resolutions, config, environment, allowDecrypt) {
|
|
|
2509
2269
|
let encrypted = false;
|
|
2510
2270
|
if (winner.file.encrypted) {
|
|
2511
2271
|
if (!allowDecrypt) {
|
|
2512
|
-
throw new
|
|
2272
|
+
throw new PenvError9(
|
|
2513
2273
|
"ENCRYPTED_VALUE_REFUSED",
|
|
2514
2274
|
`Parameter ${resolution.parameter} for environment ${environment} resolves to the encrypted value file ${recordPath(winner.location)}, and a push sends plaintext for the destination to re-seal`,
|
|
2515
2275
|
"Re-run with `--allow-decrypt` to decrypt it locally and push it, or push an environment whose values are plaintext. penv's encryption stops at the projection; the destination seals it under its own key."
|
|
@@ -2562,7 +2322,7 @@ async function ensureTargetApproved(provider, environment, options) {
|
|
|
2562
2322
|
return false;
|
|
2563
2323
|
}
|
|
2564
2324
|
if (provider.ensureTarget === void 0) {
|
|
2565
|
-
throw new
|
|
2325
|
+
throw new PenvError9(
|
|
2566
2326
|
"MISSING_TARGET",
|
|
2567
2327
|
`The destination has no environment \`${environment}\` to receive this push, and this provider cannot create one`,
|
|
2568
2328
|
`Create the environment on the destination side, then run \`penv push --env ${environment}\` again.`
|
|
@@ -2572,7 +2332,7 @@ async function ensureTargetApproved(provider, environment, options) {
|
|
|
2572
2332
|
`The destination has no environment \`${environment}\`. Create it?`
|
|
2573
2333
|
);
|
|
2574
2334
|
if (!approved) {
|
|
2575
|
-
throw new
|
|
2335
|
+
throw new PenvError9(
|
|
2576
2336
|
"MISSING_TARGET",
|
|
2577
2337
|
`The destination has no environment \`${environment}\` to receive this push`,
|
|
2578
2338
|
`Re-run with \`--yes\` to create it, answer \`y\` at the prompt, or create it on the destination side yourself.`
|
|
@@ -3342,7 +3102,7 @@ function scannableFiles(directory, out2) {
|
|
|
3342
3102
|
return;
|
|
3343
3103
|
}
|
|
3344
3104
|
for (const entry of entries) {
|
|
3345
|
-
const path =
|
|
3105
|
+
const path = join5(directory, entry.name);
|
|
3346
3106
|
if (entry.isDirectory()) {
|
|
3347
3107
|
if (!UNSCANNED_DIRS.has(entry.name)) {
|
|
3348
3108
|
scannableFiles(path, out2);
|
|
@@ -3384,10 +3144,10 @@ function artifactFindings(project) {
|
|
|
3384
3144
|
for (const file of files) {
|
|
3385
3145
|
let text;
|
|
3386
3146
|
try {
|
|
3387
|
-
if (
|
|
3147
|
+
if (statSync(file).size > ARTIFACT_SCAN_LIMIT) {
|
|
3388
3148
|
continue;
|
|
3389
3149
|
}
|
|
3390
|
-
text =
|
|
3150
|
+
text = readFileSync5(file, "utf8");
|
|
3391
3151
|
} catch {
|
|
3392
3152
|
continue;
|
|
3393
3153
|
}
|
|
@@ -3736,7 +3496,7 @@ import {
|
|
|
3736
3496
|
formatValueFile as formatValueFile5,
|
|
3737
3497
|
isSecret as isSecret5,
|
|
3738
3498
|
openValue as openValue3,
|
|
3739
|
-
PenvError as
|
|
3499
|
+
PenvError as PenvError11,
|
|
3740
3500
|
parameterId as parameterId5,
|
|
3741
3501
|
recordPath as recordPath3,
|
|
3742
3502
|
sealValue as sealValue2
|
|
@@ -3747,7 +3507,7 @@ import { defineCommand as defineCommand9 } from "citty";
|
|
|
3747
3507
|
import {
|
|
3748
3508
|
formatValueFile as formatValueFile4,
|
|
3749
3509
|
isSecret as isSecret4,
|
|
3750
|
-
PenvError as
|
|
3510
|
+
PenvError as PenvError10,
|
|
3751
3511
|
parameterId as parameterId4,
|
|
3752
3512
|
recordPath as recordPath2,
|
|
3753
3513
|
sealValue
|
|
@@ -3771,7 +3531,7 @@ function targetScope(project, options, key) {
|
|
|
3771
3531
|
return scopeFrom(options);
|
|
3772
3532
|
}
|
|
3773
3533
|
if (environment.trim().length === 0) {
|
|
3774
|
-
throw new
|
|
3534
|
+
throw new PenvError10(
|
|
3775
3535
|
"ENVIRONMENT_FLAG_EMPTY",
|
|
3776
3536
|
`\`--env\` for parameter ${key} names no environment`,
|
|
3777
3537
|
`Pass a declared environment \u2014 ${project.config.environments.map((e) => `\`${e}\``).join(", ")} \u2014 e.g. \`--env production\`, or drop \`--env\` to write the scope that has no environment.`
|
|
@@ -3784,7 +3544,7 @@ function policyEnvironment(project, options) {
|
|
|
3784
3544
|
}
|
|
3785
3545
|
function sealFor(project, file, value, parameter, environment) {
|
|
3786
3546
|
if (environment === void 0) {
|
|
3787
|
-
throw new
|
|
3547
|
+
throw new PenvError10(
|
|
3788
3548
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
3789
3549
|
`Parameter ${parameter} is a secret, and ${recordPath2(formatValueFile4(file))} names no environment`,
|
|
3790
3550
|
"Keys are declared per environment in the `keys` block of penv.config.ts, so penv cannot tell which key should seal a file that every environment reads. Write it at an environment scope \u2014 add `--env <environment>` \u2014 or drop `secret` from the parameter's meta."
|
|
@@ -3886,7 +3646,7 @@ function twins(project, key, options) {
|
|
|
3886
3646
|
}
|
|
3887
3647
|
function environmentFor(project, options, verb) {
|
|
3888
3648
|
if (options.environment === void 0) {
|
|
3889
|
-
throw new
|
|
3649
|
+
throw new PenvError11(
|
|
3890
3650
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
3891
3651
|
`\`penv ${verb}\` names no environment, and keys are declared per environment`,
|
|
3892
3652
|
"Pass `--env <environment>`. penv cannot tell which environment's key applies to a file that names none, and will not pick one for you."
|
|
@@ -3905,7 +3665,7 @@ async function runEncrypt(options) {
|
|
|
3905
3665
|
const value = await readOne(project, plain);
|
|
3906
3666
|
if (value === void 0) {
|
|
3907
3667
|
const already = await readOne(project, sealed);
|
|
3908
|
-
throw new
|
|
3668
|
+
throw new PenvError11(
|
|
3909
3669
|
"PARAMETER_ABSENT",
|
|
3910
3670
|
already === void 0 ? `Parameter ${parameter} has no value file at ${recordPath3(formatValueFile5(plain))}` : `Parameter ${parameter} is already encrypted at ${recordPath3(formatValueFile5(sealed))}`,
|
|
3911
3671
|
already === void 0 ? `Write it first with \`penv set ${options.key} --env ${environment}\`, which seals it automatically when the parameter's meta declares it a secret.` : "Nothing to do."
|
|
@@ -3926,7 +3686,7 @@ async function runDecrypt(options) {
|
|
|
3926
3686
|
const [plain, sealed] = twins(project, options.key, options);
|
|
3927
3687
|
const parameter = parameterId5(plain);
|
|
3928
3688
|
if (isSecret5(await project.provider.readMeta(plain), environment)) {
|
|
3929
|
-
throw new
|
|
3689
|
+
throw new PenvError11(
|
|
3930
3690
|
"SECRET_DECRYPT_REFUSED",
|
|
3931
3691
|
`Parameter ${parameter} is declared a secret for environment ${environment}, so penv will not write it in plaintext`,
|
|
3932
3692
|
"A secret with a plaintext value file is a `penv doctor` failure. Drop `secret` from the parameter's meta if it is not one, or run `penv generate --allow-decrypt` if you need the plaintext value in a `.env` artifact."
|
|
@@ -3934,7 +3694,7 @@ async function runDecrypt(options) {
|
|
|
3934
3694
|
}
|
|
3935
3695
|
const stored = await readOne(project, sealed);
|
|
3936
3696
|
if (stored === void 0) {
|
|
3937
|
-
throw new
|
|
3697
|
+
throw new PenvError11(
|
|
3938
3698
|
"PARAMETER_ABSENT",
|
|
3939
3699
|
`Parameter ${parameter} has no encrypted value file at ${recordPath3(formatValueFile5(sealed))}`,
|
|
3940
3700
|
`Nothing to decrypt. \`penv get ${options.key} --env ${environment} --explain\` shows every file penv looked at.`
|
|
@@ -3957,7 +3717,7 @@ async function runDecrypt(options) {
|
|
|
3957
3717
|
removed: formatValueFile5(sealed)
|
|
3958
3718
|
};
|
|
3959
3719
|
}
|
|
3960
|
-
var UndecryptableAt = class extends
|
|
3720
|
+
var UndecryptableAt = class extends PenvError11 {
|
|
3961
3721
|
constructor(parameter, environment, location, detail) {
|
|
3962
3722
|
super(
|
|
3963
3723
|
"VALUE_UNDECRYPTABLE",
|
|
@@ -4012,7 +3772,7 @@ var decryptCommand = defineCommand9({
|
|
|
4012
3772
|
});
|
|
4013
3773
|
|
|
4014
3774
|
// src/commands/fill.ts
|
|
4015
|
-
import { PenvError as
|
|
3775
|
+
import { PenvError as PenvError12, recordPath as recordPath4, SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE2 } from "@penvhq/core";
|
|
4016
3776
|
import { defineCommand as defineCommand10 } from "citty";
|
|
4017
3777
|
var BLOCKING = /* @__PURE__ */ new Set(["config", "collision", "reserved"]);
|
|
4018
3778
|
async function runFill(options) {
|
|
@@ -4027,7 +3787,7 @@ async function runFill(options) {
|
|
|
4027
3787
|
const detail = blockers.map(
|
|
4028
3788
|
(issue) => ` - ${issue.message}${issue.remedy === void 0 ? "" : ` (${issue.remedy})`}`
|
|
4029
3789
|
).join("\n");
|
|
4030
|
-
throw new
|
|
3790
|
+
throw new PenvError12(
|
|
4031
3791
|
"FILL_BLOCKED",
|
|
4032
3792
|
`penv fill cannot run: environment ${environment} has ${blockers.length} unresolved configuration ${blockers.length === 1 ? "issue" : "issues"}:
|
|
4033
3793
|
${detail}`,
|
|
@@ -4163,11 +3923,11 @@ var fillCommand = defineCommand10({
|
|
|
4163
3923
|
|
|
4164
3924
|
// src/commands/generate.ts
|
|
4165
3925
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
4166
|
-
import { isAbsolute as
|
|
3926
|
+
import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve5 } from "path";
|
|
4167
3927
|
import {
|
|
4168
3928
|
checkNameCollisions as checkNameCollisions3,
|
|
4169
3929
|
effectiveMeta as effectiveMeta2,
|
|
4170
|
-
PenvError as
|
|
3930
|
+
PenvError as PenvError13,
|
|
4171
3931
|
recordPath as recordPath5,
|
|
4172
3932
|
requireValue as requireValue2,
|
|
4173
3933
|
serializeDotenv,
|
|
@@ -4192,7 +3952,7 @@ function entriesFor(project, environment, allowDecrypt) {
|
|
|
4192
3952
|
const winner = resolution.winner;
|
|
4193
3953
|
if (winner?.file.encrypted === true) {
|
|
4194
3954
|
if (!allowDecrypt) {
|
|
4195
|
-
throw new
|
|
3955
|
+
throw new PenvError13(
|
|
4196
3956
|
"ENCRYPTED_VALUE_REFUSED",
|
|
4197
3957
|
`Parameter ${resolution.parameter} for environment ${environment} resolves to the encrypted value file ${recordPath5(winner.location)}, and \`penv generate\` writes plaintext`,
|
|
4198
3958
|
`Re-run with \`--allow-decrypt\` to write the decrypted value into the artifact, or generate for an environment whose values are plaintext. The artifact is gitignored; a committed plaintext secret is a \`penv doctor\` failure.`
|
|
@@ -4223,7 +3983,7 @@ function runGenerate(options) {
|
|
|
4223
3983
|
const project = openProject(options.cwd);
|
|
4224
3984
|
const environment = targetEnvironment(project, options.environment, options.envFlags);
|
|
4225
3985
|
const { entries, decrypted } = entriesFor(project, environment, options.allowDecrypt === true);
|
|
4226
|
-
const file = options.out === void 0 ? resolve5(project.root, DEFAULT_OUTPUT) :
|
|
3986
|
+
const file = options.out === void 0 ? resolve5(project.root, DEFAULT_OUTPUT) : isAbsolute2(options.out) ? options.out : resolve5(options.cwd, options.out);
|
|
4227
3987
|
writeFileSync3(file, serializeDotenv(entries), "utf8");
|
|
4228
3988
|
return { file, environment, entries: entries.length, decrypted };
|
|
4229
3989
|
}
|
|
@@ -4276,7 +4036,7 @@ var generateCommand = defineCommand11({
|
|
|
4276
4036
|
});
|
|
4277
4037
|
|
|
4278
4038
|
// src/commands/get.ts
|
|
4279
|
-
import { PenvError as
|
|
4039
|
+
import { PenvError as PenvError14, recordPath as recordPath6, requireValue as requireValue3, resolveParameter } from "@penvhq/core";
|
|
4280
4040
|
import { defineCommand as defineCommand12 } from "citty";
|
|
4281
4041
|
async function runGet(options) {
|
|
4282
4042
|
const project = openProject(options.cwd);
|
|
@@ -4286,7 +4046,7 @@ async function runGet(options) {
|
|
|
4286
4046
|
const resolution = await resolveParameter(ref, environment, project.provider, keys);
|
|
4287
4047
|
const value = requireValue3(resolution, environment);
|
|
4288
4048
|
if (value === void 0) {
|
|
4289
|
-
throw new
|
|
4049
|
+
throw new PenvError14(
|
|
4290
4050
|
"PARAMETER_ABSENT",
|
|
4291
4051
|
`Parameter ${resolution.parameter} resolves to no value for environment ${environment}`,
|
|
4292
4052
|
`Set it with \`penv set ${options.key} --env ${environment}\`, or run \`penv get ${options.key} --env ${environment} --explain\` to see every file penv looked at.`
|
|
@@ -4362,15 +4122,15 @@ var getCommand = defineCommand12({
|
|
|
4362
4122
|
});
|
|
4363
4123
|
|
|
4364
4124
|
// src/commands/import.ts
|
|
4365
|
-
import { copyFileSync, existsSync as
|
|
4366
|
-
import { basename as basename2, isAbsolute as
|
|
4125
|
+
import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
|
|
4126
|
+
import { basename as basename2, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve7 } from "path";
|
|
4367
4127
|
import {
|
|
4368
4128
|
assertNever as assertNever2,
|
|
4369
4129
|
FilenameGrammarError,
|
|
4370
4130
|
findConfigFile as findConfigFile2,
|
|
4371
4131
|
loadConfigFrom as loadConfigFrom2,
|
|
4372
4132
|
lookupEnvironment,
|
|
4373
|
-
PenvError as
|
|
4133
|
+
PenvError as PenvError17,
|
|
4374
4134
|
parseDotenv as parseDotenv2,
|
|
4375
4135
|
schemaFileOf as schemaFileOf4,
|
|
4376
4136
|
UnknownEnvironmentError
|
|
@@ -4381,7 +4141,7 @@ import { defineCommand as defineCommand14 } from "citty";
|
|
|
4381
4141
|
import {
|
|
4382
4142
|
checkNameCollisions as checkNameCollisions4,
|
|
4383
4143
|
isReservedToken as isReservedToken3,
|
|
4384
|
-
PenvError as
|
|
4144
|
+
PenvError as PenvError15,
|
|
4385
4145
|
ReservedTokenError as ReservedTokenError3,
|
|
4386
4146
|
refFromVariable as refFromVariable2,
|
|
4387
4147
|
roundTripsCleanly,
|
|
@@ -4391,15 +4151,15 @@ function assertImportable(ref, variable) {
|
|
|
4391
4151
|
if (!ref.name.includes(".")) {
|
|
4392
4152
|
return;
|
|
4393
4153
|
}
|
|
4394
|
-
throw new
|
|
4154
|
+
throw new PenvError15(
|
|
4395
4155
|
"IMPORT_UNPARSEABLE_NAME",
|
|
4396
4156
|
`The variable ${variable} becomes the parameter \`${ref.name}\`, whose \`.\` would be read as a scope`,
|
|
4397
4157
|
`Filenames are split on \`.\`. Rename ${variable} in the source file, then import it again.`
|
|
4398
4158
|
);
|
|
4399
4159
|
}
|
|
4400
|
-
function assertNotReserved(ref, variable,
|
|
4160
|
+
function assertNotReserved(ref, variable, where2, config) {
|
|
4401
4161
|
if (isReservedToken3(ref.name, config)) {
|
|
4402
|
-
throw new ReservedTokenError3("parameter", variable,
|
|
4162
|
+
throw new ReservedTokenError3("parameter", variable, where2);
|
|
4403
4163
|
}
|
|
4404
4164
|
}
|
|
4405
4165
|
function assertRoundTrips(ref, variable, config) {
|
|
@@ -4410,18 +4170,18 @@ function assertRoundTrips(ref, variable, config) {
|
|
|
4410
4170
|
return;
|
|
4411
4171
|
}
|
|
4412
4172
|
const generated = variableName7(ref, config);
|
|
4413
|
-
throw new
|
|
4173
|
+
throw new PenvError15(
|
|
4414
4174
|
"IMPORT_LOSSY_NAME",
|
|
4415
4175
|
`The variable ${variable} becomes the parameter \`${ref.name}\`, which regenerates as ${generated}`,
|
|
4416
4176
|
`\`penv generate\` would write ${generated}, so anything reading \`process.env["${variable}"]\` would read \`undefined\`. Declare the name you want in the \`override\` block of penv.config.ts \u2014 \`override: { "${ref.name}": "${variable}" }\` \u2014 then import it again. Nothing was imported.`
|
|
4417
4177
|
);
|
|
4418
4178
|
}
|
|
4419
|
-
function refsForEntries(entries,
|
|
4179
|
+
function refsForEntries(entries, where2, config) {
|
|
4420
4180
|
const refs = [];
|
|
4421
4181
|
for (const entry of entries) {
|
|
4422
4182
|
const ref = refFromVariable2(entry.key);
|
|
4423
4183
|
assertImportable(ref, entry.key);
|
|
4424
|
-
assertNotReserved(ref, entry.key,
|
|
4184
|
+
assertNotReserved(ref, entry.key, where2, config);
|
|
4425
4185
|
assertRoundTrips(ref, entry.key, config);
|
|
4426
4186
|
refs.push(ref);
|
|
4427
4187
|
}
|
|
@@ -4451,8 +4211,8 @@ function writeEntries(tree, entries, refs, scope) {
|
|
|
4451
4211
|
}
|
|
4452
4212
|
|
|
4453
4213
|
// src/detect.ts
|
|
4454
|
-
import { existsSync as
|
|
4455
|
-
import { join as
|
|
4214
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
|
|
4215
|
+
import { join as join6 } from "path";
|
|
4456
4216
|
import { DEFAULT_SCHEMA_FILE } from "@penvhq/core";
|
|
4457
4217
|
var SIGNATURES = [
|
|
4458
4218
|
{ name: "Next.js", packages: ["next"], publicPrefixes: ["NEXT_PUBLIC_"] },
|
|
@@ -4469,7 +4229,7 @@ var SIGNATURES = [
|
|
|
4469
4229
|
function exportsSchema(file) {
|
|
4470
4230
|
let source;
|
|
4471
4231
|
try {
|
|
4472
|
-
source =
|
|
4232
|
+
source = readFileSync6(file, "utf8");
|
|
4473
4233
|
} catch {
|
|
4474
4234
|
return false;
|
|
4475
4235
|
}
|
|
@@ -4480,8 +4240,8 @@ function exportsSchema(file) {
|
|
|
4480
4240
|
);
|
|
4481
4241
|
}
|
|
4482
4242
|
function occupied(cwd, relative5) {
|
|
4483
|
-
const file =
|
|
4484
|
-
return
|
|
4243
|
+
const file = join6(cwd, ...relative5.split("/"));
|
|
4244
|
+
return existsSync4(file) && !exportsSchema(file);
|
|
4485
4245
|
}
|
|
4486
4246
|
function schemaFileFor(cwd) {
|
|
4487
4247
|
const dir = srcPrefix(cwd);
|
|
@@ -4496,10 +4256,10 @@ function schemaFileFor(cwd) {
|
|
|
4496
4256
|
return { file: DEFAULT_SCHEMA_FILE, displaced: preferred };
|
|
4497
4257
|
}
|
|
4498
4258
|
function srcPrefix(cwd) {
|
|
4499
|
-
return
|
|
4259
|
+
return existsSync4(join6(cwd, "src")) ? "src/" : "";
|
|
4500
4260
|
}
|
|
4501
4261
|
function dependenciesOf(cwd) {
|
|
4502
|
-
const manifest =
|
|
4262
|
+
const manifest = manifestOf(cwd);
|
|
4503
4263
|
if (manifest === void 0) {
|
|
4504
4264
|
return void 0;
|
|
4505
4265
|
}
|
|
@@ -4514,14 +4274,14 @@ function dependenciesOf(cwd) {
|
|
|
4514
4274
|
}
|
|
4515
4275
|
return names;
|
|
4516
4276
|
}
|
|
4517
|
-
function
|
|
4518
|
-
const file =
|
|
4519
|
-
if (!
|
|
4277
|
+
function manifestOf(cwd) {
|
|
4278
|
+
const file = join6(cwd, "package.json");
|
|
4279
|
+
if (!existsSync4(file)) {
|
|
4520
4280
|
return void 0;
|
|
4521
4281
|
}
|
|
4522
4282
|
let manifest;
|
|
4523
4283
|
try {
|
|
4524
|
-
manifest = JSON.parse(
|
|
4284
|
+
manifest = JSON.parse(readFileSync6(file, "utf8"));
|
|
4525
4285
|
} catch {
|
|
4526
4286
|
return void 0;
|
|
4527
4287
|
}
|
|
@@ -4537,7 +4297,7 @@ function detectFramework(cwd) {
|
|
|
4537
4297
|
return detectedFrom(cwd, signature.name, signature.publicPrefixes);
|
|
4538
4298
|
}
|
|
4539
4299
|
}
|
|
4540
|
-
if (
|
|
4300
|
+
if (existsSync4(join6(cwd, "bunfig.toml")) || dependencies.has("bun-types")) {
|
|
4541
4301
|
return detectedFrom(cwd, "Bun", []);
|
|
4542
4302
|
}
|
|
4543
4303
|
return void 0;
|
|
@@ -4557,7 +4317,7 @@ function detectAlias(cwd) {
|
|
|
4557
4317
|
return hasImportsBlock(cwd) ? IMPORTS_ALIAS : DEFAULT_ALIAS;
|
|
4558
4318
|
}
|
|
4559
4319
|
function hasImportsBlock(cwd) {
|
|
4560
|
-
const manifest =
|
|
4320
|
+
const manifest = manifestOf(cwd);
|
|
4561
4321
|
const imports = manifest?.imports;
|
|
4562
4322
|
return imports !== null && typeof imports === "object" && !Array.isArray(imports);
|
|
4563
4323
|
}
|
|
@@ -4616,8 +4376,8 @@ function draftFieldsAcross(sources, environments) {
|
|
|
4616
4376
|
}
|
|
4617
4377
|
|
|
4618
4378
|
// src/commands/init.ts
|
|
4619
|
-
import { existsSync as
|
|
4620
|
-
import { dirname as dirname6, join as
|
|
4379
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
4380
|
+
import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
|
|
4621
4381
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
4622
4382
|
import {
|
|
4623
4383
|
CUTOVER_PATH as CUTOVER_PATH3,
|
|
@@ -4625,7 +4385,7 @@ import {
|
|
|
4625
4385
|
isLegalEnvironmentName as isLegalEnvironmentName2,
|
|
4626
4386
|
loadConfigFrom,
|
|
4627
4387
|
PENV_DIR as PENV_DIR2,
|
|
4628
|
-
PenvError as
|
|
4388
|
+
PenvError as PenvError16,
|
|
4629
4389
|
parameterId as parameterId6,
|
|
4630
4390
|
parseDotenv,
|
|
4631
4391
|
RECORDS_PATH as RECORDS_PATH3,
|
|
@@ -4642,32 +4402,32 @@ import { defineCommand as defineCommand13 } from "citty";
|
|
|
4642
4402
|
|
|
4643
4403
|
// src/scaffold-undo.ts
|
|
4644
4404
|
import {
|
|
4645
|
-
existsSync as
|
|
4405
|
+
existsSync as existsSync5,
|
|
4646
4406
|
mkdirSync as mkdirSync3,
|
|
4647
4407
|
readdirSync as readdirSync4,
|
|
4648
|
-
readFileSync as
|
|
4408
|
+
readFileSync as readFileSync7,
|
|
4649
4409
|
rmdirSync,
|
|
4650
|
-
statSync as
|
|
4410
|
+
statSync as statSync2,
|
|
4651
4411
|
unlinkSync,
|
|
4652
4412
|
writeFileSync as writeFileSync4
|
|
4653
4413
|
} from "fs";
|
|
4654
|
-
import { dirname as dirname5, join as
|
|
4414
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
4655
4415
|
function record(path, files, dirs) {
|
|
4656
4416
|
let stats;
|
|
4657
4417
|
try {
|
|
4658
|
-
stats =
|
|
4418
|
+
stats = statSync2(path);
|
|
4659
4419
|
} catch {
|
|
4660
4420
|
return;
|
|
4661
4421
|
}
|
|
4662
4422
|
if (stats.isDirectory()) {
|
|
4663
4423
|
dirs.add(path);
|
|
4664
4424
|
for (const entry of readdirSync4(path)) {
|
|
4665
|
-
record(
|
|
4425
|
+
record(join7(path, entry), files, dirs);
|
|
4666
4426
|
}
|
|
4667
4427
|
return;
|
|
4668
4428
|
}
|
|
4669
4429
|
if (stats.isFile()) {
|
|
4670
|
-
files.set(path,
|
|
4430
|
+
files.set(path, readFileSync7(path));
|
|
4671
4431
|
}
|
|
4672
4432
|
}
|
|
4673
4433
|
function captureScaffold(root, paths) {
|
|
@@ -4675,7 +4435,7 @@ function captureScaffold(root, paths) {
|
|
|
4675
4435
|
const dirs = /* @__PURE__ */ new Set();
|
|
4676
4436
|
for (const path of paths) {
|
|
4677
4437
|
for (let dir = dirname5(path); dir.startsWith(root) && dir !== root; dir = dirname5(dir)) {
|
|
4678
|
-
if (
|
|
4438
|
+
if (existsSync5(dir)) {
|
|
4679
4439
|
dirs.add(dir);
|
|
4680
4440
|
}
|
|
4681
4441
|
}
|
|
@@ -4698,7 +4458,7 @@ function restoreScaffold(undo) {
|
|
|
4698
4458
|
function removeAdded(path, undo) {
|
|
4699
4459
|
let stats;
|
|
4700
4460
|
try {
|
|
4701
|
-
stats =
|
|
4461
|
+
stats = statSync2(path);
|
|
4702
4462
|
} catch {
|
|
4703
4463
|
return;
|
|
4704
4464
|
}
|
|
@@ -4712,7 +4472,7 @@ function removeAdded(path, undo) {
|
|
|
4712
4472
|
return;
|
|
4713
4473
|
}
|
|
4714
4474
|
for (const entry of readdirSync4(path)) {
|
|
4715
|
-
removeAdded(
|
|
4475
|
+
removeAdded(join7(path, entry), undo);
|
|
4716
4476
|
}
|
|
4717
4477
|
if (!undo.dirs.has(path) && readdirSync4(path).length === 0) {
|
|
4718
4478
|
rmdirSync(path);
|
|
@@ -4906,7 +4666,7 @@ function suggestEnvironments(root) {
|
|
|
4906
4666
|
return [...found].sort();
|
|
4907
4667
|
}
|
|
4908
4668
|
function emptyFlag(flag) {
|
|
4909
|
-
return new
|
|
4669
|
+
return new PenvError16(
|
|
4910
4670
|
"INIT_FLAG_EMPTY",
|
|
4911
4671
|
`\`--${flag}\` was given without a value`,
|
|
4912
4672
|
flag === "schema" ? `Name the module that exports the schema, e.g. \`--schema src/env.ts\`, or drop the flag to use ${DEFAULT_SCHEMA_FILE2}.` : "Name the environment, e.g. `--env production`, or drop the flag to leave the whitelist empty and declare it in penv.config.ts."
|
|
@@ -4930,8 +4690,8 @@ function configOf(decisions) {
|
|
|
4930
4690
|
return { environments: decisions.environments, providers: {}, schemaFile: decisions.schemaFile };
|
|
4931
4691
|
}
|
|
4932
4692
|
function declaredIn(root) {
|
|
4933
|
-
const file =
|
|
4934
|
-
if (!
|
|
4693
|
+
const file = join8(root, CONFIG_FILE);
|
|
4694
|
+
if (!existsSync6(file)) {
|
|
4935
4695
|
return void 0;
|
|
4936
4696
|
}
|
|
4937
4697
|
return loadConfigFrom(file);
|
|
@@ -4969,7 +4729,7 @@ function planInit(root, flags = {}) {
|
|
|
4969
4729
|
throw emptyFlag("alias");
|
|
4970
4730
|
}
|
|
4971
4731
|
if (!ALIAS_NAME.test(alias)) {
|
|
4972
|
-
throw new
|
|
4732
|
+
throw new PenvError16(
|
|
4973
4733
|
"INIT_ALIAS_INVALID",
|
|
4974
4734
|
`\`${alias}\` is not an alias penv can write`,
|
|
4975
4735
|
`An alias is \`@name\` \u2014 a tsconfig \`paths\` entry a bundler resolves \u2014 or \`#name\`, a package.json \`imports\` entry Node resolves itself. Those are the two things a module specifier can be that is not a package.`
|
|
@@ -5303,7 +5063,7 @@ ${objectIndent}${source.slice(close)}`;
|
|
|
5303
5063
|
${entryIndent}${member},${source.slice(open + 1)}`;
|
|
5304
5064
|
}
|
|
5305
5065
|
function shapeError(what, target, alias) {
|
|
5306
|
-
return new
|
|
5066
|
+
return new PenvError16(
|
|
5307
5067
|
"TSCONFIG_SHAPE",
|
|
5308
5068
|
`penv cannot add the \`${alias}\` path alias to tsconfig.json: ${what}`,
|
|
5309
5069
|
`Add it by hand: \`{ "compilerOptions": { "paths": { "${alias}": ["${target}"] } } }\`.`
|
|
@@ -5374,15 +5134,15 @@ function renderTsconfig(target, alias) {
|
|
|
5374
5134
|
}
|
|
5375
5135
|
function ensurePenvDir(root) {
|
|
5376
5136
|
const dir = resolve6(root, PENV_DIR2);
|
|
5377
|
-
if (
|
|
5137
|
+
if (existsSync6(dir)) {
|
|
5378
5138
|
return { target: "penv-dir", action: "kept", text: `Found ${PENV_DIR2}/` };
|
|
5379
5139
|
}
|
|
5380
5140
|
mkdirSync4(dir, { recursive: true });
|
|
5381
5141
|
return { target: "penv-dir", action: "created", text: `Created ${PENV_DIR2}/` };
|
|
5382
5142
|
}
|
|
5383
5143
|
function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS) {
|
|
5384
|
-
const file =
|
|
5385
|
-
if (
|
|
5144
|
+
const file = join8(root, SCHEMA_SHAPE_FILE3);
|
|
5145
|
+
if (existsSync6(file)) {
|
|
5386
5146
|
return {
|
|
5387
5147
|
target: "schema",
|
|
5388
5148
|
action: "kept",
|
|
@@ -5408,8 +5168,8 @@ function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS
|
|
|
5408
5168
|
};
|
|
5409
5169
|
}
|
|
5410
5170
|
function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
|
|
5411
|
-
const file =
|
|
5412
|
-
if (
|
|
5171
|
+
const file = join8(root, ...decisions.schemaFile.split("/"));
|
|
5172
|
+
if (existsSync6(file)) {
|
|
5413
5173
|
return {
|
|
5414
5174
|
target: "env",
|
|
5415
5175
|
action: "kept",
|
|
@@ -5427,8 +5187,8 @@ function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5427
5187
|
};
|
|
5428
5188
|
}
|
|
5429
5189
|
function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
|
|
5430
|
-
const file =
|
|
5431
|
-
if (
|
|
5190
|
+
const file = join8(root, CONFIG_FILE);
|
|
5191
|
+
if (existsSync6(file)) {
|
|
5432
5192
|
return { target: "config", action: "kept", text: `Kept ${CONFIG_FILE}` };
|
|
5433
5193
|
}
|
|
5434
5194
|
writeFileSync5(file, renderConfigModule(decisions), "utf8");
|
|
@@ -5437,9 +5197,9 @@ function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5437
5197
|
function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
|
|
5438
5198
|
const alias = decisions.alias;
|
|
5439
5199
|
const imports = alias.startsWith(IMPORTS_PREFIX);
|
|
5440
|
-
const file =
|
|
5441
|
-
const
|
|
5442
|
-
if (!
|
|
5200
|
+
const file = join8(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
|
|
5201
|
+
const where2 = imports ? PACKAGE_FILE : TSCONFIG_FILE;
|
|
5202
|
+
if (!existsSync6(file)) {
|
|
5443
5203
|
if (imports) {
|
|
5444
5204
|
return {
|
|
5445
5205
|
target: "tsconfig",
|
|
@@ -5455,13 +5215,13 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5455
5215
|
text: `Created ${TSCONFIG_FILE} with the ${alias} path alias`
|
|
5456
5216
|
};
|
|
5457
5217
|
}
|
|
5458
|
-
const source =
|
|
5218
|
+
const source = readFileSync8(file, "utf8");
|
|
5459
5219
|
const edit = imports ? insertImportsAlias(source, decisions.schemaFile, alias) : insertEnvAlias(source, decisions.schemaFile, alias);
|
|
5460
5220
|
if (edit.conflict !== void 0) {
|
|
5461
5221
|
return {
|
|
5462
5222
|
target: "tsconfig",
|
|
5463
5223
|
action: "conflicted",
|
|
5464
|
-
text: `${
|
|
5224
|
+
text: `${where2} already maps ${alias} to ${edit.conflict}`,
|
|
5465
5225
|
note: `(left alone \u2014 \`import { env } from "${alias}"\` will not reach ${decisions.schemaFile})`
|
|
5466
5226
|
};
|
|
5467
5227
|
}
|
|
@@ -5469,20 +5229,20 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5469
5229
|
return {
|
|
5470
5230
|
target: "tsconfig",
|
|
5471
5231
|
action: "kept",
|
|
5472
|
-
text: `Kept the ${alias} alias in ${
|
|
5232
|
+
text: `Kept the ${alias} alias in ${where2}`
|
|
5473
5233
|
};
|
|
5474
5234
|
}
|
|
5475
5235
|
writeFileSync5(file, edit.source, "utf8");
|
|
5476
5236
|
return {
|
|
5477
5237
|
target: "tsconfig",
|
|
5478
5238
|
action: "updated",
|
|
5479
|
-
text: `Added ${alias} alias to ${
|
|
5239
|
+
text: `Added ${alias} alias to ${where2}`
|
|
5480
5240
|
};
|
|
5481
5241
|
}
|
|
5482
5242
|
function writeGitignore(root, decisions = DEFAULT_DECISIONS) {
|
|
5483
|
-
const file =
|
|
5243
|
+
const file = join8(root, ...STATE_GITIGNORE_PATH.split("/"));
|
|
5484
5244
|
const wanted = renderStateGitignore(configOf(decisions));
|
|
5485
|
-
const existing =
|
|
5245
|
+
const existing = existsSync6(file) ? readFileSync8(file, "utf8") : void 0;
|
|
5486
5246
|
if (existing === wanted) {
|
|
5487
5247
|
return { target: "gitignore", action: "kept", text: `Kept ${STATE_GITIGNORE_PATH}` };
|
|
5488
5248
|
}
|
|
@@ -5503,12 +5263,12 @@ function outdatedRuntimeWarning(root) {
|
|
|
5503
5263
|
return `Injection needs @penvhq/penv ${INJECT_MIN_VERSION}+ \u2014 this project has ${version}, whose \`load\` ignores \`{ inject: true }\`. Upgrade, or process.env stays empty.`;
|
|
5504
5264
|
}
|
|
5505
5265
|
function installedPenvVersion(root) {
|
|
5506
|
-
const file =
|
|
5507
|
-
if (!
|
|
5266
|
+
const file = join8(root, "node_modules", "@penvhq", "penv", "package.json");
|
|
5267
|
+
if (!existsSync6(file)) {
|
|
5508
5268
|
return void 0;
|
|
5509
5269
|
}
|
|
5510
5270
|
try {
|
|
5511
|
-
const version = JSON.parse(
|
|
5271
|
+
const version = JSON.parse(readFileSync8(file, "utf8")).version;
|
|
5512
5272
|
return typeof version === "string" ? version : void 0;
|
|
5513
5273
|
} catch {
|
|
5514
5274
|
return void 0;
|
|
@@ -5548,11 +5308,11 @@ function writeSeam(root, decisions = DEFAULT_DECISIONS, framework = detectFramew
|
|
|
5548
5308
|
};
|
|
5549
5309
|
}
|
|
5550
5310
|
const alsoNote = writeAlso(root, seam.also);
|
|
5551
|
-
const file =
|
|
5311
|
+
const file = join8(root, ...seam.file.split("/"));
|
|
5552
5312
|
const baseNotes = [...seam.notes, ...alsoNote === void 0 ? [] : [alsoNote]];
|
|
5553
5313
|
const notes = baseNotes.length === 0 ? "" : `
|
|
5554
5314
|
${baseNotes.map((n) => ` ${n}`).join("\n")}`;
|
|
5555
|
-
if (
|
|
5315
|
+
if (existsSync6(file)) {
|
|
5556
5316
|
return {
|
|
5557
5317
|
target: "seam",
|
|
5558
5318
|
action: "info",
|
|
@@ -5573,8 +5333,8 @@ function writeAlso(root, also) {
|
|
|
5573
5333
|
if (also === void 0) {
|
|
5574
5334
|
return void 0;
|
|
5575
5335
|
}
|
|
5576
|
-
const file =
|
|
5577
|
-
if (
|
|
5336
|
+
const file = join8(root, ...also.file.split("/"));
|
|
5337
|
+
if (existsSync6(file)) {
|
|
5578
5338
|
return also.ifPresent;
|
|
5579
5339
|
}
|
|
5580
5340
|
mkdirSync4(dirname6(file), { recursive: true });
|
|
@@ -5604,7 +5364,7 @@ function scaffold(root, fields, draft, decisions = DEFAULT_DECISIONS, framework
|
|
|
5604
5364
|
return seam === void 0 ? steps : [...steps, seam];
|
|
5605
5365
|
}
|
|
5606
5366
|
function scaffoldPaths(root, decisions = DEFAULT_DECISIONS, framework = detectFramework(root)?.name) {
|
|
5607
|
-
const fileAt = (relative5) =>
|
|
5367
|
+
const fileAt = (relative5) => join8(root, ...relative5.split("/"));
|
|
5608
5368
|
const seam = decisions.inject ? seamFor(framework, {
|
|
5609
5369
|
alias: decisions.alias,
|
|
5610
5370
|
srcDir: srcPrefix(root),
|
|
@@ -5645,7 +5405,7 @@ function planCutover(input) {
|
|
|
5645
5405
|
assertBundleResolved(root);
|
|
5646
5406
|
const selected = [...input.selected];
|
|
5647
5407
|
if (selected.length === 0) {
|
|
5648
|
-
throw new
|
|
5408
|
+
throw new PenvError16(
|
|
5649
5409
|
"INIT_NOTHING_SELECTED",
|
|
5650
5410
|
"No dotenv file was selected, so there is nothing to migrate",
|
|
5651
5411
|
"Run `penv init` again and choose the files penv should adopt."
|
|
@@ -5657,7 +5417,7 @@ function planCutover(input) {
|
|
|
5657
5417
|
const environments = declared === void 0 ? chosen : declared.environments;
|
|
5658
5418
|
for (const environment of chosen) {
|
|
5659
5419
|
if (!environments.includes(environment)) {
|
|
5660
|
-
throw new
|
|
5420
|
+
throw new PenvError16(
|
|
5661
5421
|
"INIT_ENVIRONMENT_UNDECLARED",
|
|
5662
5422
|
`${CONFIG_FILE} declares ${describeList(environments)}, and adopting these files needs \`${environment}\` declared too`,
|
|
5663
5423
|
`Add \`${environment}\` to \`environments\` in ${CONFIG_FILE}, with a provider for it, then run \`penv init\` again. Nothing was changed.`
|
|
@@ -5682,7 +5442,7 @@ function planCutover(input) {
|
|
|
5682
5442
|
const diagnostics = [];
|
|
5683
5443
|
let variables = 0;
|
|
5684
5444
|
for (const file of selected) {
|
|
5685
|
-
const parsed = parseDotenv(
|
|
5445
|
+
const parsed = parseDotenv(readFileSync8(join8(root, file.name), "utf8"));
|
|
5686
5446
|
const fileRefs = refsForEntries(parsed.entries, file.name, config);
|
|
5687
5447
|
adopted.push({ file, entries: parsed.entries, refs: fileRefs, scope: scopeOf(file) });
|
|
5688
5448
|
refs.push(...fileRefs);
|
|
@@ -5744,7 +5504,7 @@ function requireEnvironment(input) {
|
|
|
5744
5504
|
if (environment.length > 0) {
|
|
5745
5505
|
return environment;
|
|
5746
5506
|
}
|
|
5747
|
-
throw new
|
|
5507
|
+
throw new PenvError16(
|
|
5748
5508
|
"INIT_ENVIRONMENT_UNNAMED",
|
|
5749
5509
|
"The selected files name no environment, and penv does not invent one",
|
|
5750
5510
|
`Run \`penv init --env ${DEVELOPMENT}\` to say which environment these values are for.`
|
|
@@ -5756,7 +5516,7 @@ function assertCascadeComplete(root, selected, environments) {
|
|
|
5756
5516
|
for (const environment of environments) {
|
|
5757
5517
|
for (const name of cascadeFor(environment)) {
|
|
5758
5518
|
if (present.has(name) && !taken.has(name)) {
|
|
5759
|
-
throw new
|
|
5519
|
+
throw new PenvError16(
|
|
5760
5520
|
"INIT_CUTOVER_INCOMPLETE",
|
|
5761
5521
|
`${name} is part of ${environment}'s cascade and was not selected, so your framework would keep reading it beside penv`,
|
|
5762
5522
|
"Run `penv init` again and take every file penv listed for that environment. Nothing was changed."
|
|
@@ -5769,7 +5529,7 @@ function assertBundleResolved(root) {
|
|
|
5769
5529
|
if (!bundleUnresolved(root)) {
|
|
5770
5530
|
return;
|
|
5771
5531
|
}
|
|
5772
|
-
throw new
|
|
5532
|
+
throw new PenvError16(
|
|
5773
5533
|
"INIT_BUNDLE_UNRESOLVED",
|
|
5774
5534
|
`The dotenv files from the last cutover are still in ${ROLLBACK_DOTENV_PATH3}/, and penv will not migrate a second time over them`,
|
|
5775
5535
|
"Run `penv init undo` to put them back, or `penv cleanup` to drop them once you are happy with the migration."
|
|
@@ -5781,7 +5541,7 @@ function selectionForYes(plan2) {
|
|
|
5781
5541
|
(file) => file.environment !== void 0 && file.environment !== DEVELOPMENT
|
|
5782
5542
|
);
|
|
5783
5543
|
if (shared !== void 0 && other !== void 0) {
|
|
5784
|
-
throw new
|
|
5544
|
+
throw new PenvError16(
|
|
5785
5545
|
"INIT_YES_SHARED_FALLBACK",
|
|
5786
5546
|
`${other.name} falls back to the shared ${shared.name} this cutover would move, so \`--yes\` will not decide what happens to ${other.environment ?? ""}`,
|
|
5787
5547
|
"Run `penv init` without `--yes` and choose every file the cutover takes. Nothing was changed."
|
|
@@ -5827,7 +5587,7 @@ function issueLines(result2) {
|
|
|
5827
5587
|
}
|
|
5828
5588
|
var SCAFFOLD_ROLLED_BACK = "penv put the project back as it found it \u2014 your dotenv files and everything else are exactly where they were.";
|
|
5829
5589
|
function invalidAfterImport(result2) {
|
|
5830
|
-
return new
|
|
5590
|
+
return new PenvError16(
|
|
5831
5591
|
"INIT_CUTOVER_INVALID",
|
|
5832
5592
|
`The imported values do not satisfy the draft schema for ${result2.environment}:
|
|
5833
5593
|
${issueLines(result2)}`,
|
|
@@ -5835,7 +5595,7 @@ ${issueLines(result2)}`,
|
|
|
5835
5595
|
);
|
|
5836
5596
|
}
|
|
5837
5597
|
function draftNotLoaded(result2) {
|
|
5838
|
-
return new
|
|
5598
|
+
return new PenvError16(
|
|
5839
5599
|
"INIT_DRAFT_NOT_LOADED",
|
|
5840
5600
|
`penv could not load the schema it drafted for ${result2.environment}, so nothing was checked against it:
|
|
5841
5601
|
${issueLines(result2)}`,
|
|
@@ -5926,12 +5686,12 @@ function dailyCommand(root) {
|
|
|
5926
5686
|
return `penv run -- ${detectPackageManager(root)} ${devScript(root)}`;
|
|
5927
5687
|
}
|
|
5928
5688
|
function devScript(root) {
|
|
5929
|
-
const file =
|
|
5930
|
-
if (!
|
|
5689
|
+
const file = join8(root, PACKAGE_FILE);
|
|
5690
|
+
if (!existsSync6(file)) {
|
|
5931
5691
|
return "dev";
|
|
5932
5692
|
}
|
|
5933
5693
|
try {
|
|
5934
|
-
const scripts = JSON.parse(
|
|
5694
|
+
const scripts = JSON.parse(readFileSync8(file, "utf8")).scripts;
|
|
5935
5695
|
if (scripts !== null && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
5936
5696
|
const named = Object.keys(scripts);
|
|
5937
5697
|
return ["dev", "start"].find((script) => named.includes(script)) ?? named[0] ?? "dev";
|
|
@@ -5975,7 +5735,7 @@ async function promptForSelection(plan2, io) {
|
|
|
5975
5735
|
for (const name of names) {
|
|
5976
5736
|
const file = plan2.found.find((candidate) => candidate.name === name);
|
|
5977
5737
|
if (file === void 0) {
|
|
5978
|
-
throw new
|
|
5738
|
+
throw new PenvError16(
|
|
5979
5739
|
"INIT_SELECTION_UNKNOWN",
|
|
5980
5740
|
`\`${name}\` is not one of the dotenv files penv found`,
|
|
5981
5741
|
"Run `penv init` again and name the files exactly as they are listed, e.g. `.env.production`."
|
|
@@ -6138,7 +5898,7 @@ var initCommand = defineCommand13({
|
|
|
6138
5898
|
});
|
|
6139
5899
|
function runUndoAction(root, action) {
|
|
6140
5900
|
if (action !== "undo") {
|
|
6141
|
-
throw new
|
|
5901
|
+
throw new PenvError16(
|
|
6142
5902
|
"INIT_UNKNOWN_ACTION",
|
|
6143
5903
|
`\`penv init ${action}\` is not something init does`,
|
|
6144
5904
|
"Run `penv init undo` to put back the dotenv files of the last cutover."
|
|
@@ -6244,7 +6004,7 @@ function explicitEnvironment(options, source, config) {
|
|
|
6244
6004
|
if (value.length > 0) {
|
|
6245
6005
|
return value;
|
|
6246
6006
|
}
|
|
6247
|
-
throw new
|
|
6007
|
+
throw new PenvError17(
|
|
6248
6008
|
"IMPORT_ENV_FLAG_EMPTY",
|
|
6249
6009
|
`\`--env\` for the import of ${source} names no environment`,
|
|
6250
6010
|
`Pass a declared environment \u2014 ${config.environments.map((e) => `\`${e}\``).join(", ")} \u2014 e.g. \`--env production\`, or drop \`--env\` to import ${source} as the scope that has no environment. Nothing was imported.`
|
|
@@ -6254,7 +6014,7 @@ function assertEnvironmentAgrees(derived, explicit, source) {
|
|
|
6254
6014
|
if (derived === void 0 || explicit === void 0 || derived === explicit) {
|
|
6255
6015
|
return;
|
|
6256
6016
|
}
|
|
6257
|
-
throw new
|
|
6017
|
+
throw new PenvError17(
|
|
6258
6018
|
"IMPORT_ENV_CONFLICT",
|
|
6259
6019
|
`The file ${source} is scoped to environment ${derived}, but \`--env ${explicit}\` names ${explicit}`,
|
|
6260
6020
|
`Drop \`--env\` to import ${source} as ${derived}, pass \`--env ${derived}\` to say the same thing twice, or point \`penv import\` at the file that holds ${explicit}'s values. Nothing was imported.`
|
|
@@ -6299,15 +6059,15 @@ function configInEffect(cwd, environment) {
|
|
|
6299
6059
|
}
|
|
6300
6060
|
function importDotenv(options) {
|
|
6301
6061
|
const cwd = resolve7(options.cwd);
|
|
6302
|
-
const file =
|
|
6303
|
-
if (!
|
|
6304
|
-
throw new
|
|
6062
|
+
const file = isAbsolute3(options.file) ? options.file : resolve7(cwd, options.file);
|
|
6063
|
+
if (!existsSync7(file)) {
|
|
6064
|
+
throw new PenvError17(
|
|
6305
6065
|
"IMPORT_FILE_MISSING",
|
|
6306
6066
|
`There is no file at ${file} to import`,
|
|
6307
6067
|
"Point `penv import` at an existing dotenv file, e.g. `penv import .env`."
|
|
6308
6068
|
);
|
|
6309
6069
|
}
|
|
6310
|
-
const parsed = parseDotenv2(
|
|
6070
|
+
const parsed = parseDotenv2(readFileSync9(file, "utf8"));
|
|
6311
6071
|
const { config, decisions } = configInEffect(cwd, environmentNamed(file, options.environment));
|
|
6312
6072
|
const source = displayPath3(cwd, file);
|
|
6313
6073
|
const named = scopeFromFilename(file, config);
|
|
@@ -6425,19 +6185,19 @@ var importCommand = defineCommand14({
|
|
|
6425
6185
|
|
|
6426
6186
|
// src/commands/key.ts
|
|
6427
6187
|
import { randomBytes } from "crypto";
|
|
6428
|
-
import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as
|
|
6188
|
+
import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as PenvError19 } from "@penvhq/core";
|
|
6429
6189
|
import { defineCommand as defineCommand15 } from "citty";
|
|
6430
6190
|
|
|
6431
6191
|
// src/keychain.ts
|
|
6432
6192
|
import { createRequire as createRequire3 } from "module";
|
|
6433
|
-
import { PenvError as
|
|
6193
|
+
import { PenvError as PenvError18 } from "@penvhq/core";
|
|
6434
6194
|
var KEYRING_MODULE = "@napi-rs/keyring";
|
|
6435
6195
|
var nodeRequire = (id) => createRequire3(import.meta.url)(id);
|
|
6436
6196
|
function firstLine2(cause) {
|
|
6437
6197
|
return (cause instanceof Error ? cause.message : String(cause)).split("\n")[0] ?? "";
|
|
6438
6198
|
}
|
|
6439
6199
|
function keyringMissing(cause) {
|
|
6440
|
-
return new
|
|
6200
|
+
return new PenvError18(
|
|
6441
6201
|
"KEYCHAIN_BINDING_MISSING",
|
|
6442
6202
|
`penv could not load ${KEYRING_MODULE}, the native binding it reads your OS keychain through`,
|
|
6443
6203
|
`This penv engine was installed as a plain tarball, which carries no native modules. Install penv with \`npm install -g @penvhq/launcher\`, or declare \`source: "env"\` for this environment and export its key. Original error: ${firstLine2(cause)}`
|
|
@@ -6479,7 +6239,7 @@ function runKeyCreate(options) {
|
|
|
6479
6239
|
const environment = targetEnvironment(project, options.environment);
|
|
6480
6240
|
const declared = project.config.keys?.[environment];
|
|
6481
6241
|
if (declared === void 0) {
|
|
6482
|
-
throw new
|
|
6242
|
+
throw new PenvError19(
|
|
6483
6243
|
"KEY_SOURCE_UNDECLARED",
|
|
6484
6244
|
`Environment ${environment} declares no key source, so penv does not know what a key for it would be`,
|
|
6485
6245
|
`Add a \`keys\` entry to penv.config.ts \u2014 e.g. \`keys: { ${environment}: { source: "env", id: "${environment}" } }\` \u2014 then run this again.`
|
|
@@ -6493,17 +6253,17 @@ function runKeyCreate(options) {
|
|
|
6493
6253
|
try {
|
|
6494
6254
|
existing = keychain.getPassword(KEYCHAIN_SERVICE, declared.id);
|
|
6495
6255
|
} catch (cause) {
|
|
6496
|
-
if (cause instanceof
|
|
6256
|
+
if (cause instanceof PenvError19) {
|
|
6497
6257
|
throw cause;
|
|
6498
6258
|
}
|
|
6499
|
-
throw new
|
|
6259
|
+
throw new PenvError19(
|
|
6500
6260
|
"KEYCHAIN_UNAVAILABLE",
|
|
6501
6261
|
`penv could not read your OS keychain to check for an existing key \`${declared.id}\``,
|
|
6502
6262
|
`Unlock your keychain and run this again. Original error: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
6503
6263
|
);
|
|
6504
6264
|
}
|
|
6505
6265
|
if (existing !== null) {
|
|
6506
|
-
throw new
|
|
6266
|
+
throw new PenvError19(
|
|
6507
6267
|
"KEY_EXISTS",
|
|
6508
6268
|
`Environment ${environment} already has a key \`${declared.id}\` in your OS keychain`,
|
|
6509
6269
|
"Replacing it would orphan every value already sealed under it \u2014 they could never be decrypted again. Re-run with `--force` only if you are certain nothing is sealed under the current key."
|
|
@@ -6658,21 +6418,21 @@ var listCommand = defineCommand16({
|
|
|
6658
6418
|
|
|
6659
6419
|
// src/commands/migrate.ts
|
|
6660
6420
|
import {
|
|
6661
|
-
existsSync as
|
|
6421
|
+
existsSync as existsSync8,
|
|
6662
6422
|
mkdirSync as mkdirSync5,
|
|
6663
6423
|
readdirSync as readdirSync6,
|
|
6664
|
-
readFileSync as
|
|
6424
|
+
readFileSync as readFileSync10,
|
|
6665
6425
|
renameSync as renameSync2,
|
|
6666
6426
|
rmSync as rmSync2,
|
|
6667
6427
|
writeFileSync as writeFileSync6
|
|
6668
6428
|
} from "fs";
|
|
6669
|
-
import { dirname as dirname7, join as
|
|
6429
|
+
import { dirname as dirname7, join as join9 } from "path";
|
|
6670
6430
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
6671
6431
|
import {
|
|
6672
6432
|
loadConfig as loadConfig2,
|
|
6673
6433
|
oldLayoutEntries,
|
|
6674
6434
|
PENV_DIR as PENV_DIR3,
|
|
6675
|
-
PenvError as
|
|
6435
|
+
PenvError as PenvError20,
|
|
6676
6436
|
RECORDS_PATH as RECORDS_PATH5,
|
|
6677
6437
|
recordsDir as recordsDir3,
|
|
6678
6438
|
renderStateGitignore as renderStateGitignore2,
|
|
@@ -6687,17 +6447,17 @@ function planMigrate(cwd) {
|
|
|
6687
6447
|
const tree = recordsDir3(root);
|
|
6688
6448
|
const collisions = collidingEntries(entries, tree);
|
|
6689
6449
|
if (collisions.length > 0) {
|
|
6690
|
-
throw new
|
|
6450
|
+
throw new PenvError20(
|
|
6691
6451
|
"HALF_MIGRATED",
|
|
6692
|
-
`${
|
|
6452
|
+
`${describe(collisions)} in both \`${PENV_DIR3}/\` and \`${RECORDS_PATH5}/\`, and penv cannot tell which copy is current`,
|
|
6693
6453
|
`Move what is left under \`${PENV_DIR3}/\` into \`${RECORDS_PATH5}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
|
|
6694
6454
|
);
|
|
6695
6455
|
}
|
|
6696
6456
|
const creates = [];
|
|
6697
|
-
if (entries.length > 0 && !
|
|
6457
|
+
if (entries.length > 0 && !existsSync8(tree)) {
|
|
6698
6458
|
creates.push(`${RECORDS_PATH5}/`);
|
|
6699
6459
|
}
|
|
6700
|
-
if (readIfPresent(
|
|
6460
|
+
if (readIfPresent(join9(root, ...STATE_GITIGNORE_PATH2.split("/"))) !== renderStateGitignore2(config)) {
|
|
6701
6461
|
creates.push(STATE_GITIGNORE_PATH2);
|
|
6702
6462
|
}
|
|
6703
6463
|
return {
|
|
@@ -6707,11 +6467,11 @@ function planMigrate(cwd) {
|
|
|
6707
6467
|
to: `${RECORDS_PATH5}/${entry}`
|
|
6708
6468
|
})),
|
|
6709
6469
|
creates,
|
|
6710
|
-
removes:
|
|
6470
|
+
removes: existsSync8(join9(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
|
|
6711
6471
|
};
|
|
6712
6472
|
}
|
|
6713
6473
|
function readIfPresent(file) {
|
|
6714
|
-
return
|
|
6474
|
+
return existsSync8(file) ? readFileSync10(file, "utf8") : void 0;
|
|
6715
6475
|
}
|
|
6716
6476
|
function collidingEntries(entries, tree) {
|
|
6717
6477
|
let held;
|
|
@@ -6723,7 +6483,7 @@ function collidingEntries(entries, tree) {
|
|
|
6723
6483
|
const taken = new Set(held.map((name) => name.toLowerCase()));
|
|
6724
6484
|
return entries.filter((entry) => taken.has(entry.toLowerCase())).sort();
|
|
6725
6485
|
}
|
|
6726
|
-
function
|
|
6486
|
+
function describe(names) {
|
|
6727
6487
|
return names.length === 1 ? `\`${names[0]}\` is` : `${names.map((name) => `\`${name}\``).join(", ")} are`;
|
|
6728
6488
|
}
|
|
6729
6489
|
function isNoop(plan2) {
|
|
@@ -6737,16 +6497,16 @@ function applyMigrate(plan2) {
|
|
|
6737
6497
|
if (plan2.moves.length > 0) {
|
|
6738
6498
|
mkdirSync5(recordsDir3(plan2.root), { recursive: true });
|
|
6739
6499
|
for (const move of plan2.moves) {
|
|
6740
|
-
renameSync2(
|
|
6500
|
+
renameSync2(join9(plan2.root, ...move.from.split("/")), join9(plan2.root, ...move.to.split("/")));
|
|
6741
6501
|
}
|
|
6742
6502
|
}
|
|
6743
6503
|
if (plan2.creates.includes(STATE_GITIGNORE_PATH2)) {
|
|
6744
|
-
const ignore =
|
|
6504
|
+
const ignore = join9(plan2.root, ...STATE_GITIGNORE_PATH2.split("/"));
|
|
6745
6505
|
mkdirSync5(dirname7(ignore), { recursive: true });
|
|
6746
6506
|
writeFileSync6(ignore, renderStateGitignore2(config), "utf8");
|
|
6747
6507
|
}
|
|
6748
6508
|
for (const removed of plan2.removes) {
|
|
6749
|
-
rmSync2(
|
|
6509
|
+
rmSync2(join9(plan2.root, ...removed.split("/")), { force: true });
|
|
6750
6510
|
}
|
|
6751
6511
|
return { ...plan2, status: "migrated" };
|
|
6752
6512
|
}
|
|
@@ -6828,7 +6588,7 @@ import {
|
|
|
6828
6588
|
formatMetaFile,
|
|
6829
6589
|
formatValueFile as formatValueFile6,
|
|
6830
6590
|
openValue as openValue4,
|
|
6831
|
-
PenvError as
|
|
6591
|
+
PenvError as PenvError21,
|
|
6832
6592
|
parameterId as parameterId7,
|
|
6833
6593
|
recordPath as recordPath7,
|
|
6834
6594
|
sealValue as sealValue3
|
|
@@ -6848,7 +6608,7 @@ async function planFile(project, source, target, parameter) {
|
|
|
6848
6608
|
}
|
|
6849
6609
|
const environment = environmentOf2(source);
|
|
6850
6610
|
if (environment === void 0) {
|
|
6851
|
-
throw new
|
|
6611
|
+
throw new PenvError21(
|
|
6852
6612
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
6853
6613
|
`${recordPath7(formatValueFile6(source))} is encrypted at a scope that names no environment, so penv cannot tell which key would re-seal it`,
|
|
6854
6614
|
"Keys are declared per environment in the `keys` block of penv.config.ts. Decrypt it with `penv decrypt`, move the parameter, then encrypt it again at its new address."
|
|
@@ -6857,7 +6617,7 @@ async function planFile(project, source, target, parameter) {
|
|
|
6857
6617
|
const keys = keySourceFor(project, environment);
|
|
6858
6618
|
const opened = openValue4(source, stored, keys);
|
|
6859
6619
|
if (opened.kind === "failed") {
|
|
6860
|
-
throw new
|
|
6620
|
+
throw new PenvError21(
|
|
6861
6621
|
"VALUE_UNDECRYPTABLE",
|
|
6862
6622
|
`${recordPath7(formatValueFile6(source))} could not be decrypted, so penv cannot re-seal it at its new address: ${opened.failure.detail}`,
|
|
6863
6623
|
"A sealed value is bound to the file it lives in, so moving it means opening it and sealing it again. Make the key available and run this again. Nothing has been moved."
|
|
@@ -6880,7 +6640,7 @@ async function runMove(options) {
|
|
|
6880
6640
|
assertWritableKey(options.to);
|
|
6881
6641
|
const to = refFromKey(options.to, project.config);
|
|
6882
6642
|
if (parameterId7(from) === parameterId7(to)) {
|
|
6883
|
-
throw new
|
|
6643
|
+
throw new PenvError21(
|
|
6884
6644
|
"PARAMETER_UNCHANGED",
|
|
6885
6645
|
`\`${options.from}\` and \`${options.to}\` are the same parameter`,
|
|
6886
6646
|
"Name a different destination, e.g. `penv mv redis-password redis/password`."
|
|
@@ -6890,7 +6650,7 @@ async function runMove(options) {
|
|
|
6890
6650
|
const sources = filesOf(all, from);
|
|
6891
6651
|
const meta = await project.provider.readMeta(from);
|
|
6892
6652
|
if (sources.length === 0 && meta === void 0) {
|
|
6893
|
-
throw new
|
|
6653
|
+
throw new PenvError21(
|
|
6894
6654
|
"PARAMETER_ABSENT",
|
|
6895
6655
|
`Parameter ${parameterId7(from)} has no value files and no meta, so there is nothing to move`,
|
|
6896
6656
|
`\`penv list\` shows every parameter penv holds.`
|
|
@@ -6898,7 +6658,7 @@ async function runMove(options) {
|
|
|
6898
6658
|
}
|
|
6899
6659
|
const occupied2 = filesOf(all, to);
|
|
6900
6660
|
if (occupied2.length > 0 || await project.provider.readMeta(to) !== void 0) {
|
|
6901
|
-
throw new
|
|
6661
|
+
throw new PenvError21(
|
|
6902
6662
|
"PARAMETER_EXISTS",
|
|
6903
6663
|
`Parameter ${parameterId7(to)} already exists, and penv will not merge two parameters into one`,
|
|
6904
6664
|
`Remove or rename ${parameterId7(to)} first. \`penv get ${options.to} --explain\` shows every file it holds.`
|
|
@@ -7057,7 +6817,7 @@ import {
|
|
|
7057
6817
|
beginRotation,
|
|
7058
6818
|
completeRotation,
|
|
7059
6819
|
holdsRecords as holdsRecords2,
|
|
7060
|
-
PenvError as
|
|
6820
|
+
PenvError as PenvError22,
|
|
7061
6821
|
retainsPrevious,
|
|
7062
6822
|
rotationOf as rotationOf2
|
|
7063
6823
|
} from "@penvhq/core";
|
|
@@ -7086,7 +6846,7 @@ async function writeRotatedValue(project, provider, ref, environment, value) {
|
|
|
7086
6846
|
}
|
|
7087
6847
|
function requireNewValue(value, phase, key) {
|
|
7088
6848
|
if (value === void 0) {
|
|
7089
|
-
throw new
|
|
6849
|
+
throw new PenvError22(
|
|
7090
6850
|
"ROTATION_NO_VALUE",
|
|
7091
6851
|
`A ${phase} rotation of ${key} writes a new value, and none was given`,
|
|
7092
6852
|
"Pass the new value as the argument \u2014 `penv rotate <key> <value>` \u2014 or pipe it in on stdin."
|
|
@@ -7096,7 +6856,7 @@ function requireNewValue(value, phase, key) {
|
|
|
7096
6856
|
}
|
|
7097
6857
|
function requireRetaining(provider, environment) {
|
|
7098
6858
|
if (!retainsPrevious(provider)) {
|
|
7099
|
-
throw new
|
|
6859
|
+
throw new PenvError22(
|
|
7100
6860
|
"ROTATION_NOT_RETAINING",
|
|
7101
6861
|
`A dual-valid rotation needs the previous value to stay readable during the grace window, and the \`${provider.type}\` provider for environment ${environment} does not retain it`,
|
|
7102
6862
|
"Point this environment at a provider that keeps prior versions (its `readPrevious` is what penv reads during the window), or, if a momentary overlap is not required, declare the parameter `atomic-cutover` in its meta and flip it in one step."
|
|
@@ -7110,7 +6870,7 @@ async function runRotate(options) {
|
|
|
7110
6870
|
const ref = refFromKey(options.key, project.config);
|
|
7111
6871
|
const source = await sourceProviderFor(project, environment);
|
|
7112
6872
|
if (!holdsRecords2(source)) {
|
|
7113
|
-
throw new
|
|
6873
|
+
throw new PenvError22(
|
|
7114
6874
|
"ROTATION_NOT_RECORDS",
|
|
7115
6875
|
`Environment ${environment} is backed by \`${source.type}\`, which holds a resolved projection penv cannot rotate in place`,
|
|
7116
6876
|
"Rotate the parameter in the environment that holds the records (the local tree, Vault, SSM), then `penv push` the result to this destination."
|
|
@@ -7121,7 +6881,7 @@ async function runRotate(options) {
|
|
|
7121
6881
|
const before = await provider.readMeta(ref);
|
|
7122
6882
|
const { mechanism } = rotationOf2(before, environment);
|
|
7123
6883
|
if (mechanism === void 0) {
|
|
7124
|
-
throw new
|
|
6884
|
+
throw new PenvError22(
|
|
7125
6885
|
"ROTATION_NO_MECHANISM",
|
|
7126
6886
|
`Parameter ${options.key} declares no rotation mechanism for environment ${environment}, so penv does not know how to rotate it`,
|
|
7127
6887
|
'Set `rotationMechanism` in the parameter\'s meta to `"dual-valid"` (a grace-window overlap) or `"atomic-cutover"` (a single flip), then run `penv rotate` again.'
|
|
@@ -7131,7 +6891,7 @@ async function runRotate(options) {
|
|
|
7131
6891
|
const complete = options.complete === true;
|
|
7132
6892
|
if (mechanism === "atomic-cutover") {
|
|
7133
6893
|
if (begin || complete) {
|
|
7134
|
-
throw new
|
|
6894
|
+
throw new PenvError22(
|
|
7135
6895
|
"ROTATION_MECHANISM_MISMATCH",
|
|
7136
6896
|
`Parameter ${options.key} is atomic-cutover, which flips in one step, so \`--begin\`/\`--complete\` do not apply`,
|
|
7137
6897
|
"Run `penv rotate <key> <value>` with no phase flag to flip it. `--begin`/`--complete` bracket a dual-valid grace window, which atomic-cutover has none of."
|
|
@@ -7145,7 +6905,7 @@ async function runRotate(options) {
|
|
|
7145
6905
|
}
|
|
7146
6906
|
const retaining = requireRetaining(provider, environment);
|
|
7147
6907
|
if (begin === complete) {
|
|
7148
|
-
throw new
|
|
6908
|
+
throw new PenvError22(
|
|
7149
6909
|
"ROTATION_PHASE_REQUIRED",
|
|
7150
6910
|
`A dual-valid rotation of ${options.key} needs exactly one of \`--begin\` or \`--complete\``,
|
|
7151
6911
|
"`--begin` writes the new value and opens the grace window; `--complete` closes it once every reader has moved to the new value. Run them in that order, one at a time."
|
|
@@ -7248,7 +7008,7 @@ var rotateCommand = defineCommand20({
|
|
|
7248
7008
|
});
|
|
7249
7009
|
|
|
7250
7010
|
// src/commands/watch.ts
|
|
7251
|
-
import { existsSync as
|
|
7011
|
+
import { existsSync as existsSync9, watch as watch2 } from "fs";
|
|
7252
7012
|
import { basename as basename3, dirname as dirname8, resolve as resolve8 } from "path";
|
|
7253
7013
|
import { SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE4, schemaFileOf as schemaFileOf5, schemaInsideTree } from "@penvhq/core";
|
|
7254
7014
|
import { defineCommand as defineCommand21 } from "citty";
|
|
@@ -7316,14 +7076,14 @@ function runWatch(options) {
|
|
|
7316
7076
|
if (closed || recovery === void 0) {
|
|
7317
7077
|
return;
|
|
7318
7078
|
}
|
|
7319
|
-
if (!
|
|
7079
|
+
if (!existsSync9(parent)) {
|
|
7320
7080
|
stop2(recovery);
|
|
7321
7081
|
return;
|
|
7322
7082
|
}
|
|
7323
7083
|
if (filename !== null && basename3(filename) !== name) {
|
|
7324
7084
|
return;
|
|
7325
7085
|
}
|
|
7326
|
-
if (!
|
|
7086
|
+
if (!existsSync9(target)) {
|
|
7327
7087
|
return;
|
|
7328
7088
|
}
|
|
7329
7089
|
stop2(recovery);
|
|
@@ -7347,7 +7107,7 @@ function runWatch(options) {
|
|
|
7347
7107
|
if (closed) {
|
|
7348
7108
|
return;
|
|
7349
7109
|
}
|
|
7350
|
-
if (!
|
|
7110
|
+
if (!existsSync9(target)) {
|
|
7351
7111
|
if (watcher !== void 0) {
|
|
7352
7112
|
stop2(watcher);
|
|
7353
7113
|
}
|