@penvhq/cli 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.cjs CHANGED
@@ -47374,17 +47374,55 @@ var import_url = require("url");
47374
47374
  var import_crypto2 = require("crypto");
47375
47375
  var import_fs2 = require("fs");
47376
47376
  var import_path2 = require("path");
47377
+ var REMEDY_ARROW = "\u2192";
47378
+ function framesOf(stack) {
47379
+ return (stack ?? "").split("\n").filter((line) => /^\s+at\s/.test(line));
47380
+ }
47381
+ function renderRefusal(error51) {
47382
+ const captured = error51.stack;
47383
+ Object.defineProperty(error51, "stack", {
47384
+ configurable: true,
47385
+ get: () => [error51.toString(), ...framesOf(captured)].join("\n"),
47386
+ set(value2) {
47387
+ Object.defineProperty(error51, "stack", { value: value2, writable: true, configurable: true });
47388
+ }
47389
+ });
47390
+ }
47377
47391
  var PenvError = class extends Error {
47378
47392
  name = "PenvError";
47379
47393
  /** A stable, machine-readable discriminator. */
47380
47394
  code;
47381
47395
  /** What the user should do about it. */
47382
47396
  remedy;
47397
+ /** The message alone, without the remedy the constructor folds into it. */
47398
+ summary;
47383
47399
  constructor(code, message, remedy) {
47384
47400
  super(remedy ? `${message}
47385
47401
  ${remedy}` : message);
47386
47402
  this.code = code;
47387
47403
  this.remedy = remedy;
47404
+ this.summary = message;
47405
+ renderRefusal(this);
47406
+ }
47407
+ /**
47408
+ * Drops every frame from `below` upward, so the stack shows where the
47409
+ * application called in rather than the path penv took inside itself. The
47410
+ * caller names its own entry point; nothing here guesses which files are
47411
+ * penv's.
47412
+ */
47413
+ hideFramesAbove(below) {
47414
+ const capture = Error.captureStackTrace;
47415
+ if (capture !== void 0) {
47416
+ capture(this, below);
47417
+ renderRefusal(this);
47418
+ }
47419
+ return this;
47420
+ }
47421
+ /** The refusal as every penv command prints it: the message, then the remedy. */
47422
+ toString() {
47423
+ const head = `${this.name}: ${this.summary}`;
47424
+ return this.remedy === void 0 ? head : `${head}
47425
+ ${REMEDY_ARROW} ${this.remedy}`;
47388
47426
  }
47389
47427
  };
47390
47428
  var FilenameGrammarError = class extends PenvError {
@@ -47720,8 +47758,8 @@ var META_FORMATS = ["json", "toml", "yml"];
47720
47758
  function assertNever2(value2, context) {
47721
47759
  throw new Error(`Unhandled ${context}: ${JSON.stringify(value2)}`);
47722
47760
  }
47723
- function own(record2, key) {
47724
- return record2 !== void 0 && Object.hasOwn(record2, key) ? record2[key] : void 0;
47761
+ function own(record3, key) {
47762
+ return record3 !== void 0 && Object.hasOwn(record3, key) ? record3[key] : void 0;
47725
47763
  }
47726
47764
  function retainsPrevious(provider) {
47727
47765
  return typeof provider.readPrevious === "function";
@@ -48912,6 +48950,7 @@ var RECORDS_PATH2 = `${STATE_PATH}/records`;
48912
48950
  var STATE_GITIGNORE_PATH = `${STATE_PATH}/.gitignore`;
48913
48951
  var MANIFEST_PATH = `${STATE_PATH}/manifest.json`;
48914
48952
  var EXTENSIONS_PATH = `${STATE_PATH}/extensions`;
48953
+ var LOCAL_EXTENSIONS_PATH = `${STATE_PATH}/local-extensions.json`;
48915
48954
  var CUTOVER_FILE = "cutover.json";
48916
48955
  var ROLLBACK_DIR = "rollback";
48917
48956
  var CUTOVER_PATH = `${STATE_PATH}/${CUTOVER_FILE}`;
@@ -48920,6 +48959,9 @@ var ROLLBACK_DOTENV_PATH = `${ROLLBACK_PATH}/dotenv`;
48920
48959
  function penvDir(projectRoot) {
48921
48960
  return (0, import_path2.resolve)(projectRoot, PENV_DIR);
48922
48961
  }
48962
+ function localExtensionsFile(projectRoot) {
48963
+ return (0, import_path2.resolve)(projectRoot, ...LOCAL_EXTENSIONS_PATH.split("/"));
48964
+ }
48923
48965
  function recordsDir(projectRoot) {
48924
48966
  return (0, import_path2.resolve)(projectRoot, ...RECORDS_PATH2.split("/"));
48925
48967
  }
@@ -48964,6 +49006,33 @@ ${inside === void 0 ? "" : `!${inside}
48964
49006
  ${ROLLBACK_DIR}/
48965
49007
  `;
48966
49008
  }
49009
+ function invalid(problem) {
49010
+ return new PenvError(
49011
+ "LOCAL_EXTENSIONS_INVALID",
49012
+ `${LOCAL_EXTENSIONS_PATH} does not hold the list penv writes: ${problem}`,
49013
+ "It is a JSON array of package names. Delete it and run `penv add --local <package>` for each extension this project develops."
49014
+ );
49015
+ }
49016
+ function parseLocalExtensions(text) {
49017
+ let parsed;
49018
+ try {
49019
+ parsed = JSON.parse(text);
49020
+ } catch {
49021
+ throw invalid("it is not valid JSON");
49022
+ }
49023
+ if (!Array.isArray(parsed)) {
49024
+ throw invalid("its root is not an array");
49025
+ }
49026
+ for (const name of parsed) {
49027
+ if (typeof name !== "string" || name.trim() === "") {
49028
+ throw invalid("it holds something that is not a package name");
49029
+ }
49030
+ }
49031
+ return normalize(parsed);
49032
+ }
49033
+ function normalize(names) {
49034
+ return [...new Set(names.map((name) => name.trim()))].sort();
49035
+ }
48967
49036
  var MANIFEST_FORMAT = 1;
48968
49037
  var ENGINE_PACKAGE = "@penvhq/cli";
48969
49038
  var OFFICIAL_SCOPE = "@penvhq/";
@@ -49969,7 +50038,7 @@ function _getBuiltinFlags(long, short, userNames, userAliases) {
49969
50038
 
49970
50039
  // src/commands/artifact.ts
49971
50040
  init_cjs_shims();
49972
- var import_node_fs6 = require("fs");
50041
+ var import_node_fs7 = require("fs");
49973
50042
  var import_node_path7 = require("path");
49974
50043
 
49975
50044
  // ../runtime/dist/index.js
@@ -50139,11 +50208,13 @@ var RUN_MARKER = "PENV_RUN";
50139
50208
  var ENVIRONMENT_VARIABLE = "PENV_ENV";
50140
50209
  var DELIVERY_VARIABLE = "PENV_DELIVERY";
50141
50210
  var SNAPSHOT_VARIABLE = "PENV_SNAPSHOT";
50211
+ var LAUNCHER_HOME = "PENV_HOME";
50142
50212
  var CONTROL_VARIABLES = [
50143
50213
  RUN_MARKER,
50144
50214
  DELIVERY_VARIABLE,
50145
50215
  SCHEMA_HARVEST_ENV,
50146
- SNAPSHOT_VARIABLE
50216
+ SNAPSHOT_VARIABLE,
50217
+ LAUNCHER_HOME
50147
50218
  ];
50148
50219
  var RESERVED_VARIABLES = [...CONTROL_VARIABLES, ENVIRONMENT_VARIABLE];
50149
50220
  function assertDeliverableNames(refs, config2) {
@@ -50275,15 +50346,15 @@ function caseInsensitiveNames() {
50275
50346
  return process.platform === "win32";
50276
50347
  }
50277
50348
  function childRecord(host) {
50278
- const record2 = { ...host };
50349
+ const record3 = { ...host };
50279
50350
  if (!caseInsensitiveNames()) {
50280
- return record2;
50351
+ return record3;
50281
50352
  }
50282
50353
  const spelling = (name) => {
50283
50354
  const upper = name.toUpperCase();
50284
- return Object.keys(record2).find((key) => key.toUpperCase() === upper);
50355
+ return Object.keys(record3).find((key) => key.toUpperCase() === upper);
50285
50356
  };
50286
- return new Proxy(record2, {
50357
+ return new Proxy(record3, {
50287
50358
  get: (target, property) => typeof property === "string" ? target[spelling(property) ?? property] : Reflect.get(target, property),
50288
50359
  set(target, property, value2) {
50289
50360
  if (typeof property !== "string") {
@@ -50437,7 +50508,7 @@ var startChild = (invocation) => {
50437
50508
  const ended = new Promise((resolve12, reject) => {
50438
50509
  child.on("error", (cause) => {
50439
50510
  release();
50440
- reject(cannotStart(executable, cause));
50511
+ reject(cannotStart(executable, cause, invocation.purpose));
50441
50512
  });
50442
50513
  child.on("exit", (code, signal) => {
50443
50514
  release();
@@ -50458,8 +50529,15 @@ function noCommand() {
50458
50529
  "Put the command after `--`, e.g. `penv run -- pnpm dev`."
50459
50530
  );
50460
50531
  }
50461
- function cannotStart(executable, cause) {
50532
+ function cannotStart(executable, cause, purpose) {
50462
50533
  const detail = cause instanceof Error ? cause.message : String(cause);
50534
+ if (purpose !== void 0) {
50535
+ return new PenvError(
50536
+ "PENV_COMMAND_NOT_STARTED",
50537
+ `penv could not start \`${executable}\` to ${purpose}: ${detail}`,
50538
+ `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.`
50539
+ );
50540
+ }
50463
50541
  return new PenvError(
50464
50542
  "RUN_COMMAND_NOT_STARTED",
50465
50543
  `\`${executable}\` could not be started: ${detail}`,
@@ -50488,12 +50566,15 @@ function cmdCommandLine(resolved, args) {
50488
50566
  " "
50489
50567
  );
50490
50568
  }
50491
- function extensions(env) {
50569
+ function extensions(env, platform) {
50570
+ if (platform !== "win32") {
50571
+ return [""];
50572
+ }
50492
50573
  const declared = env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
50493
- return ["", ...declared.split(";").filter((extension) => extension.length > 0)];
50574
+ return [...declared.split(";").filter((extension) => extension.length > 0), ""];
50494
50575
  }
50495
- function findExecutable(executable, env) {
50496
- const candidates = extensions(env);
50576
+ function findExecutable(executable, env, platform = process.platform) {
50577
+ const candidates = extensions(env, platform);
50497
50578
  const isFile = (path2) => (0, import_node_fs.existsSync)(path2) && (0, import_node_fs.statSync)(path2).isFile();
50498
50579
  if (executable.includes("/") || executable.includes("\\") || (0, import_node_path.isAbsolute)(executable)) {
50499
50580
  return candidates.map((extension) => executable + extension).find(isFile);
@@ -50519,6 +50600,7 @@ function escapeArgument(argument, doubleEscape) {
50519
50600
 
50520
50601
  // src/install.ts
50521
50602
  var RUNTIME_PACKAGE = "@penvhq/penv";
50603
+ var SCHEMA_PACKAGE = "zod";
50522
50604
  var LOCKFILES = [
50523
50605
  ["pnpm", "pnpm-lock.yaml"],
50524
50606
  ["yarn", "yarn.lock"],
@@ -50533,13 +50615,9 @@ var ADD = {
50533
50615
  bun: ["bun", "add", "--exact"]
50534
50616
  };
50535
50617
  function engineVersion() {
50536
- const manifest = new URL("../package.json", importMetaUrl);
50537
- try {
50538
- const version2 = JSON.parse((0, import_node_fs2.readFileSync)(manifest, "utf8")).version;
50539
- if (typeof version2 === "string" && version2.length > 0) {
50540
- return version2;
50541
- }
50542
- } catch {
50618
+ const version2 = ownManifest()?.version;
50619
+ if (typeof version2 === "string" && version2.length > 0) {
50620
+ return version2;
50543
50621
  }
50544
50622
  throw new PenvError(
50545
50623
  "ENGINE_VERSION_UNREADABLE",
@@ -50547,6 +50625,29 @@ function engineVersion() {
50547
50625
  `Reinstall penv, then run \`penv init\` again.`
50548
50626
  );
50549
50627
  }
50628
+ function schemaPackageVersion() {
50629
+ const peers = ownManifest()?.peerDependencies;
50630
+ const declared = peers !== null && typeof peers === "object" && !Array.isArray(peers) ? peers[SCHEMA_PACKAGE] : void 0;
50631
+ const floor = typeof declared === "string" ? declared.replace(/^[\^~>=\s]+/, "").trim() : "";
50632
+ if (floor.length > 0) {
50633
+ return floor;
50634
+ }
50635
+ throw new PenvError(
50636
+ "ENGINE_PEER_UNREADABLE",
50637
+ `penv could not read its own \`${SCHEMA_PACKAGE}\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,
50638
+ `Reinstall penv, then run \`penv init\` again.`
50639
+ );
50640
+ }
50641
+ function ownManifest() {
50642
+ try {
50643
+ const parsed = JSON.parse(
50644
+ (0, import_node_fs2.readFileSync)(new URL("../package.json", importMetaUrl), "utf8")
50645
+ );
50646
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
50647
+ } catch {
50648
+ return void 0;
50649
+ }
50650
+ }
50550
50651
  function detectPackageManager(root) {
50551
50652
  for (const [manager, lockfile] of LOCKFILES) {
50552
50653
  if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, lockfile))) {
@@ -50575,12 +50676,12 @@ function manifestOf(root) {
50575
50676
  return void 0;
50576
50677
  }
50577
50678
  }
50578
- function declaredVersion(root) {
50679
+ function declaredVersion(root, name) {
50579
50680
  const manifest = manifestOf(root);
50580
50681
  for (const field of ["dependencies", "devDependencies"]) {
50581
50682
  const block = manifest?.[field];
50582
50683
  if (block !== null && typeof block === "object" && !Array.isArray(block)) {
50583
- const version2 = block[RUNTIME_PACKAGE];
50684
+ const version2 = block[name];
50584
50685
  if (typeof version2 === "string") {
50585
50686
  return version2;
50586
50687
  }
@@ -50593,27 +50694,61 @@ function planInstall(root, version2 = engineVersion()) {
50593
50694
  const lockfile = LOCKFILES.find(
50594
50695
  ([name, file2]) => name === manager && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, file2))
50595
50696
  )?.[1];
50596
- const declared = declaredVersion(root);
50697
+ const runtimeDeclared = declaredVersion(root, RUNTIME_PACKAGE);
50698
+ const zodDeclared = declaredVersion(root, SCHEMA_PACKAGE);
50699
+ const packages = [
50700
+ {
50701
+ name: RUNTIME_PACKAGE,
50702
+ version: version2,
50703
+ ...runtimeDeclared === void 0 ? {} : { declared: runtimeDeclared },
50704
+ satisfied: runtimeDeclared === version2
50705
+ },
50706
+ {
50707
+ name: SCHEMA_PACKAGE,
50708
+ version: schemaPackageVersion(),
50709
+ ...zodDeclared === void 0 ? {} : { declared: zodDeclared },
50710
+ // Any declared zod counts: which zod a project uses is the project's
50711
+ // decision, and penv is here to make sure there is one, not to move it.
50712
+ satisfied: zodDeclared !== void 0
50713
+ }
50714
+ ];
50715
+ const pending = packages.filter((entry) => !entry.satisfied);
50716
+ const specs = (pending.length === 0 ? packages : pending).map(
50717
+ (entry) => `${entry.name}@${entry.version}`
50718
+ );
50597
50719
  return {
50598
50720
  root,
50599
50721
  manager,
50600
- package: RUNTIME_PACKAGE,
50601
- version: version2,
50602
- command: [...ADD[manager], `${RUNTIME_PACKAGE}@${version2}`],
50722
+ packages,
50723
+ command: [...ADD[manager], ...specs],
50603
50724
  ...lockfile === void 0 ? {} : { lockfile },
50604
- ...declared === void 0 ? {} : { declared },
50605
- satisfied: declared === version2
50725
+ satisfied: pending.length === 0
50606
50726
  };
50607
50727
  }
50728
+ function describe4(entry) {
50729
+ return `${entry.name} ${entry.version}`;
50730
+ }
50608
50731
  function renderInstallPlan(plan2) {
50609
50732
  if (plan2.satisfied) {
50610
- return [`package.json already pins ${plan2.package} ${plan2.version} \u2014 nothing to install.`];
50733
+ return [
50734
+ `package.json already has ${plan2.packages.map(describe4).join(" and ")} \u2014 nothing to install.`
50735
+ ];
50611
50736
  }
50612
- const line = `"${plan2.package}": "${plan2.version}"`;
50737
+ const pending = plan2.packages.filter((entry) => !entry.satisfied);
50738
+ const added = pending.filter((entry) => entry.declared === void 0);
50739
+ const replaced = pending.filter((entry) => entry.declared !== void 0);
50613
50740
  return [
50614
50741
  "package.json",
50615
- ...plan2.declared === void 0 ? [' + "dependencies": {', ` + ${line}`, " + }"] : [` - "${plan2.package}": "${plan2.declared}"`, ` + ${line}`],
50616
- ...plan2.lockfile === void 0 ? [] : [plan2.lockfile, ` + ${plan2.package}@${plan2.version}`],
50742
+ ...added.length === 0 ? [] : [
50743
+ ' + "dependencies": {',
50744
+ ...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
50745
+ " + }"
50746
+ ],
50747
+ ...replaced.flatMap((entry) => [
50748
+ ` - "${entry.name}": "${entry.declared}"`,
50749
+ ` + "${entry.name}": "${entry.version}"`
50750
+ ]),
50751
+ ...plan2.lockfile === void 0 ? [] : [plan2.lockfile, ...pending.map((entry) => ` + ${entry.name}@${entry.version}`)],
50617
50752
  "",
50618
50753
  `Run with: ${plan2.command.join(" ")}`
50619
50754
  ];
@@ -50622,7 +50757,8 @@ var installWithPackageManager = async (plan2) => {
50622
50757
  const child = startChild({
50623
50758
  command: plan2.command,
50624
50759
  env: process.env,
50625
- cwd: plan2.root
50760
+ cwd: plan2.root,
50761
+ purpose: `install ${plan2.packages.map(describe4).join(" and ")}`
50626
50762
  });
50627
50763
  const ended = await child.ended;
50628
50764
  if (ended.exitCode !== 0 || ended.signal !== null) {
@@ -50639,7 +50775,7 @@ function installFailed(plan2) {
50639
50775
 
50640
50776
  // src/project.ts
50641
50777
  init_cjs_shims();
50642
- var import_node_fs3 = require("fs");
50778
+ var import_node_fs4 = require("fs");
50643
50779
  var import_node_path4 = require("path");
50644
50780
 
50645
50781
  // src/env-flags.ts
@@ -50702,6 +50838,7 @@ function environmentFromShorthand(config2, candidates, explicit) {
50702
50838
 
50703
50839
  // src/registry.ts
50704
50840
  init_cjs_shims();
50841
+ var import_node_fs3 = require("fs");
50705
50842
  var import_node_module = require("module");
50706
50843
  var import_node_path3 = require("path");
50707
50844
  var import_node_url = require("url");
@@ -50889,16 +51026,40 @@ async function loadPluginProvider(type, context) {
50889
51026
  assertSatisfiesContract(provider, type);
50890
51027
  return provider;
50891
51028
  }
50892
- function assertProvidersRegistered(config2, projectRoot) {
51029
+ function assertProvidersRegistered(config2, projectRoot, options) {
51030
+ const local = localExtensions(projectRoot);
51031
+ const ci = options?.ci ?? isCi(process.env.CI);
50893
51032
  for (const [environment, provider] of Object.entries(config2.providers)) {
50894
51033
  if (isProviderRegistered(provider.type)) {
50895
51034
  continue;
50896
51035
  }
51036
+ if (ci && local.includes(provider.type)) {
51037
+ throw localExtensionInCi(provider.type, environment);
51038
+ }
50897
51039
  if (resolvePlugin(provider.type, projectRoot) === void 0) {
50898
51040
  throw unknownProvider(provider.type, environment);
50899
51041
  }
50900
51042
  }
50901
51043
  }
51044
+ function localExtensions(projectRoot) {
51045
+ let text;
51046
+ try {
51047
+ text = (0, import_node_fs3.readFileSync)(localExtensionsFile(projectRoot), "utf8");
51048
+ } catch {
51049
+ return [];
51050
+ }
51051
+ return parseLocalExtensions(text);
51052
+ }
51053
+ function isCi(value2) {
51054
+ return value2 !== void 0 && value2 !== "" && value2 !== "0" && value2.toLowerCase() !== "false";
51055
+ }
51056
+ function localExtensionInCi(type, environment) {
51057
+ return new PenvError(
51058
+ "LOCAL_EXTENSION_IN_CI",
51059
+ `The provider \`${type}\` for environment ${environment} is a local extension, and this is CI`,
51060
+ `${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.`
51061
+ );
51062
+ }
50902
51063
  function resolvePlugin(specifier, fromDir) {
50903
51064
  try {
50904
51065
  const require2 = (0, import_node_module.createRequire)((0, import_node_path3.resolve)(fromDir, "noop.js"));
@@ -50936,7 +51097,7 @@ function unknownProvider(type, environment) {
50936
51097
  return new PenvError(
50937
51098
  "UNKNOWN_PROVIDER",
50938
51099
  `The provider \`${type}\`${where} in penv.config.ts is not installed in this project`,
50939
- `Install it with \`npm i ${type}\` \u2014 a provider's \`type\` is the package penv imports. The CLI ships ${preinstalled} pre-installed.`
51100
+ `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.`
50940
51101
  );
50941
51102
  }
50942
51103
 
@@ -50966,10 +51127,10 @@ function localTree(project) {
50966
51127
  }
50967
51128
  function selfContainedSchemaModule(root, schemaFile) {
50968
51129
  const file2 = (0, import_node_path4.join)(root, ...schemaFile.split("/"));
50969
- if (!(0, import_node_fs3.existsSync)(file2)) {
51130
+ if (!(0, import_node_fs4.existsSync)(file2)) {
50970
51131
  return void 0;
50971
51132
  }
50972
- const source = (0, import_node_fs3.readFileSync)(file2, "utf8");
51133
+ const source = (0, import_node_fs4.readFileSync)(file2, "utf8");
50973
51134
  return source.includes("PenvSchemaShape") || source.includes("z.object") ? schemaFile : void 0;
50974
51135
  }
50975
51136
  function schemaShapeFileOf(project) {
@@ -51206,10 +51367,7 @@ function writeError(lines) {
51206
51367
  }
51207
51368
  function reportError(error51) {
51208
51369
  if (error51 instanceof PenvError) {
51209
- const suffix = error51.remedy === void 0 ? void 0 : `
51210
- ${error51.remedy}`;
51211
- const message = suffix !== void 0 && error51.message.endsWith(suffix) ? error51.message.slice(0, -suffix.length) : error51.message;
51212
- process.stderr.write(`${err.red(CROSS)} ${message}
51370
+ process.stderr.write(`${err.red(CROSS)} ${error51.summary}
51213
51371
  `);
51214
51372
  if (error51.remedy !== void 0) {
51215
51373
  process.stderr.write(` ${err.cyan("\u2192")} ${error51.remedy}
@@ -51240,13 +51398,13 @@ async function guard(run) {
51240
51398
 
51241
51399
  // src/commands/run.ts
51242
51400
  init_cjs_shims();
51243
- var import_node_fs5 = require("fs");
51401
+ var import_node_fs6 = require("fs");
51244
51402
  var import_node_module2 = require("module");
51245
51403
  var import_node_path6 = require("path");
51246
51404
 
51247
51405
  // src/dotenv-files.ts
51248
51406
  init_cjs_shims();
51249
- var import_node_fs4 = require("fs");
51407
+ var import_node_fs5 = require("fs");
51250
51408
  var SHARED = ".env";
51251
51409
  var LOCAL2 = "local";
51252
51410
  var NOT_ENVIRONMENTS = [
@@ -51305,7 +51463,7 @@ function order(file2) {
51305
51463
  function discoverDotenvFiles(root) {
51306
51464
  let entries;
51307
51465
  try {
51308
- entries = (0, import_node_fs4.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name);
51466
+ entries = (0, import_node_fs5.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name);
51309
51467
  } catch {
51310
51468
  return [];
51311
51469
  }
@@ -52040,9 +52198,9 @@ function packageManifest(specifier, root) {
52040
52198
  }
52041
52199
  for (; ; ) {
52042
52200
  const file2 = (0, import_node_path6.join)(directory, "package.json");
52043
- if ((0, import_node_fs5.existsSync)(file2)) {
52201
+ if ((0, import_node_fs6.existsSync)(file2)) {
52044
52202
  try {
52045
- const parsed = JSON.parse((0, import_node_fs5.readFileSync)(file2, "utf8"));
52203
+ const parsed = JSON.parse((0, import_node_fs6.readFileSync)(file2, "utf8"));
52046
52204
  if (isPlainObject4(parsed) && parsed.name === specifier) {
52047
52205
  return parsed;
52048
52206
  }
@@ -52115,7 +52273,7 @@ function snapshotPath(host) {
52115
52273
  }
52116
52274
  function readSnapshot(path) {
52117
52275
  try {
52118
- return (0, import_node_fs5.readFileSync)(path, "utf8");
52276
+ return (0, import_node_fs6.readFileSync)(path, "utf8");
52119
52277
  } catch {
52120
52278
  throw new PenvError(
52121
52279
  "RUN_SNAPSHOT_MISSING",
@@ -52194,11 +52352,11 @@ function watchProject(project, onChange) {
52194
52352
  timer = setTimeout(onChange, DEBOUNCE_MS);
52195
52353
  };
52196
52354
  const add = (target, recursive, only) => {
52197
- if (!(0, import_node_fs5.existsSync)(target)) {
52355
+ if (!(0, import_node_fs6.existsSync)(target)) {
52198
52356
  return;
52199
52357
  }
52200
52358
  try {
52201
- const watcher = (0, import_node_fs5.watch)(target, { recursive }, (_event, filename) => {
52359
+ const watcher = (0, import_node_fs6.watch)(target, { recursive }, (_event, filename) => {
52202
52360
  if (only !== void 0 && (filename === null || (0, import_node_path6.basename)(filename) !== only)) {
52203
52361
  return;
52204
52362
  }
@@ -52461,8 +52619,8 @@ ${lines}`,
52461
52619
  keySource: keySourceIdentifier(project.config, environment),
52462
52620
  values
52463
52621
  };
52464
- (0, import_node_fs6.mkdirSync)((0, import_node_path7.dirname)(file2), { recursive: true });
52465
- (0, import_node_fs6.writeFileSync)(file2, serializeArtifact(artifact), "utf8");
52622
+ (0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(file2), { recursive: true });
52623
+ (0, import_node_fs7.writeFileSync)(file2, serializeArtifact(artifact), "utf8");
52466
52624
  const inside = (0, import_node_path7.relative)(project.root, file2);
52467
52625
  return {
52468
52626
  file: file2,
@@ -52532,7 +52690,7 @@ init_cjs_shims();
52532
52690
 
52533
52691
  // src/cutover.ts
52534
52692
  init_cjs_shims();
52535
- var import_node_fs7 = require("fs");
52693
+ var import_node_fs8 = require("fs");
52536
52694
  var import_node_path8 = require("path");
52537
52695
  var CUTOVER_FORMAT = 1;
52538
52696
  function fileFor(root, relativePosix) {
@@ -52550,7 +52708,7 @@ function cutoverRoot(cwd) {
52550
52708
  }
52551
52709
  function bundledFiles(root) {
52552
52710
  try {
52553
- return (0, import_node_fs7.readdirSync)(bundleDir(root)).sort();
52711
+ return (0, import_node_fs8.readdirSync)(bundleDir(root)).sort();
52554
52712
  } catch {
52555
52713
  return [];
52556
52714
  }
@@ -52560,32 +52718,32 @@ function bundleUnresolved(root) {
52560
52718
  }
52561
52719
  function readCutover(root) {
52562
52720
  const file2 = cutoverFile(root);
52563
- if (!(0, import_node_fs7.existsSync)(file2)) {
52721
+ if (!(0, import_node_fs8.existsSync)(file2)) {
52564
52722
  return void 0;
52565
52723
  }
52566
52724
  let parsed;
52567
52725
  try {
52568
- parsed = JSON.parse((0, import_node_fs7.readFileSync)(file2, "utf8"));
52726
+ parsed = JSON.parse((0, import_node_fs8.readFileSync)(file2, "utf8"));
52569
52727
  } catch {
52570
52728
  throw unreadable("it is not JSON");
52571
52729
  }
52572
52730
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
52573
52731
  throw unreadable("it is not an object");
52574
52732
  }
52575
- const record2 = parsed;
52576
- if (record2.format !== CUTOVER_FORMAT) {
52733
+ const record3 = parsed;
52734
+ if (record3.format !== CUTOVER_FORMAT) {
52577
52735
  throw unreadable(
52578
- `it is format ${JSON.stringify(record2.format)}, and penv reads format ${CUTOVER_FORMAT}`
52736
+ `it is format ${JSON.stringify(record3.format)}, and penv reads format ${CUTOVER_FORMAT}`
52579
52737
  );
52580
52738
  }
52581
- const files = record2.files;
52739
+ const files = record3.files;
52582
52740
  if (!Array.isArray(files) || files.some((name) => typeof name !== "string")) {
52583
52741
  throw unreadable("it lists no filenames");
52584
52742
  }
52585
- const environments = Array.isArray(record2.environments) ? record2.environments.filter((name) => typeof name === "string") : [];
52743
+ const environments = Array.isArray(record3.environments) ? record3.environments.filter((name) => typeof name === "string") : [];
52586
52744
  return {
52587
52745
  format: CUTOVER_FORMAT,
52588
- movedAt: typeof record2.movedAt === "string" ? record2.movedAt : "",
52746
+ movedAt: typeof record3.movedAt === "string" ? record3.movedAt : "",
52589
52747
  files,
52590
52748
  environments
52591
52749
  };
@@ -52618,12 +52776,12 @@ function bundleDotenvFiles(root, files, environments, now = /* @__PURE__ */ new
52618
52776
  environments: [...environments]
52619
52777
  };
52620
52778
  const file2 = cutoverFile(root);
52621
- (0, import_node_fs7.mkdirSync)((0, import_node_path8.dirname)(file2), { recursive: true });
52622
- (0, import_node_fs7.writeFileSync)(file2, serializeCutover(cutover), "utf8");
52779
+ (0, import_node_fs8.mkdirSync)((0, import_node_path8.dirname)(file2), { recursive: true });
52780
+ (0, import_node_fs8.writeFileSync)(file2, serializeCutover(cutover), "utf8");
52623
52781
  const bundle = bundleDir(root);
52624
- (0, import_node_fs7.mkdirSync)(bundle, { recursive: true });
52782
+ (0, import_node_fs8.mkdirSync)(bundle, { recursive: true });
52625
52783
  for (const name of files) {
52626
- (0, import_node_fs7.renameSync)((0, import_node_path8.join)(root, name), (0, import_node_path8.join)(bundle, name));
52784
+ (0, import_node_fs8.renameSync)((0, import_node_path8.join)(root, name), (0, import_node_path8.join)(bundle, name));
52627
52785
  }
52628
52786
  return cutover;
52629
52787
  }
@@ -52642,7 +52800,7 @@ function runUndo(options) {
52642
52800
  const names = [...recorded, ...bundled.filter((name) => !recorded.includes(name))];
52643
52801
  const bundle = bundleDir(root);
52644
52802
  const held = new Set(bundled);
52645
- const occupied2 = names.filter((name) => held.has(name) && (0, import_node_fs7.existsSync)((0, import_node_path8.join)(root, name)));
52803
+ const occupied2 = names.filter((name) => held.has(name) && (0, import_node_fs8.existsSync)((0, import_node_path8.join)(root, name)));
52646
52804
  if (occupied2.length > 0) {
52647
52805
  const listed = occupied2.join(", ");
52648
52806
  const many = occupied2.length > 1;
@@ -52657,9 +52815,9 @@ function runUndo(options) {
52657
52815
  const missing = [];
52658
52816
  for (const name of names) {
52659
52817
  if (held.has(name)) {
52660
- (0, import_node_fs7.renameSync)((0, import_node_path8.join)(bundle, name), (0, import_node_path8.join)(root, name));
52818
+ (0, import_node_fs8.renameSync)((0, import_node_path8.join)(bundle, name), (0, import_node_path8.join)(root, name));
52661
52819
  restored.push(name);
52662
- } else if ((0, import_node_fs7.existsSync)((0, import_node_path8.join)(root, name))) {
52820
+ } else if ((0, import_node_fs8.existsSync)((0, import_node_path8.join)(root, name))) {
52663
52821
  alreadyBack.push(name);
52664
52822
  } else {
52665
52823
  missing.push(name);
@@ -52671,13 +52829,13 @@ function runUndo(options) {
52671
52829
  function runCleanup(options) {
52672
52830
  const root = cutoverRoot(options.cwd);
52673
52831
  const held = bundledFiles(root);
52674
- const cleaned = held.length > 0 || (0, import_node_fs7.existsSync)(cutoverFile(root)) || (0, import_node_fs7.existsSync)(fileFor(root, ROLLBACK_PATH));
52832
+ const cleaned = held.length > 0 || (0, import_node_fs8.existsSync)(cutoverFile(root)) || (0, import_node_fs8.existsSync)(fileFor(root, ROLLBACK_PATH));
52675
52833
  removeBundle(root);
52676
52834
  return { root, removed: held, cleaned };
52677
52835
  }
52678
52836
  function removeBundle(root) {
52679
- (0, import_node_fs7.rmSync)(fileFor(root, ROLLBACK_PATH), { recursive: true, force: true });
52680
- (0, import_node_fs7.rmSync)(cutoverFile(root), { force: true });
52837
+ (0, import_node_fs8.rmSync)(fileFor(root, ROLLBACK_PATH), { recursive: true, force: true });
52838
+ (0, import_node_fs8.rmSync)(cutoverFile(root), { force: true });
52681
52839
  }
52682
52840
 
52683
52841
  // src/commands/cleanup.ts
@@ -52713,7 +52871,7 @@ var cleanupCommand = defineCommand({
52713
52871
 
52714
52872
  // src/commands/doctor.ts
52715
52873
  init_cjs_shims();
52716
- var import_node_fs8 = require("fs");
52874
+ var import_node_fs9 = require("fs");
52717
52875
  var import_node_path9 = require("path");
52718
52876
 
52719
52877
  // src/commands/push.ts
@@ -53157,6 +53315,7 @@ async function runDoctor(options) {
53157
53315
  findings.push(...unusedFindings(drift));
53158
53316
  }
53159
53317
  findings.push(...fallbackFindings(subjects, environment));
53318
+ findings.push(...secrecyFindings(subjects, environment));
53160
53319
  findings.push(...plaintextSecretFindings(subjects, environment));
53161
53320
  findings.push(...publicSecretFindings(subjects, environment, project.config));
53162
53321
  findings.push(...encryptionFindings(subjects, environment));
@@ -53169,6 +53328,7 @@ async function runDoctor(options) {
53169
53328
  }
53170
53329
  findings.push(...await providerDriftFindings(project, environment, options.source));
53171
53330
  findings.push(...await projectionFindings(project, environment, options.projection));
53331
+ findings.push(...localExtensionFindings(project));
53172
53332
  findings.push(...artifactFindings(project));
53173
53333
  findings.push({
53174
53334
  check: "provider",
@@ -53306,6 +53466,31 @@ function fallbackFindings(subjects, environment) {
53306
53466
  }
53307
53467
  ];
53308
53468
  }
53469
+ function secrecyFindings(subjects, environment) {
53470
+ const undeclared = subjects.filter(
53471
+ ({ meta: meta3 }) => effectiveMeta(meta3, environment).secret === void 0
53472
+ );
53473
+ if (undeclared.length === 0) {
53474
+ return [
53475
+ {
53476
+ check: "secrecy-undeclared",
53477
+ severity: "pass",
53478
+ label: "Secrecy policy",
53479
+ subject: subjects.length === 0 ? `no parameter resolves for ${environment}` : `every parameter declares whether it is secret for ${environment}`
53480
+ }
53481
+ ];
53482
+ }
53483
+ return [
53484
+ {
53485
+ check: "secrecy-undeclared",
53486
+ severity: "unknown",
53487
+ label: "Secrecy policy",
53488
+ subject: `${undeclared.length} of ${subjects.length} declare neither way for ${environment}`,
53489
+ detail: "penv was never told which of these values must be encrypted",
53490
+ remedy: `declare \`"secret": true\` or \`"secret": false\` in a parameter's meta file, ${RECORDS_PATH2}/<name>.json`
53491
+ }
53492
+ ];
53493
+ }
53309
53494
  function plaintextSecretFindings(subjects, environment) {
53310
53495
  const secrets = subjects.filter(({ meta: meta3 }) => isSecret(meta3, environment));
53311
53496
  const findings = [];
@@ -53609,7 +53794,7 @@ function looksLikeArtifact(text) {
53609
53794
  function scannableFiles(directory, out2) {
53610
53795
  let entries;
53611
53796
  try {
53612
- entries = (0, import_node_fs8.readdirSync)(directory, { withFileTypes: true });
53797
+ entries = (0, import_node_fs9.readdirSync)(directory, { withFileTypes: true });
53613
53798
  } catch {
53614
53799
  return;
53615
53800
  }
@@ -53626,6 +53811,29 @@ function scannableFiles(directory, out2) {
53626
53811
  }
53627
53812
  }
53628
53813
  }
53814
+ function localExtensionFindings(project) {
53815
+ const names = localExtensions(project.root);
53816
+ if (names.length === 0) {
53817
+ return [
53818
+ {
53819
+ check: "local-extension",
53820
+ severity: "pass",
53821
+ label: "Extensions",
53822
+ subject: "every extension this project names is pinned"
53823
+ }
53824
+ ];
53825
+ }
53826
+ return [
53827
+ {
53828
+ check: "local-extension",
53829
+ severity: "unknown",
53830
+ label: "Extensions",
53831
+ subject: names.join(", "),
53832
+ detail: "resolved from this project's node_modules \u2014 no release, no pinned bytes",
53833
+ remedy: `publish it and run \`penv add ${names[0]}\` when this stops being a development path`
53834
+ }
53835
+ ];
53836
+ }
53629
53837
  function artifactFindings(project) {
53630
53838
  const files = [];
53631
53839
  scannableFiles(project.root, files);
@@ -53633,10 +53841,10 @@ function artifactFindings(project) {
53633
53841
  for (const file2 of files) {
53634
53842
  let text;
53635
53843
  try {
53636
- if ((0, import_node_fs8.statSync)(file2).size > ARTIFACT_SCAN_LIMIT) {
53844
+ if ((0, import_node_fs9.statSync)(file2).size > ARTIFACT_SCAN_LIMIT) {
53637
53845
  continue;
53638
53846
  }
53639
- text = (0, import_node_fs8.readFileSync)(file2, "utf8");
53847
+ text = (0, import_node_fs9.readFileSync)(file2, "utf8");
53640
53848
  } catch {
53641
53849
  continue;
53642
53850
  }
@@ -54394,7 +54602,7 @@ var fillCommand = defineCommand({
54394
54602
 
54395
54603
  // src/commands/generate.ts
54396
54604
  init_cjs_shims();
54397
- var import_node_fs9 = require("fs");
54605
+ var import_node_fs10 = require("fs");
54398
54606
  var import_node_path10 = require("path");
54399
54607
  var DEFAULT_OUTPUT = ".env";
54400
54608
  function entriesFor(project, environment, allowDecrypt) {
@@ -54441,7 +54649,7 @@ function runGenerate(options) {
54441
54649
  const environment = targetEnvironment(project, options.environment, options.envFlags);
54442
54650
  const { entries, decrypted } = entriesFor(project, environment, options.allowDecrypt === true);
54443
54651
  const file2 = options.out === void 0 ? (0, import_node_path10.resolve)(project.root, DEFAULT_OUTPUT) : (0, import_node_path10.isAbsolute)(options.out) ? options.out : (0, import_node_path10.resolve)(options.cwd, options.out);
54444
- (0, import_node_fs9.writeFileSync)(file2, serializeDotenv(entries), "utf8");
54652
+ (0, import_node_fs10.writeFileSync)(file2, serializeDotenv(entries), "utf8");
54445
54653
  return { file: file2, environment, entries: entries.length, decrypted };
54446
54654
  }
54447
54655
  function displayPath2(cwd, file2) {
@@ -54579,8 +54787,8 @@ var getCommand = defineCommand({
54579
54787
 
54580
54788
  // src/commands/import.ts
54581
54789
  init_cjs_shims();
54582
- var import_node_fs12 = require("fs");
54583
- var import_node_path13 = require("path");
54790
+ var import_node_fs14 = require("fs");
54791
+ var import_node_path14 = require("path");
54584
54792
 
54585
54793
  // src/adopt.ts
54586
54794
  init_cjs_shims();
@@ -54649,7 +54857,7 @@ function writeEntries(tree, entries, refs, scope) {
54649
54857
 
54650
54858
  // src/detect.ts
54651
54859
  init_cjs_shims();
54652
- var import_node_fs10 = require("fs");
54860
+ var import_node_fs11 = require("fs");
54653
54861
  var import_node_path11 = require("path");
54654
54862
  var SIGNATURES = [
54655
54863
  { name: "Next.js", packages: ["next"], publicPrefixes: ["NEXT_PUBLIC_"] },
@@ -54666,7 +54874,7 @@ var SIGNATURES = [
54666
54874
  function exportsSchema(file2) {
54667
54875
  let source;
54668
54876
  try {
54669
- source = (0, import_node_fs10.readFileSync)(file2, "utf8");
54877
+ source = (0, import_node_fs11.readFileSync)(file2, "utf8");
54670
54878
  } catch {
54671
54879
  return false;
54672
54880
  }
@@ -54678,7 +54886,7 @@ function exportsSchema(file2) {
54678
54886
  }
54679
54887
  function occupied(cwd, relative5) {
54680
54888
  const file2 = (0, import_node_path11.join)(cwd, ...relative5.split("/"));
54681
- return (0, import_node_fs10.existsSync)(file2) && !exportsSchema(file2);
54889
+ return (0, import_node_fs11.existsSync)(file2) && !exportsSchema(file2);
54682
54890
  }
54683
54891
  function schemaFileFor(cwd) {
54684
54892
  const dir = srcPrefix(cwd);
@@ -54693,7 +54901,7 @@ function schemaFileFor(cwd) {
54693
54901
  return { file: DEFAULT_SCHEMA_FILE, displaced: preferred };
54694
54902
  }
54695
54903
  function srcPrefix(cwd) {
54696
- return (0, import_node_fs10.existsSync)((0, import_node_path11.join)(cwd, "src")) ? "src/" : "";
54904
+ return (0, import_node_fs11.existsSync)((0, import_node_path11.join)(cwd, "src")) ? "src/" : "";
54697
54905
  }
54698
54906
  function dependenciesOf(cwd) {
54699
54907
  const manifest = manifestOf2(cwd);
@@ -54713,12 +54921,12 @@ function dependenciesOf(cwd) {
54713
54921
  }
54714
54922
  function manifestOf2(cwd) {
54715
54923
  const file2 = (0, import_node_path11.join)(cwd, "package.json");
54716
- if (!(0, import_node_fs10.existsSync)(file2)) {
54924
+ if (!(0, import_node_fs11.existsSync)(file2)) {
54717
54925
  return void 0;
54718
54926
  }
54719
54927
  let manifest;
54720
54928
  try {
54721
- manifest = JSON.parse((0, import_node_fs10.readFileSync)(file2, "utf8"));
54929
+ manifest = JSON.parse((0, import_node_fs11.readFileSync)(file2, "utf8"));
54722
54930
  } catch {
54723
54931
  return void 0;
54724
54932
  }
@@ -54734,7 +54942,7 @@ function detectFramework(cwd) {
54734
54942
  return detectedFrom(cwd, signature.name, signature.publicPrefixes);
54735
54943
  }
54736
54944
  }
54737
- if ((0, import_node_fs10.existsSync)((0, import_node_path11.join)(cwd, "bunfig.toml")) || dependencies.has("bun-types")) {
54945
+ if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(cwd, "bunfig.toml")) || dependencies.has("bun-types")) {
54738
54946
  return detectedFrom(cwd, "Bun", []);
54739
54947
  }
54740
54948
  return void 0;
@@ -54814,10 +55022,93 @@ function draftFieldsAcross(sources, environments) {
54814
55022
 
54815
55023
  // src/commands/init.ts
54816
55024
  init_cjs_shims();
54817
- var import_node_fs11 = require("fs");
54818
- var import_node_path12 = require("path");
55025
+ var import_node_fs13 = require("fs");
55026
+ var import_node_path13 = require("path");
54819
55027
  var import_promises = require("readline/promises");
54820
55028
 
55029
+ // src/scaffold-undo.ts
55030
+ init_cjs_shims();
55031
+ var import_node_fs12 = require("fs");
55032
+ var import_node_path12 = require("path");
55033
+ function record2(path, files, dirs) {
55034
+ let stats;
55035
+ try {
55036
+ stats = (0, import_node_fs12.statSync)(path);
55037
+ } catch {
55038
+ return;
55039
+ }
55040
+ if (stats.isDirectory()) {
55041
+ dirs.add(path);
55042
+ for (const entry of (0, import_node_fs12.readdirSync)(path)) {
55043
+ record2((0, import_node_path12.join)(path, entry), files, dirs);
55044
+ }
55045
+ return;
55046
+ }
55047
+ if (stats.isFile()) {
55048
+ files.set(path, (0, import_node_fs12.readFileSync)(path));
55049
+ }
55050
+ }
55051
+ function captureScaffold(root, paths) {
55052
+ const files = /* @__PURE__ */ new Map();
55053
+ const dirs = /* @__PURE__ */ new Set();
55054
+ for (const path of paths) {
55055
+ for (let dir = (0, import_node_path12.dirname)(path); dir.startsWith(root) && dir !== root; dir = (0, import_node_path12.dirname)(dir)) {
55056
+ if ((0, import_node_fs12.existsSync)(dir)) {
55057
+ dirs.add(dir);
55058
+ }
55059
+ }
55060
+ record2(path, files, dirs);
55061
+ }
55062
+ return { root, paths: [...paths], files, dirs };
55063
+ }
55064
+ function restoreScaffold(undo) {
55065
+ for (const path of undo.paths) {
55066
+ removeAdded(path, undo);
55067
+ }
55068
+ for (const path of undo.paths) {
55069
+ pruneAncestors(path, undo);
55070
+ }
55071
+ for (const [file2, contents] of undo.files) {
55072
+ (0, import_node_fs12.mkdirSync)((0, import_node_path12.dirname)(file2), { recursive: true });
55073
+ (0, import_node_fs12.writeFileSync)(file2, contents);
55074
+ }
55075
+ }
55076
+ function removeAdded(path, undo) {
55077
+ let stats;
55078
+ try {
55079
+ stats = (0, import_node_fs12.statSync)(path);
55080
+ } catch {
55081
+ return;
55082
+ }
55083
+ if (stats.isFile()) {
55084
+ if (!undo.files.has(path)) {
55085
+ (0, import_node_fs12.unlinkSync)(path);
55086
+ }
55087
+ return;
55088
+ }
55089
+ if (!stats.isDirectory()) {
55090
+ return;
55091
+ }
55092
+ for (const entry of (0, import_node_fs12.readdirSync)(path)) {
55093
+ removeAdded((0, import_node_path12.join)(path, entry), undo);
55094
+ }
55095
+ if (!undo.dirs.has(path) && (0, import_node_fs12.readdirSync)(path).length === 0) {
55096
+ (0, import_node_fs12.rmdirSync)(path);
55097
+ }
55098
+ }
55099
+ function pruneAncestors(path, undo) {
55100
+ for (let dir = (0, import_node_path12.dirname)(path); dir.startsWith(undo.root) && dir !== undo.root; dir = (0, import_node_path12.dirname)(dir)) {
55101
+ if (undo.dirs.has(dir)) {
55102
+ return;
55103
+ }
55104
+ try {
55105
+ (0, import_node_fs12.rmdirSync)(dir);
55106
+ } catch {
55107
+ return;
55108
+ }
55109
+ }
55110
+ }
55111
+
54821
55112
  // src/seams.ts
54822
55113
  init_cjs_shims();
54823
55114
  function nextjs({ alias, srcDir }) {
@@ -54974,7 +55265,7 @@ var NOT_ENVIRONMENTS2 = [...RESERVED_TOKENS, "example", "sample", "template"];
54974
55265
  function suggestEnvironments(root) {
54975
55266
  let entries;
54976
55267
  try {
54977
- entries = (0, import_node_fs11.readdirSync)(root);
55268
+ entries = (0, import_node_fs13.readdirSync)(root);
54978
55269
  } catch {
54979
55270
  return [];
54980
55271
  }
@@ -55018,8 +55309,8 @@ function configOf(decisions) {
55018
55309
  return { environments: decisions.environments, providers: {}, schemaFile: decisions.schemaFile };
55019
55310
  }
55020
55311
  function declaredIn(root) {
55021
- const file2 = (0, import_node_path12.join)(root, CONFIG_FILE);
55022
- if (!(0, import_node_fs11.existsSync)(file2)) {
55312
+ const file2 = (0, import_node_path13.join)(root, CONFIG_FILE);
55313
+ if (!(0, import_node_fs13.existsSync)(file2)) {
55023
55314
  return void 0;
55024
55315
  }
55025
55316
  return loadConfigFrom(file2);
@@ -55461,16 +55752,16 @@ function renderTsconfig(target, alias) {
55461
55752
  `;
55462
55753
  }
55463
55754
  function ensurePenvDir(root) {
55464
- const dir = (0, import_node_path12.resolve)(root, PENV_DIR);
55465
- if ((0, import_node_fs11.existsSync)(dir)) {
55755
+ const dir = (0, import_node_path13.resolve)(root, PENV_DIR);
55756
+ if ((0, import_node_fs13.existsSync)(dir)) {
55466
55757
  return { target: "penv-dir", action: "kept", text: `Found ${PENV_DIR}/` };
55467
55758
  }
55468
- (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
55759
+ (0, import_node_fs13.mkdirSync)(dir, { recursive: true });
55469
55760
  return { target: "penv-dir", action: "created", text: `Created ${PENV_DIR}/` };
55470
55761
  }
55471
55762
  function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS) {
55472
- const file2 = (0, import_node_path12.join)(root, SCHEMA_SHAPE_FILE);
55473
- if ((0, import_node_fs11.existsSync)(file2)) {
55763
+ const file2 = (0, import_node_path13.join)(root, SCHEMA_SHAPE_FILE);
55764
+ if ((0, import_node_fs13.existsSync)(file2)) {
55474
55765
  return {
55475
55766
  target: "schema",
55476
55767
  action: "kept",
@@ -55487,7 +55778,7 @@ function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS
55487
55778
  note: `(the shape lives there, from before the ${SCHEMA_SHAPE_FILE} split \u2014 penv did not add a second one. To adopt the split: move the \`z.object\` (and any \`declare module\` block) into ${SCHEMA_SHAPE_FILE}, leaving ${oldLayout} importing \`schema\` from it and calling \`load\`.)`
55488
55779
  };
55489
55780
  }
55490
- (0, import_node_fs11.writeFileSync)(file2, renderSchemaShapeModule(fields, draft), "utf8");
55781
+ (0, import_node_fs13.writeFileSync)(file2, renderSchemaShapeModule(fields, draft), "utf8");
55491
55782
  return {
55492
55783
  target: "schema",
55493
55784
  action: "created",
@@ -55496,8 +55787,8 @@ function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS
55496
55787
  };
55497
55788
  }
55498
55789
  function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
55499
- const file2 = (0, import_node_path12.join)(root, ...decisions.schemaFile.split("/"));
55500
- if ((0, import_node_fs11.existsSync)(file2)) {
55790
+ const file2 = (0, import_node_path13.join)(root, ...decisions.schemaFile.split("/"));
55791
+ if ((0, import_node_fs13.existsSync)(file2)) {
55501
55792
  return {
55502
55793
  target: "env",
55503
55794
  action: "kept",
@@ -55505,8 +55796,8 @@ function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
55505
55796
  note: "(yours \u2014 penv never regenerates it)"
55506
55797
  };
55507
55798
  }
55508
- (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file2), { recursive: true });
55509
- (0, import_node_fs11.writeFileSync)(file2, renderEnvModule(decisions.schemaFile, decisions.inject), "utf8");
55799
+ (0, import_node_fs13.mkdirSync)((0, import_node_path13.dirname)(file2), { recursive: true });
55800
+ (0, import_node_fs13.writeFileSync)(file2, renderEnvModule(decisions.schemaFile, decisions.inject), "utf8");
55510
55801
  return {
55511
55802
  target: "env",
55512
55803
  action: "created",
@@ -55515,19 +55806,19 @@ function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
55515
55806
  };
55516
55807
  }
55517
55808
  function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
55518
- const file2 = (0, import_node_path12.join)(root, CONFIG_FILE);
55519
- if ((0, import_node_fs11.existsSync)(file2)) {
55809
+ const file2 = (0, import_node_path13.join)(root, CONFIG_FILE);
55810
+ if ((0, import_node_fs13.existsSync)(file2)) {
55520
55811
  return { target: "config", action: "kept", text: `Kept ${CONFIG_FILE}` };
55521
55812
  }
55522
- (0, import_node_fs11.writeFileSync)(file2, renderConfigModule(decisions), "utf8");
55813
+ (0, import_node_fs13.writeFileSync)(file2, renderConfigModule(decisions), "utf8");
55523
55814
  return { target: "config", action: "created", text: `Generated ${CONFIG_FILE}` };
55524
55815
  }
55525
55816
  function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
55526
55817
  const alias = decisions.alias;
55527
55818
  const imports = alias.startsWith(IMPORTS_PREFIX);
55528
- const file2 = (0, import_node_path12.join)(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
55819
+ const file2 = (0, import_node_path13.join)(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
55529
55820
  const where = imports ? PACKAGE_FILE : TSCONFIG_FILE;
55530
- if (!(0, import_node_fs11.existsSync)(file2)) {
55821
+ if (!(0, import_node_fs13.existsSync)(file2)) {
55531
55822
  if (imports) {
55532
55823
  return {
55533
55824
  target: "tsconfig",
@@ -55536,14 +55827,14 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
55536
55827
  note: `(run \`npm init\` first, or use \`--alias @env\` to alias through ${TSCONFIG_FILE})`
55537
55828
  };
55538
55829
  }
55539
- (0, import_node_fs11.writeFileSync)(file2, renderTsconfig(decisions.schemaFile, alias), "utf8");
55830
+ (0, import_node_fs13.writeFileSync)(file2, renderTsconfig(decisions.schemaFile, alias), "utf8");
55540
55831
  return {
55541
55832
  target: "tsconfig",
55542
55833
  action: "created",
55543
55834
  text: `Created ${TSCONFIG_FILE} with the ${alias} path alias`
55544
55835
  };
55545
55836
  }
55546
- const source = (0, import_node_fs11.readFileSync)(file2, "utf8");
55837
+ const source = (0, import_node_fs13.readFileSync)(file2, "utf8");
55547
55838
  const edit = imports ? insertImportsAlias(source, decisions.schemaFile, alias) : insertEnvAlias(source, decisions.schemaFile, alias);
55548
55839
  if (edit.conflict !== void 0) {
55549
55840
  return {
@@ -55560,7 +55851,7 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
55560
55851
  text: `Kept the ${alias} alias in ${where}`
55561
55852
  };
55562
55853
  }
55563
- (0, import_node_fs11.writeFileSync)(file2, edit.source, "utf8");
55854
+ (0, import_node_fs13.writeFileSync)(file2, edit.source, "utf8");
55564
55855
  return {
55565
55856
  target: "tsconfig",
55566
55857
  action: "updated",
@@ -55568,14 +55859,14 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
55568
55859
  };
55569
55860
  }
55570
55861
  function writeGitignore(root, decisions = DEFAULT_DECISIONS) {
55571
- const file2 = (0, import_node_path12.join)(root, ...STATE_GITIGNORE_PATH.split("/"));
55862
+ const file2 = (0, import_node_path13.join)(root, ...STATE_GITIGNORE_PATH.split("/"));
55572
55863
  const wanted = renderStateGitignore(configOf(decisions));
55573
- const existing = (0, import_node_fs11.existsSync)(file2) ? (0, import_node_fs11.readFileSync)(file2, "utf8") : void 0;
55864
+ const existing = (0, import_node_fs13.existsSync)(file2) ? (0, import_node_fs13.readFileSync)(file2, "utf8") : void 0;
55574
55865
  if (existing === wanted) {
55575
55866
  return { target: "gitignore", action: "kept", text: `Kept ${STATE_GITIGNORE_PATH}` };
55576
55867
  }
55577
- (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file2), { recursive: true });
55578
- (0, import_node_fs11.writeFileSync)(file2, wanted, "utf8");
55868
+ (0, import_node_fs13.mkdirSync)((0, import_node_path13.dirname)(file2), { recursive: true });
55869
+ (0, import_node_fs13.writeFileSync)(file2, wanted, "utf8");
55579
55870
  return {
55580
55871
  target: "gitignore",
55581
55872
  action: existing === void 0 ? "created" : "updated",
@@ -55591,12 +55882,12 @@ function outdatedRuntimeWarning(root) {
55591
55882
  return `Injection needs @penvhq/penv ${INJECT_MIN_VERSION}+ \u2014 this project has ${version2}, whose \`load\` ignores \`{ inject: true }\`. Upgrade, or process.env stays empty.`;
55592
55883
  }
55593
55884
  function installedPenvVersion(root) {
55594
- const file2 = (0, import_node_path12.join)(root, "node_modules", "@penvhq", "penv", "package.json");
55595
- if (!(0, import_node_fs11.existsSync)(file2)) {
55885
+ const file2 = (0, import_node_path13.join)(root, "node_modules", "@penvhq", "penv", "package.json");
55886
+ if (!(0, import_node_fs13.existsSync)(file2)) {
55596
55887
  return void 0;
55597
55888
  }
55598
55889
  try {
55599
- const version2 = JSON.parse((0, import_node_fs11.readFileSync)(file2, "utf8")).version;
55890
+ const version2 = JSON.parse((0, import_node_fs13.readFileSync)(file2, "utf8")).version;
55600
55891
  return typeof version2 === "string" ? version2 : void 0;
55601
55892
  } catch {
55602
55893
  return void 0;
@@ -55636,11 +55927,11 @@ function writeSeam(root, decisions = DEFAULT_DECISIONS, framework = detectFramew
55636
55927
  };
55637
55928
  }
55638
55929
  const alsoNote = writeAlso(root, seam.also);
55639
- const file2 = (0, import_node_path12.join)(root, ...seam.file.split("/"));
55930
+ const file2 = (0, import_node_path13.join)(root, ...seam.file.split("/"));
55640
55931
  const baseNotes = [...seam.notes, ...alsoNote === void 0 ? [] : [alsoNote]];
55641
55932
  const notes = baseNotes.length === 0 ? "" : `
55642
55933
  ${baseNotes.map((n) => ` ${n}`).join("\n")}`;
55643
- if ((0, import_node_fs11.existsSync)(file2)) {
55934
+ if ((0, import_node_fs13.existsSync)(file2)) {
55644
55935
  return {
55645
55936
  target: "seam",
55646
55937
  action: "info",
@@ -55648,8 +55939,8 @@ ${baseNotes.map((n) => ` ${n}`).join("\n")}`;
55648
55939
  note: withWarning(`${seam.ifPresent}${notes}`, outdated)
55649
55940
  };
55650
55941
  }
55651
- (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file2), { recursive: true });
55652
- (0, import_node_fs11.writeFileSync)(file2, seam.content, "utf8");
55942
+ (0, import_node_fs13.mkdirSync)((0, import_node_path13.dirname)(file2), { recursive: true });
55943
+ (0, import_node_fs13.writeFileSync)(file2, seam.content, "utf8");
55653
55944
  return {
55654
55945
  target: "seam",
55655
55946
  action: outdated === void 0 ? "created" : "info",
@@ -55661,12 +55952,12 @@ function writeAlso(root, also) {
55661
55952
  if (also === void 0) {
55662
55953
  return void 0;
55663
55954
  }
55664
- const file2 = (0, import_node_path12.join)(root, ...also.file.split("/"));
55665
- if ((0, import_node_fs11.existsSync)(file2)) {
55955
+ const file2 = (0, import_node_path13.join)(root, ...also.file.split("/"));
55956
+ if ((0, import_node_fs13.existsSync)(file2)) {
55666
55957
  return also.ifPresent;
55667
55958
  }
55668
- (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file2), { recursive: true });
55669
- (0, import_node_fs11.writeFileSync)(file2, also.content, "utf8");
55959
+ (0, import_node_fs13.mkdirSync)((0, import_node_path13.dirname)(file2), { recursive: true });
55960
+ (0, import_node_fs13.writeFileSync)(file2, also.content, "utf8");
55670
55961
  return `Wrote ${also.file} to register it.`;
55671
55962
  }
55672
55963
  function withWarning(note, warning) {
@@ -55691,6 +55982,23 @@ function scaffold(root, fields, draft, decisions = DEFAULT_DECISIONS, framework
55691
55982
  const seam = writeSeam(root, decisions, framework);
55692
55983
  return seam === void 0 ? steps : [...steps, seam];
55693
55984
  }
55985
+ function scaffoldPaths(root, decisions = DEFAULT_DECISIONS, framework = detectFramework(root)?.name) {
55986
+ const fileAt = (relative5) => (0, import_node_path13.join)(root, ...relative5.split("/"));
55987
+ const seam = decisions.inject ? seamFor(framework, {
55988
+ alias: decisions.alias,
55989
+ srcDir: srcPrefix(root),
55990
+ schemaFile: decisions.schemaFile
55991
+ }) : void 0;
55992
+ return [
55993
+ fileAt(PENV_DIR),
55994
+ fileAt(SCHEMA_SHAPE_FILE),
55995
+ fileAt(decisions.schemaFile),
55996
+ fileAt(CONFIG_FILE),
55997
+ fileAt(TSCONFIG_FILE),
55998
+ fileAt(PACKAGE_FILE),
55999
+ ...seam?.kind === "scaffold" ? [fileAt(seam.file), ...seam.also === void 0 ? [] : [fileAt(seam.also.file)]] : []
56000
+ ];
56001
+ }
55694
56002
  var DEVELOPMENT = "development";
55695
56003
  var LOCAL_PROVIDER = "@penvhq/provider-filesystem";
55696
56004
  function planAdoption(root) {
@@ -55753,7 +56061,7 @@ function planCutover(input) {
55753
56061
  const diagnostics = [];
55754
56062
  let variables = 0;
55755
56063
  for (const file2 of selected) {
55756
- const parsed = parseDotenv((0, import_node_fs11.readFileSync)((0, import_node_path12.join)(root, file2.name), "utf8"));
56064
+ const parsed = parseDotenv((0, import_node_fs13.readFileSync)((0, import_node_path13.join)(root, file2.name), "utf8"));
55757
56065
  const fileRefs = refsForEntries(parsed.entries, file2.name, config2);
55758
56066
  adopted.push({ file: file2, entries: parsed.entries, refs: fileRefs, scope: scopeOf(file2) });
55759
56067
  refs.push(...fileRefs);
@@ -55864,17 +56172,27 @@ async function applyCutover(plan2, options = {}) {
55864
56172
  if (!plan2.install.satisfied) {
55865
56173
  await (options.install ?? installWithPackageManager)(plan2.install);
55866
56174
  }
55867
- const steps = scaffold(plan2.root, plan2.fields, true, plan2.decisions, plan2.framework);
55868
- const project = openProject(plan2.root);
55869
- const tree = localTree(project);
55870
- for (const adopted of plan2.adopted) {
55871
- writeEntries(tree, adopted.entries, adopted.refs, adopted.scope);
55872
- }
55873
- for (const environment of plan2.adopting) {
55874
- const check2 = await checkEnvironment(project, environment);
55875
- if (!check2.result.ok) {
55876
- throw invalidAfterImport(check2.result);
56175
+ const undo = captureScaffold(plan2.root, scaffoldPaths(plan2.root, plan2.decisions, plan2.framework));
56176
+ let steps;
56177
+ try {
56178
+ steps = scaffold(plan2.root, plan2.fields, true, plan2.decisions, plan2.framework);
56179
+ const project = openProject(plan2.root);
56180
+ const tree = localTree(project);
56181
+ for (const adopted of plan2.adopted) {
56182
+ writeEntries(tree, adopted.entries, adopted.refs, adopted.scope);
55877
56183
  }
56184
+ for (const environment of plan2.adopting) {
56185
+ const check2 = await checkEnvironment(project, environment);
56186
+ if (check2.schema === void 0) {
56187
+ throw draftNotLoaded(check2.result);
56188
+ }
56189
+ if (!check2.result.ok) {
56190
+ throw invalidAfterImport(check2.result);
56191
+ }
56192
+ }
56193
+ } catch (error51) {
56194
+ restoreScaffold(undo);
56195
+ throw error51;
55878
56196
  }
55879
56197
  const cutover = bundleDotenvFiles(
55880
56198
  plan2.root,
@@ -55883,13 +56201,24 @@ async function applyCutover(plan2, options = {}) {
55883
56201
  );
55884
56202
  return { plan: plan2, steps, moved: cutover.files, validated: plan2.adopting };
55885
56203
  }
56204
+ function issueLines(result2) {
56205
+ return result2.issues.map((issue2) => ` ${issue2.subject}: ${issue2.message}`).join("\n");
56206
+ }
56207
+ 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.";
55886
56208
  function invalidAfterImport(result2) {
55887
- const lines = result2.issues.map((issue2) => ` ${issue2.subject}: ${issue2.message}`).join("\n");
55888
56209
  return new PenvError(
55889
56210
  "INIT_CUTOVER_INVALID",
55890
- `The imported values do not satisfy the draft schema for ${result2.environment}, so your dotenv files were left where they are:
55891
- ${lines}`,
55892
- `Correct ${SCHEMA_SHAPE_FILE} or the values above, then run \`penv init\` again.`
56211
+ `The imported values do not satisfy the draft schema for ${result2.environment}:
56212
+ ${issueLines(result2)}`,
56213
+ `Correct the values above, then run \`penv init\` again. ${SCAFFOLD_ROLLED_BACK}`
56214
+ );
56215
+ }
56216
+ function draftNotLoaded(result2) {
56217
+ return new PenvError(
56218
+ "INIT_DRAFT_NOT_LOADED",
56219
+ `penv could not load the schema it drafted for ${result2.environment}, so nothing was checked against it:
56220
+ ${issueLines(result2)}`,
56221
+ `Fix the error above, then run \`penv init\` again. ${SCAFFOLD_ROLLED_BACK}`
55893
56222
  );
55894
56223
  }
55895
56224
  function renderSelection(plan2, selected = plan2.preselected) {
@@ -55952,7 +56281,7 @@ function renderCutover(result2) {
55952
56281
  const glyph = step.action === "conflicted" ? WARN : step.action === "info" ? "\u2192" : CHECK;
55953
56282
  return step.note === void 0 ? { glyph, text: step.text } : { glyph, text: step.text, note: step.note };
55954
56283
  }),
55955
- ...plan2.install.satisfied ? [] : [{ glyph: CHECK, text: `Installed ${plan2.install.package}`, note: plan2.install.version }],
56284
+ ...plan2.install.packages.filter((entry) => !entry.satisfied).map((entry) => ({ glyph: CHECK, text: `Installed ${entry.name}`, note: entry.version })),
55956
56285
  {
55957
56286
  glyph: CHECK,
55958
56287
  text: `Imported ${plan2.fields.length} parameters`,
@@ -55976,12 +56305,12 @@ function dailyCommand(root) {
55976
56305
  return `penv run -- ${detectPackageManager(root)} ${devScript(root)}`;
55977
56306
  }
55978
56307
  function devScript(root) {
55979
- const file2 = (0, import_node_path12.join)(root, PACKAGE_FILE);
55980
- if (!(0, import_node_fs11.existsSync)(file2)) {
56308
+ const file2 = (0, import_node_path13.join)(root, PACKAGE_FILE);
56309
+ if (!(0, import_node_fs13.existsSync)(file2)) {
55981
56310
  return "dev";
55982
56311
  }
55983
56312
  try {
55984
- const scripts = JSON.parse((0, import_node_fs11.readFileSync)(file2, "utf8")).scripts;
56313
+ const scripts = JSON.parse((0, import_node_fs13.readFileSync)(file2, "utf8")).scripts;
55985
56314
  if (scripts !== null && typeof scripts === "object" && !Array.isArray(scripts)) {
55986
56315
  const named = Object.keys(scripts);
55987
56316
  return ["dev", "start"].find((script) => named.includes(script)) ?? named[0] ?? "dev";
@@ -55991,7 +56320,7 @@ function devScript(root) {
55991
56320
  return "dev";
55992
56321
  }
55993
56322
  function runInit(options) {
55994
- const root = (0, import_node_path12.resolve)(options.cwd);
56323
+ const root = (0, import_node_path13.resolve)(options.cwd);
55995
56324
  const decisions = options.decisions ?? planInit(root).decisions;
55996
56325
  return { root, decisions, steps: scaffold(root, [], false, decisions, options.framework) };
55997
56326
  }
@@ -56154,7 +56483,7 @@ var initCommand = defineCommand({
56154
56483
  },
56155
56484
  run({ args }) {
56156
56485
  return guard(async () => {
56157
- const root = (0, import_node_path12.resolve)(process.cwd());
56486
+ const root = (0, import_node_path13.resolve)(process.cwd());
56158
56487
  if (args.action !== void 0) {
56159
56488
  runUndoAction(root, String(args.action));
56160
56489
  return;
@@ -56230,7 +56559,7 @@ function assertDeclared(segment, config2) {
56230
56559
  throw new UnknownEnvironmentError(segment, config2.environments);
56231
56560
  }
56232
56561
  function scopeFromFilename(file2, config2) {
56233
- const name = (0, import_node_path13.basename)(file2);
56562
+ const name = (0, import_node_path14.basename)(file2);
56234
56563
  const segments = name.split(".");
56235
56564
  const start = segments.indexOf(DOTENV_SEGMENT);
56236
56565
  if (start === -1) {
@@ -56314,7 +56643,7 @@ function environmentNamed(file2, explicit) {
56314
56643
  if (explicit !== void 0 && explicit.trim().length > 0) {
56315
56644
  return explicit.trim();
56316
56645
  }
56317
- const segments = (0, import_node_path13.basename)(file2).split(".");
56646
+ const segments = (0, import_node_path14.basename)(file2).split(".");
56318
56647
  const start = segments.indexOf(DOTENV_SEGMENT);
56319
56648
  if (start === -1) {
56320
56649
  return void 0;
@@ -56348,16 +56677,16 @@ function configInEffect(cwd, environment) {
56348
56677
  return { config: openProject(cwd).config, decisions };
56349
56678
  }
56350
56679
  function importDotenv(options) {
56351
- const cwd = (0, import_node_path13.resolve)(options.cwd);
56352
- const file2 = (0, import_node_path13.isAbsolute)(options.file) ? options.file : (0, import_node_path13.resolve)(cwd, options.file);
56353
- if (!(0, import_node_fs12.existsSync)(file2)) {
56680
+ const cwd = (0, import_node_path14.resolve)(options.cwd);
56681
+ const file2 = (0, import_node_path14.isAbsolute)(options.file) ? options.file : (0, import_node_path14.resolve)(cwd, options.file);
56682
+ if (!(0, import_node_fs14.existsSync)(file2)) {
56354
56683
  throw new PenvError(
56355
56684
  "IMPORT_FILE_MISSING",
56356
56685
  `There is no file at ${file2} to import`,
56357
56686
  "Point `penv import` at an existing dotenv file, e.g. `penv import .env`."
56358
56687
  );
56359
56688
  }
56360
- const parsed = parseDotenv((0, import_node_fs12.readFileSync)(file2, "utf8"));
56689
+ const parsed = parseDotenv((0, import_node_fs14.readFileSync)(file2, "utf8"));
56361
56690
  const { config: config2, decisions } = configInEffect(cwd, environmentNamed(file2, options.environment));
56362
56691
  const source = displayPath3(cwd, file2);
56363
56692
  const named = scopeFromFilename(file2, config2);
@@ -56373,7 +56702,7 @@ function importDotenv(options) {
56373
56702
  const tree = localTree(project);
56374
56703
  writeEntries(tree, parsed.entries, refs, scope);
56375
56704
  const backup = `${file2}${BACKUP_SUFFIX}`;
56376
- (0, import_node_fs12.copyFileSync)(file2, backup);
56705
+ (0, import_node_fs14.copyFileSync)(file2, backup);
56377
56706
  return {
56378
56707
  root: project.root,
56379
56708
  file: file2,
@@ -56387,7 +56716,7 @@ function importDotenv(options) {
56387
56716
  };
56388
56717
  }
56389
56718
  function displayPath3(root, file2) {
56390
- const rel = (0, import_node_path13.relative)(root, file2);
56719
+ const rel = (0, import_node_path14.relative)(root, file2);
56391
56720
  return rel === "" || rel.startsWith("..") ? file2 : rel.split("\\").join("/");
56392
56721
  }
56393
56722
  function keptSchemaStep(step, variables) {
@@ -56706,28 +57035,28 @@ var listCommand = defineCommand({
56706
57035
 
56707
57036
  // src/commands/migrate.ts
56708
57037
  init_cjs_shims();
56709
- var import_node_fs13 = require("fs");
56710
- var import_node_path14 = require("path");
57038
+ var import_node_fs15 = require("fs");
57039
+ var import_node_path15 = require("path");
56711
57040
  var import_promises2 = require("readline/promises");
56712
57041
  var OLD_GITIGNORE = `${PENV_DIR}/.gitignore`;
56713
57042
  function planMigrate(cwd) {
56714
57043
  const { config: config2, file: file2 } = loadConfig(cwd);
56715
- const root = (0, import_node_path14.dirname)(file2);
57044
+ const root = (0, import_node_path15.dirname)(file2);
56716
57045
  const entries = oldLayoutEntries(root, config2);
56717
57046
  const tree = recordsDir(root);
56718
57047
  const collisions = collidingEntries(entries, tree);
56719
57048
  if (collisions.length > 0) {
56720
57049
  throw new PenvError(
56721
57050
  "HALF_MIGRATED",
56722
- `${describe4(collisions)} in both \`${PENV_DIR}/\` and \`${RECORDS_PATH2}/\`, and penv cannot tell which copy is current`,
57051
+ `${describe5(collisions)} in both \`${PENV_DIR}/\` and \`${RECORDS_PATH2}/\`, and penv cannot tell which copy is current`,
56723
57052
  `Move what is left under \`${PENV_DIR}/\` into \`${RECORDS_PATH2}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
56724
57053
  );
56725
57054
  }
56726
57055
  const creates = [];
56727
- if (entries.length > 0 && !(0, import_node_fs13.existsSync)(tree)) {
57056
+ if (entries.length > 0 && !(0, import_node_fs15.existsSync)(tree)) {
56728
57057
  creates.push(`${RECORDS_PATH2}/`);
56729
57058
  }
56730
- if (readIfPresent((0, import_node_path14.join)(root, ...STATE_GITIGNORE_PATH.split("/"))) !== renderStateGitignore(config2)) {
57059
+ if (readIfPresent((0, import_node_path15.join)(root, ...STATE_GITIGNORE_PATH.split("/"))) !== renderStateGitignore(config2)) {
56731
57060
  creates.push(STATE_GITIGNORE_PATH);
56732
57061
  }
56733
57062
  return {
@@ -56737,23 +57066,23 @@ function planMigrate(cwd) {
56737
57066
  to: `${RECORDS_PATH2}/${entry}`
56738
57067
  })),
56739
57068
  creates,
56740
- removes: (0, import_node_fs13.existsSync)((0, import_node_path14.join)(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
57069
+ removes: (0, import_node_fs15.existsSync)((0, import_node_path15.join)(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
56741
57070
  };
56742
57071
  }
56743
57072
  function readIfPresent(file2) {
56744
- return (0, import_node_fs13.existsSync)(file2) ? (0, import_node_fs13.readFileSync)(file2, "utf8") : void 0;
57073
+ return (0, import_node_fs15.existsSync)(file2) ? (0, import_node_fs15.readFileSync)(file2, "utf8") : void 0;
56745
57074
  }
56746
57075
  function collidingEntries(entries, tree) {
56747
57076
  let held;
56748
57077
  try {
56749
- held = (0, import_node_fs13.readdirSync)(tree);
57078
+ held = (0, import_node_fs15.readdirSync)(tree);
56750
57079
  } catch {
56751
57080
  return [];
56752
57081
  }
56753
57082
  const taken = new Set(held.map((name) => name.toLowerCase()));
56754
57083
  return entries.filter((entry) => taken.has(entry.toLowerCase())).sort();
56755
57084
  }
56756
- function describe4(names) {
57085
+ function describe5(names) {
56757
57086
  return names.length === 1 ? `\`${names[0]}\` is` : `${names.map((name) => `\`${name}\``).join(", ")} are`;
56758
57087
  }
56759
57088
  function isNoop(plan2) {
@@ -56765,18 +57094,18 @@ function applyMigrate(plan2) {
56765
57094
  }
56766
57095
  const { config: config2 } = loadConfig(plan2.root);
56767
57096
  if (plan2.moves.length > 0) {
56768
- (0, import_node_fs13.mkdirSync)(recordsDir(plan2.root), { recursive: true });
57097
+ (0, import_node_fs15.mkdirSync)(recordsDir(plan2.root), { recursive: true });
56769
57098
  for (const move of plan2.moves) {
56770
- (0, import_node_fs13.renameSync)((0, import_node_path14.join)(plan2.root, ...move.from.split("/")), (0, import_node_path14.join)(plan2.root, ...move.to.split("/")));
57099
+ (0, import_node_fs15.renameSync)((0, import_node_path15.join)(plan2.root, ...move.from.split("/")), (0, import_node_path15.join)(plan2.root, ...move.to.split("/")));
56771
57100
  }
56772
57101
  }
56773
57102
  if (plan2.creates.includes(STATE_GITIGNORE_PATH)) {
56774
- const ignore = (0, import_node_path14.join)(plan2.root, ...STATE_GITIGNORE_PATH.split("/"));
56775
- (0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(ignore), { recursive: true });
56776
- (0, import_node_fs13.writeFileSync)(ignore, renderStateGitignore(config2), "utf8");
57103
+ const ignore = (0, import_node_path15.join)(plan2.root, ...STATE_GITIGNORE_PATH.split("/"));
57104
+ (0, import_node_fs15.mkdirSync)((0, import_node_path15.dirname)(ignore), { recursive: true });
57105
+ (0, import_node_fs15.writeFileSync)(ignore, renderStateGitignore(config2), "utf8");
56777
57106
  }
56778
57107
  for (const removed of plan2.removes) {
56779
- (0, import_node_fs13.rmSync)((0, import_node_path14.join)(plan2.root, ...removed.split("/")), { force: true });
57108
+ (0, import_node_fs15.rmSync)((0, import_node_path15.join)(plan2.root, ...removed.split("/")), { force: true });
56780
57109
  }
56781
57110
  return { ...plan2, status: "migrated" };
56782
57111
  }
@@ -57253,12 +57582,12 @@ var rotateCommand = defineCommand({
57253
57582
 
57254
57583
  // src/commands/watch.ts
57255
57584
  init_cjs_shims();
57256
- var import_node_fs14 = require("fs");
57257
- var import_node_path15 = require("path");
57585
+ var import_node_fs16 = require("fs");
57586
+ var import_node_path16 = require("path");
57258
57587
  var DEBOUNCE_MS2 = 100;
57259
57588
  function runWatch(options) {
57260
57589
  const project = openProject(options.cwd);
57261
- const configFile = (0, import_node_path15.basename)(project.configFile);
57590
+ const configFile = (0, import_node_path16.basename)(project.configFile);
57262
57591
  const debounceMs = options.debounceMs ?? DEBOUNCE_MS2;
57263
57592
  const watchers = /* @__PURE__ */ new Set();
57264
57593
  let timer;
@@ -57311,22 +57640,22 @@ function runWatch(options) {
57311
57640
  watcher.close();
57312
57641
  }
57313
57642
  function armRecovery(target, recursive, only) {
57314
- const parent = (0, import_node_path15.dirname)(target);
57315
- const name = (0, import_node_path15.basename)(target);
57643
+ const parent = (0, import_node_path16.dirname)(target);
57644
+ const name = (0, import_node_path16.basename)(target);
57316
57645
  let recovery;
57317
57646
  try {
57318
- recovery = (0, import_node_fs14.watch)(parent, { recursive: false }, (_event, filename) => {
57647
+ recovery = (0, import_node_fs16.watch)(parent, { recursive: false }, (_event, filename) => {
57319
57648
  if (closed || recovery === void 0) {
57320
57649
  return;
57321
57650
  }
57322
- if (!(0, import_node_fs14.existsSync)(parent)) {
57651
+ if (!(0, import_node_fs16.existsSync)(parent)) {
57323
57652
  stop2(recovery);
57324
57653
  return;
57325
57654
  }
57326
- if (filename !== null && (0, import_node_path15.basename)(filename) !== name) {
57655
+ if (filename !== null && (0, import_node_path16.basename)(filename) !== name) {
57327
57656
  return;
57328
57657
  }
57329
- if (!(0, import_node_fs14.existsSync)(target)) {
57658
+ if (!(0, import_node_fs16.existsSync)(target)) {
57330
57659
  return;
57331
57660
  }
57332
57661
  stop2(recovery);
@@ -57346,11 +57675,11 @@ function runWatch(options) {
57346
57675
  }
57347
57676
  function addWatcher(target, recursive, only) {
57348
57677
  let watcher;
57349
- const listen = (useRecursive) => (0, import_node_fs14.watch)(target, { recursive: useRecursive }, (_event, filename) => {
57678
+ const listen = (useRecursive) => (0, import_node_fs16.watch)(target, { recursive: useRecursive }, (_event, filename) => {
57350
57679
  if (closed) {
57351
57680
  return;
57352
57681
  }
57353
- if (!(0, import_node_fs14.existsSync)(target)) {
57682
+ if (!(0, import_node_fs16.existsSync)(target)) {
57354
57683
  if (watcher !== void 0) {
57355
57684
  stop2(watcher);
57356
57685
  }
@@ -57358,7 +57687,7 @@ function runWatch(options) {
57358
57687
  schedule();
57359
57688
  return;
57360
57689
  }
57361
- if (only !== void 0 && (filename === null || (0, import_node_path15.basename)(filename) !== only)) {
57690
+ if (only !== void 0 && (filename === null || (0, import_node_path16.basename)(filename) !== only)) {
57362
57691
  return;
57363
57692
  }
57364
57693
  schedule();
@@ -57385,11 +57714,11 @@ function runWatch(options) {
57385
57714
  watchers.add(watcher);
57386
57715
  }
57387
57716
  addWatcher(project.recordsDir, true);
57388
- addWatcher((0, import_node_path15.dirname)(project.configFile), false, configFile);
57717
+ addWatcher((0, import_node_path16.dirname)(project.configFile), false, configFile);
57389
57718
  addWatcher(project.root, false, SCHEMA_SHAPE_FILE);
57390
57719
  if (schemaInsideTree(project.config) === void 0) {
57391
- const schemaFile = (0, import_node_path15.resolve)(project.root, schemaFileOf(project.config));
57392
- addWatcher((0, import_node_path15.dirname)(schemaFile), false, (0, import_node_path15.basename)(schemaFile));
57720
+ const schemaFile = (0, import_node_path16.resolve)(project.root, schemaFileOf(project.config));
57721
+ addWatcher((0, import_node_path16.dirname)(schemaFile), false, (0, import_node_path16.basename)(schemaFile));
57393
57722
  }
57394
57723
  void validate();
57395
57724
  return {