@penvhq/cli 0.9.5 → 0.11.0

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