@penvhq/launcher 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -24,10 +24,10 @@ __export(src_exports, {
24
24
  AddLocalFlagError: () => AddLocalFlagError,
25
25
  AddLocalInCiError: () => AddLocalInCiError,
26
26
  AddNoDownloadError: () => AddNoDownloadError,
27
- AddNotInteractiveError: () => AddNotInteractiveError,
28
27
  AddPackageNameError: () => AddPackageNameError,
29
28
  AddRegistryError: () => AddRegistryError,
30
29
  AddSubjectError: () => AddSubjectError,
30
+ AddTrustUnattendedError: () => AddTrustUnattendedError,
31
31
  ArchiveError: () => ArchiveError,
32
32
  BUNDLED_ENGINE_PIN: () => BUNDLED_ENGINE_PIN,
33
33
  DEFAULT_REGISTRY: () => DEFAULT_REGISTRY,
@@ -384,7 +384,7 @@ var AddFlagError = class extends import_core.PenvError {
384
384
  super(
385
385
  "PENV_ADD_FLAG",
386
386
  `\`penv add\` does not understand \`${flag}\``,
387
- `Run \`penv add <package>\` with \`${TRUST_YOUNG_FLAG}\`, \`--registry <url>\`, or \`${LOCAL_FLAG}\` \u2014 those are the three it takes.`
387
+ `Run \`penv add <package>\` with \`${TRUST_YOUNG_FLAG}\`, \`--registry <url>\`, \`${LOCAL_FLAG}\`, or \`${YES_FLAG}\` \u2014 those are the four it takes.`
388
388
  );
389
389
  }
390
390
  };
@@ -530,13 +530,13 @@ var AddNoDownloadError = class extends import_core.PenvError {
530
530
  );
531
531
  }
532
532
  };
533
- var AddNotInteractiveError = class extends import_core.PenvError {
534
- name = "AddNotInteractiveError";
535
- constructor(name) {
533
+ var AddTrustUnattendedError = class extends import_core.PenvError {
534
+ name = "AddTrustUnattendedError";
535
+ constructor(name, ceremony) {
536
536
  super(
537
- "PENV_ADD_NOT_INTERACTIVE",
538
- `Adding ${name} rewrites ${import_core.MANIFEST_PATH}, and this run has nobody to decide that`,
539
- `Run \`${addCommand(name)}\` from a terminal and commit what it writes. In CI, run \`${INSTALL_COMMAND}\` \u2014 it installs the versions the committed manifest already pins.`
537
+ "PENV_ADD_TRUST_UNATTENDED",
538
+ `Adding ${name} records ${ceremony}, and this run has nobody to write it`,
539
+ `Run \`${addCommand(name)}\` from a terminal, without \`${YES_FLAG}\` \u2014 the trust block is a line only a person can write. In CI, run \`${INSTALL_COMMAND}\`: it installs the versions the committed manifest already pins.`
540
540
  );
541
541
  }
542
542
  };
@@ -662,11 +662,11 @@ var UpgradeDeclinedError = class extends import_core.PenvError {
662
662
  };
663
663
  var UpgradeInstallFailedError = class extends import_core.PenvError {
664
664
  name = "UpgradeInstallFailedError";
665
- constructor(command) {
665
+ constructor(manager, version) {
666
666
  super(
667
667
  "PENV_UPGRADE_INSTALL_FAILED",
668
- `${command} did not finish, so ${import_core.MANIFEST_PATH} still pins the engine it pinned before`,
669
- `Run \`${command}\` yourself, then run \`${UPGRADE_COMMAND}\` again \u2014 the pin and the dependency move together or not at all.`
668
+ `${manager} did not finish moving this project to ${import_core.ENGINE_PACKAGE} ${version}, so ${import_core.MANIFEST_PATH} still pins the engine it pinned before`,
669
+ `Read what ${manager} printed above \u2014 it names what it refused. Fix that and run \`${UPGRADE_COMMAND} ${version}\` again; the pin and the dependency move together or not at all.`
670
670
  );
671
671
  }
672
672
  };
@@ -1083,12 +1083,17 @@ function parseRequest(argv) {
1083
1083
  let registry;
1084
1084
  let trustYoung = false;
1085
1085
  let local = false;
1086
+ let yes = false;
1086
1087
  for (let index = 0; index < argv.length; index += 1) {
1087
1088
  const token = argv[index] ?? "";
1088
1089
  if (token === LOCAL_FLAG) {
1089
1090
  local = true;
1090
1091
  continue;
1091
1092
  }
1093
+ if (token === YES_FLAG) {
1094
+ yes = true;
1095
+ continue;
1096
+ }
1092
1097
  if (token === TRUST_YOUNG_FLAG) {
1093
1098
  trustYoung = true;
1094
1099
  continue;
@@ -1128,7 +1133,10 @@ function parseRequest(argv) {
1128
1133
  throw new AddLocalFlagError(`\`${TRUST_YOUNG_FLAG}\``);
1129
1134
  }
1130
1135
  }
1131
- return { name, version, registry, trustYoung, local };
1136
+ return { name, version, registry, trustYoung, local, yes };
1137
+ }
1138
+ function ceremonyFor(tier) {
1139
+ return tier === "official" ? void 0 : "who publishes it and why you trust it";
1132
1140
  }
1133
1141
  function tierOf(request) {
1134
1142
  if (request.name.startsWith(import_core5.OFFICIAL_SCOPE)) {
@@ -1207,7 +1215,29 @@ function recordExtension(manifestFile, name, entry) {
1207
1215
  };
1208
1216
  return (0, import_core5.serializeManifest)(next);
1209
1217
  }
1210
- async function offerConfigEdit(options, name) {
1218
+ var NONE = /* @__PURE__ */ new Set(["", "none", "no", "n"]);
1219
+ var ALL = /* @__PURE__ */ new Set(["all", "a", "yes", "y"]);
1220
+ function chooseEnvironments(answer, offered) {
1221
+ const normalized = answer.trim().toLowerCase();
1222
+ if (NONE.has(normalized)) {
1223
+ return { chosen: [], unknown: [] };
1224
+ }
1225
+ if (ALL.has(normalized)) {
1226
+ return { chosen: offered, unknown: [] };
1227
+ }
1228
+ const chosen = [];
1229
+ const unknown = [];
1230
+ for (const token of normalized.split(/[\s,]+/).filter((part) => part !== "")) {
1231
+ const match = offered.find((environment) => environment.toLowerCase() === token);
1232
+ if (match === void 0) {
1233
+ unknown.push(token);
1234
+ } else {
1235
+ chosen.push(match);
1236
+ }
1237
+ }
1238
+ return { chosen, unknown };
1239
+ }
1240
+ async function offerConfigEdit(options, name, ask) {
1211
1241
  const { io, root } = options;
1212
1242
  const configFile = (0, import_core5.findConfigFile)(root);
1213
1243
  const line = `Add \`type: ${JSON.stringify(name)}\` to an environment in penv.config.ts.`;
@@ -1222,31 +1252,35 @@ async function offerConfigEdit(options, name) {
1222
1252
  io.out(line);
1223
1253
  return;
1224
1254
  }
1225
- if (entries.every((entry) => entry.type === name)) {
1255
+ const offered = entries.filter((entry) => entry.type !== name).map((entry) => entry.environment);
1256
+ if (offered.length === 0) {
1226
1257
  return;
1227
1258
  }
1228
- if (!io.interactive) {
1259
+ if (!ask) {
1229
1260
  io.out(line);
1230
1261
  return;
1231
1262
  }
1232
- for (const entry of entries) {
1233
- if (entry.type === name) {
1234
- continue;
1235
- }
1236
- if (!await io.confirm(`Point \`${entry.environment}\` at ${name} in ${shown}?`)) {
1237
- continue;
1238
- }
1239
- const next = setProviderType(current, entry.environment, name);
1263
+ const { chosen, unknown } = chooseEnvironments(
1264
+ await io.ask(`Point which of ${offered.join(", ")} at ${name} in ${shown}? all / none / names`),
1265
+ offered
1266
+ );
1267
+ if (unknown.length > 0) {
1268
+ io.out(`${shown} declares no ${unknown.join(", ")}, so nothing was repointed.`);
1269
+ io.out(line);
1270
+ return;
1271
+ }
1272
+ for (const environment of chosen) {
1273
+ const next = setProviderType(current, environment, name);
1240
1274
  if (next === void 0) {
1241
- io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${entry.environment}\` in ${shown}.`);
1275
+ io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${environment}\` in ${shown}.`);
1242
1276
  continue;
1243
1277
  }
1244
1278
  current = next;
1245
1279
  (0, import_node_fs4.writeFileSync)(configFile, current);
1246
- io.out(`\u2713 ${shown} points \`${entry.environment}\` at ${name}`);
1280
+ io.out(`\u2713 ${shown} points \`${environment}\` at ${name}`);
1247
1281
  }
1248
1282
  }
1249
- async function offerOnboarding(io, name, onboard) {
1283
+ async function offerOnboarding(io, name, onboard, ask) {
1250
1284
  if (onboard === void 0) {
1251
1285
  return void 0;
1252
1286
  }
@@ -1255,7 +1289,7 @@ async function offerOnboarding(io, name, onboard) {
1255
1289
  return void 0;
1256
1290
  }
1257
1291
  const command = `penv ${args.join(" ")}`;
1258
- if (io.interactive && await io.confirm(`Run \`${command}\` now?`)) {
1292
+ if (ask && await io.confirm(`Run \`${command}\` now?`)) {
1259
1293
  return args;
1260
1294
  }
1261
1295
  io.out(`Run \`${command}\` to finish setting ${name} up.`);
@@ -1336,8 +1370,9 @@ async function addLocal(options, request) {
1336
1370
  io.out(`\u2713 ${request.name} resolves from this project and imports \u2014 nothing is pinned`);
1337
1371
  io.out(`\u2713 ${import_core5.LOCAL_EXTENSIONS_PATH} records it`);
1338
1372
  io.out(`\u2713 ${declaration} declares its config type`);
1339
- await offerConfigEdit(options, request.name);
1340
- return { onboard: await offerOnboarding(io, request.name, installed.onboard) };
1373
+ const ask = io.interactive && !request.yes;
1374
+ await offerConfigEdit(options, request.name, ask);
1375
+ return { onboard: await offerOnboarding(io, request.name, installed.onboard, ask) };
1341
1376
  }
1342
1377
  async function add(options) {
1343
1378
  const { io, fetcher, home, root, manifestFile } = options;
@@ -1347,11 +1382,13 @@ async function add(options) {
1347
1382
  }
1348
1383
  const tier = tierOf(request);
1349
1384
  const now = (options.now ?? (() => /* @__PURE__ */ new Date()))();
1385
+ const ask = io.interactive && !request.yes;
1350
1386
  if (options.noDownload === true) {
1351
1387
  throw new AddNoDownloadError(request.name);
1352
1388
  }
1353
- if (options.ci === true || !io.interactive) {
1354
- throw new AddNotInteractiveError(request.name);
1389
+ const ceremony = ceremonyFor(tier);
1390
+ if (ceremony !== void 0 && (options.ci === true || !ask)) {
1391
+ throw new AddTrustUnattendedError(request.name, ceremony);
1355
1392
  }
1356
1393
  const release = await fetchRelease({
1357
1394
  name: request.name,
@@ -1393,8 +1430,8 @@ async function add(options) {
1393
1430
  io.out(`\u2713 ${release.name} ${release.version} installed \u2014 ${provenance}`);
1394
1431
  io.out(`\u2713 ${import_core5.MANIFEST_PATH} pins it`);
1395
1432
  io.out(`\u2713 ${declaration} declares its config type`);
1396
- await offerConfigEdit(options, release.name);
1397
- return { onboard: await offerOnboarding(io, release.name, installed.onboard) };
1433
+ await offerConfigEdit(options, release.name, ask);
1434
+ return { onboard: await offerOnboarding(io, release.name, installed.onboard, ask) };
1398
1435
  }
1399
1436
 
1400
1437
  // src/delegate.ts
@@ -1541,10 +1578,15 @@ function printAddHelp(io) {
1541
1578
  io.out(
1542
1579
  ` ${TRUST_YOUNG_FLAG} Add a release published less than ${MIN_PACKAGE_AGE_DAYS} days ago`
1543
1580
  );
1581
+ io.out(` ${YES_FLAG} Ask nothing \u2014 the config and onboarding offers print instead`);
1544
1582
  io.out("");
1545
1583
  io.out(`Pins the release in ${import_core7.MANIFEST_PATH} and writes its type declaration to`);
1546
1584
  io.out(`${import_core7.EXTENSIONS_PATH}/. Both are committed, so \`${INSTALL_COMMAND}\` gives every machine`);
1547
1585
  io.out(`the bytes you reviewed. \`${LOCAL_FLAG}\` pins nothing and touches no manifest.`);
1586
+ io.out("");
1587
+ io.out(`An \`${import_core7.OFFICIAL_SCOPE}*\` package asks nothing, so it adds unattended. Anything else`);
1588
+ io.out("records who publishes it and why you trust it, which needs a person at a terminal \u2014");
1589
+ io.out(`\`${YES_FLAG}\` cannot write that line for you.`);
1548
1590
  }
1549
1591
  function printUpgradeHelp(io) {
1550
1592
  io.out("penv upgrade [version]");
@@ -1564,8 +1606,8 @@ var DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
1564
1606
  var DEV_PIN_VERSION = "0.0.0-dev";
1565
1607
  var BUNDLED_ENGINE_PIN = {
1566
1608
  package: import_core8.ENGINE_PACKAGE,
1567
- version: "0.11.0",
1568
- integrity: "sha512-Y+WKyuPsX4U8/1j0e+VMUhj0LaGrp67YEoDAWyyinrgA+c8IM4H1blqje/YK4LsQwzQDQQDF9bhWIhfmDAWoxA=="
1609
+ version: "0.12.0",
1610
+ integrity: "sha512-9HUylEIbFq0BGTOW54Vxhkzv8AvuB7H3bImTFP5J+dfglkR7udP2KUsr8b0I2mf45YMTQO5xsYpI5+nhLERGtA=="
1569
1611
  };
1570
1612
  function assertReleasePin(pin) {
1571
1613
  if (pin.integrity === DEV_PIN_INTEGRITY || pin.version === DEV_PIN_VERSION) {
@@ -1693,9 +1735,11 @@ async function upgrade(options) {
1693
1735
  try {
1694
1736
  await (options.install ?? import_install2.installWithPackageManager)(plan);
1695
1737
  } catch {
1696
- throw new UpgradeInstallFailedError(plan.command.join(" "));
1738
+ throw new UpgradeInstallFailedError(plan.manager, release.version);
1739
+ }
1740
+ for (const step of plan.steps.filter((entry) => !entry.satisfied)) {
1741
+ io.out(`\u2713 ${step.manifest} depends on ${import_install2.RUNTIME_PACKAGE} ${release.version}`);
1697
1742
  }
1698
- io.out(`\u2713 package.json depends on ${import_install2.RUNTIME_PACKAGE} ${release.version}`);
1699
1743
  }
1700
1744
  (0, import_node_fs7.writeFileSync)(manifestFile, manifestText);
1701
1745
  io.out(`\u2713 ${import_core10.MANIFEST_PATH} pins ${import_core10.ENGINE_PACKAGE} ${release.version}`);
@@ -1924,12 +1968,14 @@ async function install(options, pins, home, broken) {
1924
1968
  if (state === "corrupt") {
1925
1969
  throw new PackageCorruptError(pin.name, pin.version, dir);
1926
1970
  }
1927
- if (state === "installed") {
1928
- options.io.out(`\u2713 ${pin.name} ${pin.version} already installed`);
1929
- continue;
1971
+ if (state === "absent") {
1972
+ await installPin({ home, kind, pin, fetcher: options.fetcher });
1973
+ }
1974
+ if (kind === "extensions") {
1975
+ await assertLoadable(pin.name, (0, import_core11.packageEntry)(dir), false);
1930
1976
  }
1931
- await installPin({ home, kind, pin, fetcher: options.fetcher });
1932
- options.io.out(`\u2713 ${pin.name} ${pin.version} installed`);
1977
+ const verb = state === "absent" ? "installed" : "already installed";
1978
+ options.io.out(`\u2713 ${pin.name} ${pin.version} ${verb}`);
1933
1979
  }
1934
1980
  if (broken.length > 0) {
1935
1981
  report(new ManifestEntriesUnreadableError(broken), options.io);
@@ -1978,10 +2024,10 @@ async function delegate(options, engine, forwarded, home, cwd) {
1978
2024
  AddLocalFlagError,
1979
2025
  AddLocalInCiError,
1980
2026
  AddNoDownloadError,
1981
- AddNotInteractiveError,
1982
2027
  AddPackageNameError,
1983
2028
  AddRegistryError,
1984
2029
  AddSubjectError,
2030
+ AddTrustUnattendedError,
1985
2031
  ArchiveError,
1986
2032
  BUNDLED_ENGINE_PIN,
1987
2033
  DEFAULT_REGISTRY,