@penvhq/cli 0.9.3 → 0.9.4

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.cjs CHANGED
@@ -114,7 +114,7 @@ var startChild = (invocation) => {
114
114
  const ended = new Promise((resolve9, reject) => {
115
115
  child.on("error", (cause) => {
116
116
  release();
117
- reject(cannotStart(executable, cause));
117
+ reject(cannotStart(executable, cause, invocation.purpose));
118
118
  });
119
119
  child.on("exit", (code, signal) => {
120
120
  release();
@@ -135,8 +135,15 @@ function noCommand() {
135
135
  "Put the command after `--`, e.g. `penv run -- pnpm dev`."
136
136
  );
137
137
  }
138
- function cannotStart(executable, cause) {
138
+ function cannotStart(executable, cause, purpose) {
139
139
  const detail = cause instanceof Error ? cause.message : String(cause);
140
+ if (purpose !== void 0) {
141
+ return new import_core.PenvError(
142
+ "PENV_COMMAND_NOT_STARTED",
143
+ `penv could not start \`${executable}\` to ${purpose}: ${detail}`,
144
+ `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.`
145
+ );
146
+ }
140
147
  return new import_core.PenvError(
141
148
  "RUN_COMMAND_NOT_STARTED",
142
149
  `\`${executable}\` could not be started: ${detail}`,
@@ -165,12 +172,15 @@ function cmdCommandLine(resolved, args) {
165
172
  " "
166
173
  );
167
174
  }
168
- function extensions(env) {
175
+ function extensions(env, platform) {
176
+ if (platform !== "win32") {
177
+ return [""];
178
+ }
169
179
  const declared = env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
170
- return ["", ...declared.split(";").filter((extension) => extension.length > 0)];
180
+ return [...declared.split(";").filter((extension) => extension.length > 0), ""];
171
181
  }
172
- function findExecutable(executable, env) {
173
- const candidates = extensions(env);
182
+ function findExecutable(executable, env, platform = process.platform) {
183
+ const candidates = extensions(env, platform);
174
184
  const isFile = (path2) => (0, import_node_fs.existsSync)(path2) && (0, import_node_fs.statSync)(path2).isFile();
175
185
  if (executable.includes("/") || executable.includes("\\") || (0, import_node_path.isAbsolute)(executable)) {
176
186
  return candidates.map((extension) => executable + extension).find(isFile);
@@ -197,6 +207,7 @@ function escapeArgument(argument, doubleEscape) {
197
207
  // src/install.ts
198
208
  var import_meta = {};
199
209
  var RUNTIME_PACKAGE = "@penvhq/penv";
210
+ var SCHEMA_PACKAGE = "zod";
200
211
  var LOCKFILES = [
201
212
  ["pnpm", "pnpm-lock.yaml"],
202
213
  ["yarn", "yarn.lock"],
@@ -211,13 +222,9 @@ var ADD = {
211
222
  bun: ["bun", "add", "--exact"]
212
223
  };
213
224
  function engineVersion() {
214
- const manifest = new URL("../package.json", import_meta.url);
215
- try {
216
- const version = JSON.parse((0, import_node_fs2.readFileSync)(manifest, "utf8")).version;
217
- if (typeof version === "string" && version.length > 0) {
218
- return version;
219
- }
220
- } catch {
225
+ const version = ownManifest()?.version;
226
+ if (typeof version === "string" && version.length > 0) {
227
+ return version;
221
228
  }
222
229
  throw new import_core2.PenvError(
223
230
  "ENGINE_VERSION_UNREADABLE",
@@ -225,6 +232,29 @@ function engineVersion() {
225
232
  `Reinstall penv, then run \`penv init\` again.`
226
233
  );
227
234
  }
235
+ function schemaPackageVersion() {
236
+ const peers = ownManifest()?.peerDependencies;
237
+ const declared = peers !== null && typeof peers === "object" && !Array.isArray(peers) ? peers[SCHEMA_PACKAGE] : void 0;
238
+ const floor = typeof declared === "string" ? declared.replace(/^[\^~>=\s]+/, "").trim() : "";
239
+ if (floor.length > 0) {
240
+ return floor;
241
+ }
242
+ throw new import_core2.PenvError(
243
+ "ENGINE_PEER_UNREADABLE",
244
+ `penv could not read its own \`${SCHEMA_PACKAGE}\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,
245
+ `Reinstall penv, then run \`penv init\` again.`
246
+ );
247
+ }
248
+ function ownManifest() {
249
+ try {
250
+ const parsed = JSON.parse(
251
+ (0, import_node_fs2.readFileSync)(new URL("../package.json", import_meta.url), "utf8")
252
+ );
253
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
254
+ } catch {
255
+ return void 0;
256
+ }
257
+ }
228
258
  function detectPackageManager(root) {
229
259
  for (const [manager, lockfile] of LOCKFILES) {
230
260
  if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, lockfile))) {
@@ -253,12 +283,12 @@ function manifestOf(root) {
253
283
  return void 0;
254
284
  }
255
285
  }
256
- function declaredVersion(root) {
286
+ function declaredVersion(root, name) {
257
287
  const manifest = manifestOf(root);
258
288
  for (const field of ["dependencies", "devDependencies"]) {
259
289
  const block = manifest?.[field];
260
290
  if (block !== null && typeof block === "object" && !Array.isArray(block)) {
261
- const version = block[RUNTIME_PACKAGE];
291
+ const version = block[name];
262
292
  if (typeof version === "string") {
263
293
  return version;
264
294
  }
@@ -271,27 +301,61 @@ function planInstall(root, version = engineVersion()) {
271
301
  const lockfile = LOCKFILES.find(
272
302
  ([name, file]) => name === manager && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, file))
273
303
  )?.[1];
274
- const declared = declaredVersion(root);
304
+ const runtimeDeclared = declaredVersion(root, RUNTIME_PACKAGE);
305
+ const zodDeclared = declaredVersion(root, SCHEMA_PACKAGE);
306
+ const packages = [
307
+ {
308
+ name: RUNTIME_PACKAGE,
309
+ version,
310
+ ...runtimeDeclared === void 0 ? {} : { declared: runtimeDeclared },
311
+ satisfied: runtimeDeclared === version
312
+ },
313
+ {
314
+ name: SCHEMA_PACKAGE,
315
+ version: schemaPackageVersion(),
316
+ ...zodDeclared === void 0 ? {} : { declared: zodDeclared },
317
+ // Any declared zod counts: which zod a project uses is the project's
318
+ // decision, and penv is here to make sure there is one, not to move it.
319
+ satisfied: zodDeclared !== void 0
320
+ }
321
+ ];
322
+ const pending = packages.filter((entry) => !entry.satisfied);
323
+ const specs = (pending.length === 0 ? packages : pending).map(
324
+ (entry) => `${entry.name}@${entry.version}`
325
+ );
275
326
  return {
276
327
  root,
277
328
  manager,
278
- package: RUNTIME_PACKAGE,
279
- version,
280
- command: [...ADD[manager], `${RUNTIME_PACKAGE}@${version}`],
329
+ packages,
330
+ command: [...ADD[manager], ...specs],
281
331
  ...lockfile === void 0 ? {} : { lockfile },
282
- ...declared === void 0 ? {} : { declared },
283
- satisfied: declared === version
332
+ satisfied: pending.length === 0
284
333
  };
285
334
  }
335
+ function describe(entry) {
336
+ return `${entry.name} ${entry.version}`;
337
+ }
286
338
  function renderInstallPlan(plan2) {
287
339
  if (plan2.satisfied) {
288
- return [`package.json already pins ${plan2.package} ${plan2.version} \u2014 nothing to install.`];
340
+ return [
341
+ `package.json already has ${plan2.packages.map(describe).join(" and ")} \u2014 nothing to install.`
342
+ ];
289
343
  }
290
- const line = `"${plan2.package}": "${plan2.version}"`;
344
+ const pending = plan2.packages.filter((entry) => !entry.satisfied);
345
+ const added = pending.filter((entry) => entry.declared === void 0);
346
+ const replaced = pending.filter((entry) => entry.declared !== void 0);
291
347
  return [
292
348
  "package.json",
293
- ...plan2.declared === void 0 ? [' + "dependencies": {', ` + ${line}`, " + }"] : [` - "${plan2.package}": "${plan2.declared}"`, ` + ${line}`],
294
- ...plan2.lockfile === void 0 ? [] : [plan2.lockfile, ` + ${plan2.package}@${plan2.version}`],
349
+ ...added.length === 0 ? [] : [
350
+ ' + "dependencies": {',
351
+ ...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
352
+ " + }"
353
+ ],
354
+ ...replaced.flatMap((entry) => [
355
+ ` - "${entry.name}": "${entry.declared}"`,
356
+ ` + "${entry.name}": "${entry.version}"`
357
+ ]),
358
+ ...plan2.lockfile === void 0 ? [] : [plan2.lockfile, ...pending.map((entry) => ` + ${entry.name}@${entry.version}`)],
295
359
  "",
296
360
  `Run with: ${plan2.command.join(" ")}`
297
361
  ];
@@ -300,7 +364,8 @@ var installWithPackageManager = async (plan2) => {
300
364
  const child = startChild({
301
365
  command: plan2.command,
302
366
  env: process.env,
303
- cwd: plan2.root
367
+ cwd: plan2.root,
368
+ purpose: `install ${plan2.packages.map(describe).join(" and ")}`
304
369
  });
305
370
  const ended = await child.ended;
306
371
  if (ended.exitCode !== 0 || ended.signal !== null) {
@@ -2139,20 +2204,20 @@ function readCutover(root) {
2139
2204
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2140
2205
  throw unreadable("it is not an object");
2141
2206
  }
2142
- const record = parsed;
2143
- if (record.format !== CUTOVER_FORMAT) {
2207
+ const record2 = parsed;
2208
+ if (record2.format !== CUTOVER_FORMAT) {
2144
2209
  throw unreadable(
2145
- `it is format ${JSON.stringify(record.format)}, and penv reads format ${CUTOVER_FORMAT}`
2210
+ `it is format ${JSON.stringify(record2.format)}, and penv reads format ${CUTOVER_FORMAT}`
2146
2211
  );
2147
2212
  }
2148
- const files = record.files;
2213
+ const files = record2.files;
2149
2214
  if (!Array.isArray(files) || files.some((name) => typeof name !== "string")) {
2150
2215
  throw unreadable("it lists no filenames");
2151
2216
  }
2152
- const environments = Array.isArray(record.environments) ? record.environments.filter((name) => typeof name === "string") : [];
2217
+ const environments = Array.isArray(record2.environments) ? record2.environments.filter((name) => typeof name === "string") : [];
2153
2218
  return {
2154
2219
  format: CUTOVER_FORMAT,
2155
- movedAt: typeof record.movedAt === "string" ? record.movedAt : "",
2220
+ movedAt: typeof record2.movedAt === "string" ? record2.movedAt : "",
2156
2221
  files,
2157
2222
  environments
2158
2223
  };
@@ -4156,8 +4221,8 @@ var getCommand = (0, import_citty12.defineCommand)({
4156
4221
  });
4157
4222
 
4158
4223
  // src/commands/import.ts
4159
- var import_node_fs12 = require("fs");
4160
- var import_node_path13 = require("path");
4224
+ var import_node_fs13 = require("fs");
4225
+ var import_node_path14 = require("path");
4161
4226
  var import_core26 = require("@penvhq/core");
4162
4227
  var import_citty14 = require("citty");
4163
4228
 
@@ -4392,12 +4457,94 @@ function draftFieldsAcross(sources, environments) {
4392
4457
  }
4393
4458
 
4394
4459
  // src/commands/init.ts
4395
- var import_node_fs11 = require("fs");
4396
- var import_node_path12 = require("path");
4460
+ var import_node_fs12 = require("fs");
4461
+ var import_node_path13 = require("path");
4397
4462
  var import_promises = require("readline/promises");
4398
4463
  var import_core25 = require("@penvhq/core");
4399
4464
  var import_citty13 = require("citty");
4400
4465
 
4466
+ // src/scaffold-undo.ts
4467
+ var import_node_fs11 = require("fs");
4468
+ var import_node_path12 = require("path");
4469
+ function record(path, files, dirs) {
4470
+ let stats;
4471
+ try {
4472
+ stats = (0, import_node_fs11.statSync)(path);
4473
+ } catch {
4474
+ return;
4475
+ }
4476
+ if (stats.isDirectory()) {
4477
+ dirs.add(path);
4478
+ for (const entry of (0, import_node_fs11.readdirSync)(path)) {
4479
+ record((0, import_node_path12.join)(path, entry), files, dirs);
4480
+ }
4481
+ return;
4482
+ }
4483
+ if (stats.isFile()) {
4484
+ files.set(path, (0, import_node_fs11.readFileSync)(path));
4485
+ }
4486
+ }
4487
+ function captureScaffold(root, paths) {
4488
+ const files = /* @__PURE__ */ new Map();
4489
+ const dirs = /* @__PURE__ */ new Set();
4490
+ for (const path of paths) {
4491
+ for (let dir = (0, import_node_path12.dirname)(path); dir.startsWith(root) && dir !== root; dir = (0, import_node_path12.dirname)(dir)) {
4492
+ if ((0, import_node_fs11.existsSync)(dir)) {
4493
+ dirs.add(dir);
4494
+ }
4495
+ }
4496
+ record(path, files, dirs);
4497
+ }
4498
+ return { root, paths: [...paths], files, dirs };
4499
+ }
4500
+ function restoreScaffold(undo) {
4501
+ for (const path of undo.paths) {
4502
+ removeAdded(path, undo);
4503
+ }
4504
+ for (const path of undo.paths) {
4505
+ pruneAncestors(path, undo);
4506
+ }
4507
+ for (const [file, contents] of undo.files) {
4508
+ (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file), { recursive: true });
4509
+ (0, import_node_fs11.writeFileSync)(file, contents);
4510
+ }
4511
+ }
4512
+ function removeAdded(path, undo) {
4513
+ let stats;
4514
+ try {
4515
+ stats = (0, import_node_fs11.statSync)(path);
4516
+ } catch {
4517
+ return;
4518
+ }
4519
+ if (stats.isFile()) {
4520
+ if (!undo.files.has(path)) {
4521
+ (0, import_node_fs11.unlinkSync)(path);
4522
+ }
4523
+ return;
4524
+ }
4525
+ if (!stats.isDirectory()) {
4526
+ return;
4527
+ }
4528
+ for (const entry of (0, import_node_fs11.readdirSync)(path)) {
4529
+ removeAdded((0, import_node_path12.join)(path, entry), undo);
4530
+ }
4531
+ if (!undo.dirs.has(path) && (0, import_node_fs11.readdirSync)(path).length === 0) {
4532
+ (0, import_node_fs11.rmdirSync)(path);
4533
+ }
4534
+ }
4535
+ function pruneAncestors(path, undo) {
4536
+ for (let dir = (0, import_node_path12.dirname)(path); dir.startsWith(undo.root) && dir !== undo.root; dir = (0, import_node_path12.dirname)(dir)) {
4537
+ if (undo.dirs.has(dir)) {
4538
+ return;
4539
+ }
4540
+ try {
4541
+ (0, import_node_fs11.rmdirSync)(dir);
4542
+ } catch {
4543
+ return;
4544
+ }
4545
+ }
4546
+ }
4547
+
4401
4548
  // src/seams.ts
4402
4549
  function nextjs({ alias, srcDir }) {
4403
4550
  return {
@@ -4553,7 +4700,7 @@ var NOT_ENVIRONMENTS2 = [...import_core25.RESERVED_TOKENS, "example", "sample",
4553
4700
  function suggestEnvironments(root) {
4554
4701
  let entries;
4555
4702
  try {
4556
- entries = (0, import_node_fs11.readdirSync)(root);
4703
+ entries = (0, import_node_fs12.readdirSync)(root);
4557
4704
  } catch {
4558
4705
  return [];
4559
4706
  }
@@ -4597,8 +4744,8 @@ function configOf(decisions) {
4597
4744
  return { environments: decisions.environments, providers: {}, schemaFile: decisions.schemaFile };
4598
4745
  }
4599
4746
  function declaredIn(root) {
4600
- const file = (0, import_node_path12.join)(root, CONFIG_FILE);
4601
- if (!(0, import_node_fs11.existsSync)(file)) {
4747
+ const file = (0, import_node_path13.join)(root, CONFIG_FILE);
4748
+ if (!(0, import_node_fs12.existsSync)(file)) {
4602
4749
  return void 0;
4603
4750
  }
4604
4751
  return (0, import_core25.loadConfigFrom)(file);
@@ -5040,16 +5187,16 @@ function renderTsconfig(target, alias) {
5040
5187
  `;
5041
5188
  }
5042
5189
  function ensurePenvDir(root) {
5043
- const dir = (0, import_node_path12.resolve)(root, import_core25.PENV_DIR);
5044
- if ((0, import_node_fs11.existsSync)(dir)) {
5190
+ const dir = (0, import_node_path13.resolve)(root, import_core25.PENV_DIR);
5191
+ if ((0, import_node_fs12.existsSync)(dir)) {
5045
5192
  return { target: "penv-dir", action: "kept", text: `Found ${import_core25.PENV_DIR}/` };
5046
5193
  }
5047
- (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
5194
+ (0, import_node_fs12.mkdirSync)(dir, { recursive: true });
5048
5195
  return { target: "penv-dir", action: "created", text: `Created ${import_core25.PENV_DIR}/` };
5049
5196
  }
5050
5197
  function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS) {
5051
- const file = (0, import_node_path12.join)(root, import_core25.SCHEMA_SHAPE_FILE);
5052
- if ((0, import_node_fs11.existsSync)(file)) {
5198
+ const file = (0, import_node_path13.join)(root, import_core25.SCHEMA_SHAPE_FILE);
5199
+ if ((0, import_node_fs12.existsSync)(file)) {
5053
5200
  return {
5054
5201
  target: "schema",
5055
5202
  action: "kept",
@@ -5066,7 +5213,7 @@ function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS
5066
5213
  note: `(the shape lives there, from before the ${import_core25.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 ${import_core25.SCHEMA_SHAPE_FILE}, leaving ${oldLayout} importing \`schema\` from it and calling \`load\`.)`
5067
5214
  };
5068
5215
  }
5069
- (0, import_node_fs11.writeFileSync)(file, renderSchemaShapeModule(fields, draft), "utf8");
5216
+ (0, import_node_fs12.writeFileSync)(file, renderSchemaShapeModule(fields, draft), "utf8");
5070
5217
  return {
5071
5218
  target: "schema",
5072
5219
  action: "created",
@@ -5075,8 +5222,8 @@ function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS
5075
5222
  };
5076
5223
  }
5077
5224
  function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
5078
- const file = (0, import_node_path12.join)(root, ...decisions.schemaFile.split("/"));
5079
- if ((0, import_node_fs11.existsSync)(file)) {
5225
+ const file = (0, import_node_path13.join)(root, ...decisions.schemaFile.split("/"));
5226
+ if ((0, import_node_fs12.existsSync)(file)) {
5080
5227
  return {
5081
5228
  target: "env",
5082
5229
  action: "kept",
@@ -5084,8 +5231,8 @@ function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
5084
5231
  note: "(yours \u2014 penv never regenerates it)"
5085
5232
  };
5086
5233
  }
5087
- (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file), { recursive: true });
5088
- (0, import_node_fs11.writeFileSync)(file, renderEnvModule(decisions.schemaFile, decisions.inject), "utf8");
5234
+ (0, import_node_fs12.mkdirSync)((0, import_node_path13.dirname)(file), { recursive: true });
5235
+ (0, import_node_fs12.writeFileSync)(file, renderEnvModule(decisions.schemaFile, decisions.inject), "utf8");
5089
5236
  return {
5090
5237
  target: "env",
5091
5238
  action: "created",
@@ -5094,19 +5241,19 @@ function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
5094
5241
  };
5095
5242
  }
5096
5243
  function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
5097
- const file = (0, import_node_path12.join)(root, CONFIG_FILE);
5098
- if ((0, import_node_fs11.existsSync)(file)) {
5244
+ const file = (0, import_node_path13.join)(root, CONFIG_FILE);
5245
+ if ((0, import_node_fs12.existsSync)(file)) {
5099
5246
  return { target: "config", action: "kept", text: `Kept ${CONFIG_FILE}` };
5100
5247
  }
5101
- (0, import_node_fs11.writeFileSync)(file, renderConfigModule(decisions), "utf8");
5248
+ (0, import_node_fs12.writeFileSync)(file, renderConfigModule(decisions), "utf8");
5102
5249
  return { target: "config", action: "created", text: `Generated ${CONFIG_FILE}` };
5103
5250
  }
5104
5251
  function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5105
5252
  const alias = decisions.alias;
5106
5253
  const imports = alias.startsWith(IMPORTS_PREFIX);
5107
- const file = (0, import_node_path12.join)(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
5254
+ const file = (0, import_node_path13.join)(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
5108
5255
  const where = imports ? PACKAGE_FILE : TSCONFIG_FILE;
5109
- if (!(0, import_node_fs11.existsSync)(file)) {
5256
+ if (!(0, import_node_fs12.existsSync)(file)) {
5110
5257
  if (imports) {
5111
5258
  return {
5112
5259
  target: "tsconfig",
@@ -5115,14 +5262,14 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5115
5262
  note: `(run \`npm init\` first, or use \`--alias @env\` to alias through ${TSCONFIG_FILE})`
5116
5263
  };
5117
5264
  }
5118
- (0, import_node_fs11.writeFileSync)(file, renderTsconfig(decisions.schemaFile, alias), "utf8");
5265
+ (0, import_node_fs12.writeFileSync)(file, renderTsconfig(decisions.schemaFile, alias), "utf8");
5119
5266
  return {
5120
5267
  target: "tsconfig",
5121
5268
  action: "created",
5122
5269
  text: `Created ${TSCONFIG_FILE} with the ${alias} path alias`
5123
5270
  };
5124
5271
  }
5125
- const source = (0, import_node_fs11.readFileSync)(file, "utf8");
5272
+ const source = (0, import_node_fs12.readFileSync)(file, "utf8");
5126
5273
  const edit = imports ? insertImportsAlias(source, decisions.schemaFile, alias) : insertEnvAlias(source, decisions.schemaFile, alias);
5127
5274
  if (edit.conflict !== void 0) {
5128
5275
  return {
@@ -5139,7 +5286,7 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5139
5286
  text: `Kept the ${alias} alias in ${where}`
5140
5287
  };
5141
5288
  }
5142
- (0, import_node_fs11.writeFileSync)(file, edit.source, "utf8");
5289
+ (0, import_node_fs12.writeFileSync)(file, edit.source, "utf8");
5143
5290
  return {
5144
5291
  target: "tsconfig",
5145
5292
  action: "updated",
@@ -5147,14 +5294,14 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
5147
5294
  };
5148
5295
  }
5149
5296
  function writeGitignore(root, decisions = DEFAULT_DECISIONS) {
5150
- const file = (0, import_node_path12.join)(root, ...import_core25.STATE_GITIGNORE_PATH.split("/"));
5297
+ const file = (0, import_node_path13.join)(root, ...import_core25.STATE_GITIGNORE_PATH.split("/"));
5151
5298
  const wanted = (0, import_core25.renderStateGitignore)(configOf(decisions));
5152
- const existing = (0, import_node_fs11.existsSync)(file) ? (0, import_node_fs11.readFileSync)(file, "utf8") : void 0;
5299
+ const existing = (0, import_node_fs12.existsSync)(file) ? (0, import_node_fs12.readFileSync)(file, "utf8") : void 0;
5153
5300
  if (existing === wanted) {
5154
5301
  return { target: "gitignore", action: "kept", text: `Kept ${import_core25.STATE_GITIGNORE_PATH}` };
5155
5302
  }
5156
- (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file), { recursive: true });
5157
- (0, import_node_fs11.writeFileSync)(file, wanted, "utf8");
5303
+ (0, import_node_fs12.mkdirSync)((0, import_node_path13.dirname)(file), { recursive: true });
5304
+ (0, import_node_fs12.writeFileSync)(file, wanted, "utf8");
5158
5305
  return {
5159
5306
  target: "gitignore",
5160
5307
  action: existing === void 0 ? "created" : "updated",
@@ -5170,12 +5317,12 @@ function outdatedRuntimeWarning(root) {
5170
5317
  return `Injection needs @penvhq/penv ${INJECT_MIN_VERSION}+ \u2014 this project has ${version}, whose \`load\` ignores \`{ inject: true }\`. Upgrade, or process.env stays empty.`;
5171
5318
  }
5172
5319
  function installedPenvVersion(root) {
5173
- const file = (0, import_node_path12.join)(root, "node_modules", "@penvhq", "penv", "package.json");
5174
- if (!(0, import_node_fs11.existsSync)(file)) {
5320
+ const file = (0, import_node_path13.join)(root, "node_modules", "@penvhq", "penv", "package.json");
5321
+ if (!(0, import_node_fs12.existsSync)(file)) {
5175
5322
  return void 0;
5176
5323
  }
5177
5324
  try {
5178
- const version = JSON.parse((0, import_node_fs11.readFileSync)(file, "utf8")).version;
5325
+ const version = JSON.parse((0, import_node_fs12.readFileSync)(file, "utf8")).version;
5179
5326
  return typeof version === "string" ? version : void 0;
5180
5327
  } catch {
5181
5328
  return void 0;
@@ -5215,11 +5362,11 @@ function writeSeam(root, decisions = DEFAULT_DECISIONS, framework = detectFramew
5215
5362
  };
5216
5363
  }
5217
5364
  const alsoNote = writeAlso(root, seam.also);
5218
- const file = (0, import_node_path12.join)(root, ...seam.file.split("/"));
5365
+ const file = (0, import_node_path13.join)(root, ...seam.file.split("/"));
5219
5366
  const baseNotes = [...seam.notes, ...alsoNote === void 0 ? [] : [alsoNote]];
5220
5367
  const notes = baseNotes.length === 0 ? "" : `
5221
5368
  ${baseNotes.map((n) => ` ${n}`).join("\n")}`;
5222
- if ((0, import_node_fs11.existsSync)(file)) {
5369
+ if ((0, import_node_fs12.existsSync)(file)) {
5223
5370
  return {
5224
5371
  target: "seam",
5225
5372
  action: "info",
@@ -5227,8 +5374,8 @@ ${baseNotes.map((n) => ` ${n}`).join("\n")}`;
5227
5374
  note: withWarning(`${seam.ifPresent}${notes}`, outdated)
5228
5375
  };
5229
5376
  }
5230
- (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file), { recursive: true });
5231
- (0, import_node_fs11.writeFileSync)(file, seam.content, "utf8");
5377
+ (0, import_node_fs12.mkdirSync)((0, import_node_path13.dirname)(file), { recursive: true });
5378
+ (0, import_node_fs12.writeFileSync)(file, seam.content, "utf8");
5232
5379
  return {
5233
5380
  target: "seam",
5234
5381
  action: outdated === void 0 ? "created" : "info",
@@ -5240,12 +5387,12 @@ function writeAlso(root, also) {
5240
5387
  if (also === void 0) {
5241
5388
  return void 0;
5242
5389
  }
5243
- const file = (0, import_node_path12.join)(root, ...also.file.split("/"));
5244
- if ((0, import_node_fs11.existsSync)(file)) {
5390
+ const file = (0, import_node_path13.join)(root, ...also.file.split("/"));
5391
+ if ((0, import_node_fs12.existsSync)(file)) {
5245
5392
  return also.ifPresent;
5246
5393
  }
5247
- (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file), { recursive: true });
5248
- (0, import_node_fs11.writeFileSync)(file, also.content, "utf8");
5394
+ (0, import_node_fs12.mkdirSync)((0, import_node_path13.dirname)(file), { recursive: true });
5395
+ (0, import_node_fs12.writeFileSync)(file, also.content, "utf8");
5249
5396
  return `Wrote ${also.file} to register it.`;
5250
5397
  }
5251
5398
  function withWarning(note, warning) {
@@ -5270,6 +5417,23 @@ function scaffold(root, fields, draft, decisions = DEFAULT_DECISIONS, framework
5270
5417
  const seam = writeSeam(root, decisions, framework);
5271
5418
  return seam === void 0 ? steps : [...steps, seam];
5272
5419
  }
5420
+ function scaffoldPaths(root, decisions = DEFAULT_DECISIONS, framework = detectFramework(root)?.name) {
5421
+ const fileAt = (relative5) => (0, import_node_path13.join)(root, ...relative5.split("/"));
5422
+ const seam = decisions.inject ? seamFor(framework, {
5423
+ alias: decisions.alias,
5424
+ srcDir: srcPrefix(root),
5425
+ schemaFile: decisions.schemaFile
5426
+ }) : void 0;
5427
+ return [
5428
+ fileAt(import_core25.PENV_DIR),
5429
+ fileAt(import_core25.SCHEMA_SHAPE_FILE),
5430
+ fileAt(decisions.schemaFile),
5431
+ fileAt(CONFIG_FILE),
5432
+ fileAt(TSCONFIG_FILE),
5433
+ fileAt(PACKAGE_FILE),
5434
+ ...seam?.kind === "scaffold" ? [fileAt(seam.file), ...seam.also === void 0 ? [] : [fileAt(seam.also.file)]] : []
5435
+ ];
5436
+ }
5273
5437
  var DEVELOPMENT = "development";
5274
5438
  var LOCAL_PROVIDER = "@penvhq/provider-filesystem";
5275
5439
  function planAdoption(root) {
@@ -5332,7 +5496,7 @@ function planCutover(input) {
5332
5496
  const diagnostics = [];
5333
5497
  let variables = 0;
5334
5498
  for (const file of selected) {
5335
- const parsed = (0, import_core25.parseDotenv)((0, import_node_fs11.readFileSync)((0, import_node_path12.join)(root, file.name), "utf8"));
5499
+ const parsed = (0, import_core25.parseDotenv)((0, import_node_fs12.readFileSync)((0, import_node_path13.join)(root, file.name), "utf8"));
5336
5500
  const fileRefs = refsForEntries(parsed.entries, file.name, config);
5337
5501
  adopted.push({ file, entries: parsed.entries, refs: fileRefs, scope: scopeOf(file) });
5338
5502
  refs.push(...fileRefs);
@@ -5443,17 +5607,27 @@ async function applyCutover(plan2, options = {}) {
5443
5607
  if (!plan2.install.satisfied) {
5444
5608
  await (options.install ?? installWithPackageManager)(plan2.install);
5445
5609
  }
5446
- const steps = scaffold(plan2.root, plan2.fields, true, plan2.decisions, plan2.framework);
5447
- const project = openProject(plan2.root);
5448
- const tree = localTree(project);
5449
- for (const adopted of plan2.adopted) {
5450
- writeEntries(tree, adopted.entries, adopted.refs, adopted.scope);
5451
- }
5452
- for (const environment of plan2.adopting) {
5453
- const check = await checkEnvironment(project, environment);
5454
- if (!check.result.ok) {
5455
- throw invalidAfterImport(check.result);
5610
+ const undo = captureScaffold(plan2.root, scaffoldPaths(plan2.root, plan2.decisions, plan2.framework));
5611
+ let steps;
5612
+ try {
5613
+ steps = scaffold(plan2.root, plan2.fields, true, plan2.decisions, plan2.framework);
5614
+ const project = openProject(plan2.root);
5615
+ const tree = localTree(project);
5616
+ for (const adopted of plan2.adopted) {
5617
+ writeEntries(tree, adopted.entries, adopted.refs, adopted.scope);
5618
+ }
5619
+ for (const environment of plan2.adopting) {
5620
+ const check = await checkEnvironment(project, environment);
5621
+ if (check.schema === void 0) {
5622
+ throw draftNotLoaded(check.result);
5623
+ }
5624
+ if (!check.result.ok) {
5625
+ throw invalidAfterImport(check.result);
5626
+ }
5456
5627
  }
5628
+ } catch (error) {
5629
+ restoreScaffold(undo);
5630
+ throw error;
5457
5631
  }
5458
5632
  const cutover = bundleDotenvFiles(
5459
5633
  plan2.root,
@@ -5462,13 +5636,24 @@ async function applyCutover(plan2, options = {}) {
5462
5636
  );
5463
5637
  return { plan: plan2, steps, moved: cutover.files, validated: plan2.adopting };
5464
5638
  }
5639
+ function issueLines(result2) {
5640
+ return result2.issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
5641
+ }
5642
+ 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.";
5465
5643
  function invalidAfterImport(result2) {
5466
- const lines = result2.issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
5467
5644
  return new import_core25.PenvError(
5468
5645
  "INIT_CUTOVER_INVALID",
5469
- `The imported values do not satisfy the draft schema for ${result2.environment}, so your dotenv files were left where they are:
5470
- ${lines}`,
5471
- `Correct ${import_core25.SCHEMA_SHAPE_FILE} or the values above, then run \`penv init\` again.`
5646
+ `The imported values do not satisfy the draft schema for ${result2.environment}:
5647
+ ${issueLines(result2)}`,
5648
+ `Correct the values above, then run \`penv init\` again. ${SCAFFOLD_ROLLED_BACK}`
5649
+ );
5650
+ }
5651
+ function draftNotLoaded(result2) {
5652
+ return new import_core25.PenvError(
5653
+ "INIT_DRAFT_NOT_LOADED",
5654
+ `penv could not load the schema it drafted for ${result2.environment}, so nothing was checked against it:
5655
+ ${issueLines(result2)}`,
5656
+ `Fix the error above, then run \`penv init\` again. ${SCAFFOLD_ROLLED_BACK}`
5472
5657
  );
5473
5658
  }
5474
5659
  function renderSelection(plan2, selected = plan2.preselected) {
@@ -5531,7 +5716,7 @@ function renderCutover(result2) {
5531
5716
  const glyph = step.action === "conflicted" ? WARN : step.action === "info" ? "\u2192" : CHECK;
5532
5717
  return step.note === void 0 ? { glyph, text: step.text } : { glyph, text: step.text, note: step.note };
5533
5718
  }),
5534
- ...plan2.install.satisfied ? [] : [{ glyph: CHECK, text: `Installed ${plan2.install.package}`, note: plan2.install.version }],
5719
+ ...plan2.install.packages.filter((entry) => !entry.satisfied).map((entry) => ({ glyph: CHECK, text: `Installed ${entry.name}`, note: entry.version })),
5535
5720
  {
5536
5721
  glyph: CHECK,
5537
5722
  text: `Imported ${plan2.fields.length} parameters`,
@@ -5555,12 +5740,12 @@ function dailyCommand(root) {
5555
5740
  return `penv run -- ${detectPackageManager(root)} ${devScript(root)}`;
5556
5741
  }
5557
5742
  function devScript(root) {
5558
- const file = (0, import_node_path12.join)(root, PACKAGE_FILE);
5559
- if (!(0, import_node_fs11.existsSync)(file)) {
5743
+ const file = (0, import_node_path13.join)(root, PACKAGE_FILE);
5744
+ if (!(0, import_node_fs12.existsSync)(file)) {
5560
5745
  return "dev";
5561
5746
  }
5562
5747
  try {
5563
- const scripts = JSON.parse((0, import_node_fs11.readFileSync)(file, "utf8")).scripts;
5748
+ const scripts = JSON.parse((0, import_node_fs12.readFileSync)(file, "utf8")).scripts;
5564
5749
  if (scripts !== null && typeof scripts === "object" && !Array.isArray(scripts)) {
5565
5750
  const named = Object.keys(scripts);
5566
5751
  return ["dev", "start"].find((script) => named.includes(script)) ?? named[0] ?? "dev";
@@ -5570,7 +5755,7 @@ function devScript(root) {
5570
5755
  return "dev";
5571
5756
  }
5572
5757
  function runInit(options) {
5573
- const root = (0, import_node_path12.resolve)(options.cwd);
5758
+ const root = (0, import_node_path13.resolve)(options.cwd);
5574
5759
  const decisions = options.decisions ?? planInit(root).decisions;
5575
5760
  return { root, decisions, steps: scaffold(root, [], false, decisions, options.framework) };
5576
5761
  }
@@ -5733,7 +5918,7 @@ var initCommand = (0, import_citty13.defineCommand)({
5733
5918
  },
5734
5919
  run({ args }) {
5735
5920
  return guard(async () => {
5736
- const root = (0, import_node_path12.resolve)(process.cwd());
5921
+ const root = (0, import_node_path13.resolve)(process.cwd());
5737
5922
  if (args.action !== void 0) {
5738
5923
  runUndoAction(root, String(args.action));
5739
5924
  return;
@@ -5809,7 +5994,7 @@ function assertDeclared(segment, config) {
5809
5994
  throw new import_core26.UnknownEnvironmentError(segment, config.environments);
5810
5995
  }
5811
5996
  function scopeFromFilename(file, config) {
5812
- const name = (0, import_node_path13.basename)(file);
5997
+ const name = (0, import_node_path14.basename)(file);
5813
5998
  const segments = name.split(".");
5814
5999
  const start = segments.indexOf(DOTENV_SEGMENT);
5815
6000
  if (start === -1) {
@@ -5893,7 +6078,7 @@ function environmentNamed(file, explicit) {
5893
6078
  if (explicit !== void 0 && explicit.trim().length > 0) {
5894
6079
  return explicit.trim();
5895
6080
  }
5896
- const segments = (0, import_node_path13.basename)(file).split(".");
6081
+ const segments = (0, import_node_path14.basename)(file).split(".");
5897
6082
  const start = segments.indexOf(DOTENV_SEGMENT);
5898
6083
  if (start === -1) {
5899
6084
  return void 0;
@@ -5927,16 +6112,16 @@ function configInEffect(cwd, environment) {
5927
6112
  return { config: openProject(cwd).config, decisions };
5928
6113
  }
5929
6114
  function importDotenv(options) {
5930
- const cwd = (0, import_node_path13.resolve)(options.cwd);
5931
- const file = (0, import_node_path13.isAbsolute)(options.file) ? options.file : (0, import_node_path13.resolve)(cwd, options.file);
5932
- if (!(0, import_node_fs12.existsSync)(file)) {
6115
+ const cwd = (0, import_node_path14.resolve)(options.cwd);
6116
+ const file = (0, import_node_path14.isAbsolute)(options.file) ? options.file : (0, import_node_path14.resolve)(cwd, options.file);
6117
+ if (!(0, import_node_fs13.existsSync)(file)) {
5933
6118
  throw new import_core26.PenvError(
5934
6119
  "IMPORT_FILE_MISSING",
5935
6120
  `There is no file at ${file} to import`,
5936
6121
  "Point `penv import` at an existing dotenv file, e.g. `penv import .env`."
5937
6122
  );
5938
6123
  }
5939
- const parsed = (0, import_core26.parseDotenv)((0, import_node_fs12.readFileSync)(file, "utf8"));
6124
+ const parsed = (0, import_core26.parseDotenv)((0, import_node_fs13.readFileSync)(file, "utf8"));
5940
6125
  const { config, decisions } = configInEffect(cwd, environmentNamed(file, options.environment));
5941
6126
  const source = displayPath3(cwd, file);
5942
6127
  const named = scopeFromFilename(file, config);
@@ -5952,7 +6137,7 @@ function importDotenv(options) {
5952
6137
  const tree = localTree(project);
5953
6138
  writeEntries(tree, parsed.entries, refs, scope);
5954
6139
  const backup = `${file}${BACKUP_SUFFIX}`;
5955
- (0, import_node_fs12.copyFileSync)(file, backup);
6140
+ (0, import_node_fs13.copyFileSync)(file, backup);
5956
6141
  return {
5957
6142
  root: project.root,
5958
6143
  file,
@@ -5966,7 +6151,7 @@ function importDotenv(options) {
5966
6151
  };
5967
6152
  }
5968
6153
  function displayPath3(root, file) {
5969
- const rel = (0, import_node_path13.relative)(root, file);
6154
+ const rel = (0, import_node_path14.relative)(root, file);
5970
6155
  return rel === "" || rel.startsWith("..") ? file : rel.split("\\").join("/");
5971
6156
  }
5972
6157
  function keptSchemaStep(step, variables) {
@@ -6287,30 +6472,30 @@ var listCommand = (0, import_citty16.defineCommand)({
6287
6472
  });
6288
6473
 
6289
6474
  // src/commands/migrate.ts
6290
- var import_node_fs13 = require("fs");
6291
- var import_node_path14 = require("path");
6475
+ var import_node_fs14 = require("fs");
6476
+ var import_node_path15 = require("path");
6292
6477
  var import_promises2 = require("readline/promises");
6293
6478
  var import_core30 = require("@penvhq/core");
6294
6479
  var import_citty17 = require("citty");
6295
6480
  var OLD_GITIGNORE = `${import_core30.PENV_DIR}/.gitignore`;
6296
6481
  function planMigrate(cwd) {
6297
6482
  const { config, file } = (0, import_core30.loadConfig)(cwd);
6298
- const root = (0, import_node_path14.dirname)(file);
6483
+ const root = (0, import_node_path15.dirname)(file);
6299
6484
  const entries = (0, import_core30.oldLayoutEntries)(root, config);
6300
6485
  const tree = (0, import_core30.recordsDir)(root);
6301
6486
  const collisions = collidingEntries(entries, tree);
6302
6487
  if (collisions.length > 0) {
6303
6488
  throw new import_core30.PenvError(
6304
6489
  "HALF_MIGRATED",
6305
- `${describe(collisions)} in both \`${import_core30.PENV_DIR}/\` and \`${import_core30.RECORDS_PATH}/\`, and penv cannot tell which copy is current`,
6490
+ `${describe2(collisions)} in both \`${import_core30.PENV_DIR}/\` and \`${import_core30.RECORDS_PATH}/\`, and penv cannot tell which copy is current`,
6306
6491
  `Move what is left under \`${import_core30.PENV_DIR}/\` into \`${import_core30.RECORDS_PATH}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
6307
6492
  );
6308
6493
  }
6309
6494
  const creates = [];
6310
- if (entries.length > 0 && !(0, import_node_fs13.existsSync)(tree)) {
6495
+ if (entries.length > 0 && !(0, import_node_fs14.existsSync)(tree)) {
6311
6496
  creates.push(`${import_core30.RECORDS_PATH}/`);
6312
6497
  }
6313
- if (readIfPresent((0, import_node_path14.join)(root, ...import_core30.STATE_GITIGNORE_PATH.split("/"))) !== (0, import_core30.renderStateGitignore)(config)) {
6498
+ if (readIfPresent((0, import_node_path15.join)(root, ...import_core30.STATE_GITIGNORE_PATH.split("/"))) !== (0, import_core30.renderStateGitignore)(config)) {
6314
6499
  creates.push(import_core30.STATE_GITIGNORE_PATH);
6315
6500
  }
6316
6501
  return {
@@ -6320,23 +6505,23 @@ function planMigrate(cwd) {
6320
6505
  to: `${import_core30.RECORDS_PATH}/${entry}`
6321
6506
  })),
6322
6507
  creates,
6323
- removes: (0, import_node_fs13.existsSync)((0, import_node_path14.join)(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
6508
+ removes: (0, import_node_fs14.existsSync)((0, import_node_path15.join)(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
6324
6509
  };
6325
6510
  }
6326
6511
  function readIfPresent(file) {
6327
- return (0, import_node_fs13.existsSync)(file) ? (0, import_node_fs13.readFileSync)(file, "utf8") : void 0;
6512
+ return (0, import_node_fs14.existsSync)(file) ? (0, import_node_fs14.readFileSync)(file, "utf8") : void 0;
6328
6513
  }
6329
6514
  function collidingEntries(entries, tree) {
6330
6515
  let held;
6331
6516
  try {
6332
- held = (0, import_node_fs13.readdirSync)(tree);
6517
+ held = (0, import_node_fs14.readdirSync)(tree);
6333
6518
  } catch {
6334
6519
  return [];
6335
6520
  }
6336
6521
  const taken = new Set(held.map((name) => name.toLowerCase()));
6337
6522
  return entries.filter((entry) => taken.has(entry.toLowerCase())).sort();
6338
6523
  }
6339
- function describe(names) {
6524
+ function describe2(names) {
6340
6525
  return names.length === 1 ? `\`${names[0]}\` is` : `${names.map((name) => `\`${name}\``).join(", ")} are`;
6341
6526
  }
6342
6527
  function isNoop(plan2) {
@@ -6348,18 +6533,18 @@ function applyMigrate(plan2) {
6348
6533
  }
6349
6534
  const { config } = (0, import_core30.loadConfig)(plan2.root);
6350
6535
  if (plan2.moves.length > 0) {
6351
- (0, import_node_fs13.mkdirSync)((0, import_core30.recordsDir)(plan2.root), { recursive: true });
6536
+ (0, import_node_fs14.mkdirSync)((0, import_core30.recordsDir)(plan2.root), { recursive: true });
6352
6537
  for (const move of plan2.moves) {
6353
- (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("/")));
6538
+ (0, import_node_fs14.renameSync)((0, import_node_path15.join)(plan2.root, ...move.from.split("/")), (0, import_node_path15.join)(plan2.root, ...move.to.split("/")));
6354
6539
  }
6355
6540
  }
6356
6541
  if (plan2.creates.includes(import_core30.STATE_GITIGNORE_PATH)) {
6357
- const ignore = (0, import_node_path14.join)(plan2.root, ...import_core30.STATE_GITIGNORE_PATH.split("/"));
6358
- (0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(ignore), { recursive: true });
6359
- (0, import_node_fs13.writeFileSync)(ignore, (0, import_core30.renderStateGitignore)(config), "utf8");
6542
+ const ignore = (0, import_node_path15.join)(plan2.root, ...import_core30.STATE_GITIGNORE_PATH.split("/"));
6543
+ (0, import_node_fs14.mkdirSync)((0, import_node_path15.dirname)(ignore), { recursive: true });
6544
+ (0, import_node_fs14.writeFileSync)(ignore, (0, import_core30.renderStateGitignore)(config), "utf8");
6360
6545
  }
6361
6546
  for (const removed of plan2.removes) {
6362
- (0, import_node_fs13.rmSync)((0, import_node_path14.join)(plan2.root, ...removed.split("/")), { force: true });
6547
+ (0, import_node_fs14.rmSync)((0, import_node_path15.join)(plan2.root, ...removed.split("/")), { force: true });
6363
6548
  }
6364
6549
  return { ...plan2, status: "migrated" };
6365
6550
  }
@@ -6845,14 +7030,14 @@ var rotateCommand = (0, import_citty20.defineCommand)({
6845
7030
  });
6846
7031
 
6847
7032
  // src/commands/watch.ts
6848
- var import_node_fs14 = require("fs");
6849
- var import_node_path15 = require("path");
7033
+ var import_node_fs15 = require("fs");
7034
+ var import_node_path16 = require("path");
6850
7035
  var import_core34 = require("@penvhq/core");
6851
7036
  var import_citty21 = require("citty");
6852
7037
  var DEBOUNCE_MS2 = 100;
6853
7038
  function runWatch(options) {
6854
7039
  const project = openProject(options.cwd);
6855
- const configFile = (0, import_node_path15.basename)(project.configFile);
7040
+ const configFile = (0, import_node_path16.basename)(project.configFile);
6856
7041
  const debounceMs = options.debounceMs ?? DEBOUNCE_MS2;
6857
7042
  const watchers = /* @__PURE__ */ new Set();
6858
7043
  let timer;
@@ -6905,22 +7090,22 @@ function runWatch(options) {
6905
7090
  watcher.close();
6906
7091
  }
6907
7092
  function armRecovery(target, recursive, only) {
6908
- const parent = (0, import_node_path15.dirname)(target);
6909
- const name = (0, import_node_path15.basename)(target);
7093
+ const parent = (0, import_node_path16.dirname)(target);
7094
+ const name = (0, import_node_path16.basename)(target);
6910
7095
  let recovery;
6911
7096
  try {
6912
- recovery = (0, import_node_fs14.watch)(parent, { recursive: false }, (_event, filename) => {
7097
+ recovery = (0, import_node_fs15.watch)(parent, { recursive: false }, (_event, filename) => {
6913
7098
  if (closed || recovery === void 0) {
6914
7099
  return;
6915
7100
  }
6916
- if (!(0, import_node_fs14.existsSync)(parent)) {
7101
+ if (!(0, import_node_fs15.existsSync)(parent)) {
6917
7102
  stop2(recovery);
6918
7103
  return;
6919
7104
  }
6920
- if (filename !== null && (0, import_node_path15.basename)(filename) !== name) {
7105
+ if (filename !== null && (0, import_node_path16.basename)(filename) !== name) {
6921
7106
  return;
6922
7107
  }
6923
- if (!(0, import_node_fs14.existsSync)(target)) {
7108
+ if (!(0, import_node_fs15.existsSync)(target)) {
6924
7109
  return;
6925
7110
  }
6926
7111
  stop2(recovery);
@@ -6940,11 +7125,11 @@ function runWatch(options) {
6940
7125
  }
6941
7126
  function addWatcher(target, recursive, only) {
6942
7127
  let watcher;
6943
- const listen = (useRecursive) => (0, import_node_fs14.watch)(target, { recursive: useRecursive }, (_event, filename) => {
7128
+ const listen = (useRecursive) => (0, import_node_fs15.watch)(target, { recursive: useRecursive }, (_event, filename) => {
6944
7129
  if (closed) {
6945
7130
  return;
6946
7131
  }
6947
- if (!(0, import_node_fs14.existsSync)(target)) {
7132
+ if (!(0, import_node_fs15.existsSync)(target)) {
6948
7133
  if (watcher !== void 0) {
6949
7134
  stop2(watcher);
6950
7135
  }
@@ -6952,7 +7137,7 @@ function runWatch(options) {
6952
7137
  schedule();
6953
7138
  return;
6954
7139
  }
6955
- if (only !== void 0 && (filename === null || (0, import_node_path15.basename)(filename) !== only)) {
7140
+ if (only !== void 0 && (filename === null || (0, import_node_path16.basename)(filename) !== only)) {
6956
7141
  return;
6957
7142
  }
6958
7143
  schedule();
@@ -6979,11 +7164,11 @@ function runWatch(options) {
6979
7164
  watchers.add(watcher);
6980
7165
  }
6981
7166
  addWatcher(project.recordsDir, true);
6982
- addWatcher((0, import_node_path15.dirname)(project.configFile), false, configFile);
7167
+ addWatcher((0, import_node_path16.dirname)(project.configFile), false, configFile);
6983
7168
  addWatcher(project.root, false, import_core34.SCHEMA_SHAPE_FILE);
6984
7169
  if ((0, import_core34.schemaInsideTree)(project.config) === void 0) {
6985
- const schemaFile = (0, import_node_path15.resolve)(project.root, (0, import_core34.schemaFileOf)(project.config));
6986
- addWatcher((0, import_node_path15.dirname)(schemaFile), false, (0, import_node_path15.basename)(schemaFile));
7170
+ const schemaFile = (0, import_node_path16.resolve)(project.root, (0, import_core34.schemaFileOf)(project.config));
7171
+ addWatcher((0, import_node_path16.dirname)(schemaFile), false, (0, import_node_path16.basename)(schemaFile));
6987
7172
  }
6988
7173
  void validate();
6989
7174
  return {