@penvhq/cli 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/install.cjs CHANGED
@@ -22,10 +22,13 @@ var install_exports = {};
22
22
  __export(install_exports, {
23
23
  RUNTIME_PACKAGE: () => RUNTIME_PACKAGE,
24
24
  SCHEMA_PACKAGE: () => SCHEMA_PACKAGE,
25
+ TYPES_PACKAGE: () => TYPES_PACKAGE,
25
26
  detectPackageManager: () => detectPackageManager,
26
27
  engineVersion: () => engineVersion,
27
28
  installFailed: () => installFailed,
28
29
  installWithPackageManager: () => installWithPackageManager,
30
+ installedPackages: () => installedPackages,
31
+ isPnpmWorkspaceRoot: () => isPnpmWorkspaceRoot,
29
32
  planInstall: () => planInstall,
30
33
  renderInstallPlan: () => renderInstallPlan,
31
34
  schemaPackageVersion: () => schemaPackageVersion
@@ -163,6 +166,7 @@ function escapeArgument(argument, doubleEscape) {
163
166
  var import_meta = {};
164
167
  var RUNTIME_PACKAGE = "@penvhq/penv";
165
168
  var SCHEMA_PACKAGE = "zod";
169
+ var TYPES_PACKAGE = "@penvhq/core";
166
170
  var LOCKFILES = [
167
171
  ["pnpm", "pnpm-lock.yaml"],
168
172
  ["yarn", "yarn.lock"],
@@ -171,11 +175,25 @@ var LOCKFILES = [
171
175
  ["npm", "package-lock.json"]
172
176
  ];
173
177
  var ADD = {
174
- pnpm: ["pnpm", "add", "--save-exact"],
175
- npm: ["npm", "install", "--save-exact"],
176
- yarn: ["yarn", "add", "--exact"],
177
- bun: ["bun", "add", "--exact"]
178
+ pnpm: ["pnpm", "add"],
179
+ npm: ["npm", "install"],
180
+ yarn: ["yarn", "add"],
181
+ bun: ["bun", "add"]
178
182
  };
183
+ var EXACT = {
184
+ pnpm: "--save-exact",
185
+ npm: "--save-exact",
186
+ yarn: "--exact",
187
+ bun: "--exact"
188
+ };
189
+ var DEV = {
190
+ pnpm: "-D",
191
+ npm: "--save-dev",
192
+ yarn: "--dev",
193
+ bun: "--dev"
194
+ };
195
+ var WORKSPACE_ROOT_FLAG = "-w";
196
+ var PNPM_WORKSPACE = "pnpm-workspace.yaml";
179
197
  function engineVersion() {
180
198
  const version = ownManifest()?.version;
181
199
  if (typeof version === "string" && version.length > 0) {
@@ -238,110 +256,301 @@ function manifestOf(root) {
238
256
  return void 0;
239
257
  }
240
258
  }
241
- function declaredVersion(root, name) {
242
- const manifest = manifestOf(root);
259
+ function declaredIn(dir, name) {
260
+ const manifest = manifestOf(dir);
243
261
  for (const field of ["dependencies", "devDependencies"]) {
244
262
  const block = manifest?.[field];
245
263
  if (block !== null && typeof block === "object" && !Array.isArray(block)) {
246
264
  const version = block[name];
247
265
  if (typeof version === "string") {
248
- return version;
266
+ return { version, dev: field === "devDependencies" };
249
267
  }
250
268
  }
251
269
  }
252
270
  return void 0;
253
271
  }
272
+ function isPnpmWorkspaceRoot(root) {
273
+ return (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, PNPM_WORKSPACE)) && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, "package.json"));
274
+ }
275
+ function workspaceGlobs(root) {
276
+ let text;
277
+ try {
278
+ text = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(root, PNPM_WORKSPACE), "utf8");
279
+ } catch {
280
+ return [];
281
+ }
282
+ const unquote = (raw) => raw.replace(/^['"]|['"]$/g, "").trim();
283
+ const globs = [];
284
+ let inside = false;
285
+ for (const line of text.split(/\r?\n/)) {
286
+ const flow = /^packages:\s*\[(.*)\]\s*$/.exec(line);
287
+ if (flow?.[1] !== void 0) {
288
+ return flow[1].split(",").map(unquote).filter((glob) => glob !== "");
289
+ }
290
+ if (/^packages:\s*$/.test(line)) {
291
+ inside = true;
292
+ continue;
293
+ }
294
+ if (!inside) {
295
+ continue;
296
+ }
297
+ const item = /^\s+-\s*(.+?)\s*$/.exec(line);
298
+ if (item?.[1] !== void 0) {
299
+ globs.push(unquote(item[1]));
300
+ continue;
301
+ }
302
+ if (line.trim() !== "" && !line.trimStart().startsWith("#")) {
303
+ break;
304
+ }
305
+ }
306
+ return globs;
307
+ }
308
+ function directoriesIn(dir) {
309
+ try {
310
+ return (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== "node_modules").map((entry) => (0, import_node_path2.join)(dir, entry.name));
311
+ } catch {
312
+ return [];
313
+ }
314
+ }
315
+ function isDirectory(path) {
316
+ try {
317
+ return (0, import_node_fs2.statSync)(path).isDirectory();
318
+ } catch {
319
+ return false;
320
+ }
321
+ }
322
+ function expandGlob(root, glob) {
323
+ let dirs = [root];
324
+ for (const segment of glob.split("/").filter((part) => part !== "" && part !== ".")) {
325
+ const next = [];
326
+ for (const dir of dirs) {
327
+ if (segment === "**") {
328
+ const stack = [dir];
329
+ while (stack.length > 0) {
330
+ const current = stack.pop();
331
+ next.push(current);
332
+ stack.push(...directoriesIn(current));
333
+ }
334
+ continue;
335
+ }
336
+ if (segment.includes("*")) {
337
+ const pattern = new RegExp(
338
+ `^${segment.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*")}$`
339
+ );
340
+ next.push(
341
+ ...directoriesIn(dir).filter((child) => pattern.test(child.slice(dir.length + 1)))
342
+ );
343
+ continue;
344
+ }
345
+ const candidate = (0, import_node_path2.join)(dir, segment);
346
+ if (isDirectory(candidate)) {
347
+ next.push(candidate);
348
+ }
349
+ }
350
+ dirs = next;
351
+ }
352
+ return dirs;
353
+ }
354
+ function workspaceMembers(root, name) {
355
+ if (!isPnpmWorkspaceRoot(root)) {
356
+ return [];
357
+ }
358
+ const globs = workspaceGlobs(root);
359
+ const excluded = globs.filter((glob) => glob.startsWith("!")).flatMap((glob) => expandGlob(root, glob.slice(1)));
360
+ const found = /* @__PURE__ */ new Set();
361
+ for (const glob of globs.filter((entry) => !entry.startsWith("!"))) {
362
+ for (const dir of expandGlob(root, glob)) {
363
+ if (dir !== root && !excluded.includes(dir) && declaredIn(dir, name) !== void 0) {
364
+ found.add(dir);
365
+ }
366
+ }
367
+ }
368
+ return [...found].sort();
369
+ }
370
+ function manifestPathOf(root, dir) {
371
+ const within = (0, import_node_path2.relative)(root, dir).split(import_node_path2.sep).filter((part) => part !== "");
372
+ return [...within, "package.json"].join("/");
373
+ }
374
+ function addCommand(manager, options, specs) {
375
+ const [bin, verb] = ADD[manager];
376
+ return [
377
+ bin,
378
+ ...options.filter === void 0 ? [] : ["--filter", options.filter],
379
+ verb,
380
+ ...options.workspaceRoot ? [WORKSPACE_ROOT_FLAG] : [],
381
+ EXACT[manager],
382
+ ...options.dev ? [DEV[manager]] : [],
383
+ ...specs
384
+ ];
385
+ }
386
+ function stepFor(manager, manifest, packages, options) {
387
+ const pending = packages.filter((entry) => !entry.satisfied);
388
+ const specs = (pending.length === 0 ? packages : pending).map(
389
+ (entry) => `${entry.name}@${entry.version}`
390
+ );
391
+ return {
392
+ manifest,
393
+ packages,
394
+ command: addCommand(manager, options, specs),
395
+ dev: options.dev,
396
+ satisfied: pending.length === 0
397
+ };
398
+ }
254
399
  function planInstall(root, version = engineVersion()) {
255
400
  const manager = detectPackageManager(root);
256
401
  const lockfile = LOCKFILES.find(
257
402
  ([name, file]) => name === manager && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, file))
258
403
  )?.[1];
259
- const runtimeDeclared = declaredVersion(root, RUNTIME_PACKAGE);
260
- const zodDeclared = declaredVersion(root, SCHEMA_PACKAGE);
404
+ const workspaceRoot = manager === "pnpm" && isPnpmWorkspaceRoot(root);
405
+ const runtime = declaredIn(root, RUNTIME_PACKAGE);
406
+ const zod = declaredIn(root, SCHEMA_PACKAGE);
407
+ const types = declaredIn(root, TYPES_PACKAGE);
261
408
  const packages = [
262
409
  {
263
410
  name: RUNTIME_PACKAGE,
264
411
  version,
265
- ...runtimeDeclared === void 0 ? {} : { declared: runtimeDeclared },
266
- satisfied: runtimeDeclared === version
412
+ ...runtime === void 0 ? {} : { declared: runtime.version },
413
+ satisfied: runtime?.version === version
267
414
  },
268
415
  {
269
416
  name: SCHEMA_PACKAGE,
270
417
  version: schemaPackageVersion(),
271
- ...zodDeclared === void 0 ? {} : { declared: zodDeclared },
418
+ ...zod === void 0 ? {} : { declared: zod.version },
272
419
  // Any declared zod counts: which zod a project uses is the project's
273
420
  // decision, and penv is here to make sure there is one, not to move it.
274
- satisfied: zodDeclared !== void 0
421
+ satisfied: zod !== void 0
275
422
  }
276
423
  ];
424
+ const typesPackage = {
425
+ name: TYPES_PACKAGE,
426
+ version,
427
+ ...types === void 0 ? {} : { declared: types.version },
428
+ // Any declared version counts, for zod's reason: the augmentation binds on
429
+ // the module resolving, not on which release of it a project pinned.
430
+ satisfied: types !== void 0
431
+ };
277
432
  const pending = packages.filter((entry) => !entry.satisfied);
278
- const specs = (pending.length === 0 ? packages : pending).map(
279
- (entry) => `${entry.name}@${entry.version}`
280
- );
433
+ const steps = [
434
+ stepFor(manager, "package.json", packages, {
435
+ workspaceRoot,
436
+ dev: runtime?.dev === true && pending.every((entry) => entry.name === RUNTIME_PACKAGE) && pending.length > 0
437
+ }),
438
+ stepFor(manager, "package.json", [typesPackage], { workspaceRoot, dev: true })
439
+ ];
440
+ for (const dir of workspaceMembers(root, RUNTIME_PACKAGE)) {
441
+ const declared = declaredIn(dir, RUNTIME_PACKAGE);
442
+ steps.push(
443
+ stepFor(
444
+ manager,
445
+ manifestPathOf(root, dir),
446
+ [
447
+ {
448
+ name: RUNTIME_PACKAGE,
449
+ version,
450
+ declared: declared.version,
451
+ satisfied: declared.version === version
452
+ }
453
+ ],
454
+ {
455
+ filter: `./${(0, import_node_path2.relative)(root, dir).split(import_node_path2.sep).join("/")}`,
456
+ workspaceRoot: false,
457
+ dev: declared.dev
458
+ }
459
+ )
460
+ );
461
+ }
281
462
  return {
282
463
  root,
283
464
  manager,
284
- packages,
285
- command: [...ADD[manager], ...specs],
465
+ steps,
286
466
  ...lockfile === void 0 ? {} : { lockfile },
287
- satisfied: pending.length === 0
467
+ satisfied: steps.every((step) => step.satisfied)
288
468
  };
289
469
  }
290
470
  function describe(entry) {
291
471
  return `${entry.name} ${entry.version}`;
292
472
  }
293
- function renderInstallPlan(plan) {
294
- if (plan.satisfied) {
295
- return [
296
- `package.json already has ${plan.packages.map(describe).join(" and ")} \u2014 nothing to install.`
297
- ];
298
- }
299
- const pending = plan.packages.filter((entry) => !entry.satisfied);
473
+ function installedPackages(plan) {
474
+ const pending = plan.steps.flatMap((step) => step.packages).filter((entry) => !entry.satisfied);
475
+ return [...new Map(pending.map((entry) => [entry.name, entry])).values()];
476
+ }
477
+ function plannedPackages(plan) {
478
+ const all = plan.steps.flatMap((step) => step.packages);
479
+ return [...new Map(all.map((entry) => [entry.name, entry])).values()];
480
+ }
481
+ function series(values) {
482
+ return values.length < 2 ? values[0] ?? "" : `${values.slice(0, -1).join(", ")} and ${values.at(-1)}`;
483
+ }
484
+ function renderStep(step) {
485
+ const pending = step.packages.filter((entry) => !entry.satisfied);
300
486
  const added = pending.filter((entry) => entry.declared === void 0);
301
487
  const replaced = pending.filter((entry) => entry.declared !== void 0);
488
+ const block = step.dev ? "devDependencies" : "dependencies";
302
489
  return [
303
- "package.json",
490
+ step.manifest,
304
491
  ...added.length === 0 ? [] : [
305
- ' + "dependencies": {',
492
+ ` + "${block}": {`,
306
493
  ...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
307
494
  " + }"
308
495
  ],
309
496
  ...replaced.flatMap((entry) => [
310
497
  ` - "${entry.name}": "${entry.declared}"`,
311
498
  ` + "${entry.name}": "${entry.version}"`
312
- ]),
313
- ...plan.lockfile === void 0 ? [] : [plan.lockfile, ...pending.map((entry) => ` + ${entry.name}@${entry.version}`)],
499
+ ])
500
+ ];
501
+ }
502
+ function renderInstallPlan(plan) {
503
+ if (plan.satisfied) {
504
+ return [
505
+ `package.json already has ${series(plannedPackages(plan).map(describe))} \u2014 nothing to install.`
506
+ ];
507
+ }
508
+ const pending = plan.steps.filter((step) => !step.satisfied);
509
+ const [first, ...rest] = pending.map((step) => step.command.join(" "));
510
+ const landing = installedPackages(plan).map((entry) => ` + ${entry.name}@${entry.version}`);
511
+ return [
512
+ ...pending.flatMap(renderStep),
513
+ ...plan.lockfile === void 0 ? [] : [plan.lockfile, ...landing],
314
514
  "",
315
- `Run with: ${plan.command.join(" ")}`
515
+ `Run with: ${first ?? ""}`,
516
+ ...rest.map((command) => ` then ${command}`)
316
517
  ];
317
518
  }
318
519
  var installWithPackageManager = async (plan) => {
319
- const child = startChild({
320
- command: plan.command,
321
- env: process.env,
322
- cwd: plan.root,
323
- purpose: `install ${plan.packages.map(describe).join(" and ")}`
324
- });
325
- const ended = await child.ended;
326
- if (ended.exitCode !== 0 || ended.signal !== null) {
327
- throw installFailed(plan);
520
+ for (const step of plan.steps) {
521
+ if (step.satisfied) {
522
+ continue;
523
+ }
524
+ const child = startChild({
525
+ command: step.command,
526
+ env: process.env,
527
+ cwd: plan.root,
528
+ purpose: `install ${step.packages.map(describe).join(" and ")} in ${step.manifest}`
529
+ });
530
+ const ended = await child.ended;
531
+ if (ended.exitCode !== 0 || ended.signal !== null) {
532
+ throw installFailed(plan, step);
533
+ }
328
534
  }
329
535
  };
330
- function installFailed(plan) {
536
+ function installFailed(plan, step) {
331
537
  return new import_core2.PenvError(
332
538
  "INIT_INSTALL_FAILED",
333
- `${plan.command.join(" ")} did not finish, so penv migrated nothing`,
334
- `Run \`${plan.command.join(" ")}\` yourself, then start this command again. Your dotenv files are exactly where they were.`
539
+ `${step.command.join(" ")} did not finish, so penv migrated nothing`,
540
+ `Read what ${plan.manager} printed above \u2014 it names what it refused. Fix that and run this command again; your dotenv files are exactly where they were.`
335
541
  );
336
542
  }
337
543
  // Annotate the CommonJS export names for ESM import in node:
338
544
  0 && (module.exports = {
339
545
  RUNTIME_PACKAGE,
340
546
  SCHEMA_PACKAGE,
547
+ TYPES_PACKAGE,
341
548
  detectPackageManager,
342
549
  engineVersion,
343
550
  installFailed,
344
551
  installWithPackageManager,
552
+ installedPackages,
553
+ isPnpmWorkspaceRoot,
345
554
  planInstall,
346
555
  renderInstallPlan,
347
556
  schemaPackageVersion
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/install.ts","../src/child.ts"],"sourcesContent":["/**\n * The runtime dependencies an adopted project takes, and how they get there.\n *\n * PRD §3: an adopted project depends on `@penvhq/penv` at the engine's own\n * version — the typed `@env` surface, not a CLI distribution. It also depends on\n * zod, because the `penv.schema.ts` init scaffolds imports it: zod is a *peer* of\n * `@penvhq/penv`, and a peer is a package the project supplies. Under pnpm's\n * strict layout nothing hoists it to the project root, so an install that named\n * only `@penvhq/penv` left the very schema init had just written unable to\n * resolve `zod` — and adoption could never finish.\n *\n * Both are installed with the package manager the project already uses, and only\n * after showing the exact `package.json` and lockfile change: an install is the\n * one step of adoption that reaches outside the repository, so it is the one step\n * that is shown before it happens rather than reported after.\n *\n * The install itself is a seam. It shells out to a package manager, which the\n * tests must never do — and a fake here is not a weaker test, because what init\n * has to get right is the plan, the consent, and the refusal when the install\n * does not happen.\n *\n * Two commands write that dependency line: `penv init`, which is the engine's,\n * and `penv upgrade`, which is the launcher's. This module is published at\n * `@penvhq/cli/install` so the launcher reaches it without loading the command\n * surface — one answer to \"which package manager, which diff, which spawn\",\n * rather than a second copy on the other side of the launcher/engine split.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { PenvError } from \"@penvhq/core\";\nimport { startChild } from \"./child.js\";\n\n/** The package an adopted project depends on. The CLI engine is not one of its dependencies. */\nexport const RUNTIME_PACKAGE = \"@penvhq/penv\";\n\n/** The peer `penv.schema.ts` imports, which the project supplies because a peer is not hoisted. */\nexport const SCHEMA_PACKAGE = \"zod\";\n\nexport type PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\n/** The lockfile that names each manager, checked in this order. */\nconst LOCKFILES: readonly (readonly [PackageManager, string])[] = [\n [\"pnpm\", \"pnpm-lock.yaml\"],\n [\"yarn\", \"yarn.lock\"],\n [\"bun\", \"bun.lock\"],\n [\"bun\", \"bun.lockb\"],\n [\"npm\", \"package-lock.json\"],\n];\n\n/** How each manager is told to add one exact version. */\nconst ADD: Readonly<Record<PackageManager, readonly string[]>> = {\n pnpm: [\"pnpm\", \"add\", \"--save-exact\"],\n npm: [\"npm\", \"install\", \"--save-exact\"],\n yarn: [\"yarn\", \"add\", \"--exact\"],\n bun: [\"bun\", \"add\", \"--exact\"],\n};\n\n/** One package the adopted project needs, and what its `package.json` says today. */\nexport interface InstallPackage {\n readonly name: string;\n readonly version: string;\n /** What `package.json` already says about it, when it says anything. */\n readonly declared?: string;\n /** True when this project already has it — nothing to install for this one. */\n readonly satisfied: boolean;\n}\n\nexport interface InstallPlan {\n readonly root: string;\n readonly manager: PackageManager;\n /** Everything an adopted project needs, in the order the diff shows them. */\n readonly packages: readonly InstallPackage[];\n /** The command, argv-shaped — what runs, and what a refusal tells the user to run. */\n readonly command: readonly string[];\n /** The lockfile the manager will rewrite, when the project has one. */\n readonly lockfile?: string;\n /** True when every package is already there — nothing to install. */\n readonly satisfied: boolean;\n}\n\n/** Runs an install plan, or throws. Replaced in tests; never spawns there. */\nexport type InstallRuntime = (plan: InstallPlan) => Promise<void>;\n\n/**\n * The engine's own version, read from its manifest rather than restated in the\n * source: `@penvhq/penv` must match the engine exactly, and a constant beside\n * the version a release bumps is a second answer waiting to drift.\n */\nexport function engineVersion(): string {\n const version = ownManifest()?.version;\n if (typeof version === \"string\" && version.length > 0) {\n return version;\n }\n throw new PenvError(\n \"ENGINE_VERSION_UNREADABLE\",\n \"penv could not read its own version, so it cannot say which `@penvhq/penv` this project needs\",\n `Reinstall penv, then run \\`penv init\\` again.`,\n );\n}\n\n/**\n * The zod an adopted project installs: the floor of the peer range the engine\n * and `@penvhq/penv` both declare, which is the version penv is built and tested\n * against.\n *\n * The floor rather than the range, because the diff shown before the install has\n * to be the line that actually lands — `--save-exact` on `^4.4.3` would write\n * whatever the registry resolved that day, which is not something a reader can\n * consent to in advance.\n */\nexport function schemaPackageVersion(): string {\n const peers = ownManifest()?.peerDependencies;\n const declared =\n peers !== null && typeof peers === \"object\" && !Array.isArray(peers)\n ? (peers as Record<string, unknown>)[SCHEMA_PACKAGE]\n : undefined;\n const floor = typeof declared === \"string\" ? declared.replace(/^[\\^~>=\\s]+/, \"\").trim() : \"\";\n if (floor.length > 0) {\n return floor;\n }\n throw new PenvError(\n \"ENGINE_PEER_UNREADABLE\",\n `penv could not read its own \\`${SCHEMA_PACKAGE}\\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,\n `Reinstall penv, then run \\`penv init\\` again.`,\n );\n}\n\n/** The engine's own manifest, or `undefined` when it cannot be read. */\nfunction ownManifest(): Record<string, unknown> | undefined {\n try {\n const parsed: unknown = JSON.parse(\n readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\"),\n );\n return parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : undefined;\n } catch {\n // The callers refuse: a version penv guessed would pin a project's\n // dependency to something nobody chose.\n return undefined;\n }\n}\n\n/** The package manager this project already uses: its lockfile, then what it declares, then npm. */\nexport function detectPackageManager(root: string): PackageManager {\n for (const [manager, lockfile] of LOCKFILES) {\n if (existsSync(join(root, lockfile))) {\n return manager;\n }\n }\n return declaredManager(root) ?? \"npm\";\n}\n\n/** `\"packageManager\": \"pnpm@9.1.0\"` — corepack's field, and a project's own answer. */\nfunction declaredManager(root: string): PackageManager | undefined {\n const declared = manifestOf(root)?.packageManager;\n if (typeof declared !== \"string\") {\n return undefined;\n }\n const name = declared.split(\"@\")[0];\n return name === \"pnpm\" || name === \"npm\" || name === \"yarn\" || name === \"bun\" ? name : undefined;\n}\n\nfunction manifestOf(root: string): Record<string, unknown> | undefined {\n const file = join(root, \"package.json\");\n if (!existsSync(file)) {\n return undefined;\n }\n try {\n const manifest: unknown = JSON.parse(readFileSync(file, \"utf8\"));\n return manifest !== null && typeof manifest === \"object\" && !Array.isArray(manifest)\n ? (manifest as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** What `package.json` says about one package today, from either dependency block. */\nfunction declaredVersion(root: string, name: string): string | undefined {\n const manifest = manifestOf(root);\n for (const field of [\"dependencies\", \"devDependencies\"] as const) {\n const block: unknown = manifest?.[field];\n if (block !== null && typeof block === \"object\" && !Array.isArray(block)) {\n const version: unknown = (block as Record<string, unknown>)[name];\n if (typeof version === \"string\") {\n return version;\n }\n }\n }\n return undefined;\n}\n\nexport function planInstall(root: string, version: string = engineVersion()): InstallPlan {\n const manager = detectPackageManager(root);\n const lockfile = LOCKFILES.find(\n ([name, file]) => name === manager && existsSync(join(root, file)),\n )?.[1];\n\n const runtimeDeclared = declaredVersion(root, RUNTIME_PACKAGE);\n const zodDeclared = declaredVersion(root, SCHEMA_PACKAGE);\n const packages: InstallPackage[] = [\n {\n name: RUNTIME_PACKAGE,\n version,\n ...(runtimeDeclared === undefined ? {} : { declared: runtimeDeclared }),\n satisfied: runtimeDeclared === version,\n },\n {\n name: SCHEMA_PACKAGE,\n version: schemaPackageVersion(),\n ...(zodDeclared === undefined ? {} : { declared: zodDeclared }),\n // Any declared zod counts: which zod a project uses is the project's\n // decision, and penv is here to make sure there is one, not to move it.\n satisfied: zodDeclared !== undefined,\n },\n ];\n\n const pending = packages.filter((entry) => !entry.satisfied);\n const specs = (pending.length === 0 ? packages : pending).map(\n (entry) => `${entry.name}@${entry.version}`,\n );\n return {\n root,\n manager,\n packages,\n command: [...ADD[manager], ...specs],\n ...(lockfile === undefined ? {} : { lockfile }),\n satisfied: pending.length === 0,\n };\n}\n\nfunction describe(entry: InstallPackage): string {\n return `${entry.name} ${entry.version}`;\n}\n\n/**\n * The change, as it will appear in the diff — the whole point of showing it is\n * that the reader recognises their own file, so these are the `package.json`\n * lines that land and the lockfile that gets rewritten, not a summary of both.\n */\nexport function renderInstallPlan(plan: InstallPlan): string[] {\n if (plan.satisfied) {\n return [\n `package.json already has ${plan.packages.map(describe).join(\" and \")} — nothing to install.`,\n ];\n }\n const pending = plan.packages.filter((entry) => !entry.satisfied);\n const added = pending.filter((entry) => entry.declared === undefined);\n const replaced = pending.filter((entry) => entry.declared !== undefined);\n return [\n \"package.json\",\n ...(added.length === 0\n ? []\n : [\n ' + \"dependencies\": {',\n ...added.map((entry) => ` + \"${entry.name}\": \"${entry.version}\"`),\n \" + }\",\n ]),\n ...replaced.flatMap((entry) => [\n ` - \"${entry.name}\": \"${entry.declared}\"`,\n ` + \"${entry.name}\": \"${entry.version}\"`,\n ]),\n ...(plan.lockfile === undefined\n ? []\n : [plan.lockfile, ...pending.map((entry) => ` + ${entry.name}@${entry.version}`)]),\n \"\",\n `Run with: ${plan.command.join(\" \")}`,\n ];\n}\n\n/**\n * The real install: the project's own package manager, started the way any other\n * child is (`.cmd` shims on Windows included), with its output the user's to see.\n */\nexport const installWithPackageManager: InstallRuntime = async (plan) => {\n const child = startChild({\n command: plan.command,\n env: process.env as Record<string, string>,\n cwd: plan.root,\n purpose: `install ${plan.packages.map(describe).join(\" and \")}`,\n });\n const ended = await child.ended;\n if (ended.exitCode !== 0 || ended.signal !== null) {\n throw installFailed(plan);\n }\n};\n\nexport function installFailed(plan: InstallPlan): PenvError {\n return new PenvError(\n \"INIT_INSTALL_FAILED\",\n `${plan.command.join(\" \")} did not finish, so penv migrated nothing`,\n `Run \\`${plan.command.join(\" \")}\\` yourself, then start this command again. Your dotenv files are exactly where they were.`,\n );\n}\n","/**\n * Starting someone else's command, opaquely.\n *\n * `penv run -- <command>` starts exactly what follows `--`: the argument\n * boundaries the shell already worked out are handed to the operating system\n * untouched, stdio is the parent's, and the child's exit code and terminating\n * signal come back out. penv never parses the command, never rebuilds a command\n * line from it, never wraps it in a shell — a shell would re-split what the user\n * already split, and `penv run -- node -e \"console.log(1 > 2)\"` would redirect to\n * a file called `2`.\n *\n * Windows is the one place where \"hand it to the operating system\" needs help.\n * `pnpm`, `next` and every other node-installed tool are `.cmd` shims there, and\n * Node refuses to execute one without a shell. So a `.cmd`/`.bat` target — and\n * only that — is started through `cmd.exe /d /s /c` with\n * `windowsVerbatimArguments`, building the one command line cmd will accept and\n * escaping every argument so that cmd hands the child the same bytes penv was\n * given. Everything else spawns directly, on every platform.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { delimiter, isAbsolute, join, win32 } from \"node:path\";\nimport { PenvError } from \"@penvhq/core\";\n\n/** How a child ended. Exactly one of these is meaningful, and both are forwarded. */\nexport interface ChildResult {\n /** The child's own exit code, or 1 when a signal ended it. */\n readonly exitCode: number;\n /** The signal that ended the child, when one did. */\n readonly signal: NodeJS.Signals | null;\n}\n\nexport interface ChildInvocation {\n /** The command exactly as it followed `--`: the executable, then its arguments. */\n readonly command: readonly string[];\n readonly env: Record<string, string>;\n readonly cwd: string;\n /**\n * What penv is starting this on its own behalf to do — `init`'s dependency\n * install. Absent means the command is the user's, from after `--`, and the\n * two failures have opposite remedies: one is about what they typed, the other\n * about a program penv chose to run.\n */\n readonly purpose?: string;\n}\n\n/** A started child: how it ends, and the one thing a wrapper may do to it. */\nexport interface ChildHandle {\n /** Resolves when the child has ended, however it ended. */\n readonly ended: Promise<ChildResult>;\n /** Asks the child to stop — what `--watch` does before it starts the next one. */\n kill(signal?: NodeJS.Signals): void;\n}\n\n/** The seam `run` starts a child through — replaced in tests that assert what it was given. */\nexport type StartChild = (invocation: ChildInvocation) => ChildHandle;\n\n/** The signals a wrapper must pass through rather than absorb. */\nconst FORWARDED: readonly NodeJS.Signals[] = [\"SIGINT\", \"SIGTERM\", \"SIGHUP\", \"SIGBREAK\"];\n\nexport const startChild: StartChild = (invocation) => {\n const [executable, ...args] = invocation.command;\n if (executable === undefined) {\n throw noCommand();\n }\n\n const target = resolveTarget(executable, args, invocation.env);\n const child = spawn(target.file, target.args, {\n cwd: invocation.cwd,\n env: invocation.env,\n stdio: \"inherit\",\n ...(target.verbatim ? { windowsVerbatimArguments: true } : {}),\n });\n\n // Forwarded rather than handled: penv is a wrapper, and a Ctrl-C belongs to\n // the program the user is looking at. The child decides what to do with it,\n // and its answer comes back as the signal below.\n const forward = new Map<NodeJS.Signals, () => void>();\n for (const signal of FORWARDED) {\n const handler = (): void => {\n child.kill(signal);\n };\n forward.set(signal, handler);\n process.on(signal, handler);\n }\n const release = (): void => {\n for (const [signal, handler] of forward) {\n process.off(signal, handler);\n }\n };\n\n const ended = new Promise<ChildResult>((resolve, reject) => {\n child.on(\"error\", (cause) => {\n release();\n reject(cannotStart(executable, cause, invocation.purpose));\n });\n child.on(\"exit\", (code, signal) => {\n release();\n resolve({ exitCode: code ?? 1, signal });\n });\n });\n\n return {\n ended,\n kill(signal) {\n child.kill(signal);\n },\n };\n};\n\nexport function noCommand(): PenvError {\n return new PenvError(\n \"RUN_NO_COMMAND\",\n \"`penv run` was given no command to start\",\n \"Put the command after `--`, e.g. `penv run -- pnpm dev`.\",\n );\n}\n\nfunction cannotStart(executable: string, cause: unknown, purpose: string | undefined): PenvError {\n const detail = cause instanceof Error ? cause.message : String(cause);\n if (purpose !== undefined) {\n return new PenvError(\n \"PENV_COMMAND_NOT_STARTED\",\n `penv could not start \\`${executable}\\` to ${purpose}: ${detail}`,\n `Check that \\`${executable}\\` runs on its own — penv starts it the way your shell does, so it has to be on PATH. Nothing was changed.`,\n );\n }\n return new PenvError(\n \"RUN_COMMAND_NOT_STARTED\",\n `\\`${executable}\\` could not be started: ${detail}`,\n `Check the command after \\`--\\` runs on its own — \\`${executable}\\` has to be on PATH, exactly as it is spelled here.`,\n );\n}\n\ninterface SpawnTarget {\n readonly file: string;\n readonly args: readonly string[];\n /** True when the args are one pre-built command line rather than a list. */\n readonly verbatim: boolean;\n}\n\nfunction resolveTarget(\n executable: string,\n args: readonly string[],\n env: Readonly<Record<string, string | undefined>>,\n): SpawnTarget {\n if (process.platform !== \"win32\") {\n return { file: executable, args, verbatim: false };\n }\n const resolved = findExecutable(executable, env);\n if (resolved === undefined || !/\\.(cmd|bat)$/i.test(resolved)) {\n return { file: resolved ?? executable, args, verbatim: false };\n }\n return {\n file: env.ComSpec ?? \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", `\"${cmdCommandLine(resolved, args)}\"`],\n verbatim: true,\n };\n}\n\n/** A package-manager shim, which re-invokes cmd on its own way through. */\nconst SHIM = /(?:^|\\\\)node_modules\\\\\\.bin\\\\[^\\\\]+\\.cmd$/i;\n\n/**\n * The one command line cmd.exe is handed, escaped so the child receives the\n * bytes penv was given.\n *\n * The path is normalized first and *then* judged: `./node_modules/.bin/next.cmd`\n * and `.\\node_modules\\.bin\\next.cmd` are the same shim, and deciding on the\n * un-normalized spelling would escape a forward-slash invocation once while cmd\n * expands it twice — so an argument holding `&` would run as a command inside\n * the shim's second round. Windows' own separator, whatever this process runs\n * on, because this line is only ever read by cmd.exe.\n */\nexport function cmdCommandLine(resolved: string, args: readonly string[]): string {\n const command = win32.normalize(resolved);\n const shim = SHIM.test(command);\n return [escapeCommand(command), ...args.map((argument) => escapeArgument(argument, shim))].join(\n \" \",\n );\n}\n\n/**\n * The extensions a name is tried with, in the order the platform's own launcher\n * tries them.\n *\n * On Windows PATHEXT leads and the bare name comes last, because the bare name\n * is almost never what Windows would run: `pnpm`, `npx` and every\n * `node_modules/.bin` tool ship an extensionless POSIX shell script *beside*\n * their `.CMD` shim, in the same directory. Trying the empty extension first\n * matched that script, which is not executable by CreateProcess and is not a\n * `.cmd`, so the wrapper below was skipped and the spawn failed with ENOENT.\n * Everywhere else there are no extensions at all.\n */\nfunction extensions(\n env: Readonly<Record<string, string | undefined>>,\n platform: NodeJS.Platform,\n): string[] {\n if (platform !== \"win32\") {\n return [\"\"];\n }\n const declared = env.PATHEXT ?? \".COM;.EXE;.BAT;.CMD\";\n return [...declared.split(\";\").filter((extension) => extension.length > 0), \"\"];\n}\n\n/**\n * What the shell would have run, found the way the shell finds it: the name as\n * given if it carries a path, else each PATH directory, each with each\n * executable extension.\n *\n * `platform` is a parameter so the ordering above is testable on either kind of\n * machine — it is the whole behavior, and it differs by platform.\n */\nexport function findExecutable(\n executable: string,\n env: Readonly<Record<string, string | undefined>>,\n platform: NodeJS.Platform = process.platform,\n): string | undefined {\n const candidates = extensions(env, platform);\n const isFile = (path: string): boolean => existsSync(path) && statSync(path).isFile();\n\n if (executable.includes(\"/\") || executable.includes(\"\\\\\") || isAbsolute(executable)) {\n return candidates.map((extension) => executable + extension).find(isFile);\n }\n const path = env.PATH ?? env.Path ?? \"\";\n for (const directory of path.split(delimiter).filter((entry) => entry.length > 0)) {\n const hit = candidates.map((extension) => join(directory, executable + extension)).find(isFile);\n if (hit !== undefined) {\n return hit;\n }\n }\n return undefined;\n}\n\n/** The characters cmd.exe expands before the program ever sees them. */\nconst CMD_METACHARACTERS = /([()\\][%!^\"`<>&|;, *?])/g;\n\n/** The command's own path: cmd's metacharacters escaped, and no quotes to confuse it. */\nfunction escapeCommand(command: string): string {\n return command.replace(CMD_METACHARACTERS, \"^$1\");\n}\n\n/**\n * One argument, quoted so the child's runtime splits it exactly where penv was\n * given it, then escaped so cmd.exe passes those quotes through instead of\n * acting on them.\n */\nfunction escapeArgument(argument: string, doubleEscape: boolean): string {\n const quoted = `\"${argument.replace(/(\\\\*)\"/g, '$1$1\\\\\"').replace(/(\\\\*)$/, \"$1$1\")}\"`;\n const escaped = quoted.replace(CMD_METACHARACTERS, \"^$1\");\n return doubleEscape ? escaped.replace(CMD_METACHARACTERS, \"^$1\") : escaped;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BA,IAAAA,kBAAyC;AACzC,IAAAC,oBAAqB;AACrB,IAAAC,eAA0B;;;ACV1B,gCAAsB;AACtB,qBAAqC;AACrC,uBAAmD;AACnD,kBAA0B;AAoC1B,IAAM,YAAuC,CAAC,UAAU,WAAW,UAAU,UAAU;AAEhF,IAAM,aAAyB,CAAC,eAAe;AACpD,QAAM,CAAC,YAAY,GAAG,IAAI,IAAI,WAAW;AACzC,MAAI,eAAe,QAAW;AAC5B,UAAM,UAAU;AAAA,EAClB;AAEA,QAAM,SAAS,cAAc,YAAY,MAAM,WAAW,GAAG;AAC7D,QAAM,YAAQ,iCAAM,OAAO,MAAM,OAAO,MAAM;AAAA,IAC5C,KAAK,WAAW;AAAA,IAChB,KAAK,WAAW;AAAA,IAChB,OAAO;AAAA,IACP,GAAI,OAAO,WAAW,EAAE,0BAA0B,KAAK,IAAI,CAAC;AAAA,EAC9D,CAAC;AAKD,QAAM,UAAU,oBAAI,IAAgC;AACpD,aAAW,UAAU,WAAW;AAC9B,UAAM,UAAU,MAAY;AAC1B,YAAM,KAAK,MAAM;AAAA,IACnB;AACA,YAAQ,IAAI,QAAQ,OAAO;AAC3B,YAAQ,GAAG,QAAQ,OAAO;AAAA,EAC5B;AACA,QAAM,UAAU,MAAY;AAC1B,eAAW,CAAC,QAAQ,OAAO,KAAK,SAAS;AACvC,cAAQ,IAAI,QAAQ,OAAO;AAAA,IAC7B;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,QAAqB,CAAC,SAAS,WAAW;AAC1D,UAAM,GAAG,SAAS,CAAC,UAAU;AAC3B,cAAQ;AACR,aAAO,YAAY,YAAY,OAAO,WAAW,OAAO,CAAC;AAAA,IAC3D,CAAC;AACD,UAAM,GAAG,QAAQ,CAAC,MAAM,WAAW;AACjC,cAAQ;AACR,cAAQ,EAAE,UAAU,QAAQ,GAAG,OAAO,CAAC;AAAA,IACzC,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,KAAK,MAAM;AAAA,IACnB;AAAA,EACF;AACF;AAEO,SAAS,YAAuB;AACrC,SAAO,IAAI;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,YAAY,YAAoB,OAAgB,SAAwC;AAC/F,QAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,MAAI,YAAY,QAAW;AACzB,WAAO,IAAI;AAAA,MACT;AAAA,MACA,0BAA0B,UAAU,SAAS,OAAO,KAAK,MAAM;AAAA,MAC/D,gBAAgB,UAAU;AAAA,IAC5B;AAAA,EACF;AACA,SAAO,IAAI;AAAA,IACT;AAAA,IACA,KAAK,UAAU,4BAA4B,MAAM;AAAA,IACjD,2DAAsD,UAAU;AAAA,EAClE;AACF;AASA,SAAS,cACP,YACA,MACA,KACa;AACb,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAO,EAAE,MAAM,YAAY,MAAM,UAAU,MAAM;AAAA,EACnD;AACA,QAAM,WAAW,eAAe,YAAY,GAAG;AAC/C,MAAI,aAAa,UAAa,CAAC,gBAAgB,KAAK,QAAQ,GAAG;AAC7D,WAAO,EAAE,MAAM,YAAY,YAAY,MAAM,UAAU,MAAM;AAAA,EAC/D;AACA,SAAO;AAAA,IACL,MAAM,IAAI,WAAW;AAAA,IACrB,MAAM,CAAC,MAAM,MAAM,MAAM,IAAI,eAAe,UAAU,IAAI,CAAC,GAAG;AAAA,IAC9D,UAAU;AAAA,EACZ;AACF;AAGA,IAAM,OAAO;AAaN,SAAS,eAAe,UAAkB,MAAiC;AAChF,QAAM,UAAU,uBAAM,UAAU,QAAQ;AACxC,QAAM,OAAO,KAAK,KAAK,OAAO;AAC9B,SAAO,CAAC,cAAc,OAAO,GAAG,GAAG,KAAK,IAAI,CAAC,aAAa,eAAe,UAAU,IAAI,CAAC,CAAC,EAAE;AAAA,IACzF;AAAA,EACF;AACF;AAcA,SAAS,WACP,KACA,UACU;AACV,MAAI,aAAa,SAAS;AACxB,WAAO,CAAC,EAAE;AAAA,EACZ;AACA,QAAM,WAAW,IAAI,WAAW;AAChC,SAAO,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,OAAO,CAAC,cAAc,UAAU,SAAS,CAAC,GAAG,EAAE;AAChF;AAUO,SAAS,eACd,YACA,KACA,WAA4B,QAAQ,UAChB;AACpB,QAAM,aAAa,WAAW,KAAK,QAAQ;AAC3C,QAAM,SAAS,CAACC,cAA0B,2BAAWA,KAAI,SAAK,yBAASA,KAAI,EAAE,OAAO;AAEpF,MAAI,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,IAAI,SAAK,6BAAW,UAAU,GAAG;AACnF,WAAO,WAAW,IAAI,CAAC,cAAc,aAAa,SAAS,EAAE,KAAK,MAAM;AAAA,EAC1E;AACA,QAAM,OAAO,IAAI,QAAQ,IAAI,QAAQ;AACrC,aAAW,aAAa,KAAK,MAAM,0BAAS,EAAE,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC,GAAG;AACjF,UAAM,MAAM,WAAW,IAAI,CAAC,kBAAc,uBAAK,WAAW,aAAa,SAAS,CAAC,EAAE,KAAK,MAAM;AAC9F,QAAI,QAAQ,QAAW;AACrB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAGA,IAAM,qBAAqB;AAG3B,SAAS,cAAc,SAAyB;AAC9C,SAAO,QAAQ,QAAQ,oBAAoB,KAAK;AAClD;AAOA,SAAS,eAAe,UAAkB,cAA+B;AACvE,QAAM,SAAS,IAAI,SAAS,QAAQ,WAAW,SAAS,EAAE,QAAQ,UAAU,MAAM,CAAC;AACnF,QAAM,UAAU,OAAO,QAAQ,oBAAoB,KAAK;AACxD,SAAO,eAAe,QAAQ,QAAQ,oBAAoB,KAAK,IAAI;AACrE;;;AD5PA;AAkCO,IAAM,kBAAkB;AAGxB,IAAM,iBAAiB;AAK9B,IAAM,YAA4D;AAAA,EAChE,CAAC,QAAQ,gBAAgB;AAAA,EACzB,CAAC,QAAQ,WAAW;AAAA,EACpB,CAAC,OAAO,UAAU;AAAA,EAClB,CAAC,OAAO,WAAW;AAAA,EACnB,CAAC,OAAO,mBAAmB;AAC7B;AAGA,IAAM,MAA2D;AAAA,EAC/D,MAAM,CAAC,QAAQ,OAAO,cAAc;AAAA,EACpC,KAAK,CAAC,OAAO,WAAW,cAAc;AAAA,EACtC,MAAM,CAAC,QAAQ,OAAO,SAAS;AAAA,EAC/B,KAAK,CAAC,OAAO,OAAO,SAAS;AAC/B;AAiCO,SAAS,gBAAwB;AACtC,QAAM,UAAU,YAAY,GAAG;AAC/B,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,GAAG;AACrD,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAYO,SAAS,uBAA+B;AAC7C,QAAM,QAAQ,YAAY,GAAG;AAC7B,QAAM,WACJ,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAC9D,MAAkC,cAAc,IACjD;AACN,QAAM,QAAQ,OAAO,aAAa,WAAW,SAAS,QAAQ,eAAe,EAAE,EAAE,KAAK,IAAI;AAC1F,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAAA,IACR;AAAA,IACA,iCAAiC,cAAc,yCAAyC,cAAc;AAAA,IACtG;AAAA,EACF;AACF;AAGA,SAAS,cAAmD;AAC1D,MAAI;AACF,UAAM,SAAkB,KAAK;AAAA,UAC3B,8BAAa,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM;AAAA,IAClE;AACA,WAAO,WAAW,QAAQ,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IACxE,SACD;AAAA,EACN,QAAQ;AAGN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,qBAAqB,MAA8B;AACjE,aAAW,CAAC,SAAS,QAAQ,KAAK,WAAW;AAC3C,YAAI,gCAAW,wBAAK,MAAM,QAAQ,CAAC,GAAG;AACpC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,gBAAgB,IAAI,KAAK;AAClC;AAGA,SAAS,gBAAgB,MAA0C;AACjE,QAAM,WAAW,WAAW,IAAI,GAAG;AACnC,MAAI,OAAO,aAAa,UAAU;AAChC,WAAO;AAAA,EACT;AACA,QAAM,OAAO,SAAS,MAAM,GAAG,EAAE,CAAC;AAClC,SAAO,SAAS,UAAU,SAAS,SAAS,SAAS,UAAU,SAAS,QAAQ,OAAO;AACzF;AAEA,SAAS,WAAW,MAAmD;AACrE,QAAM,WAAO,wBAAK,MAAM,cAAc;AACtC,MAAI,KAAC,4BAAW,IAAI,GAAG;AACrB,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,WAAoB,KAAK,UAAM,8BAAa,MAAM,MAAM,CAAC;AAC/D,WAAO,aAAa,QAAQ,OAAO,aAAa,YAAY,CAAC,MAAM,QAAQ,QAAQ,IAC9E,WACD;AAAA,EACN,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,gBAAgB,MAAc,MAAkC;AACvE,QAAM,WAAW,WAAW,IAAI;AAChC,aAAW,SAAS,CAAC,gBAAgB,iBAAiB,GAAY;AAChE,UAAM,QAAiB,WAAW,KAAK;AACvC,QAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACxE,YAAM,UAAoB,MAAkC,IAAI;AAChE,UAAI,OAAO,YAAY,UAAU;AAC/B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,YAAY,MAAc,UAAkB,cAAc,GAAgB;AACxF,QAAM,UAAU,qBAAqB,IAAI;AACzC,QAAM,WAAW,UAAU;AAAA,IACzB,CAAC,CAAC,MAAM,IAAI,MAAM,SAAS,eAAW,gCAAW,wBAAK,MAAM,IAAI,CAAC;AAAA,EACnE,IAAI,CAAC;AAEL,QAAM,kBAAkB,gBAAgB,MAAM,eAAe;AAC7D,QAAM,cAAc,gBAAgB,MAAM,cAAc;AACxD,QAAM,WAA6B;AAAA,IACjC;AAAA,MACE,MAAM;AAAA,MACN;AAAA,MACA,GAAI,oBAAoB,SAAY,CAAC,IAAI,EAAE,UAAU,gBAAgB;AAAA,MACrE,WAAW,oBAAoB;AAAA,IACjC;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS,qBAAqB;AAAA,MAC9B,GAAI,gBAAgB,SAAY,CAAC,IAAI,EAAE,UAAU,YAAY;AAAA;AAAA;AAAA,MAG7D,WAAW,gBAAgB;AAAA,IAC7B;AAAA,EACF;AAEA,QAAM,UAAU,SAAS,OAAO,CAAC,UAAU,CAAC,MAAM,SAAS;AAC3D,QAAM,SAAS,QAAQ,WAAW,IAAI,WAAW,SAAS;AAAA,IACxD,CAAC,UAAU,GAAG,MAAM,IAAI,IAAI,MAAM,OAAO;AAAA,EAC3C;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,CAAC,GAAG,IAAI,OAAO,GAAG,GAAG,KAAK;AAAA,IACnC,GAAI,aAAa,SAAY,CAAC,IAAI,EAAE,SAAS;AAAA,IAC7C,WAAW,QAAQ,WAAW;AAAA,EAChC;AACF;AAEA,SAAS,SAAS,OAA+B;AAC/C,SAAO,GAAG,MAAM,IAAI,IAAI,MAAM,OAAO;AACvC;AAOO,SAAS,kBAAkB,MAA6B;AAC7D,MAAI,KAAK,WAAW;AAClB,WAAO;AAAA,MACL,4BAA4B,KAAK,SAAS,IAAI,QAAQ,EAAE,KAAK,OAAO,CAAC;AAAA,IACvE;AAAA,EACF;AACA,QAAM,UAAU,KAAK,SAAS,OAAO,CAAC,UAAU,CAAC,MAAM,SAAS;AAChE,QAAM,QAAQ,QAAQ,OAAO,CAAC,UAAU,MAAM,aAAa,MAAS;AACpE,QAAM,WAAW,QAAQ,OAAO,CAAC,UAAU,MAAM,aAAa,MAAS;AACvE,SAAO;AAAA,IACL;AAAA,IACA,GAAI,MAAM,WAAW,IACjB,CAAC,IACD;AAAA,MACE;AAAA,MACA,GAAG,MAAM,IAAI,CAAC,UAAU,UAAU,MAAM,IAAI,OAAO,MAAM,OAAO,GAAG;AAAA,MACnE;AAAA,IACF;AAAA,IACJ,GAAG,SAAS,QAAQ,CAAC,UAAU;AAAA,MAC7B,QAAQ,MAAM,IAAI,OAAO,MAAM,QAAQ;AAAA,MACvC,QAAQ,MAAM,IAAI,OAAO,MAAM,OAAO;AAAA,IACxC,CAAC;AAAA,IACD,GAAI,KAAK,aAAa,SAClB,CAAC,IACD,CAAC,KAAK,UAAU,GAAG,QAAQ,IAAI,CAAC,UAAU,OAAO,MAAM,IAAI,IAAI,MAAM,OAAO,EAAE,CAAC;AAAA,IACnF;AAAA,IACA,aAAa,KAAK,QAAQ,KAAK,GAAG,CAAC;AAAA,EACrC;AACF;AAMO,IAAM,4BAA4C,OAAO,SAAS;AACvE,QAAM,QAAQ,WAAW;AAAA,IACvB,SAAS,KAAK;AAAA,IACd,KAAK,QAAQ;AAAA,IACb,KAAK,KAAK;AAAA,IACV,SAAS,WAAW,KAAK,SAAS,IAAI,QAAQ,EAAE,KAAK,OAAO,CAAC;AAAA,EAC/D,CAAC;AACD,QAAM,QAAQ,MAAM,MAAM;AAC1B,MAAI,MAAM,aAAa,KAAK,MAAM,WAAW,MAAM;AACjD,UAAM,cAAc,IAAI;AAAA,EAC1B;AACF;AAEO,SAAS,cAAc,MAA8B;AAC1D,SAAO,IAAI;AAAA,IACT;AAAA,IACA,GAAG,KAAK,QAAQ,KAAK,GAAG,CAAC;AAAA,IACzB,SAAS,KAAK,QAAQ,KAAK,GAAG,CAAC;AAAA,EACjC;AACF;","names":["import_node_fs","import_node_path","import_core","path"]}
1
+ {"version":3,"sources":["../src/install.ts","../src/child.ts"],"sourcesContent":["/**\n * The runtime dependencies an adopted project takes, and how they get there.\n *\n * PRD §3: an adopted project depends on `@penvhq/penv` at the engine's own\n * version — the typed `@env` surface, not a CLI distribution. It also depends on\n * zod, because the `penv.schema.ts` init scaffolds imports it: zod is a *peer* of\n * `@penvhq/penv`, and a peer is a package the project supplies. Under pnpm's\n * strict layout nothing hoists it to the project root, so an install that named\n * only `@penvhq/penv` left the very schema init had just written unable to\n * resolve `zod` — and adoption could never finish.\n *\n * Both are installed with the package manager the project already uses, and only\n * after showing the exact `package.json` and lockfile change: an install is the\n * one step of adoption that reaches outside the repository, so it is the one step\n * that is shown before it happens rather than reported after.\n *\n * The install itself is a seam. It shells out to a package manager, which the\n * tests must never do — and a fake here is not a weaker test, because what init\n * has to get right is the plan, the consent, and the refusal when the install\n * does not happen.\n *\n * A plan is a list of steps, because in a workspace \"the project's dependency\"\n * is plural. pnpm refuses a bare `add` at a workspace root (`-w` is how you say\n * you meant the root), and a workspace package that declares `@penvhq/penv`\n * itself is a second copy of the very version the manifest pins — one repository\n * ran the 0.8 bridge under a 0.11 pin for three releases because nothing looked\n * below the root. So every `package.json` that declares it moves, under one\n * consent, and the commands shown are the ones that run.\n *\n * Two commands write that dependency line: `penv init`, which is the engine's,\n * and `penv upgrade`, which is the launcher's. This module is published at\n * `@penvhq/cli/install` so the launcher reaches it without loading the command\n * surface — one answer to \"which package manager, which diff, which spawn\",\n * rather than a second copy on the other side of the launcher/engine split.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\nimport { PenvError } from \"@penvhq/core\";\nimport { startChild } from \"./child.js\";\n\n/** The package an adopted project depends on. The CLI engine is not one of its dependencies. */\nexport const RUNTIME_PACKAGE = \"@penvhq/penv\";\n\n/** The peer `penv.schema.ts` imports, which the project supplies because a peer is not hoisted. */\nexport const SCHEMA_PACKAGE = \"zod\";\n\n/**\n * The module every committed provider declaration augments, which the project\n * declares for the same reason it declares zod: nothing hoists it.\n *\n * `penv add` commits a `declare module \"@penvhq/core\"` block, and TypeScript\n * resolves that specifier from the project's own files. Under pnpm's strict\n * layout a transitive dependency is not at the project root, so the specifier\n * resolves to nothing — and an augmentation whose module cannot be found is not\n * an error, it silently degrades to an *ambient* declaration. The map\n * `defineConfig` reads stays empty, a misspelled provider field compiles clean,\n * and no diagnostic anywhere says so. A hoisting package manager hides this; the\n * project declaring it is what makes the declaration bind under all of them.\n *\n * `devDependencies`, because that is the whole of what it is: a type-only\n * augmentation target. No application code imports it, and `@penvhq/penv`\n * carries its own copy of the runtime — so the PRD's one runtime dependency is\n * still one.\n */\nexport const TYPES_PACKAGE = \"@penvhq/core\";\n\nexport type PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\n/** The lockfile that names each manager, checked in this order. */\nconst LOCKFILES: readonly (readonly [PackageManager, string])[] = [\n [\"pnpm\", \"pnpm-lock.yaml\"],\n [\"yarn\", \"yarn.lock\"],\n [\"bun\", \"bun.lock\"],\n [\"bun\", \"bun.lockb\"],\n [\"npm\", \"package-lock.json\"],\n];\n\n/** How each manager is told to add a package. */\nconst ADD: Readonly<Record<PackageManager, readonly [string, string]>> = {\n pnpm: [\"pnpm\", \"add\"],\n npm: [\"npm\", \"install\"],\n yarn: [\"yarn\", \"add\"],\n bun: [\"bun\", \"add\"],\n};\n\n/** How each manager is told to write the version down exactly, with no range. */\nconst EXACT: Readonly<Record<PackageManager, string>> = {\n pnpm: \"--save-exact\",\n npm: \"--save-exact\",\n yarn: \"--exact\",\n bun: \"--exact\",\n};\n\n/** How each manager is told to keep the dependency in the block it is already in. */\nconst DEV: Readonly<Record<PackageManager, string>> = {\n pnpm: \"-D\",\n npm: \"--save-dev\",\n yarn: \"--dev\",\n bun: \"--dev\",\n};\n\n/** pnpm refuses an install at a workspace root without this — `ERR_PNPM_ADDING_TO_ROOT`. */\nconst WORKSPACE_ROOT_FLAG = \"-w\";\n\n/** pnpm's workspace file, which is both what declares the members and what makes the root refuse. */\nconst PNPM_WORKSPACE = \"pnpm-workspace.yaml\";\n\n/** One package the adopted project needs, and what its `package.json` says today. */\nexport interface InstallPackage {\n readonly name: string;\n readonly version: string;\n /** What `package.json` already says about it, when it says anything. */\n readonly declared?: string;\n /** True when this project already has it — nothing to install for this one. */\n readonly satisfied: boolean;\n}\n\n/** One `package.json` the install rewrites, and the command that rewrites it. */\nexport interface InstallStep {\n /** The file the diff names — `package.json`, or a workspace package's path to it. */\n readonly manifest: string;\n /** Everything this file needs, in the order the diff shows them. */\n readonly packages: readonly InstallPackage[];\n /** The command, argv-shaped — run from the project root, whichever file it writes. */\n readonly command: readonly string[];\n /** The block this step writes into, which is what the diff shows. */\n readonly dev: boolean;\n /** True when this file already declares every one of them. */\n readonly satisfied: boolean;\n}\n\nexport interface InstallPlan {\n readonly root: string;\n readonly manager: PackageManager;\n /**\n * The root's `package.json` — one step per block it writes, since one command\n * names one — then every workspace package that declares the runtime one.\n */\n readonly steps: readonly InstallStep[];\n /** The lockfile the manager will rewrite, when the project has one. */\n readonly lockfile?: string;\n /** True when every step is already satisfied — nothing to install. */\n readonly satisfied: boolean;\n}\n\n/** Runs an install plan, or throws. Replaced in tests; never spawns there. */\nexport type InstallRuntime = (plan: InstallPlan) => Promise<void>;\n\n/**\n * The engine's own version, read from its manifest rather than restated in the\n * source: `@penvhq/penv` must match the engine exactly, and a constant beside\n * the version a release bumps is a second answer waiting to drift.\n */\nexport function engineVersion(): string {\n const version = ownManifest()?.version;\n if (typeof version === \"string\" && version.length > 0) {\n return version;\n }\n throw new PenvError(\n \"ENGINE_VERSION_UNREADABLE\",\n \"penv could not read its own version, so it cannot say which `@penvhq/penv` this project needs\",\n `Reinstall penv, then run \\`penv init\\` again.`,\n );\n}\n\n/**\n * The zod an adopted project installs: the floor of the peer range the engine\n * and `@penvhq/penv` both declare, which is the version penv is built and tested\n * against.\n *\n * The floor rather than the range, because the diff shown before the install has\n * to be the line that actually lands — `--save-exact` on `^4.4.3` would write\n * whatever the registry resolved that day, which is not something a reader can\n * consent to in advance.\n */\nexport function schemaPackageVersion(): string {\n const peers = ownManifest()?.peerDependencies;\n const declared =\n peers !== null && typeof peers === \"object\" && !Array.isArray(peers)\n ? (peers as Record<string, unknown>)[SCHEMA_PACKAGE]\n : undefined;\n const floor = typeof declared === \"string\" ? declared.replace(/^[\\^~>=\\s]+/, \"\").trim() : \"\";\n if (floor.length > 0) {\n return floor;\n }\n throw new PenvError(\n \"ENGINE_PEER_UNREADABLE\",\n `penv could not read its own \\`${SCHEMA_PACKAGE}\\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,\n `Reinstall penv, then run \\`penv init\\` again.`,\n );\n}\n\n/** The engine's own manifest, or `undefined` when it cannot be read. */\nfunction ownManifest(): Record<string, unknown> | undefined {\n try {\n const parsed: unknown = JSON.parse(\n readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\"),\n );\n return parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : undefined;\n } catch {\n // The callers refuse: a version penv guessed would pin a project's\n // dependency to something nobody chose.\n return undefined;\n }\n}\n\n/** The package manager this project already uses: its lockfile, then what it declares, then npm. */\nexport function detectPackageManager(root: string): PackageManager {\n for (const [manager, lockfile] of LOCKFILES) {\n if (existsSync(join(root, lockfile))) {\n return manager;\n }\n }\n return declaredManager(root) ?? \"npm\";\n}\n\n/** `\"packageManager\": \"pnpm@9.1.0\"` — corepack's field, and a project's own answer. */\nfunction declaredManager(root: string): PackageManager | undefined {\n const declared = manifestOf(root)?.packageManager;\n if (typeof declared !== \"string\") {\n return undefined;\n }\n const name = declared.split(\"@\")[0];\n return name === \"pnpm\" || name === \"npm\" || name === \"yarn\" || name === \"bun\" ? name : undefined;\n}\n\nfunction manifestOf(root: string): Record<string, unknown> | undefined {\n const file = join(root, \"package.json\");\n if (!existsSync(file)) {\n return undefined;\n }\n try {\n const manifest: unknown = JSON.parse(readFileSync(file, \"utf8\"));\n return manifest !== null && typeof manifest === \"object\" && !Array.isArray(manifest)\n ? (manifest as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** What one `package.json` says about a package today, and which block says it. */\ninterface Declaration {\n readonly version: string;\n /** True when it sits in `devDependencies` — where an install has to leave it. */\n readonly dev: boolean;\n}\n\nfunction declaredIn(dir: string, name: string): Declaration | undefined {\n const manifest = manifestOf(dir);\n for (const field of [\"dependencies\", \"devDependencies\"] as const) {\n const block: unknown = manifest?.[field];\n if (block !== null && typeof block === \"object\" && !Array.isArray(block)) {\n const version: unknown = (block as Record<string, unknown>)[name];\n if (typeof version === \"string\") {\n return { version, dev: field === \"devDependencies\" };\n }\n }\n }\n return undefined;\n}\n\n/** True when `root` is the root of a pnpm workspace, which is what `-w` is for. */\nexport function isPnpmWorkspaceRoot(root: string): boolean {\n return existsSync(join(root, PNPM_WORKSPACE)) && existsSync(join(root, \"package.json\"));\n}\n\n/** The `packages:` list from `pnpm-workspace.yaml`, block or flow form. */\nfunction workspaceGlobs(root: string): string[] {\n let text: string;\n try {\n text = readFileSync(join(root, PNPM_WORKSPACE), \"utf8\");\n } catch {\n return [];\n }\n const unquote = (raw: string): string => raw.replace(/^['\"]|['\"]$/g, \"\").trim();\n const globs: string[] = [];\n let inside = false;\n for (const line of text.split(/\\r?\\n/)) {\n const flow = /^packages:\\s*\\[(.*)\\]\\s*$/.exec(line);\n if (flow?.[1] !== undefined) {\n return flow[1]\n .split(\",\")\n .map(unquote)\n .filter((glob) => glob !== \"\");\n }\n if (/^packages:\\s*$/.test(line)) {\n inside = true;\n continue;\n }\n if (!inside) {\n continue;\n }\n const item = /^\\s+-\\s*(.+?)\\s*$/.exec(line);\n if (item?.[1] !== undefined) {\n globs.push(unquote(item[1]));\n continue;\n }\n if (line.trim() !== \"\" && !line.trimStart().startsWith(\"#\")) {\n break;\n }\n }\n return globs;\n}\n\nfunction directoriesIn(dir: string): string[] {\n try {\n return readdirSync(dir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && entry.name !== \"node_modules\")\n .map((entry) => join(dir, entry.name));\n } catch {\n return [];\n }\n}\n\nfunction isDirectory(path: string): boolean {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\n/** `packages/*` and `apps/**` against the filesystem, one path segment at a time. */\nfunction expandGlob(root: string, glob: string): string[] {\n let dirs = [root];\n for (const segment of glob.split(\"/\").filter((part) => part !== \"\" && part !== \".\")) {\n const next: string[] = [];\n for (const dir of dirs) {\n if (segment === \"**\") {\n const stack = [dir];\n while (stack.length > 0) {\n const current = stack.pop() as string;\n next.push(current);\n stack.push(...directoriesIn(current));\n }\n continue;\n }\n if (segment.includes(\"*\")) {\n const pattern = new RegExp(\n `^${segment.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \"[^/]*\")}$`,\n );\n next.push(\n ...directoriesIn(dir).filter((child) => pattern.test(child.slice(dir.length + 1))),\n );\n continue;\n }\n const candidate = join(dir, segment);\n if (isDirectory(candidate)) {\n next.push(candidate);\n }\n }\n dirs = next;\n }\n return dirs;\n}\n\n/**\n * Every workspace package that declares `@penvhq/penv` itself, root excluded.\n *\n * Only the ones that already declare it: penv moves a dependency a package\n * chose, and adding one to a package that never asked for it is a different\n * decision than the one being consented to.\n */\nfunction workspaceMembers(root: string, name: string): string[] {\n if (!isPnpmWorkspaceRoot(root)) {\n return [];\n }\n const globs = workspaceGlobs(root);\n const excluded = globs\n .filter((glob) => glob.startsWith(\"!\"))\n .flatMap((glob) => expandGlob(root, glob.slice(1)));\n const found = new Set<string>();\n for (const glob of globs.filter((entry) => !entry.startsWith(\"!\"))) {\n for (const dir of expandGlob(root, glob)) {\n if (dir !== root && !excluded.includes(dir) && declaredIn(dir, name) !== undefined) {\n found.add(dir);\n }\n }\n }\n return [...found].sort();\n}\n\n/** The path a diff shows for one of them, in the spelling every penv path uses. */\nfunction manifestPathOf(root: string, dir: string): string {\n const within = relative(root, dir)\n .split(sep)\n .filter((part) => part !== \"\");\n return [...within, \"package.json\"].join(\"/\");\n}\n\nfunction addCommand(\n manager: PackageManager,\n options: { readonly filter?: string; readonly workspaceRoot: boolean; readonly dev: boolean },\n specs: readonly string[],\n): string[] {\n const [bin, verb] = ADD[manager];\n return [\n bin,\n ...(options.filter === undefined ? [] : [\"--filter\", options.filter]),\n verb,\n ...(options.workspaceRoot ? [WORKSPACE_ROOT_FLAG] : []),\n EXACT[manager],\n ...(options.dev ? [DEV[manager]] : []),\n ...specs,\n ];\n}\n\nfunction stepFor(\n manager: PackageManager,\n manifest: string,\n packages: readonly InstallPackage[],\n options: { readonly filter?: string; readonly workspaceRoot: boolean; readonly dev: boolean },\n): InstallStep {\n const pending = packages.filter((entry) => !entry.satisfied);\n const specs = (pending.length === 0 ? packages : pending).map(\n (entry) => `${entry.name}@${entry.version}`,\n );\n return {\n manifest,\n packages,\n command: addCommand(manager, options, specs),\n dev: options.dev,\n satisfied: pending.length === 0,\n };\n}\n\nexport function planInstall(root: string, version: string = engineVersion()): InstallPlan {\n const manager = detectPackageManager(root);\n const lockfile = LOCKFILES.find(\n ([name, file]) => name === manager && existsSync(join(root, file)),\n )?.[1];\n const workspaceRoot = manager === \"pnpm\" && isPnpmWorkspaceRoot(root);\n\n const runtime = declaredIn(root, RUNTIME_PACKAGE);\n const zod = declaredIn(root, SCHEMA_PACKAGE);\n const types = declaredIn(root, TYPES_PACKAGE);\n const packages: InstallPackage[] = [\n {\n name: RUNTIME_PACKAGE,\n version,\n ...(runtime === undefined ? {} : { declared: runtime.version }),\n satisfied: runtime?.version === version,\n },\n {\n name: SCHEMA_PACKAGE,\n version: schemaPackageVersion(),\n ...(zod === undefined ? {} : { declared: zod.version }),\n // Any declared zod counts: which zod a project uses is the project's\n // decision, and penv is here to make sure there is one, not to move it.\n satisfied: zod !== undefined,\n },\n ];\n const typesPackage: InstallPackage = {\n name: TYPES_PACKAGE,\n version,\n ...(types === undefined ? {} : { declared: types.version }),\n // Any declared version counts, for zod's reason: the augmentation binds on\n // the module resolving, not on which release of it a project pinned.\n satisfied: types !== undefined,\n };\n\n // The block a package chose is the block penv writes back to — but only when\n // every package this step installs lives there, since one command names one.\n // Which is also why the types package is its own step: it belongs in\n // `devDependencies` whatever the other two chose.\n const pending = packages.filter((entry) => !entry.satisfied);\n const steps: InstallStep[] = [\n stepFor(manager, \"package.json\", packages, {\n workspaceRoot,\n dev:\n runtime?.dev === true &&\n pending.every((entry) => entry.name === RUNTIME_PACKAGE) &&\n pending.length > 0,\n }),\n stepFor(manager, \"package.json\", [typesPackage], { workspaceRoot, dev: true }),\n ];\n for (const dir of workspaceMembers(root, RUNTIME_PACKAGE)) {\n const declared = declaredIn(dir, RUNTIME_PACKAGE) as Declaration;\n steps.push(\n stepFor(\n manager,\n manifestPathOf(root, dir),\n [\n {\n name: RUNTIME_PACKAGE,\n version,\n declared: declared.version,\n satisfied: declared.version === version,\n },\n ],\n {\n filter: `./${relative(root, dir).split(sep).join(\"/\")}`,\n workspaceRoot: false,\n dev: declared.dev,\n },\n ),\n );\n }\n\n return {\n root,\n manager,\n steps,\n ...(lockfile === undefined ? {} : { lockfile }),\n satisfied: steps.every((step) => step.satisfied),\n };\n}\n\nfunction describe(entry: InstallPackage): string {\n return `${entry.name} ${entry.version}`;\n}\n\n/** What this plan actually installs, once per package however many files declare it. */\nexport function installedPackages(plan: InstallPlan): readonly InstallPackage[] {\n const pending = plan.steps.flatMap((step) => step.packages).filter((entry) => !entry.satisfied);\n return [...new Map(pending.map((entry) => [entry.name, entry])).values()];\n}\n\n/** Every package the plan names, once each — the root's blocks are two steps now. */\nfunction plannedPackages(plan: InstallPlan): readonly InstallPackage[] {\n const all = plan.steps.flatMap((step) => step.packages);\n return [...new Map(all.map((entry) => [entry.name, entry])).values()];\n}\n\n/** `a`, `a and b`, `a, b and c`. */\nfunction series(values: readonly string[]): string {\n return values.length < 2\n ? (values[0] ?? \"\")\n : `${values.slice(0, -1).join(\", \")} and ${values.at(-1) as string}`;\n}\n\n/** The lines one `package.json` contributes to the diff. */\nfunction renderStep(step: InstallStep): string[] {\n const pending = step.packages.filter((entry) => !entry.satisfied);\n const added = pending.filter((entry) => entry.declared === undefined);\n const replaced = pending.filter((entry) => entry.declared !== undefined);\n const block = step.dev ? \"devDependencies\" : \"dependencies\";\n return [\n step.manifest,\n ...(added.length === 0\n ? []\n : [\n ` + \"${block}\": {`,\n ...added.map((entry) => ` + \"${entry.name}\": \"${entry.version}\"`),\n \" + }\",\n ]),\n ...replaced.flatMap((entry) => [\n ` - \"${entry.name}\": \"${entry.declared}\"`,\n ` + \"${entry.name}\": \"${entry.version}\"`,\n ]),\n ];\n}\n\n/**\n * The change, as it will appear in the diff — the whole point of showing it is\n * that the reader recognises their own file, so these are the `package.json`\n * lines that land and the lockfile that gets rewritten, not a summary of both.\n *\n * In a workspace that is more than one file, and the commands underneath are the\n * ones that run: a \"Run with:\" line the reader cannot paste is worse than none.\n */\nexport function renderInstallPlan(plan: InstallPlan): string[] {\n if (plan.satisfied) {\n return [\n `package.json already has ${series(plannedPackages(plan).map(describe))} — nothing to install.`,\n ];\n }\n const pending = plan.steps.filter((step) => !step.satisfied);\n const [first, ...rest] = pending.map((step) => step.command.join(\" \"));\n const landing = installedPackages(plan).map((entry) => ` + ${entry.name}@${entry.version}`);\n return [\n ...pending.flatMap(renderStep),\n ...(plan.lockfile === undefined ? [] : [plan.lockfile, ...landing]),\n \"\",\n `Run with: ${first ?? \"\"}`,\n ...rest.map((command) => ` then ${command}`),\n ];\n}\n\n/**\n * The real install: the project's own package manager, started the way any other\n * child is (`.cmd` shims on Windows included), with its output the user's to see.\n *\n * Every step runs from the project root — `-w` and `--filter` are how a workspace\n * says which `package.json` it means, so the directory never changes.\n */\nexport const installWithPackageManager: InstallRuntime = async (plan) => {\n for (const step of plan.steps) {\n if (step.satisfied) {\n continue;\n }\n const child = startChild({\n command: step.command,\n env: process.env as Record<string, string>,\n cwd: plan.root,\n purpose: `install ${step.packages.map(describe).join(\" and \")} in ${step.manifest}`,\n });\n const ended = await child.ended;\n if (ended.exitCode !== 0 || ended.signal !== null) {\n throw installFailed(plan, step);\n }\n }\n};\n\n/**\n * What to do about a package manager that refused.\n *\n * Never the command that just failed: the one remediation guaranteed not to work\n * is the one the reader already ran. The manager said why, on their screen, and\n * nothing was migrated — so the answer is that line and a second `penv init`.\n */\nexport function installFailed(plan: InstallPlan, step: InstallStep): PenvError {\n return new PenvError(\n \"INIT_INSTALL_FAILED\",\n `${step.command.join(\" \")} did not finish, so penv migrated nothing`,\n `Read what ${plan.manager} printed above — it names what it refused. Fix that and run this ` +\n \"command again; your dotenv files are exactly where they were.\",\n );\n}\n","/**\n * Starting someone else's command, opaquely.\n *\n * `penv run -- <command>` starts exactly what follows `--`: the argument\n * boundaries the shell already worked out are handed to the operating system\n * untouched, stdio is the parent's, and the child's exit code and terminating\n * signal come back out. penv never parses the command, never rebuilds a command\n * line from it, never wraps it in a shell — a shell would re-split what the user\n * already split, and `penv run -- node -e \"console.log(1 > 2)\"` would redirect to\n * a file called `2`.\n *\n * Windows is the one place where \"hand it to the operating system\" needs help.\n * `pnpm`, `next` and every other node-installed tool are `.cmd` shims there, and\n * Node refuses to execute one without a shell. So a `.cmd`/`.bat` target — and\n * only that — is started through `cmd.exe /d /s /c` with\n * `windowsVerbatimArguments`, building the one command line cmd will accept and\n * escaping every argument so that cmd hands the child the same bytes penv was\n * given. Everything else spawns directly, on every platform.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { delimiter, isAbsolute, join, win32 } from \"node:path\";\nimport { PenvError } from \"@penvhq/core\";\n\n/** How a child ended. Exactly one of these is meaningful, and both are forwarded. */\nexport interface ChildResult {\n /** The child's own exit code, or 1 when a signal ended it. */\n readonly exitCode: number;\n /** The signal that ended the child, when one did. */\n readonly signal: NodeJS.Signals | null;\n}\n\nexport interface ChildInvocation {\n /** The command exactly as it followed `--`: the executable, then its arguments. */\n readonly command: readonly string[];\n readonly env: Record<string, string>;\n readonly cwd: string;\n /**\n * What penv is starting this on its own behalf to do — `init`'s dependency\n * install. Absent means the command is the user's, from after `--`, and the\n * two failures have opposite remedies: one is about what they typed, the other\n * about a program penv chose to run.\n */\n readonly purpose?: string;\n}\n\n/** A started child: how it ends, and the one thing a wrapper may do to it. */\nexport interface ChildHandle {\n /** Resolves when the child has ended, however it ended. */\n readonly ended: Promise<ChildResult>;\n /** Asks the child to stop — what `--watch` does before it starts the next one. */\n kill(signal?: NodeJS.Signals): void;\n}\n\n/** The seam `run` starts a child through — replaced in tests that assert what it was given. */\nexport type StartChild = (invocation: ChildInvocation) => ChildHandle;\n\n/** The signals a wrapper must pass through rather than absorb. */\nconst FORWARDED: readonly NodeJS.Signals[] = [\"SIGINT\", \"SIGTERM\", \"SIGHUP\", \"SIGBREAK\"];\n\nexport const startChild: StartChild = (invocation) => {\n const [executable, ...args] = invocation.command;\n if (executable === undefined) {\n throw noCommand();\n }\n\n const target = resolveTarget(executable, args, invocation.env);\n const child = spawn(target.file, target.args, {\n cwd: invocation.cwd,\n env: invocation.env,\n stdio: \"inherit\",\n ...(target.verbatim ? { windowsVerbatimArguments: true } : {}),\n });\n\n // Forwarded rather than handled: penv is a wrapper, and a Ctrl-C belongs to\n // the program the user is looking at. The child decides what to do with it,\n // and its answer comes back as the signal below.\n const forward = new Map<NodeJS.Signals, () => void>();\n for (const signal of FORWARDED) {\n const handler = (): void => {\n child.kill(signal);\n };\n forward.set(signal, handler);\n process.on(signal, handler);\n }\n const release = (): void => {\n for (const [signal, handler] of forward) {\n process.off(signal, handler);\n }\n };\n\n const ended = new Promise<ChildResult>((resolve, reject) => {\n child.on(\"error\", (cause) => {\n release();\n reject(cannotStart(executable, cause, invocation.purpose));\n });\n child.on(\"exit\", (code, signal) => {\n release();\n resolve({ exitCode: code ?? 1, signal });\n });\n });\n\n return {\n ended,\n kill(signal) {\n child.kill(signal);\n },\n };\n};\n\nexport function noCommand(): PenvError {\n return new PenvError(\n \"RUN_NO_COMMAND\",\n \"`penv run` was given no command to start\",\n \"Put the command after `--`, e.g. `penv run -- pnpm dev`.\",\n );\n}\n\nfunction cannotStart(executable: string, cause: unknown, purpose: string | undefined): PenvError {\n const detail = cause instanceof Error ? cause.message : String(cause);\n if (purpose !== undefined) {\n return new PenvError(\n \"PENV_COMMAND_NOT_STARTED\",\n `penv could not start \\`${executable}\\` to ${purpose}: ${detail}`,\n `Check that \\`${executable}\\` runs on its own — penv starts it the way your shell does, so it has to be on PATH. Nothing was changed.`,\n );\n }\n return new PenvError(\n \"RUN_COMMAND_NOT_STARTED\",\n `\\`${executable}\\` could not be started: ${detail}`,\n `Check the command after \\`--\\` runs on its own — \\`${executable}\\` has to be on PATH, exactly as it is spelled here.`,\n );\n}\n\ninterface SpawnTarget {\n readonly file: string;\n readonly args: readonly string[];\n /** True when the args are one pre-built command line rather than a list. */\n readonly verbatim: boolean;\n}\n\nfunction resolveTarget(\n executable: string,\n args: readonly string[],\n env: Readonly<Record<string, string | undefined>>,\n): SpawnTarget {\n if (process.platform !== \"win32\") {\n return { file: executable, args, verbatim: false };\n }\n const resolved = findExecutable(executable, env);\n if (resolved === undefined || !/\\.(cmd|bat)$/i.test(resolved)) {\n return { file: resolved ?? executable, args, verbatim: false };\n }\n return {\n file: env.ComSpec ?? \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", `\"${cmdCommandLine(resolved, args)}\"`],\n verbatim: true,\n };\n}\n\n/** A package-manager shim, which re-invokes cmd on its own way through. */\nconst SHIM = /(?:^|\\\\)node_modules\\\\\\.bin\\\\[^\\\\]+\\.cmd$/i;\n\n/**\n * The one command line cmd.exe is handed, escaped so the child receives the\n * bytes penv was given.\n *\n * The path is normalized first and *then* judged: `./node_modules/.bin/next.cmd`\n * and `.\\node_modules\\.bin\\next.cmd` are the same shim, and deciding on the\n * un-normalized spelling would escape a forward-slash invocation once while cmd\n * expands it twice — so an argument holding `&` would run as a command inside\n * the shim's second round. Windows' own separator, whatever this process runs\n * on, because this line is only ever read by cmd.exe.\n */\nexport function cmdCommandLine(resolved: string, args: readonly string[]): string {\n const command = win32.normalize(resolved);\n const shim = SHIM.test(command);\n return [escapeCommand(command), ...args.map((argument) => escapeArgument(argument, shim))].join(\n \" \",\n );\n}\n\n/**\n * The extensions a name is tried with, in the order the platform's own launcher\n * tries them.\n *\n * On Windows PATHEXT leads and the bare name comes last, because the bare name\n * is almost never what Windows would run: `pnpm`, `npx` and every\n * `node_modules/.bin` tool ship an extensionless POSIX shell script *beside*\n * their `.CMD` shim, in the same directory. Trying the empty extension first\n * matched that script, which is not executable by CreateProcess and is not a\n * `.cmd`, so the wrapper below was skipped and the spawn failed with ENOENT.\n * Everywhere else there are no extensions at all.\n */\nfunction extensions(\n env: Readonly<Record<string, string | undefined>>,\n platform: NodeJS.Platform,\n): string[] {\n if (platform !== \"win32\") {\n return [\"\"];\n }\n const declared = env.PATHEXT ?? \".COM;.EXE;.BAT;.CMD\";\n return [...declared.split(\";\").filter((extension) => extension.length > 0), \"\"];\n}\n\n/**\n * What the shell would have run, found the way the shell finds it: the name as\n * given if it carries a path, else each PATH directory, each with each\n * executable extension.\n *\n * `platform` is a parameter so the ordering above is testable on either kind of\n * machine — it is the whole behavior, and it differs by platform.\n */\nexport function findExecutable(\n executable: string,\n env: Readonly<Record<string, string | undefined>>,\n platform: NodeJS.Platform = process.platform,\n): string | undefined {\n const candidates = extensions(env, platform);\n const isFile = (path: string): boolean => existsSync(path) && statSync(path).isFile();\n\n if (executable.includes(\"/\") || executable.includes(\"\\\\\") || isAbsolute(executable)) {\n return candidates.map((extension) => executable + extension).find(isFile);\n }\n const path = env.PATH ?? env.Path ?? \"\";\n for (const directory of path.split(delimiter).filter((entry) => entry.length > 0)) {\n const hit = candidates.map((extension) => join(directory, executable + extension)).find(isFile);\n if (hit !== undefined) {\n return hit;\n }\n }\n return undefined;\n}\n\n/** The characters cmd.exe expands before the program ever sees them. */\nconst CMD_METACHARACTERS = /([()\\][%!^\"`<>&|;, *?])/g;\n\n/** The command's own path: cmd's metacharacters escaped, and no quotes to confuse it. */\nfunction escapeCommand(command: string): string {\n return command.replace(CMD_METACHARACTERS, \"^$1\");\n}\n\n/**\n * One argument, quoted so the child's runtime splits it exactly where penv was\n * given it, then escaped so cmd.exe passes those quotes through instead of\n * acting on them.\n */\nfunction escapeArgument(argument: string, doubleEscape: boolean): string {\n const quoted = `\"${argument.replace(/(\\\\*)\"/g, '$1$1\\\\\"').replace(/(\\\\*)$/, \"$1$1\")}\"`;\n const escaped = quoted.replace(CMD_METACHARACTERS, \"^$1\");\n return doubleEscape ? escaped.replace(CMD_METACHARACTERS, \"^$1\") : escaped;\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","import_core","path"]}