@penvhq/cli 0.10.0 → 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 +1 -1
- 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.map +1 -1
- package/dist/index.d.cts +1 -47
- package/dist/index.d.ts +1 -47
- package/dist/index.js +222 -516
- 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,9 +112,9 @@ 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 { join
|
|
117
|
+
import { join, resolve } from "path";
|
|
412
118
|
import { pathToFileURL } from "url";
|
|
413
119
|
import {
|
|
414
120
|
holdsProjection,
|
|
@@ -416,7 +122,7 @@ import {
|
|
|
416
122
|
localExtensionsFile,
|
|
417
123
|
MANIFEST_PATH,
|
|
418
124
|
PENV_DIR,
|
|
419
|
-
PenvError as
|
|
125
|
+
PenvError as PenvError2,
|
|
420
126
|
packageDir,
|
|
421
127
|
packageEntry,
|
|
422
128
|
parseLocalExtensions,
|
|
@@ -475,7 +181,7 @@ async function loadPluginProvider(type, context) {
|
|
|
475
181
|
}
|
|
476
182
|
const factory = mod[PLUGIN_FACTORY_EXPORT];
|
|
477
183
|
if (typeof factory !== "function") {
|
|
478
|
-
throw new
|
|
184
|
+
throw new PenvError2(
|
|
479
185
|
"PROVIDER_PLUGIN_INVALID",
|
|
480
186
|
`\`${type}\` does not export \`${PLUGIN_FACTORY_EXPORT}\``,
|
|
481
187
|
`A penv provider package must export \`${PLUGIN_FACTORY_EXPORT}(context) => Provider\`.`
|
|
@@ -522,7 +228,7 @@ function resolveExtension(type, projectRoot, environment, local = localExtension
|
|
|
522
228
|
function pinnedVersion(type, projectRoot) {
|
|
523
229
|
let manifest;
|
|
524
230
|
try {
|
|
525
|
-
manifest = parseManifest(
|
|
231
|
+
manifest = parseManifest(readFileSync(join(projectRoot, ...MANIFEST_PATH.split("/")), "utf8"));
|
|
526
232
|
} catch {
|
|
527
233
|
return void 0;
|
|
528
234
|
}
|
|
@@ -535,7 +241,7 @@ function storedExtension(type, version) {
|
|
|
535
241
|
function localExtensions(projectRoot) {
|
|
536
242
|
let text;
|
|
537
243
|
try {
|
|
538
|
-
text =
|
|
244
|
+
text = readFileSync(localExtensionsFile(projectRoot), "utf8");
|
|
539
245
|
} catch {
|
|
540
246
|
return [];
|
|
541
247
|
}
|
|
@@ -545,7 +251,7 @@ function isCi(value) {
|
|
|
545
251
|
return value !== void 0 && value !== "" && value !== "0" && value.toLowerCase() !== "false";
|
|
546
252
|
}
|
|
547
253
|
function localExtensionInCi(type, environment) {
|
|
548
|
-
return new
|
|
254
|
+
return new PenvError2(
|
|
549
255
|
"LOCAL_EXTENSION_IN_CI",
|
|
550
256
|
`The provider \`${type}\` for environment ${environment} is a local extension, and this is CI`,
|
|
551
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.`
|
|
@@ -574,7 +280,7 @@ function assertSatisfiesContract(provider, specifier) {
|
|
|
574
280
|
const contract = projection ? "the @penvhq/core ProjectionProvider contract its declared capabilities select" : "the @penvhq/core Provider contract that the filesystem provider defines";
|
|
575
281
|
for (const method of methods) {
|
|
576
282
|
if (typeof provider[method] !== "function") {
|
|
577
|
-
throw new
|
|
283
|
+
throw new PenvError2(
|
|
578
284
|
"PROVIDER_PLUGIN_INVALID",
|
|
579
285
|
`The provider from \`${specifier}\` is missing \`${method}()\``,
|
|
580
286
|
`It must satisfy ${contract}.`
|
|
@@ -587,21 +293,21 @@ function where(environment) {
|
|
|
587
293
|
}
|
|
588
294
|
function unknownProvider(type, environment) {
|
|
589
295
|
const preinstalled = [...REGISTRY.keys()].map((name) => `\`${name}\``).join(", ");
|
|
590
|
-
return new
|
|
296
|
+
return new PenvError2(
|
|
591
297
|
"UNKNOWN_PROVIDER",
|
|
592
298
|
`The provider \`${type}\`${where(environment)} in penv.config.ts is nowhere penv looks for it`,
|
|
593
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.`
|
|
594
300
|
);
|
|
595
301
|
}
|
|
596
302
|
function extensionNotInstalled(type, version, environment) {
|
|
597
|
-
return new
|
|
303
|
+
return new PenvError2(
|
|
598
304
|
"EXTENSION_NOT_INSTALLED",
|
|
599
305
|
`${MANIFEST_PATH} pins \`${type}\` ${version}${where(environment)}, and it is not installed in ${penvHome(process.env)}`,
|
|
600
306
|
"Run `penv install` \u2014 it downloads and verifies every version the manifest pins, extensions included."
|
|
601
307
|
);
|
|
602
308
|
}
|
|
603
309
|
function localExtensionUnresolved(type, projectRoot, environment) {
|
|
604
|
-
return new
|
|
310
|
+
return new PenvError2(
|
|
605
311
|
"LOCAL_EXTENSION_UNRESOLVED",
|
|
606
312
|
`${LOCAL_EXTENSIONS_PATH} records \`${type}\`${where(environment)} as a package this project develops, and it does not resolve from ${projectRoot}`,
|
|
607
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.`
|
|
@@ -609,7 +315,7 @@ function localExtensionUnresolved(type, projectRoot, environment) {
|
|
|
609
315
|
}
|
|
610
316
|
function providerLoadFailed(type, path, cause) {
|
|
611
317
|
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
612
|
-
return new
|
|
318
|
+
return new PenvError2(
|
|
613
319
|
"PROVIDER_PLUGIN_LOAD",
|
|
614
320
|
`The provider package \`${type}\` failed to load from ${path}: ${detail}`,
|
|
615
321
|
"penv imports that file exactly as it stands, with no transform, so it has to be built JavaScript with its dependencies installed."
|
|
@@ -632,7 +338,7 @@ function openProject(cwd) {
|
|
|
632
338
|
}
|
|
633
339
|
function localTree(project) {
|
|
634
340
|
if (!(project.provider instanceof FilesystemProvider)) {
|
|
635
|
-
throw new
|
|
341
|
+
throw new PenvError3(
|
|
636
342
|
"PROVIDER_NOT_LOCAL",
|
|
637
343
|
`This command reads the local .penv tree synchronously, which the \`${project.provider.type}\` provider is not`,
|
|
638
344
|
"Run this against a filesystem-backed project, or use a command that speaks the async provider contract."
|
|
@@ -641,11 +347,11 @@ function localTree(project) {
|
|
|
641
347
|
return project.provider;
|
|
642
348
|
}
|
|
643
349
|
function selfContainedSchemaModule(root, schemaFile) {
|
|
644
|
-
const file =
|
|
645
|
-
if (!
|
|
350
|
+
const file = join2(root, ...schemaFile.split("/"));
|
|
351
|
+
if (!existsSync(file)) {
|
|
646
352
|
return void 0;
|
|
647
353
|
}
|
|
648
|
-
const source =
|
|
354
|
+
const source = readFileSync2(file, "utf8");
|
|
649
355
|
return source.includes("PenvSchemaShape") || source.includes("z.object") ? schemaFile : void 0;
|
|
650
356
|
}
|
|
651
357
|
function schemaShapeFileOf(project) {
|
|
@@ -674,7 +380,7 @@ function refFromKey(key, config) {
|
|
|
674
380
|
const segments = key.split(KEY_SEPARATOR).filter((segment) => segment.length > 0);
|
|
675
381
|
const name = segments[segments.length - 1];
|
|
676
382
|
if (name === void 0) {
|
|
677
|
-
throw new
|
|
383
|
+
throw new PenvError3(
|
|
678
384
|
"PARAMETER_KEY",
|
|
679
385
|
`\`${key}\` names no parameter`,
|
|
680
386
|
"A key is `<namespace>/<name>` or `<namespace>.<name>`, e.g. `redis/password`."
|
|
@@ -693,13 +399,13 @@ function assertWritableKey(key) {
|
|
|
693
399
|
const ref = refFromAccessPath(segments);
|
|
694
400
|
if (ref !== void 0) {
|
|
695
401
|
const suggestion = [...ref.namespace, ref.name].join("/");
|
|
696
|
-
throw new
|
|
402
|
+
throw new PenvError3(
|
|
697
403
|
"PARAMETER_KEY_CASING",
|
|
698
404
|
`\`${key}\` is not a canonical parameter name`,
|
|
699
405
|
`Parameter files are lower-case and hyphenated. Did you mean \`${suggestion}\`? That is the file that backs the \`${key}\` key in your schema.`
|
|
700
406
|
);
|
|
701
407
|
}
|
|
702
|
-
throw new
|
|
408
|
+
throw new PenvError3(
|
|
703
409
|
"PARAMETER_KEY_UNREACHABLE",
|
|
704
410
|
`No value file can be named that reaches \`${key}\``,
|
|
705
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."
|
|
@@ -747,7 +453,7 @@ function refsFrom(files) {
|
|
|
747
453
|
}
|
|
748
454
|
|
|
749
455
|
// src/ui.ts
|
|
750
|
-
import { PenvError as
|
|
456
|
+
import { PenvError as PenvError4 } from "@penvhq/core";
|
|
751
457
|
|
|
752
458
|
// src/style.ts
|
|
753
459
|
function supportsColor(stream) {
|
|
@@ -880,7 +586,7 @@ function writeError(lines) {
|
|
|
880
586
|
}
|
|
881
587
|
}
|
|
882
588
|
function reportError(error) {
|
|
883
|
-
if (error instanceof
|
|
589
|
+
if (error instanceof PenvError4) {
|
|
884
590
|
process.stderr.write(`${err.red(CROSS)} ${error.summary}
|
|
885
591
|
`);
|
|
886
592
|
if (error.remedy !== void 0) {
|
|
@@ -911,9 +617,9 @@ async function guard(run) {
|
|
|
911
617
|
}
|
|
912
618
|
|
|
913
619
|
// src/commands/run.ts
|
|
914
|
-
import { existsSync as
|
|
620
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, watch } from "fs";
|
|
915
621
|
import { createRequire as createRequire2 } from "module";
|
|
916
|
-
import { basename, dirname as dirname2, join as
|
|
622
|
+
import { basename, dirname as dirname2, join as join3, resolve as resolve2 } from "path";
|
|
917
623
|
import {
|
|
918
624
|
ARTIFACT_BUILD_COMMAND,
|
|
919
625
|
assertArtifactFor,
|
|
@@ -923,7 +629,7 @@ import {
|
|
|
923
629
|
MissingMaterializationError,
|
|
924
630
|
openSealed,
|
|
925
631
|
own,
|
|
926
|
-
PenvError as
|
|
632
|
+
PenvError as PenvError6,
|
|
927
633
|
parseArtifact,
|
|
928
634
|
RECORDS_PATH,
|
|
929
635
|
UndecryptableValueError,
|
|
@@ -1172,7 +878,7 @@ import {
|
|
|
1172
878
|
checkNameCollisions,
|
|
1173
879
|
jitiFor,
|
|
1174
880
|
NameCollisionError,
|
|
1175
|
-
PenvError as
|
|
881
|
+
PenvError as PenvError5,
|
|
1176
882
|
ReservedTokenError as ReservedTokenError2,
|
|
1177
883
|
resolveAll,
|
|
1178
884
|
SCHEMA_HARVEST_ENV,
|
|
@@ -1521,7 +1227,7 @@ async function checkEnvironment(project, environment) {
|
|
|
1521
1227
|
try {
|
|
1522
1228
|
files = await project.provider.list();
|
|
1523
1229
|
} catch (error) {
|
|
1524
|
-
if (!(error instanceof
|
|
1230
|
+
if (!(error instanceof PenvError5)) {
|
|
1525
1231
|
throw error;
|
|
1526
1232
|
}
|
|
1527
1233
|
return {
|
|
@@ -1673,7 +1379,7 @@ function assertNotNested(host, inner) {
|
|
|
1673
1379
|
if (outer === void 0) {
|
|
1674
1380
|
return;
|
|
1675
1381
|
}
|
|
1676
|
-
throw new
|
|
1382
|
+
throw new PenvError6(
|
|
1677
1383
|
"RUN_NESTED",
|
|
1678
1384
|
`\`${inner}\` is starting inside \`${outer}\`, and two penv environments cannot own one process`,
|
|
1679
1385
|
`Drop one of the two wrappers \u2014 the inner one is in a package.json script \u2014 then run \`${outer}\` again.`
|
|
@@ -1683,7 +1389,7 @@ function assertSource(source, inner) {
|
|
|
1683
1389
|
if (source === "project" || source === "snapshot") {
|
|
1684
1390
|
return source;
|
|
1685
1391
|
}
|
|
1686
|
-
throw new
|
|
1392
|
+
throw new PenvError6(
|
|
1687
1393
|
"RUN_SOURCE_UNKNOWN",
|
|
1688
1394
|
`\`--source ${source}\` names no source penv reads`,
|
|
1689
1395
|
`A run reads ${SOURCES.map((name) => `\`${name}\``).join(" or ")}, and \`project\` is the default \u2014 so \`${inner}\` reads the local tree.`
|
|
@@ -1704,7 +1410,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
|
|
|
1704
1410
|
}
|
|
1705
1411
|
const parameter = [...ref.namespace, ref.name].join("/");
|
|
1706
1412
|
const prefix = prefixes.find((candidate) => variable.startsWith(candidate));
|
|
1707
|
-
throw new
|
|
1413
|
+
throw new PenvError6(
|
|
1708
1414
|
"RUN_PUBLIC_SECRET",
|
|
1709
1415
|
`The secret ${parameter} maps to ${variable}, which the \`${prefix}\` prefix publishes to the browser`,
|
|
1710
1416
|
`Rename the parameter, or drop \`secret\` from its meta if it is not one \u2014 then \`${retry}\`.`
|
|
@@ -1713,7 +1419,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
|
|
|
1713
1419
|
}
|
|
1714
1420
|
function invalidConfiguration(result2, inner) {
|
|
1715
1421
|
const lines = result2.issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
|
|
1716
|
-
return new
|
|
1422
|
+
return new PenvError6(
|
|
1717
1423
|
"RUN_INVALID_CONFIGURATION",
|
|
1718
1424
|
`Configuration for environment ${result2.environment} is not valid, so nothing was started:
|
|
1719
1425
|
${lines}`,
|
|
@@ -1735,7 +1441,7 @@ function assertNoActiveDotenv(project) {
|
|
|
1735
1441
|
if (first === void 0) {
|
|
1736
1442
|
return;
|
|
1737
1443
|
}
|
|
1738
|
-
throw new
|
|
1444
|
+
throw new PenvError6(
|
|
1739
1445
|
"RUN_DOTENV_ACTIVE",
|
|
1740
1446
|
`${first.name} is active configuration again, and your framework would read it beside penv's records`,
|
|
1741
1447
|
`Adopt it with \`penv init\`, or delete ${first.name} \u2014 its values belong in ${RECORDS_PATH}/.`
|
|
@@ -1753,10 +1459,10 @@ function packageManifest(specifier, root) {
|
|
|
1753
1459
|
return void 0;
|
|
1754
1460
|
}
|
|
1755
1461
|
for (; ; ) {
|
|
1756
|
-
const file =
|
|
1757
|
-
if (
|
|
1462
|
+
const file = join3(directory, "package.json");
|
|
1463
|
+
if (existsSync2(file)) {
|
|
1758
1464
|
try {
|
|
1759
|
-
const parsed = JSON.parse(
|
|
1465
|
+
const parsed = JSON.parse(readFileSync3(file, "utf8"));
|
|
1760
1466
|
if (isPlainObject(parsed) && parsed.name === specifier) {
|
|
1761
1467
|
return parsed;
|
|
1762
1468
|
}
|
|
@@ -1783,7 +1489,7 @@ function declaredCredentials(config, root) {
|
|
|
1783
1489
|
continue;
|
|
1784
1490
|
}
|
|
1785
1491
|
if (!Array.isArray(credentials) || credentials.some((name) => typeof name !== "string" || !VARIABLE.test(name))) {
|
|
1786
|
-
throw new
|
|
1492
|
+
throw new PenvError6(
|
|
1787
1493
|
"PROVIDER_CREDENTIALS_INVALID",
|
|
1788
1494
|
`\`${provider.type}\` declares \`penv.credentials\`, and it is not a list of variable names`,
|
|
1789
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.`
|
|
@@ -1819,7 +1525,7 @@ async function prepare(project, environment, host, inner) {
|
|
|
1819
1525
|
function snapshotPath(host) {
|
|
1820
1526
|
const path = host[SNAPSHOT_VARIABLE]?.trim();
|
|
1821
1527
|
if (path === void 0 || path.length === 0) {
|
|
1822
|
-
throw new
|
|
1528
|
+
throw new PenvError6(
|
|
1823
1529
|
"RUN_SNAPSHOT_UNSET",
|
|
1824
1530
|
`\`--source snapshot\` reads the sealed artifact ${SNAPSHOT_VARIABLE} names, and ${SNAPSHOT_VARIABLE} is not set`,
|
|
1825
1531
|
`Build one with \`${ARTIFACT_BUILD_COMMAND}\` and point ${SNAPSHOT_VARIABLE} at it.`
|
|
@@ -1829,9 +1535,9 @@ function snapshotPath(host) {
|
|
|
1829
1535
|
}
|
|
1830
1536
|
function readSnapshot(path) {
|
|
1831
1537
|
try {
|
|
1832
|
-
return
|
|
1538
|
+
return readFileSync3(path, "utf8");
|
|
1833
1539
|
} catch {
|
|
1834
|
-
throw new
|
|
1540
|
+
throw new PenvError6(
|
|
1835
1541
|
"RUN_SNAPSHOT_MISSING",
|
|
1836
1542
|
`${SNAPSHOT_VARIABLE} names ${path}, and penv cannot read a sealed artifact there`,
|
|
1837
1543
|
`Point ${SNAPSHOT_VARIABLE} at the artifact your release mounted \u2014 \`${ARTIFACT_BUILD_COMMAND}\` writes one.`
|
|
@@ -1908,7 +1614,7 @@ function watchProject(project, onChange) {
|
|
|
1908
1614
|
timer = setTimeout(onChange, DEBOUNCE_MS);
|
|
1909
1615
|
};
|
|
1910
1616
|
const add = (target, recursive, only) => {
|
|
1911
|
-
if (!
|
|
1617
|
+
if (!existsSync2(target)) {
|
|
1912
1618
|
return;
|
|
1913
1619
|
}
|
|
1914
1620
|
try {
|
|
@@ -1947,7 +1653,7 @@ async function runRun(options) {
|
|
|
1947
1653
|
}
|
|
1948
1654
|
if (source === "snapshot") {
|
|
1949
1655
|
if (options.watch === true) {
|
|
1950
|
-
throw new
|
|
1656
|
+
throw new PenvError6(
|
|
1951
1657
|
"RUN_SNAPSHOT_WATCH",
|
|
1952
1658
|
"`--watch` re-syncs the project tree, and a run from a sealed artifact has no tree to watch",
|
|
1953
1659
|
"Drop `--watch` \u2014 an artifact is built once and read unchanged. Watch the project instead: `penv run --watch -- <command>`."
|
|
@@ -2091,7 +1797,7 @@ function buildCommand(environment, out2) {
|
|
|
2091
1797
|
function targetOf(options) {
|
|
2092
1798
|
const environment = options.environment?.trim();
|
|
2093
1799
|
if (environment === void 0 || environment.length === 0) {
|
|
2094
|
-
throw new
|
|
1800
|
+
throw new PenvError7(
|
|
2095
1801
|
"ARTIFACT_ENV_REQUIRED",
|
|
2096
1802
|
"`penv artifact build` names the environment it builds for, and `--env` was not given",
|
|
2097
1803
|
`Name it: \`${buildCommand(void 0, options.out)}\`. An artifact carries one environment, and penv will not pick which.`
|
|
@@ -2102,13 +1808,13 @@ function targetOf(options) {
|
|
|
2102
1808
|
function outputOf(options, environment) {
|
|
2103
1809
|
const out2 = options.out?.trim();
|
|
2104
1810
|
if (out2 === void 0 || out2.length === 0) {
|
|
2105
|
-
throw new
|
|
1811
|
+
throw new PenvError7(
|
|
2106
1812
|
"ARTIFACT_OUT_REQUIRED",
|
|
2107
1813
|
"`penv artifact build` writes where it is told, and `--out` was not given",
|
|
2108
1814
|
`Name the path: \`${buildCommand(environment, void 0)}\`. The artifact belongs outside the repository, so there is no default worth having.`
|
|
2109
1815
|
);
|
|
2110
1816
|
}
|
|
2111
|
-
return
|
|
1817
|
+
return isAbsolute(out2) ? out2 : resolve3(options.cwd, out2);
|
|
2112
1818
|
}
|
|
2113
1819
|
async function winnerOf(project, ref, environment) {
|
|
2114
1820
|
for (const file of candidatesFor2(ref, environment, true)) {
|
|
@@ -2120,7 +1826,7 @@ async function winnerOf(project, ref, environment) {
|
|
|
2120
1826
|
return void 0;
|
|
2121
1827
|
}
|
|
2122
1828
|
function plaintextSecret(parameter, location, environment) {
|
|
2123
|
-
return new
|
|
1829
|
+
return new PenvError7(
|
|
2124
1830
|
"ARTIFACT_PLAINTEXT_SECRET",
|
|
2125
1831
|
`${parameter} is a secret for environment ${environment}, and its value comes from ${location}, which is not sealed`,
|
|
2126
1832
|
`Seal it with \`penv encrypt ${parameter} --env ${environment}\` \u2014 an artifact carries ciphertext or nothing.`
|
|
@@ -2134,7 +1840,7 @@ async function runArtifactBuild(options) {
|
|
|
2134
1840
|
const { schema, issues } = await loadSchema(project, environment);
|
|
2135
1841
|
if (schema === void 0) {
|
|
2136
1842
|
const lines = issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
|
|
2137
|
-
throw new
|
|
1843
|
+
throw new PenvError7(
|
|
2138
1844
|
"ARTIFACT_NO_SCHEMA",
|
|
2139
1845
|
`The schema did not load, so penv cannot tell what this artifact should deliver:
|
|
2140
1846
|
${lines}`,
|
|
@@ -2186,7 +1892,7 @@ ${lines}`,
|
|
|
2186
1892
|
values: present,
|
|
2187
1893
|
sealed,
|
|
2188
1894
|
absent: refs.length - present,
|
|
2189
|
-
insideRepo: inside !== "" && !inside.startsWith("..") && !
|
|
1895
|
+
insideRepo: inside !== "" && !inside.startsWith("..") && !isAbsolute(inside)
|
|
2190
1896
|
};
|
|
2191
1897
|
}
|
|
2192
1898
|
function displayPath(cwd, file) {
|
|
@@ -2247,25 +1953,25 @@ import { defineCommand as defineCommand5 } from "citty";
|
|
|
2247
1953
|
|
|
2248
1954
|
// src/cutover.ts
|
|
2249
1955
|
import {
|
|
2250
|
-
existsSync as
|
|
1956
|
+
existsSync as existsSync3,
|
|
2251
1957
|
mkdirSync as mkdirSync2,
|
|
2252
1958
|
readdirSync as readdirSync2,
|
|
2253
|
-
readFileSync as
|
|
1959
|
+
readFileSync as readFileSync4,
|
|
2254
1960
|
renameSync,
|
|
2255
1961
|
rmSync,
|
|
2256
1962
|
writeFileSync as writeFileSync2
|
|
2257
1963
|
} from "fs";
|
|
2258
|
-
import { dirname as dirname4, join as
|
|
1964
|
+
import { dirname as dirname4, join as join4, resolve as resolve4 } from "path";
|
|
2259
1965
|
import {
|
|
2260
1966
|
CUTOVER_PATH,
|
|
2261
1967
|
findConfigFile,
|
|
2262
|
-
PenvError as
|
|
1968
|
+
PenvError as PenvError8,
|
|
2263
1969
|
ROLLBACK_DOTENV_PATH,
|
|
2264
1970
|
ROLLBACK_PATH
|
|
2265
1971
|
} from "@penvhq/core";
|
|
2266
1972
|
var CUTOVER_FORMAT = 1;
|
|
2267
1973
|
function fileFor(root, relativePosix) {
|
|
2268
|
-
return
|
|
1974
|
+
return join4(root, ...relativePosix.split("/"));
|
|
2269
1975
|
}
|
|
2270
1976
|
function bundleDir(root) {
|
|
2271
1977
|
return fileFor(root, ROLLBACK_DOTENV_PATH);
|
|
@@ -2289,12 +1995,12 @@ function bundleUnresolved(root) {
|
|
|
2289
1995
|
}
|
|
2290
1996
|
function readCutover(root) {
|
|
2291
1997
|
const file = cutoverFile(root);
|
|
2292
|
-
if (!
|
|
1998
|
+
if (!existsSync3(file)) {
|
|
2293
1999
|
return void 0;
|
|
2294
2000
|
}
|
|
2295
2001
|
let parsed;
|
|
2296
2002
|
try {
|
|
2297
|
-
parsed = JSON.parse(
|
|
2003
|
+
parsed = JSON.parse(readFileSync4(file, "utf8"));
|
|
2298
2004
|
} catch {
|
|
2299
2005
|
throw unreadable("it is not JSON");
|
|
2300
2006
|
}
|
|
@@ -2320,7 +2026,7 @@ function readCutover(root) {
|
|
|
2320
2026
|
};
|
|
2321
2027
|
}
|
|
2322
2028
|
function unreadable(what) {
|
|
2323
|
-
return new
|
|
2029
|
+
return new PenvError8(
|
|
2324
2030
|
"CUTOVER_UNREADABLE",
|
|
2325
2031
|
`${CUTOVER_PATH} records the last dotenv cutover, and ${what}`,
|
|
2326
2032
|
`Run \`penv cleanup\` to drop that record and the rollback bundle it names.`
|
|
@@ -2352,7 +2058,7 @@ function bundleDotenvFiles(root, files, environments, now = /* @__PURE__ */ new
|
|
|
2352
2058
|
const bundle = bundleDir(root);
|
|
2353
2059
|
mkdirSync2(bundle, { recursive: true });
|
|
2354
2060
|
for (const name of files) {
|
|
2355
|
-
renameSync(
|
|
2061
|
+
renameSync(join4(root, name), join4(bundle, name));
|
|
2356
2062
|
}
|
|
2357
2063
|
return cutover;
|
|
2358
2064
|
}
|
|
@@ -2361,7 +2067,7 @@ function runUndo(options) {
|
|
|
2361
2067
|
const cutover = readCutover(root);
|
|
2362
2068
|
const bundled = bundledFiles(root);
|
|
2363
2069
|
if (cutover === void 0 && bundled.length === 0) {
|
|
2364
|
-
throw new
|
|
2070
|
+
throw new PenvError8(
|
|
2365
2071
|
"INIT_UNDO_NOTHING",
|
|
2366
2072
|
"There is no dotenv cutover to undo in this project",
|
|
2367
2073
|
"Run `penv init` to adopt your dotenv files; undo puts them back afterwards."
|
|
@@ -2371,11 +2077,11 @@ function runUndo(options) {
|
|
|
2371
2077
|
const names = [...recorded, ...bundled.filter((name) => !recorded.includes(name))];
|
|
2372
2078
|
const bundle = bundleDir(root);
|
|
2373
2079
|
const held = new Set(bundled);
|
|
2374
|
-
const occupied2 = names.filter((name) => held.has(name) &&
|
|
2080
|
+
const occupied2 = names.filter((name) => held.has(name) && existsSync3(join4(root, name)));
|
|
2375
2081
|
if (occupied2.length > 0) {
|
|
2376
2082
|
const listed = occupied2.join(", ");
|
|
2377
2083
|
const many = occupied2.length > 1;
|
|
2378
|
-
throw new
|
|
2084
|
+
throw new PenvError8(
|
|
2379
2085
|
"INIT_UNDO_OCCUPIED",
|
|
2380
2086
|
`${listed} ${many ? "exist" : "exists"} again, and restoring what penv moved aside would write over ${many ? "them" : "it"}`,
|
|
2381
2087
|
`Move ${listed} out of the way, or run \`penv cleanup\` to keep ${many ? "them" : "it"} and drop the bundle. Nothing was restored.`
|
|
@@ -2386,9 +2092,9 @@ function runUndo(options) {
|
|
|
2386
2092
|
const missing = [];
|
|
2387
2093
|
for (const name of names) {
|
|
2388
2094
|
if (held.has(name)) {
|
|
2389
|
-
renameSync(
|
|
2095
|
+
renameSync(join4(bundle, name), join4(root, name));
|
|
2390
2096
|
restored.push(name);
|
|
2391
|
-
} else if (
|
|
2097
|
+
} else if (existsSync3(join4(root, name))) {
|
|
2392
2098
|
alreadyBack.push(name);
|
|
2393
2099
|
} else {
|
|
2394
2100
|
missing.push(name);
|
|
@@ -2400,7 +2106,7 @@ function runUndo(options) {
|
|
|
2400
2106
|
function runCleanup(options) {
|
|
2401
2107
|
const root = cutoverRoot(options.cwd);
|
|
2402
2108
|
const held = bundledFiles(root);
|
|
2403
|
-
const cleaned = held.length > 0 ||
|
|
2109
|
+
const cleaned = held.length > 0 || existsSync3(cutoverFile(root)) || existsSync3(fileFor(root, ROLLBACK_PATH));
|
|
2404
2110
|
removeBundle(root);
|
|
2405
2111
|
return { root, removed: held, cleaned };
|
|
2406
2112
|
}
|
|
@@ -2441,8 +2147,8 @@ var cleanupCommand = defineCommand5({
|
|
|
2441
2147
|
});
|
|
2442
2148
|
|
|
2443
2149
|
// src/commands/doctor.ts
|
|
2444
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
2445
|
-
import { join as
|
|
2150
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync } from "fs";
|
|
2151
|
+
import { join as join5, relative as relative2 } from "path";
|
|
2446
2152
|
import {
|
|
2447
2153
|
ARTIFACT_BUILD_COMMAND as ARTIFACT_BUILD_COMMAND2,
|
|
2448
2154
|
accessPath as accessPath3,
|
|
@@ -2468,7 +2174,7 @@ import { defineCommand as defineCommand7 } from "citty";
|
|
|
2468
2174
|
import {
|
|
2469
2175
|
checkNameCollisions as checkNameCollisions2,
|
|
2470
2176
|
holdsProjection as holdsProjection3,
|
|
2471
|
-
PenvError as
|
|
2177
|
+
PenvError as PenvError9,
|
|
2472
2178
|
recordPath,
|
|
2473
2179
|
requireValue,
|
|
2474
2180
|
variableName as variableName4
|
|
@@ -2536,7 +2242,7 @@ async function destinationFor(project, environment, options) {
|
|
|
2536
2242
|
const declared = project.config.providers[environment];
|
|
2537
2243
|
const location = declared?.location;
|
|
2538
2244
|
if (declared === void 0 || declared.type === LOCAL_TREE_TYPE) {
|
|
2539
|
-
throw new
|
|
2245
|
+
throw new PenvError9(
|
|
2540
2246
|
"NO_DESTINATION",
|
|
2541
2247
|
`Environment ${environment}'s provider is the local records tree itself, so penv has nowhere to push`,
|
|
2542
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>\`.`
|
|
@@ -2563,7 +2269,7 @@ function plan(resolutions, config, environment, allowDecrypt) {
|
|
|
2563
2269
|
let encrypted = false;
|
|
2564
2270
|
if (winner.file.encrypted) {
|
|
2565
2271
|
if (!allowDecrypt) {
|
|
2566
|
-
throw new
|
|
2272
|
+
throw new PenvError9(
|
|
2567
2273
|
"ENCRYPTED_VALUE_REFUSED",
|
|
2568
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`,
|
|
2569
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."
|
|
@@ -2616,7 +2322,7 @@ async function ensureTargetApproved(provider, environment, options) {
|
|
|
2616
2322
|
return false;
|
|
2617
2323
|
}
|
|
2618
2324
|
if (provider.ensureTarget === void 0) {
|
|
2619
|
-
throw new
|
|
2325
|
+
throw new PenvError9(
|
|
2620
2326
|
"MISSING_TARGET",
|
|
2621
2327
|
`The destination has no environment \`${environment}\` to receive this push, and this provider cannot create one`,
|
|
2622
2328
|
`Create the environment on the destination side, then run \`penv push --env ${environment}\` again.`
|
|
@@ -2626,7 +2332,7 @@ async function ensureTargetApproved(provider, environment, options) {
|
|
|
2626
2332
|
`The destination has no environment \`${environment}\`. Create it?`
|
|
2627
2333
|
);
|
|
2628
2334
|
if (!approved) {
|
|
2629
|
-
throw new
|
|
2335
|
+
throw new PenvError9(
|
|
2630
2336
|
"MISSING_TARGET",
|
|
2631
2337
|
`The destination has no environment \`${environment}\` to receive this push`,
|
|
2632
2338
|
`Re-run with \`--yes\` to create it, answer \`y\` at the prompt, or create it on the destination side yourself.`
|
|
@@ -3396,7 +3102,7 @@ function scannableFiles(directory, out2) {
|
|
|
3396
3102
|
return;
|
|
3397
3103
|
}
|
|
3398
3104
|
for (const entry of entries) {
|
|
3399
|
-
const path =
|
|
3105
|
+
const path = join5(directory, entry.name);
|
|
3400
3106
|
if (entry.isDirectory()) {
|
|
3401
3107
|
if (!UNSCANNED_DIRS.has(entry.name)) {
|
|
3402
3108
|
scannableFiles(path, out2);
|
|
@@ -3438,10 +3144,10 @@ function artifactFindings(project) {
|
|
|
3438
3144
|
for (const file of files) {
|
|
3439
3145
|
let text;
|
|
3440
3146
|
try {
|
|
3441
|
-
if (
|
|
3147
|
+
if (statSync(file).size > ARTIFACT_SCAN_LIMIT) {
|
|
3442
3148
|
continue;
|
|
3443
3149
|
}
|
|
3444
|
-
text =
|
|
3150
|
+
text = readFileSync5(file, "utf8");
|
|
3445
3151
|
} catch {
|
|
3446
3152
|
continue;
|
|
3447
3153
|
}
|
|
@@ -3790,7 +3496,7 @@ import {
|
|
|
3790
3496
|
formatValueFile as formatValueFile5,
|
|
3791
3497
|
isSecret as isSecret5,
|
|
3792
3498
|
openValue as openValue3,
|
|
3793
|
-
PenvError as
|
|
3499
|
+
PenvError as PenvError11,
|
|
3794
3500
|
parameterId as parameterId5,
|
|
3795
3501
|
recordPath as recordPath3,
|
|
3796
3502
|
sealValue as sealValue2
|
|
@@ -3801,7 +3507,7 @@ import { defineCommand as defineCommand9 } from "citty";
|
|
|
3801
3507
|
import {
|
|
3802
3508
|
formatValueFile as formatValueFile4,
|
|
3803
3509
|
isSecret as isSecret4,
|
|
3804
|
-
PenvError as
|
|
3510
|
+
PenvError as PenvError10,
|
|
3805
3511
|
parameterId as parameterId4,
|
|
3806
3512
|
recordPath as recordPath2,
|
|
3807
3513
|
sealValue
|
|
@@ -3825,7 +3531,7 @@ function targetScope(project, options, key) {
|
|
|
3825
3531
|
return scopeFrom(options);
|
|
3826
3532
|
}
|
|
3827
3533
|
if (environment.trim().length === 0) {
|
|
3828
|
-
throw new
|
|
3534
|
+
throw new PenvError10(
|
|
3829
3535
|
"ENVIRONMENT_FLAG_EMPTY",
|
|
3830
3536
|
`\`--env\` for parameter ${key} names no environment`,
|
|
3831
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.`
|
|
@@ -3838,7 +3544,7 @@ function policyEnvironment(project, options) {
|
|
|
3838
3544
|
}
|
|
3839
3545
|
function sealFor(project, file, value, parameter, environment) {
|
|
3840
3546
|
if (environment === void 0) {
|
|
3841
|
-
throw new
|
|
3547
|
+
throw new PenvError10(
|
|
3842
3548
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
3843
3549
|
`Parameter ${parameter} is a secret, and ${recordPath2(formatValueFile4(file))} names no environment`,
|
|
3844
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."
|
|
@@ -3940,7 +3646,7 @@ function twins(project, key, options) {
|
|
|
3940
3646
|
}
|
|
3941
3647
|
function environmentFor(project, options, verb) {
|
|
3942
3648
|
if (options.environment === void 0) {
|
|
3943
|
-
throw new
|
|
3649
|
+
throw new PenvError11(
|
|
3944
3650
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
3945
3651
|
`\`penv ${verb}\` names no environment, and keys are declared per environment`,
|
|
3946
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."
|
|
@@ -3959,7 +3665,7 @@ async function runEncrypt(options) {
|
|
|
3959
3665
|
const value = await readOne(project, plain);
|
|
3960
3666
|
if (value === void 0) {
|
|
3961
3667
|
const already = await readOne(project, sealed);
|
|
3962
|
-
throw new
|
|
3668
|
+
throw new PenvError11(
|
|
3963
3669
|
"PARAMETER_ABSENT",
|
|
3964
3670
|
already === void 0 ? `Parameter ${parameter} has no value file at ${recordPath3(formatValueFile5(plain))}` : `Parameter ${parameter} is already encrypted at ${recordPath3(formatValueFile5(sealed))}`,
|
|
3965
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."
|
|
@@ -3980,7 +3686,7 @@ async function runDecrypt(options) {
|
|
|
3980
3686
|
const [plain, sealed] = twins(project, options.key, options);
|
|
3981
3687
|
const parameter = parameterId5(plain);
|
|
3982
3688
|
if (isSecret5(await project.provider.readMeta(plain), environment)) {
|
|
3983
|
-
throw new
|
|
3689
|
+
throw new PenvError11(
|
|
3984
3690
|
"SECRET_DECRYPT_REFUSED",
|
|
3985
3691
|
`Parameter ${parameter} is declared a secret for environment ${environment}, so penv will not write it in plaintext`,
|
|
3986
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."
|
|
@@ -3988,7 +3694,7 @@ async function runDecrypt(options) {
|
|
|
3988
3694
|
}
|
|
3989
3695
|
const stored = await readOne(project, sealed);
|
|
3990
3696
|
if (stored === void 0) {
|
|
3991
|
-
throw new
|
|
3697
|
+
throw new PenvError11(
|
|
3992
3698
|
"PARAMETER_ABSENT",
|
|
3993
3699
|
`Parameter ${parameter} has no encrypted value file at ${recordPath3(formatValueFile5(sealed))}`,
|
|
3994
3700
|
`Nothing to decrypt. \`penv get ${options.key} --env ${environment} --explain\` shows every file penv looked at.`
|
|
@@ -4011,7 +3717,7 @@ async function runDecrypt(options) {
|
|
|
4011
3717
|
removed: formatValueFile5(sealed)
|
|
4012
3718
|
};
|
|
4013
3719
|
}
|
|
4014
|
-
var UndecryptableAt = class extends
|
|
3720
|
+
var UndecryptableAt = class extends PenvError11 {
|
|
4015
3721
|
constructor(parameter, environment, location, detail) {
|
|
4016
3722
|
super(
|
|
4017
3723
|
"VALUE_UNDECRYPTABLE",
|
|
@@ -4066,7 +3772,7 @@ var decryptCommand = defineCommand9({
|
|
|
4066
3772
|
});
|
|
4067
3773
|
|
|
4068
3774
|
// src/commands/fill.ts
|
|
4069
|
-
import { PenvError as
|
|
3775
|
+
import { PenvError as PenvError12, recordPath as recordPath4, SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE2 } from "@penvhq/core";
|
|
4070
3776
|
import { defineCommand as defineCommand10 } from "citty";
|
|
4071
3777
|
var BLOCKING = /* @__PURE__ */ new Set(["config", "collision", "reserved"]);
|
|
4072
3778
|
async function runFill(options) {
|
|
@@ -4081,7 +3787,7 @@ async function runFill(options) {
|
|
|
4081
3787
|
const detail = blockers.map(
|
|
4082
3788
|
(issue) => ` - ${issue.message}${issue.remedy === void 0 ? "" : ` (${issue.remedy})`}`
|
|
4083
3789
|
).join("\n");
|
|
4084
|
-
throw new
|
|
3790
|
+
throw new PenvError12(
|
|
4085
3791
|
"FILL_BLOCKED",
|
|
4086
3792
|
`penv fill cannot run: environment ${environment} has ${blockers.length} unresolved configuration ${blockers.length === 1 ? "issue" : "issues"}:
|
|
4087
3793
|
${detail}`,
|
|
@@ -4217,11 +3923,11 @@ var fillCommand = defineCommand10({
|
|
|
4217
3923
|
|
|
4218
3924
|
// src/commands/generate.ts
|
|
4219
3925
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
4220
|
-
import { isAbsolute as
|
|
3926
|
+
import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve5 } from "path";
|
|
4221
3927
|
import {
|
|
4222
3928
|
checkNameCollisions as checkNameCollisions3,
|
|
4223
3929
|
effectiveMeta as effectiveMeta2,
|
|
4224
|
-
PenvError as
|
|
3930
|
+
PenvError as PenvError13,
|
|
4225
3931
|
recordPath as recordPath5,
|
|
4226
3932
|
requireValue as requireValue2,
|
|
4227
3933
|
serializeDotenv,
|
|
@@ -4246,7 +3952,7 @@ function entriesFor(project, environment, allowDecrypt) {
|
|
|
4246
3952
|
const winner = resolution.winner;
|
|
4247
3953
|
if (winner?.file.encrypted === true) {
|
|
4248
3954
|
if (!allowDecrypt) {
|
|
4249
|
-
throw new
|
|
3955
|
+
throw new PenvError13(
|
|
4250
3956
|
"ENCRYPTED_VALUE_REFUSED",
|
|
4251
3957
|
`Parameter ${resolution.parameter} for environment ${environment} resolves to the encrypted value file ${recordPath5(winner.location)}, and \`penv generate\` writes plaintext`,
|
|
4252
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.`
|
|
@@ -4277,7 +3983,7 @@ function runGenerate(options) {
|
|
|
4277
3983
|
const project = openProject(options.cwd);
|
|
4278
3984
|
const environment = targetEnvironment(project, options.environment, options.envFlags);
|
|
4279
3985
|
const { entries, decrypted } = entriesFor(project, environment, options.allowDecrypt === true);
|
|
4280
|
-
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);
|
|
4281
3987
|
writeFileSync3(file, serializeDotenv(entries), "utf8");
|
|
4282
3988
|
return { file, environment, entries: entries.length, decrypted };
|
|
4283
3989
|
}
|
|
@@ -4330,7 +4036,7 @@ var generateCommand = defineCommand11({
|
|
|
4330
4036
|
});
|
|
4331
4037
|
|
|
4332
4038
|
// src/commands/get.ts
|
|
4333
|
-
import { PenvError as
|
|
4039
|
+
import { PenvError as PenvError14, recordPath as recordPath6, requireValue as requireValue3, resolveParameter } from "@penvhq/core";
|
|
4334
4040
|
import { defineCommand as defineCommand12 } from "citty";
|
|
4335
4041
|
async function runGet(options) {
|
|
4336
4042
|
const project = openProject(options.cwd);
|
|
@@ -4340,7 +4046,7 @@ async function runGet(options) {
|
|
|
4340
4046
|
const resolution = await resolveParameter(ref, environment, project.provider, keys);
|
|
4341
4047
|
const value = requireValue3(resolution, environment);
|
|
4342
4048
|
if (value === void 0) {
|
|
4343
|
-
throw new
|
|
4049
|
+
throw new PenvError14(
|
|
4344
4050
|
"PARAMETER_ABSENT",
|
|
4345
4051
|
`Parameter ${resolution.parameter} resolves to no value for environment ${environment}`,
|
|
4346
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.`
|
|
@@ -4416,15 +4122,15 @@ var getCommand = defineCommand12({
|
|
|
4416
4122
|
});
|
|
4417
4123
|
|
|
4418
4124
|
// src/commands/import.ts
|
|
4419
|
-
import { copyFileSync, existsSync as
|
|
4420
|
-
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";
|
|
4421
4127
|
import {
|
|
4422
4128
|
assertNever as assertNever2,
|
|
4423
4129
|
FilenameGrammarError,
|
|
4424
4130
|
findConfigFile as findConfigFile2,
|
|
4425
4131
|
loadConfigFrom as loadConfigFrom2,
|
|
4426
4132
|
lookupEnvironment,
|
|
4427
|
-
PenvError as
|
|
4133
|
+
PenvError as PenvError17,
|
|
4428
4134
|
parseDotenv as parseDotenv2,
|
|
4429
4135
|
schemaFileOf as schemaFileOf4,
|
|
4430
4136
|
UnknownEnvironmentError
|
|
@@ -4435,7 +4141,7 @@ import { defineCommand as defineCommand14 } from "citty";
|
|
|
4435
4141
|
import {
|
|
4436
4142
|
checkNameCollisions as checkNameCollisions4,
|
|
4437
4143
|
isReservedToken as isReservedToken3,
|
|
4438
|
-
PenvError as
|
|
4144
|
+
PenvError as PenvError15,
|
|
4439
4145
|
ReservedTokenError as ReservedTokenError3,
|
|
4440
4146
|
refFromVariable as refFromVariable2,
|
|
4441
4147
|
roundTripsCleanly,
|
|
@@ -4445,7 +4151,7 @@ function assertImportable(ref, variable) {
|
|
|
4445
4151
|
if (!ref.name.includes(".")) {
|
|
4446
4152
|
return;
|
|
4447
4153
|
}
|
|
4448
|
-
throw new
|
|
4154
|
+
throw new PenvError15(
|
|
4449
4155
|
"IMPORT_UNPARSEABLE_NAME",
|
|
4450
4156
|
`The variable ${variable} becomes the parameter \`${ref.name}\`, whose \`.\` would be read as a scope`,
|
|
4451
4157
|
`Filenames are split on \`.\`. Rename ${variable} in the source file, then import it again.`
|
|
@@ -4464,7 +4170,7 @@ function assertRoundTrips(ref, variable, config) {
|
|
|
4464
4170
|
return;
|
|
4465
4171
|
}
|
|
4466
4172
|
const generated = variableName7(ref, config);
|
|
4467
|
-
throw new
|
|
4173
|
+
throw new PenvError15(
|
|
4468
4174
|
"IMPORT_LOSSY_NAME",
|
|
4469
4175
|
`The variable ${variable} becomes the parameter \`${ref.name}\`, which regenerates as ${generated}`,
|
|
4470
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.`
|
|
@@ -4505,8 +4211,8 @@ function writeEntries(tree, entries, refs, scope) {
|
|
|
4505
4211
|
}
|
|
4506
4212
|
|
|
4507
4213
|
// src/detect.ts
|
|
4508
|
-
import { existsSync as
|
|
4509
|
-
import { join as
|
|
4214
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
|
|
4215
|
+
import { join as join6 } from "path";
|
|
4510
4216
|
import { DEFAULT_SCHEMA_FILE } from "@penvhq/core";
|
|
4511
4217
|
var SIGNATURES = [
|
|
4512
4218
|
{ name: "Next.js", packages: ["next"], publicPrefixes: ["NEXT_PUBLIC_"] },
|
|
@@ -4523,7 +4229,7 @@ var SIGNATURES = [
|
|
|
4523
4229
|
function exportsSchema(file) {
|
|
4524
4230
|
let source;
|
|
4525
4231
|
try {
|
|
4526
|
-
source =
|
|
4232
|
+
source = readFileSync6(file, "utf8");
|
|
4527
4233
|
} catch {
|
|
4528
4234
|
return false;
|
|
4529
4235
|
}
|
|
@@ -4534,8 +4240,8 @@ function exportsSchema(file) {
|
|
|
4534
4240
|
);
|
|
4535
4241
|
}
|
|
4536
4242
|
function occupied(cwd, relative5) {
|
|
4537
|
-
const file =
|
|
4538
|
-
return
|
|
4243
|
+
const file = join6(cwd, ...relative5.split("/"));
|
|
4244
|
+
return existsSync4(file) && !exportsSchema(file);
|
|
4539
4245
|
}
|
|
4540
4246
|
function schemaFileFor(cwd) {
|
|
4541
4247
|
const dir = srcPrefix(cwd);
|
|
@@ -4550,10 +4256,10 @@ function schemaFileFor(cwd) {
|
|
|
4550
4256
|
return { file: DEFAULT_SCHEMA_FILE, displaced: preferred };
|
|
4551
4257
|
}
|
|
4552
4258
|
function srcPrefix(cwd) {
|
|
4553
|
-
return
|
|
4259
|
+
return existsSync4(join6(cwd, "src")) ? "src/" : "";
|
|
4554
4260
|
}
|
|
4555
4261
|
function dependenciesOf(cwd) {
|
|
4556
|
-
const manifest =
|
|
4262
|
+
const manifest = manifestOf(cwd);
|
|
4557
4263
|
if (manifest === void 0) {
|
|
4558
4264
|
return void 0;
|
|
4559
4265
|
}
|
|
@@ -4568,14 +4274,14 @@ function dependenciesOf(cwd) {
|
|
|
4568
4274
|
}
|
|
4569
4275
|
return names;
|
|
4570
4276
|
}
|
|
4571
|
-
function
|
|
4572
|
-
const file =
|
|
4573
|
-
if (!
|
|
4277
|
+
function manifestOf(cwd) {
|
|
4278
|
+
const file = join6(cwd, "package.json");
|
|
4279
|
+
if (!existsSync4(file)) {
|
|
4574
4280
|
return void 0;
|
|
4575
4281
|
}
|
|
4576
4282
|
let manifest;
|
|
4577
4283
|
try {
|
|
4578
|
-
manifest = JSON.parse(
|
|
4284
|
+
manifest = JSON.parse(readFileSync6(file, "utf8"));
|
|
4579
4285
|
} catch {
|
|
4580
4286
|
return void 0;
|
|
4581
4287
|
}
|
|
@@ -4591,7 +4297,7 @@ function detectFramework(cwd) {
|
|
|
4591
4297
|
return detectedFrom(cwd, signature.name, signature.publicPrefixes);
|
|
4592
4298
|
}
|
|
4593
4299
|
}
|
|
4594
|
-
if (
|
|
4300
|
+
if (existsSync4(join6(cwd, "bunfig.toml")) || dependencies.has("bun-types")) {
|
|
4595
4301
|
return detectedFrom(cwd, "Bun", []);
|
|
4596
4302
|
}
|
|
4597
4303
|
return void 0;
|
|
@@ -4611,7 +4317,7 @@ function detectAlias(cwd) {
|
|
|
4611
4317
|
return hasImportsBlock(cwd) ? IMPORTS_ALIAS : DEFAULT_ALIAS;
|
|
4612
4318
|
}
|
|
4613
4319
|
function hasImportsBlock(cwd) {
|
|
4614
|
-
const manifest =
|
|
4320
|
+
const manifest = manifestOf(cwd);
|
|
4615
4321
|
const imports = manifest?.imports;
|
|
4616
4322
|
return imports !== null && typeof imports === "object" && !Array.isArray(imports);
|
|
4617
4323
|
}
|
|
@@ -4670,8 +4376,8 @@ function draftFieldsAcross(sources, environments) {
|
|
|
4670
4376
|
}
|
|
4671
4377
|
|
|
4672
4378
|
// src/commands/init.ts
|
|
4673
|
-
import { existsSync as
|
|
4674
|
-
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";
|
|
4675
4381
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
4676
4382
|
import {
|
|
4677
4383
|
CUTOVER_PATH as CUTOVER_PATH3,
|
|
@@ -4679,7 +4385,7 @@ import {
|
|
|
4679
4385
|
isLegalEnvironmentName as isLegalEnvironmentName2,
|
|
4680
4386
|
loadConfigFrom,
|
|
4681
4387
|
PENV_DIR as PENV_DIR2,
|
|
4682
|
-
PenvError as
|
|
4388
|
+
PenvError as PenvError16,
|
|
4683
4389
|
parameterId as parameterId6,
|
|
4684
4390
|
parseDotenv,
|
|
4685
4391
|
RECORDS_PATH as RECORDS_PATH3,
|
|
@@ -4696,32 +4402,32 @@ import { defineCommand as defineCommand13 } from "citty";
|
|
|
4696
4402
|
|
|
4697
4403
|
// src/scaffold-undo.ts
|
|
4698
4404
|
import {
|
|
4699
|
-
existsSync as
|
|
4405
|
+
existsSync as existsSync5,
|
|
4700
4406
|
mkdirSync as mkdirSync3,
|
|
4701
4407
|
readdirSync as readdirSync4,
|
|
4702
|
-
readFileSync as
|
|
4408
|
+
readFileSync as readFileSync7,
|
|
4703
4409
|
rmdirSync,
|
|
4704
|
-
statSync as
|
|
4410
|
+
statSync as statSync2,
|
|
4705
4411
|
unlinkSync,
|
|
4706
4412
|
writeFileSync as writeFileSync4
|
|
4707
4413
|
} from "fs";
|
|
4708
|
-
import { dirname as dirname5, join as
|
|
4414
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
4709
4415
|
function record(path, files, dirs) {
|
|
4710
4416
|
let stats;
|
|
4711
4417
|
try {
|
|
4712
|
-
stats =
|
|
4418
|
+
stats = statSync2(path);
|
|
4713
4419
|
} catch {
|
|
4714
4420
|
return;
|
|
4715
4421
|
}
|
|
4716
4422
|
if (stats.isDirectory()) {
|
|
4717
4423
|
dirs.add(path);
|
|
4718
4424
|
for (const entry of readdirSync4(path)) {
|
|
4719
|
-
record(
|
|
4425
|
+
record(join7(path, entry), files, dirs);
|
|
4720
4426
|
}
|
|
4721
4427
|
return;
|
|
4722
4428
|
}
|
|
4723
4429
|
if (stats.isFile()) {
|
|
4724
|
-
files.set(path,
|
|
4430
|
+
files.set(path, readFileSync7(path));
|
|
4725
4431
|
}
|
|
4726
4432
|
}
|
|
4727
4433
|
function captureScaffold(root, paths) {
|
|
@@ -4729,7 +4435,7 @@ function captureScaffold(root, paths) {
|
|
|
4729
4435
|
const dirs = /* @__PURE__ */ new Set();
|
|
4730
4436
|
for (const path of paths) {
|
|
4731
4437
|
for (let dir = dirname5(path); dir.startsWith(root) && dir !== root; dir = dirname5(dir)) {
|
|
4732
|
-
if (
|
|
4438
|
+
if (existsSync5(dir)) {
|
|
4733
4439
|
dirs.add(dir);
|
|
4734
4440
|
}
|
|
4735
4441
|
}
|
|
@@ -4752,7 +4458,7 @@ function restoreScaffold(undo) {
|
|
|
4752
4458
|
function removeAdded(path, undo) {
|
|
4753
4459
|
let stats;
|
|
4754
4460
|
try {
|
|
4755
|
-
stats =
|
|
4461
|
+
stats = statSync2(path);
|
|
4756
4462
|
} catch {
|
|
4757
4463
|
return;
|
|
4758
4464
|
}
|
|
@@ -4766,7 +4472,7 @@ function removeAdded(path, undo) {
|
|
|
4766
4472
|
return;
|
|
4767
4473
|
}
|
|
4768
4474
|
for (const entry of readdirSync4(path)) {
|
|
4769
|
-
removeAdded(
|
|
4475
|
+
removeAdded(join7(path, entry), undo);
|
|
4770
4476
|
}
|
|
4771
4477
|
if (!undo.dirs.has(path) && readdirSync4(path).length === 0) {
|
|
4772
4478
|
rmdirSync(path);
|
|
@@ -4960,7 +4666,7 @@ function suggestEnvironments(root) {
|
|
|
4960
4666
|
return [...found].sort();
|
|
4961
4667
|
}
|
|
4962
4668
|
function emptyFlag(flag) {
|
|
4963
|
-
return new
|
|
4669
|
+
return new PenvError16(
|
|
4964
4670
|
"INIT_FLAG_EMPTY",
|
|
4965
4671
|
`\`--${flag}\` was given without a value`,
|
|
4966
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."
|
|
@@ -4984,8 +4690,8 @@ function configOf(decisions) {
|
|
|
4984
4690
|
return { environments: decisions.environments, providers: {}, schemaFile: decisions.schemaFile };
|
|
4985
4691
|
}
|
|
4986
4692
|
function declaredIn(root) {
|
|
4987
|
-
const file =
|
|
4988
|
-
if (!
|
|
4693
|
+
const file = join8(root, CONFIG_FILE);
|
|
4694
|
+
if (!existsSync6(file)) {
|
|
4989
4695
|
return void 0;
|
|
4990
4696
|
}
|
|
4991
4697
|
return loadConfigFrom(file);
|
|
@@ -5023,7 +4729,7 @@ function planInit(root, flags = {}) {
|
|
|
5023
4729
|
throw emptyFlag("alias");
|
|
5024
4730
|
}
|
|
5025
4731
|
if (!ALIAS_NAME.test(alias)) {
|
|
5026
|
-
throw new
|
|
4732
|
+
throw new PenvError16(
|
|
5027
4733
|
"INIT_ALIAS_INVALID",
|
|
5028
4734
|
`\`${alias}\` is not an alias penv can write`,
|
|
5029
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.`
|
|
@@ -5357,7 +5063,7 @@ ${objectIndent}${source.slice(close)}`;
|
|
|
5357
5063
|
${entryIndent}${member},${source.slice(open + 1)}`;
|
|
5358
5064
|
}
|
|
5359
5065
|
function shapeError(what, target, alias) {
|
|
5360
|
-
return new
|
|
5066
|
+
return new PenvError16(
|
|
5361
5067
|
"TSCONFIG_SHAPE",
|
|
5362
5068
|
`penv cannot add the \`${alias}\` path alias to tsconfig.json: ${what}`,
|
|
5363
5069
|
`Add it by hand: \`{ "compilerOptions": { "paths": { "${alias}": ["${target}"] } } }\`.`
|
|
@@ -5428,15 +5134,15 @@ function renderTsconfig(target, alias) {
|
|
|
5428
5134
|
}
|
|
5429
5135
|
function ensurePenvDir(root) {
|
|
5430
5136
|
const dir = resolve6(root, PENV_DIR2);
|
|
5431
|
-
if (
|
|
5137
|
+
if (existsSync6(dir)) {
|
|
5432
5138
|
return { target: "penv-dir", action: "kept", text: `Found ${PENV_DIR2}/` };
|
|
5433
5139
|
}
|
|
5434
5140
|
mkdirSync4(dir, { recursive: true });
|
|
5435
5141
|
return { target: "penv-dir", action: "created", text: `Created ${PENV_DIR2}/` };
|
|
5436
5142
|
}
|
|
5437
5143
|
function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS) {
|
|
5438
|
-
const file =
|
|
5439
|
-
if (
|
|
5144
|
+
const file = join8(root, SCHEMA_SHAPE_FILE3);
|
|
5145
|
+
if (existsSync6(file)) {
|
|
5440
5146
|
return {
|
|
5441
5147
|
target: "schema",
|
|
5442
5148
|
action: "kept",
|
|
@@ -5462,8 +5168,8 @@ function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS
|
|
|
5462
5168
|
};
|
|
5463
5169
|
}
|
|
5464
5170
|
function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
|
|
5465
|
-
const file =
|
|
5466
|
-
if (
|
|
5171
|
+
const file = join8(root, ...decisions.schemaFile.split("/"));
|
|
5172
|
+
if (existsSync6(file)) {
|
|
5467
5173
|
return {
|
|
5468
5174
|
target: "env",
|
|
5469
5175
|
action: "kept",
|
|
@@ -5481,8 +5187,8 @@ function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5481
5187
|
};
|
|
5482
5188
|
}
|
|
5483
5189
|
function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
|
|
5484
|
-
const file =
|
|
5485
|
-
if (
|
|
5190
|
+
const file = join8(root, CONFIG_FILE);
|
|
5191
|
+
if (existsSync6(file)) {
|
|
5486
5192
|
return { target: "config", action: "kept", text: `Kept ${CONFIG_FILE}` };
|
|
5487
5193
|
}
|
|
5488
5194
|
writeFileSync5(file, renderConfigModule(decisions), "utf8");
|
|
@@ -5491,9 +5197,9 @@ function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5491
5197
|
function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
|
|
5492
5198
|
const alias = decisions.alias;
|
|
5493
5199
|
const imports = alias.startsWith(IMPORTS_PREFIX);
|
|
5494
|
-
const file =
|
|
5200
|
+
const file = join8(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
|
|
5495
5201
|
const where2 = imports ? PACKAGE_FILE : TSCONFIG_FILE;
|
|
5496
|
-
if (!
|
|
5202
|
+
if (!existsSync6(file)) {
|
|
5497
5203
|
if (imports) {
|
|
5498
5204
|
return {
|
|
5499
5205
|
target: "tsconfig",
|
|
@@ -5509,7 +5215,7 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5509
5215
|
text: `Created ${TSCONFIG_FILE} with the ${alias} path alias`
|
|
5510
5216
|
};
|
|
5511
5217
|
}
|
|
5512
|
-
const source =
|
|
5218
|
+
const source = readFileSync8(file, "utf8");
|
|
5513
5219
|
const edit = imports ? insertImportsAlias(source, decisions.schemaFile, alias) : insertEnvAlias(source, decisions.schemaFile, alias);
|
|
5514
5220
|
if (edit.conflict !== void 0) {
|
|
5515
5221
|
return {
|
|
@@ -5534,9 +5240,9 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5534
5240
|
};
|
|
5535
5241
|
}
|
|
5536
5242
|
function writeGitignore(root, decisions = DEFAULT_DECISIONS) {
|
|
5537
|
-
const file =
|
|
5243
|
+
const file = join8(root, ...STATE_GITIGNORE_PATH.split("/"));
|
|
5538
5244
|
const wanted = renderStateGitignore(configOf(decisions));
|
|
5539
|
-
const existing =
|
|
5245
|
+
const existing = existsSync6(file) ? readFileSync8(file, "utf8") : void 0;
|
|
5540
5246
|
if (existing === wanted) {
|
|
5541
5247
|
return { target: "gitignore", action: "kept", text: `Kept ${STATE_GITIGNORE_PATH}` };
|
|
5542
5248
|
}
|
|
@@ -5557,12 +5263,12 @@ function outdatedRuntimeWarning(root) {
|
|
|
5557
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.`;
|
|
5558
5264
|
}
|
|
5559
5265
|
function installedPenvVersion(root) {
|
|
5560
|
-
const file =
|
|
5561
|
-
if (!
|
|
5266
|
+
const file = join8(root, "node_modules", "@penvhq", "penv", "package.json");
|
|
5267
|
+
if (!existsSync6(file)) {
|
|
5562
5268
|
return void 0;
|
|
5563
5269
|
}
|
|
5564
5270
|
try {
|
|
5565
|
-
const version = JSON.parse(
|
|
5271
|
+
const version = JSON.parse(readFileSync8(file, "utf8")).version;
|
|
5566
5272
|
return typeof version === "string" ? version : void 0;
|
|
5567
5273
|
} catch {
|
|
5568
5274
|
return void 0;
|
|
@@ -5602,11 +5308,11 @@ function writeSeam(root, decisions = DEFAULT_DECISIONS, framework = detectFramew
|
|
|
5602
5308
|
};
|
|
5603
5309
|
}
|
|
5604
5310
|
const alsoNote = writeAlso(root, seam.also);
|
|
5605
|
-
const file =
|
|
5311
|
+
const file = join8(root, ...seam.file.split("/"));
|
|
5606
5312
|
const baseNotes = [...seam.notes, ...alsoNote === void 0 ? [] : [alsoNote]];
|
|
5607
5313
|
const notes = baseNotes.length === 0 ? "" : `
|
|
5608
5314
|
${baseNotes.map((n) => ` ${n}`).join("\n")}`;
|
|
5609
|
-
if (
|
|
5315
|
+
if (existsSync6(file)) {
|
|
5610
5316
|
return {
|
|
5611
5317
|
target: "seam",
|
|
5612
5318
|
action: "info",
|
|
@@ -5627,8 +5333,8 @@ function writeAlso(root, also) {
|
|
|
5627
5333
|
if (also === void 0) {
|
|
5628
5334
|
return void 0;
|
|
5629
5335
|
}
|
|
5630
|
-
const file =
|
|
5631
|
-
if (
|
|
5336
|
+
const file = join8(root, ...also.file.split("/"));
|
|
5337
|
+
if (existsSync6(file)) {
|
|
5632
5338
|
return also.ifPresent;
|
|
5633
5339
|
}
|
|
5634
5340
|
mkdirSync4(dirname6(file), { recursive: true });
|
|
@@ -5658,7 +5364,7 @@ function scaffold(root, fields, draft, decisions = DEFAULT_DECISIONS, framework
|
|
|
5658
5364
|
return seam === void 0 ? steps : [...steps, seam];
|
|
5659
5365
|
}
|
|
5660
5366
|
function scaffoldPaths(root, decisions = DEFAULT_DECISIONS, framework = detectFramework(root)?.name) {
|
|
5661
|
-
const fileAt = (relative5) =>
|
|
5367
|
+
const fileAt = (relative5) => join8(root, ...relative5.split("/"));
|
|
5662
5368
|
const seam = decisions.inject ? seamFor(framework, {
|
|
5663
5369
|
alias: decisions.alias,
|
|
5664
5370
|
srcDir: srcPrefix(root),
|
|
@@ -5699,7 +5405,7 @@ function planCutover(input) {
|
|
|
5699
5405
|
assertBundleResolved(root);
|
|
5700
5406
|
const selected = [...input.selected];
|
|
5701
5407
|
if (selected.length === 0) {
|
|
5702
|
-
throw new
|
|
5408
|
+
throw new PenvError16(
|
|
5703
5409
|
"INIT_NOTHING_SELECTED",
|
|
5704
5410
|
"No dotenv file was selected, so there is nothing to migrate",
|
|
5705
5411
|
"Run `penv init` again and choose the files penv should adopt."
|
|
@@ -5711,7 +5417,7 @@ function planCutover(input) {
|
|
|
5711
5417
|
const environments = declared === void 0 ? chosen : declared.environments;
|
|
5712
5418
|
for (const environment of chosen) {
|
|
5713
5419
|
if (!environments.includes(environment)) {
|
|
5714
|
-
throw new
|
|
5420
|
+
throw new PenvError16(
|
|
5715
5421
|
"INIT_ENVIRONMENT_UNDECLARED",
|
|
5716
5422
|
`${CONFIG_FILE} declares ${describeList(environments)}, and adopting these files needs \`${environment}\` declared too`,
|
|
5717
5423
|
`Add \`${environment}\` to \`environments\` in ${CONFIG_FILE}, with a provider for it, then run \`penv init\` again. Nothing was changed.`
|
|
@@ -5736,7 +5442,7 @@ function planCutover(input) {
|
|
|
5736
5442
|
const diagnostics = [];
|
|
5737
5443
|
let variables = 0;
|
|
5738
5444
|
for (const file of selected) {
|
|
5739
|
-
const parsed = parseDotenv(
|
|
5445
|
+
const parsed = parseDotenv(readFileSync8(join8(root, file.name), "utf8"));
|
|
5740
5446
|
const fileRefs = refsForEntries(parsed.entries, file.name, config);
|
|
5741
5447
|
adopted.push({ file, entries: parsed.entries, refs: fileRefs, scope: scopeOf(file) });
|
|
5742
5448
|
refs.push(...fileRefs);
|
|
@@ -5798,7 +5504,7 @@ function requireEnvironment(input) {
|
|
|
5798
5504
|
if (environment.length > 0) {
|
|
5799
5505
|
return environment;
|
|
5800
5506
|
}
|
|
5801
|
-
throw new
|
|
5507
|
+
throw new PenvError16(
|
|
5802
5508
|
"INIT_ENVIRONMENT_UNNAMED",
|
|
5803
5509
|
"The selected files name no environment, and penv does not invent one",
|
|
5804
5510
|
`Run \`penv init --env ${DEVELOPMENT}\` to say which environment these values are for.`
|
|
@@ -5810,7 +5516,7 @@ function assertCascadeComplete(root, selected, environments) {
|
|
|
5810
5516
|
for (const environment of environments) {
|
|
5811
5517
|
for (const name of cascadeFor(environment)) {
|
|
5812
5518
|
if (present.has(name) && !taken.has(name)) {
|
|
5813
|
-
throw new
|
|
5519
|
+
throw new PenvError16(
|
|
5814
5520
|
"INIT_CUTOVER_INCOMPLETE",
|
|
5815
5521
|
`${name} is part of ${environment}'s cascade and was not selected, so your framework would keep reading it beside penv`,
|
|
5816
5522
|
"Run `penv init` again and take every file penv listed for that environment. Nothing was changed."
|
|
@@ -5823,7 +5529,7 @@ function assertBundleResolved(root) {
|
|
|
5823
5529
|
if (!bundleUnresolved(root)) {
|
|
5824
5530
|
return;
|
|
5825
5531
|
}
|
|
5826
|
-
throw new
|
|
5532
|
+
throw new PenvError16(
|
|
5827
5533
|
"INIT_BUNDLE_UNRESOLVED",
|
|
5828
5534
|
`The dotenv files from the last cutover are still in ${ROLLBACK_DOTENV_PATH3}/, and penv will not migrate a second time over them`,
|
|
5829
5535
|
"Run `penv init undo` to put them back, or `penv cleanup` to drop them once you are happy with the migration."
|
|
@@ -5835,7 +5541,7 @@ function selectionForYes(plan2) {
|
|
|
5835
5541
|
(file) => file.environment !== void 0 && file.environment !== DEVELOPMENT
|
|
5836
5542
|
);
|
|
5837
5543
|
if (shared !== void 0 && other !== void 0) {
|
|
5838
|
-
throw new
|
|
5544
|
+
throw new PenvError16(
|
|
5839
5545
|
"INIT_YES_SHARED_FALLBACK",
|
|
5840
5546
|
`${other.name} falls back to the shared ${shared.name} this cutover would move, so \`--yes\` will not decide what happens to ${other.environment ?? ""}`,
|
|
5841
5547
|
"Run `penv init` without `--yes` and choose every file the cutover takes. Nothing was changed."
|
|
@@ -5881,7 +5587,7 @@ function issueLines(result2) {
|
|
|
5881
5587
|
}
|
|
5882
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.";
|
|
5883
5589
|
function invalidAfterImport(result2) {
|
|
5884
|
-
return new
|
|
5590
|
+
return new PenvError16(
|
|
5885
5591
|
"INIT_CUTOVER_INVALID",
|
|
5886
5592
|
`The imported values do not satisfy the draft schema for ${result2.environment}:
|
|
5887
5593
|
${issueLines(result2)}`,
|
|
@@ -5889,7 +5595,7 @@ ${issueLines(result2)}`,
|
|
|
5889
5595
|
);
|
|
5890
5596
|
}
|
|
5891
5597
|
function draftNotLoaded(result2) {
|
|
5892
|
-
return new
|
|
5598
|
+
return new PenvError16(
|
|
5893
5599
|
"INIT_DRAFT_NOT_LOADED",
|
|
5894
5600
|
`penv could not load the schema it drafted for ${result2.environment}, so nothing was checked against it:
|
|
5895
5601
|
${issueLines(result2)}`,
|
|
@@ -5980,12 +5686,12 @@ function dailyCommand(root) {
|
|
|
5980
5686
|
return `penv run -- ${detectPackageManager(root)} ${devScript(root)}`;
|
|
5981
5687
|
}
|
|
5982
5688
|
function devScript(root) {
|
|
5983
|
-
const file =
|
|
5984
|
-
if (!
|
|
5689
|
+
const file = join8(root, PACKAGE_FILE);
|
|
5690
|
+
if (!existsSync6(file)) {
|
|
5985
5691
|
return "dev";
|
|
5986
5692
|
}
|
|
5987
5693
|
try {
|
|
5988
|
-
const scripts = JSON.parse(
|
|
5694
|
+
const scripts = JSON.parse(readFileSync8(file, "utf8")).scripts;
|
|
5989
5695
|
if (scripts !== null && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
5990
5696
|
const named = Object.keys(scripts);
|
|
5991
5697
|
return ["dev", "start"].find((script) => named.includes(script)) ?? named[0] ?? "dev";
|
|
@@ -6029,7 +5735,7 @@ async function promptForSelection(plan2, io) {
|
|
|
6029
5735
|
for (const name of names) {
|
|
6030
5736
|
const file = plan2.found.find((candidate) => candidate.name === name);
|
|
6031
5737
|
if (file === void 0) {
|
|
6032
|
-
throw new
|
|
5738
|
+
throw new PenvError16(
|
|
6033
5739
|
"INIT_SELECTION_UNKNOWN",
|
|
6034
5740
|
`\`${name}\` is not one of the dotenv files penv found`,
|
|
6035
5741
|
"Run `penv init` again and name the files exactly as they are listed, e.g. `.env.production`."
|
|
@@ -6192,7 +5898,7 @@ var initCommand = defineCommand13({
|
|
|
6192
5898
|
});
|
|
6193
5899
|
function runUndoAction(root, action) {
|
|
6194
5900
|
if (action !== "undo") {
|
|
6195
|
-
throw new
|
|
5901
|
+
throw new PenvError16(
|
|
6196
5902
|
"INIT_UNKNOWN_ACTION",
|
|
6197
5903
|
`\`penv init ${action}\` is not something init does`,
|
|
6198
5904
|
"Run `penv init undo` to put back the dotenv files of the last cutover."
|
|
@@ -6298,7 +6004,7 @@ function explicitEnvironment(options, source, config) {
|
|
|
6298
6004
|
if (value.length > 0) {
|
|
6299
6005
|
return value;
|
|
6300
6006
|
}
|
|
6301
|
-
throw new
|
|
6007
|
+
throw new PenvError17(
|
|
6302
6008
|
"IMPORT_ENV_FLAG_EMPTY",
|
|
6303
6009
|
`\`--env\` for the import of ${source} names no environment`,
|
|
6304
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.`
|
|
@@ -6308,7 +6014,7 @@ function assertEnvironmentAgrees(derived, explicit, source) {
|
|
|
6308
6014
|
if (derived === void 0 || explicit === void 0 || derived === explicit) {
|
|
6309
6015
|
return;
|
|
6310
6016
|
}
|
|
6311
|
-
throw new
|
|
6017
|
+
throw new PenvError17(
|
|
6312
6018
|
"IMPORT_ENV_CONFLICT",
|
|
6313
6019
|
`The file ${source} is scoped to environment ${derived}, but \`--env ${explicit}\` names ${explicit}`,
|
|
6314
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.`
|
|
@@ -6353,15 +6059,15 @@ function configInEffect(cwd, environment) {
|
|
|
6353
6059
|
}
|
|
6354
6060
|
function importDotenv(options) {
|
|
6355
6061
|
const cwd = resolve7(options.cwd);
|
|
6356
|
-
const file =
|
|
6357
|
-
if (!
|
|
6358
|
-
throw new
|
|
6062
|
+
const file = isAbsolute3(options.file) ? options.file : resolve7(cwd, options.file);
|
|
6063
|
+
if (!existsSync7(file)) {
|
|
6064
|
+
throw new PenvError17(
|
|
6359
6065
|
"IMPORT_FILE_MISSING",
|
|
6360
6066
|
`There is no file at ${file} to import`,
|
|
6361
6067
|
"Point `penv import` at an existing dotenv file, e.g. `penv import .env`."
|
|
6362
6068
|
);
|
|
6363
6069
|
}
|
|
6364
|
-
const parsed = parseDotenv2(
|
|
6070
|
+
const parsed = parseDotenv2(readFileSync9(file, "utf8"));
|
|
6365
6071
|
const { config, decisions } = configInEffect(cwd, environmentNamed(file, options.environment));
|
|
6366
6072
|
const source = displayPath3(cwd, file);
|
|
6367
6073
|
const named = scopeFromFilename(file, config);
|
|
@@ -6479,19 +6185,19 @@ var importCommand = defineCommand14({
|
|
|
6479
6185
|
|
|
6480
6186
|
// src/commands/key.ts
|
|
6481
6187
|
import { randomBytes } from "crypto";
|
|
6482
|
-
import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as
|
|
6188
|
+
import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as PenvError19 } from "@penvhq/core";
|
|
6483
6189
|
import { defineCommand as defineCommand15 } from "citty";
|
|
6484
6190
|
|
|
6485
6191
|
// src/keychain.ts
|
|
6486
6192
|
import { createRequire as createRequire3 } from "module";
|
|
6487
|
-
import { PenvError as
|
|
6193
|
+
import { PenvError as PenvError18 } from "@penvhq/core";
|
|
6488
6194
|
var KEYRING_MODULE = "@napi-rs/keyring";
|
|
6489
6195
|
var nodeRequire = (id) => createRequire3(import.meta.url)(id);
|
|
6490
6196
|
function firstLine2(cause) {
|
|
6491
6197
|
return (cause instanceof Error ? cause.message : String(cause)).split("\n")[0] ?? "";
|
|
6492
6198
|
}
|
|
6493
6199
|
function keyringMissing(cause) {
|
|
6494
|
-
return new
|
|
6200
|
+
return new PenvError18(
|
|
6495
6201
|
"KEYCHAIN_BINDING_MISSING",
|
|
6496
6202
|
`penv could not load ${KEYRING_MODULE}, the native binding it reads your OS keychain through`,
|
|
6497
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)}`
|
|
@@ -6533,7 +6239,7 @@ function runKeyCreate(options) {
|
|
|
6533
6239
|
const environment = targetEnvironment(project, options.environment);
|
|
6534
6240
|
const declared = project.config.keys?.[environment];
|
|
6535
6241
|
if (declared === void 0) {
|
|
6536
|
-
throw new
|
|
6242
|
+
throw new PenvError19(
|
|
6537
6243
|
"KEY_SOURCE_UNDECLARED",
|
|
6538
6244
|
`Environment ${environment} declares no key source, so penv does not know what a key for it would be`,
|
|
6539
6245
|
`Add a \`keys\` entry to penv.config.ts \u2014 e.g. \`keys: { ${environment}: { source: "env", id: "${environment}" } }\` \u2014 then run this again.`
|
|
@@ -6547,17 +6253,17 @@ function runKeyCreate(options) {
|
|
|
6547
6253
|
try {
|
|
6548
6254
|
existing = keychain.getPassword(KEYCHAIN_SERVICE, declared.id);
|
|
6549
6255
|
} catch (cause) {
|
|
6550
|
-
if (cause instanceof
|
|
6256
|
+
if (cause instanceof PenvError19) {
|
|
6551
6257
|
throw cause;
|
|
6552
6258
|
}
|
|
6553
|
-
throw new
|
|
6259
|
+
throw new PenvError19(
|
|
6554
6260
|
"KEYCHAIN_UNAVAILABLE",
|
|
6555
6261
|
`penv could not read your OS keychain to check for an existing key \`${declared.id}\``,
|
|
6556
6262
|
`Unlock your keychain and run this again. Original error: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
6557
6263
|
);
|
|
6558
6264
|
}
|
|
6559
6265
|
if (existing !== null) {
|
|
6560
|
-
throw new
|
|
6266
|
+
throw new PenvError19(
|
|
6561
6267
|
"KEY_EXISTS",
|
|
6562
6268
|
`Environment ${environment} already has a key \`${declared.id}\` in your OS keychain`,
|
|
6563
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."
|
|
@@ -6712,21 +6418,21 @@ var listCommand = defineCommand16({
|
|
|
6712
6418
|
|
|
6713
6419
|
// src/commands/migrate.ts
|
|
6714
6420
|
import {
|
|
6715
|
-
existsSync as
|
|
6421
|
+
existsSync as existsSync8,
|
|
6716
6422
|
mkdirSync as mkdirSync5,
|
|
6717
6423
|
readdirSync as readdirSync6,
|
|
6718
|
-
readFileSync as
|
|
6424
|
+
readFileSync as readFileSync10,
|
|
6719
6425
|
renameSync as renameSync2,
|
|
6720
6426
|
rmSync as rmSync2,
|
|
6721
6427
|
writeFileSync as writeFileSync6
|
|
6722
6428
|
} from "fs";
|
|
6723
|
-
import { dirname as dirname7, join as
|
|
6429
|
+
import { dirname as dirname7, join as join9 } from "path";
|
|
6724
6430
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
6725
6431
|
import {
|
|
6726
6432
|
loadConfig as loadConfig2,
|
|
6727
6433
|
oldLayoutEntries,
|
|
6728
6434
|
PENV_DIR as PENV_DIR3,
|
|
6729
|
-
PenvError as
|
|
6435
|
+
PenvError as PenvError20,
|
|
6730
6436
|
RECORDS_PATH as RECORDS_PATH5,
|
|
6731
6437
|
recordsDir as recordsDir3,
|
|
6732
6438
|
renderStateGitignore as renderStateGitignore2,
|
|
@@ -6741,17 +6447,17 @@ function planMigrate(cwd) {
|
|
|
6741
6447
|
const tree = recordsDir3(root);
|
|
6742
6448
|
const collisions = collidingEntries(entries, tree);
|
|
6743
6449
|
if (collisions.length > 0) {
|
|
6744
|
-
throw new
|
|
6450
|
+
throw new PenvError20(
|
|
6745
6451
|
"HALF_MIGRATED",
|
|
6746
|
-
`${
|
|
6452
|
+
`${describe(collisions)} in both \`${PENV_DIR3}/\` and \`${RECORDS_PATH5}/\`, and penv cannot tell which copy is current`,
|
|
6747
6453
|
`Move what is left under \`${PENV_DIR3}/\` into \`${RECORDS_PATH5}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
|
|
6748
6454
|
);
|
|
6749
6455
|
}
|
|
6750
6456
|
const creates = [];
|
|
6751
|
-
if (entries.length > 0 && !
|
|
6457
|
+
if (entries.length > 0 && !existsSync8(tree)) {
|
|
6752
6458
|
creates.push(`${RECORDS_PATH5}/`);
|
|
6753
6459
|
}
|
|
6754
|
-
if (readIfPresent(
|
|
6460
|
+
if (readIfPresent(join9(root, ...STATE_GITIGNORE_PATH2.split("/"))) !== renderStateGitignore2(config)) {
|
|
6755
6461
|
creates.push(STATE_GITIGNORE_PATH2);
|
|
6756
6462
|
}
|
|
6757
6463
|
return {
|
|
@@ -6761,11 +6467,11 @@ function planMigrate(cwd) {
|
|
|
6761
6467
|
to: `${RECORDS_PATH5}/${entry}`
|
|
6762
6468
|
})),
|
|
6763
6469
|
creates,
|
|
6764
|
-
removes:
|
|
6470
|
+
removes: existsSync8(join9(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
|
|
6765
6471
|
};
|
|
6766
6472
|
}
|
|
6767
6473
|
function readIfPresent(file) {
|
|
6768
|
-
return
|
|
6474
|
+
return existsSync8(file) ? readFileSync10(file, "utf8") : void 0;
|
|
6769
6475
|
}
|
|
6770
6476
|
function collidingEntries(entries, tree) {
|
|
6771
6477
|
let held;
|
|
@@ -6777,7 +6483,7 @@ function collidingEntries(entries, tree) {
|
|
|
6777
6483
|
const taken = new Set(held.map((name) => name.toLowerCase()));
|
|
6778
6484
|
return entries.filter((entry) => taken.has(entry.toLowerCase())).sort();
|
|
6779
6485
|
}
|
|
6780
|
-
function
|
|
6486
|
+
function describe(names) {
|
|
6781
6487
|
return names.length === 1 ? `\`${names[0]}\` is` : `${names.map((name) => `\`${name}\``).join(", ")} are`;
|
|
6782
6488
|
}
|
|
6783
6489
|
function isNoop(plan2) {
|
|
@@ -6791,16 +6497,16 @@ function applyMigrate(plan2) {
|
|
|
6791
6497
|
if (plan2.moves.length > 0) {
|
|
6792
6498
|
mkdirSync5(recordsDir3(plan2.root), { recursive: true });
|
|
6793
6499
|
for (const move of plan2.moves) {
|
|
6794
|
-
renameSync2(
|
|
6500
|
+
renameSync2(join9(plan2.root, ...move.from.split("/")), join9(plan2.root, ...move.to.split("/")));
|
|
6795
6501
|
}
|
|
6796
6502
|
}
|
|
6797
6503
|
if (plan2.creates.includes(STATE_GITIGNORE_PATH2)) {
|
|
6798
|
-
const ignore =
|
|
6504
|
+
const ignore = join9(plan2.root, ...STATE_GITIGNORE_PATH2.split("/"));
|
|
6799
6505
|
mkdirSync5(dirname7(ignore), { recursive: true });
|
|
6800
6506
|
writeFileSync6(ignore, renderStateGitignore2(config), "utf8");
|
|
6801
6507
|
}
|
|
6802
6508
|
for (const removed of plan2.removes) {
|
|
6803
|
-
rmSync2(
|
|
6509
|
+
rmSync2(join9(plan2.root, ...removed.split("/")), { force: true });
|
|
6804
6510
|
}
|
|
6805
6511
|
return { ...plan2, status: "migrated" };
|
|
6806
6512
|
}
|
|
@@ -6882,7 +6588,7 @@ import {
|
|
|
6882
6588
|
formatMetaFile,
|
|
6883
6589
|
formatValueFile as formatValueFile6,
|
|
6884
6590
|
openValue as openValue4,
|
|
6885
|
-
PenvError as
|
|
6591
|
+
PenvError as PenvError21,
|
|
6886
6592
|
parameterId as parameterId7,
|
|
6887
6593
|
recordPath as recordPath7,
|
|
6888
6594
|
sealValue as sealValue3
|
|
@@ -6902,7 +6608,7 @@ async function planFile(project, source, target, parameter) {
|
|
|
6902
6608
|
}
|
|
6903
6609
|
const environment = environmentOf2(source);
|
|
6904
6610
|
if (environment === void 0) {
|
|
6905
|
-
throw new
|
|
6611
|
+
throw new PenvError21(
|
|
6906
6612
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
6907
6613
|
`${recordPath7(formatValueFile6(source))} is encrypted at a scope that names no environment, so penv cannot tell which key would re-seal it`,
|
|
6908
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."
|
|
@@ -6911,7 +6617,7 @@ async function planFile(project, source, target, parameter) {
|
|
|
6911
6617
|
const keys = keySourceFor(project, environment);
|
|
6912
6618
|
const opened = openValue4(source, stored, keys);
|
|
6913
6619
|
if (opened.kind === "failed") {
|
|
6914
|
-
throw new
|
|
6620
|
+
throw new PenvError21(
|
|
6915
6621
|
"VALUE_UNDECRYPTABLE",
|
|
6916
6622
|
`${recordPath7(formatValueFile6(source))} could not be decrypted, so penv cannot re-seal it at its new address: ${opened.failure.detail}`,
|
|
6917
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."
|
|
@@ -6934,7 +6640,7 @@ async function runMove(options) {
|
|
|
6934
6640
|
assertWritableKey(options.to);
|
|
6935
6641
|
const to = refFromKey(options.to, project.config);
|
|
6936
6642
|
if (parameterId7(from) === parameterId7(to)) {
|
|
6937
|
-
throw new
|
|
6643
|
+
throw new PenvError21(
|
|
6938
6644
|
"PARAMETER_UNCHANGED",
|
|
6939
6645
|
`\`${options.from}\` and \`${options.to}\` are the same parameter`,
|
|
6940
6646
|
"Name a different destination, e.g. `penv mv redis-password redis/password`."
|
|
@@ -6944,7 +6650,7 @@ async function runMove(options) {
|
|
|
6944
6650
|
const sources = filesOf(all, from);
|
|
6945
6651
|
const meta = await project.provider.readMeta(from);
|
|
6946
6652
|
if (sources.length === 0 && meta === void 0) {
|
|
6947
|
-
throw new
|
|
6653
|
+
throw new PenvError21(
|
|
6948
6654
|
"PARAMETER_ABSENT",
|
|
6949
6655
|
`Parameter ${parameterId7(from)} has no value files and no meta, so there is nothing to move`,
|
|
6950
6656
|
`\`penv list\` shows every parameter penv holds.`
|
|
@@ -6952,7 +6658,7 @@ async function runMove(options) {
|
|
|
6952
6658
|
}
|
|
6953
6659
|
const occupied2 = filesOf(all, to);
|
|
6954
6660
|
if (occupied2.length > 0 || await project.provider.readMeta(to) !== void 0) {
|
|
6955
|
-
throw new
|
|
6661
|
+
throw new PenvError21(
|
|
6956
6662
|
"PARAMETER_EXISTS",
|
|
6957
6663
|
`Parameter ${parameterId7(to)} already exists, and penv will not merge two parameters into one`,
|
|
6958
6664
|
`Remove or rename ${parameterId7(to)} first. \`penv get ${options.to} --explain\` shows every file it holds.`
|
|
@@ -7111,7 +6817,7 @@ import {
|
|
|
7111
6817
|
beginRotation,
|
|
7112
6818
|
completeRotation,
|
|
7113
6819
|
holdsRecords as holdsRecords2,
|
|
7114
|
-
PenvError as
|
|
6820
|
+
PenvError as PenvError22,
|
|
7115
6821
|
retainsPrevious,
|
|
7116
6822
|
rotationOf as rotationOf2
|
|
7117
6823
|
} from "@penvhq/core";
|
|
@@ -7140,7 +6846,7 @@ async function writeRotatedValue(project, provider, ref, environment, value) {
|
|
|
7140
6846
|
}
|
|
7141
6847
|
function requireNewValue(value, phase, key) {
|
|
7142
6848
|
if (value === void 0) {
|
|
7143
|
-
throw new
|
|
6849
|
+
throw new PenvError22(
|
|
7144
6850
|
"ROTATION_NO_VALUE",
|
|
7145
6851
|
`A ${phase} rotation of ${key} writes a new value, and none was given`,
|
|
7146
6852
|
"Pass the new value as the argument \u2014 `penv rotate <key> <value>` \u2014 or pipe it in on stdin."
|
|
@@ -7150,7 +6856,7 @@ function requireNewValue(value, phase, key) {
|
|
|
7150
6856
|
}
|
|
7151
6857
|
function requireRetaining(provider, environment) {
|
|
7152
6858
|
if (!retainsPrevious(provider)) {
|
|
7153
|
-
throw new
|
|
6859
|
+
throw new PenvError22(
|
|
7154
6860
|
"ROTATION_NOT_RETAINING",
|
|
7155
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`,
|
|
7156
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."
|
|
@@ -7164,7 +6870,7 @@ async function runRotate(options) {
|
|
|
7164
6870
|
const ref = refFromKey(options.key, project.config);
|
|
7165
6871
|
const source = await sourceProviderFor(project, environment);
|
|
7166
6872
|
if (!holdsRecords2(source)) {
|
|
7167
|
-
throw new
|
|
6873
|
+
throw new PenvError22(
|
|
7168
6874
|
"ROTATION_NOT_RECORDS",
|
|
7169
6875
|
`Environment ${environment} is backed by \`${source.type}\`, which holds a resolved projection penv cannot rotate in place`,
|
|
7170
6876
|
"Rotate the parameter in the environment that holds the records (the local tree, Vault, SSM), then `penv push` the result to this destination."
|
|
@@ -7175,7 +6881,7 @@ async function runRotate(options) {
|
|
|
7175
6881
|
const before = await provider.readMeta(ref);
|
|
7176
6882
|
const { mechanism } = rotationOf2(before, environment);
|
|
7177
6883
|
if (mechanism === void 0) {
|
|
7178
|
-
throw new
|
|
6884
|
+
throw new PenvError22(
|
|
7179
6885
|
"ROTATION_NO_MECHANISM",
|
|
7180
6886
|
`Parameter ${options.key} declares no rotation mechanism for environment ${environment}, so penv does not know how to rotate it`,
|
|
7181
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.'
|
|
@@ -7185,7 +6891,7 @@ async function runRotate(options) {
|
|
|
7185
6891
|
const complete = options.complete === true;
|
|
7186
6892
|
if (mechanism === "atomic-cutover") {
|
|
7187
6893
|
if (begin || complete) {
|
|
7188
|
-
throw new
|
|
6894
|
+
throw new PenvError22(
|
|
7189
6895
|
"ROTATION_MECHANISM_MISMATCH",
|
|
7190
6896
|
`Parameter ${options.key} is atomic-cutover, which flips in one step, so \`--begin\`/\`--complete\` do not apply`,
|
|
7191
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."
|
|
@@ -7199,7 +6905,7 @@ async function runRotate(options) {
|
|
|
7199
6905
|
}
|
|
7200
6906
|
const retaining = requireRetaining(provider, environment);
|
|
7201
6907
|
if (begin === complete) {
|
|
7202
|
-
throw new
|
|
6908
|
+
throw new PenvError22(
|
|
7203
6909
|
"ROTATION_PHASE_REQUIRED",
|
|
7204
6910
|
`A dual-valid rotation of ${options.key} needs exactly one of \`--begin\` or \`--complete\``,
|
|
7205
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."
|
|
@@ -7302,7 +7008,7 @@ var rotateCommand = defineCommand20({
|
|
|
7302
7008
|
});
|
|
7303
7009
|
|
|
7304
7010
|
// src/commands/watch.ts
|
|
7305
|
-
import { existsSync as
|
|
7011
|
+
import { existsSync as existsSync9, watch as watch2 } from "fs";
|
|
7306
7012
|
import { basename as basename3, dirname as dirname8, resolve as resolve8 } from "path";
|
|
7307
7013
|
import { SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE4, schemaFileOf as schemaFileOf5, schemaInsideTree } from "@penvhq/core";
|
|
7308
7014
|
import { defineCommand as defineCommand21 } from "citty";
|
|
@@ -7370,14 +7076,14 @@ function runWatch(options) {
|
|
|
7370
7076
|
if (closed || recovery === void 0) {
|
|
7371
7077
|
return;
|
|
7372
7078
|
}
|
|
7373
|
-
if (!
|
|
7079
|
+
if (!existsSync9(parent)) {
|
|
7374
7080
|
stop2(recovery);
|
|
7375
7081
|
return;
|
|
7376
7082
|
}
|
|
7377
7083
|
if (filename !== null && basename3(filename) !== name) {
|
|
7378
7084
|
return;
|
|
7379
7085
|
}
|
|
7380
|
-
if (!
|
|
7086
|
+
if (!existsSync9(target)) {
|
|
7381
7087
|
return;
|
|
7382
7088
|
}
|
|
7383
7089
|
stop2(recovery);
|
|
@@ -7401,7 +7107,7 @@ function runWatch(options) {
|
|
|
7401
7107
|
if (closed) {
|
|
7402
7108
|
return;
|
|
7403
7109
|
}
|
|
7404
|
-
if (!
|
|
7110
|
+
if (!existsSync9(target)) {
|
|
7405
7111
|
if (watcher !== void 0) {
|
|
7406
7112
|
stop2(watcher);
|
|
7407
7113
|
}
|