@penvhq/launcher 0.9.5 → 0.10.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
@@ -41,6 +41,8 @@ __export(src_exports, {
41
41
  EngineEntryError: () => EngineEntryError,
42
42
  EnginePinMismatchError: () => EnginePinMismatchError,
43
43
  EnginePinUnreleasedError: () => EnginePinUnreleasedError,
44
+ ExtensionNotImportableError: () => ExtensionNotImportableError,
45
+ ExtensionUnloadableError: () => ExtensionUnloadableError,
44
46
  HOME_META_FILE: () => HOME_META_FILE,
45
47
  INSTALL_COMMAND: () => INSTALL_COMMAND,
46
48
  INTEGRITY_FILE: () => INTEGRITY_FILE,
@@ -51,7 +53,6 @@ __export(src_exports, {
51
53
  NPM_UPDATE_COMMAND: () => NPM_UPDATE_COMMAND,
52
54
  NoProjectError: () => NoProjectError,
53
55
  OfficialRegistryError: () => OfficialRegistryError,
54
- PENV_HOME_VAR: () => PENV_HOME_VAR,
55
56
  PackageCorruptError: () => PackageCorruptError,
56
57
  PackageMissingError: () => PackageMissingError,
57
58
  PackageTooYoungError: () => PackageTooYoungError,
@@ -77,9 +78,7 @@ __export(src_exports, {
77
78
  integrityOf: () => integrityOf,
78
79
  launcherUpdateCommand: () => launcherUpdateCommand,
79
80
  nodeSpawner: () => nodeSpawner,
80
- packageDir: () => packageDir,
81
81
  packumentUrl: () => packumentUrl,
82
- penvHome: () => penvHome,
83
82
  readExtensionPackage: () => readExtensionPackage,
84
83
  readProviderEntries: () => readProviderEntries,
85
84
  readTarball: () => readTarball,
@@ -96,6 +95,7 @@ module.exports = __toCommonJS(src_exports);
96
95
  var import_node_fs4 = require("fs");
97
96
  var import_node_module = require("module");
98
97
  var import_node_path4 = require("path");
98
+ var import_node_url = require("url");
99
99
  var import_core5 = require("@penvhq/core");
100
100
 
101
101
  // src/config-edit.ts
@@ -274,6 +274,9 @@ function addCommand(name, version, extra) {
274
274
  const spec = version === void 0 ? name : `${name}@${version}`;
275
275
  return `penv add ${spec}${extra === void 0 ? "" : ` ${extra}`}`;
276
276
  }
277
+ function addCommandFor(name, local) {
278
+ return local ? `penv add ${LOCAL_FLAG} ${name}` : addCommand(name);
279
+ }
277
280
  var NoProjectError = class extends import_core.PenvError {
278
281
  name = "NoProjectError";
279
282
  constructor(cwd) {
@@ -385,6 +388,26 @@ var AddLocalFlagError = class extends import_core.PenvError {
385
388
  );
386
389
  }
387
390
  };
391
+ var ExtensionNotImportableError = class extends import_core.PenvError {
392
+ name = "ExtensionNotImportableError";
393
+ constructor(name, entry, local) {
394
+ super(
395
+ "PENV_EXTENSION_NOT_IMPORTABLE",
396
+ entry === void 0 ? `${name} declares no entry point penv can import` : `${name} resolves to ${entry}, which penv cannot import`,
397
+ `penv imports a provider with no transform, so its entry has to be built JavaScript. Point the package's \`exports\` at its build output \u2014 \`dist/index.js\` \u2014 then run \`${addCommandFor(name, local)}\` again.`
398
+ );
399
+ }
400
+ };
401
+ var ExtensionUnloadableError = class extends import_core.PenvError {
402
+ name = "ExtensionUnloadableError";
403
+ constructor(name, entry, detail, local) {
404
+ super(
405
+ "PENV_EXTENSION_UNLOADABLE",
406
+ `${name} threw while penv imported ${entry}: ${detail}`,
407
+ `Build the package and install its dependencies, then run \`${addCommandFor(name, local)}\` again. penv adds a provider it has loaded once, or none.`
408
+ );
409
+ }
410
+ };
388
411
  var LocalExtensionUnresolvedError = class extends import_core.PenvError {
389
412
  name = "LocalExtensionUnresolvedError";
390
413
  constructor(name, root) {
@@ -689,35 +712,14 @@ function writeDeclaration(options) {
689
712
  // src/store.ts
690
713
  var import_node_fs3 = require("fs");
691
714
  var import_node_path3 = require("path");
715
+ var import_core3 = require("@penvhq/core");
692
716
 
693
717
  // src/home.ts
694
718
  var import_node_fs2 = require("fs");
695
- var import_node_os = require("os");
696
719
  var import_node_path2 = require("path");
697
- var import_core3 = require("@penvhq/core");
698
- var PENV_HOME_VAR = "PENV_HOME";
699
720
  var HOME_META_FILE = "meta.json";
700
721
  var INTEGRITY_FILE = ".penv-integrity";
701
722
  var NPM_UPDATE_COMMAND = "npm install -g @penvhq/launcher";
702
- function penvHome(env) {
703
- const declared = env[PENV_HOME_VAR];
704
- if (declared !== void 0 && declared.trim() !== "") {
705
- return (0, import_node_path2.resolve)(declared);
706
- }
707
- return (0, import_node_path2.join)((0, import_node_os.homedir)(), ".penv");
708
- }
709
- function packageDir(home, kind, name, version) {
710
- const bucket = (0, import_node_path2.resolve)(home, kind);
711
- const dir = (0, import_node_path2.resolve)(bucket, ...name.split("/"), version);
712
- if (!dir.startsWith(bucket + import_node_path2.sep)) {
713
- throw new import_core3.PenvError(
714
- "PENV_HOME_ESCAPE",
715
- `\`${name}\` at \`${version}\` resolves to ${dir}, which is outside ${bucket}`,
716
- "Name the package exactly as npm does, e.g. `@penvhq/provider-vault`."
717
- );
718
- }
719
- return dir;
720
- }
721
723
  function launcherUpdateCommand(home) {
722
724
  let meta;
723
725
  try {
@@ -819,7 +821,7 @@ function tarballUrl(pin) {
819
821
  return `${registry}/${pin.name}/-/${basename}-${pin.version}.tgz`;
820
822
  }
821
823
  function inspectInstall(home, kind, pin) {
822
- const dir = packageDir(home, kind, pin.name, pin.version);
824
+ const dir = (0, import_core3.packageDir)(home, kind, pin.name, pin.version);
823
825
  if (!(0, import_node_fs3.existsSync)(dir)) {
824
826
  return { dir, state: "absent" };
825
827
  }
@@ -833,7 +835,7 @@ function inspectInstall(home, kind, pin) {
833
835
  }
834
836
  async function installPin(options) {
835
837
  const { home, kind, pin, fetcher } = options;
836
- const dir = packageDir(home, kind, pin.name, pin.version);
838
+ const dir = (0, import_core3.packageDir)(home, kind, pin.name, pin.version);
837
839
  const url = tarballUrl(pin);
838
840
  let bytes;
839
841
  try {
@@ -1206,6 +1208,26 @@ function resolvePackageDir(name, root) {
1206
1208
  return void 0;
1207
1209
  }
1208
1210
  }
1211
+ function resolveProjectEntry(name, root) {
1212
+ let file;
1213
+ try {
1214
+ file = (0, import_node_module.createRequire)((0, import_node_path4.resolve)(root, "noop.js")).resolve(name);
1215
+ } catch {
1216
+ return void 0;
1217
+ }
1218
+ return { file, importable: (0, import_core5.isImportableEntry)(file) };
1219
+ }
1220
+ async function assertLoadable(name, entry, local) {
1221
+ if (entry === void 0 || !entry.importable) {
1222
+ throw new ExtensionNotImportableError(name, entry?.file, local);
1223
+ }
1224
+ try {
1225
+ await import((0, import_node_url.pathToFileURL)(entry.file).href);
1226
+ } catch (cause) {
1227
+ const detail = cause instanceof Error ? cause.message : String(cause);
1228
+ throw new ExtensionUnloadableError(name, entry.file, detail, local);
1229
+ }
1230
+ }
1209
1231
  function recordLocalExtension(root, name) {
1210
1232
  const file = (0, import_core5.localExtensionsFile)(root);
1211
1233
  let current;
@@ -1226,6 +1248,7 @@ async function addLocal(options, request) {
1226
1248
  if (dir === void 0) {
1227
1249
  throw new LocalExtensionUnresolvedError(request.name, root);
1228
1250
  }
1251
+ await assertLoadable(request.name, resolveProjectEntry(request.name, root), true);
1229
1252
  const installed = readExtensionPackage(dir);
1230
1253
  const declaration = writeDeclaration({
1231
1254
  root,
@@ -1237,7 +1260,7 @@ async function addLocal(options, request) {
1237
1260
  types: installed.types
1238
1261
  });
1239
1262
  recordLocalExtension(root, request.name);
1240
- io.out(`\u2713 ${request.name} resolves from this project \u2014 nothing is pinned`);
1263
+ io.out(`\u2713 ${request.name} resolves from this project and imports \u2014 nothing is pinned`);
1241
1264
  io.out(`\u2713 ${import_core5.LOCAL_EXTENSIONS_PATH} records it`);
1242
1265
  io.out(`\u2713 ${declaration} declares its config type`);
1243
1266
  await offerConfigEdit(options, request.name);
@@ -1282,6 +1305,7 @@ async function add(options) {
1282
1305
  },
1283
1306
  fetcher
1284
1307
  });
1308
+ await assertLoadable(release.name, (0, import_core5.packageEntry)(dir), false);
1285
1309
  const installed = readExtensionPackage(dir);
1286
1310
  const declaration = writeDeclaration({
1287
1311
  root,
@@ -1302,10 +1326,10 @@ async function add(options) {
1302
1326
 
1303
1327
  // src/delegate.ts
1304
1328
  var import_node_child_process = require("child_process");
1305
- var import_node_os2 = require("os");
1329
+ var import_node_os = require("os");
1306
1330
  var FORWARDED = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
1307
1331
  function signalNumber(signal) {
1308
- const signals = import_node_os2.constants.signals;
1332
+ const signals = import_node_os.constants.signals;
1309
1333
  return signals[signal] ?? 0;
1310
1334
  }
1311
1335
  function nodeSpawner() {
@@ -1414,16 +1438,48 @@ function httpFetcher() {
1414
1438
  // src/launcher.ts
1415
1439
  var import_node_fs7 = require("fs");
1416
1440
  var import_node_path7 = require("path");
1417
- var import_core9 = require("@penvhq/core");
1441
+ var import_core10 = require("@penvhq/core");
1418
1442
 
1419
- // src/pins.ts
1443
+ // src/help.ts
1420
1444
  var import_core7 = require("@penvhq/core");
1445
+ function printLauncherCommands(io) {
1446
+ io.out("");
1447
+ io.out("LAUNCHER COMMANDS (penv itself, whatever engine a project pins)");
1448
+ io.out("");
1449
+ io.out(` install Installs everything ${import_core7.MANIFEST_PATH} pins`);
1450
+ io.out(" add <package> Pins a provider extension, and commits the decision");
1451
+ io.out(` add ${LOCAL_FLAG} <package> Adds the one this repository builds \u2014 nothing is pinned`);
1452
+ io.out("");
1453
+ }
1454
+ function printInstallHelp(io) {
1455
+ io.out(`${INSTALL_COMMAND}`);
1456
+ io.out("");
1457
+ io.out(`Downloads, verifies and installs every version ${import_core7.MANIFEST_PATH} pins \u2014 the engine and`);
1458
+ io.out("every extension \u2014 into $PENV_HOME. The one penv command CI and production run that");
1459
+ io.out("may reach the registry.");
1460
+ }
1461
+ function printAddHelp(io) {
1462
+ io.out("penv add <package>[@<version>]");
1463
+ io.out("");
1464
+ io.out(` ${LOCAL_FLAG} Add the package this repository builds, resolved from it`);
1465
+ io.out(" --registry <url> Take the release from a private registry instead of npmjs");
1466
+ io.out(
1467
+ ` ${TRUST_YOUNG_FLAG} Add a release published less than ${MIN_PACKAGE_AGE_DAYS} days ago`
1468
+ );
1469
+ io.out("");
1470
+ io.out(`Pins the release in ${import_core7.MANIFEST_PATH} and writes its type declaration to`);
1471
+ io.out(`${import_core7.EXTENSIONS_PATH}/. Both are committed, so \`${INSTALL_COMMAND}\` gives every machine`);
1472
+ io.out(`the bytes you reviewed. \`${LOCAL_FLAG}\` pins nothing and touches no manifest.`);
1473
+ }
1474
+
1475
+ // src/pins.ts
1476
+ var import_core8 = require("@penvhq/core");
1421
1477
  var DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
1422
1478
  var DEV_PIN_VERSION = "0.0.0-dev";
1423
1479
  var BUNDLED_ENGINE_PIN = {
1424
- package: import_core7.ENGINE_PACKAGE,
1425
- version: "0.9.5",
1426
- integrity: "sha512-cacsvynscs6G0NThpGaK1BfKy3POQSoogI/vvQNZ5sTZaftzwg4mPtUpT+i2GSc3KbYAHQu4Kq9MpYLOvSvulA=="
1480
+ package: import_core8.ENGINE_PACKAGE,
1481
+ version: "0.10.0",
1482
+ integrity: "sha512-b+U0c3xWbk1ILtMfPjJW8x5XBxSLAaw/IAOLMBmGvT2uuYv41JPED8PrenEj4s3ZJY5BxpWU9mQcJHEBouaw/Q=="
1427
1483
  };
1428
1484
  function assertReleasePin(pin) {
1429
1485
  if (pin.integrity === DEV_PIN_INTEGRITY || pin.version === DEV_PIN_VERSION) {
@@ -1441,8 +1497,8 @@ function releaseEnginePin(pin, ranVersion) {
1441
1497
  // src/project.ts
1442
1498
  var import_node_fs6 = require("fs");
1443
1499
  var import_node_path6 = require("path");
1444
- var import_core8 = require("@penvhq/core");
1445
- var MANIFEST_SEGMENTS = import_core8.MANIFEST_PATH.split("/");
1500
+ var import_core9 = require("@penvhq/core");
1501
+ var MANIFEST_SEGMENTS = import_core9.MANIFEST_PATH.split("/");
1446
1502
  function findUp(cwd, matches) {
1447
1503
  let dir = (0, import_node_path6.resolve)(cwd);
1448
1504
  for (; ; ) {
@@ -1461,7 +1517,7 @@ function findProject(cwd) {
1461
1517
  return root === void 0 ? void 0 : { root, manifestFile: (0, import_node_path6.join)(root, ...MANIFEST_SEGMENTS) };
1462
1518
  }
1463
1519
  function findAdoptedRoot(cwd) {
1464
- return findUp(cwd, (dir) => (0, import_node_fs6.existsSync)((0, import_core8.stateDir)(dir)));
1520
+ return findUp(cwd, (dir) => (0, import_node_fs6.existsSync)((0, import_core9.stateDir)(dir)));
1465
1521
  }
1466
1522
 
1467
1523
  // src/launcher.ts
@@ -1510,14 +1566,14 @@ function pinsOf(manifest) {
1510
1566
  ];
1511
1567
  }
1512
1568
  function reportable(error, home, argv) {
1513
- return error instanceof import_core9.UnsupportedManifestFormatError ? error.withLauncherUpdate({
1569
+ return error instanceof import_core10.UnsupportedManifestFormatError ? error.withLauncherUpdate({
1514
1570
  updateCommand: launcherUpdateCommand(home),
1515
1571
  invokedCommand: invokedCommand(argv)
1516
1572
  }) : error;
1517
1573
  }
1518
1574
  function readManifest(manifestFile, home, argv) {
1519
1575
  try {
1520
- return (0, import_core9.parseManifest)((0, import_node_fs7.readFileSync)(manifestFile, "utf8"));
1576
+ return (0, import_core10.parseManifest)((0, import_node_fs7.readFileSync)(manifestFile, "utf8"));
1521
1577
  } catch (error) {
1522
1578
  throw reportable(error, home, argv);
1523
1579
  }
@@ -1530,7 +1586,7 @@ function readManifestToRepair(manifestFile, home, argv) {
1530
1586
  }
1531
1587
  }
1532
1588
  function report(error, io) {
1533
- if (error instanceof import_core9.PenvError && error.remedy !== void 0) {
1589
+ if (error instanceof import_core10.PenvError && error.remedy !== void 0) {
1534
1590
  const suffix = `
1535
1591
  ${error.remedy}`;
1536
1592
  const message = error.message.endsWith(suffix) ? error.message.slice(0, -suffix.length) : error.message;
@@ -1552,7 +1608,17 @@ async function launch(options) {
1552
1608
  const { argv, cwd, env, io } = options;
1553
1609
  const { noDownload, forwarded } = splitLauncherFlags(argv);
1554
1610
  const first = forwarded[0];
1555
- const home = penvHome(env);
1611
+ const home = (0, import_core10.penvHome)(env);
1612
+ if (first !== void 0 && forwarded.slice(1).some((token) => HELP_FLAGS.has(token))) {
1613
+ if (first === INSTALL) {
1614
+ printInstallHelp(io);
1615
+ return 0;
1616
+ }
1617
+ if (first === ADD) {
1618
+ printAddHelp(io);
1619
+ return 0;
1620
+ }
1621
+ }
1556
1622
  const project = findProject(cwd);
1557
1623
  if (project === void 0) {
1558
1624
  if (first !== void 0 && VERSION_FLAGS.has(first)) {
@@ -1597,16 +1663,16 @@ async function adopt(options, forwarded, home, cwd, noDownload) {
1597
1663
  if (root === void 0) {
1598
1664
  return 0;
1599
1665
  }
1600
- const manifestFile = (0, import_node_path7.join)(root, ...import_core9.MANIFEST_PATH.split("/"));
1666
+ const manifestFile = (0, import_node_path7.join)(root, ...import_core10.MANIFEST_PATH.split("/"));
1601
1667
  if ((0, import_node_fs7.existsSync)(manifestFile)) {
1602
1668
  return 0;
1603
1669
  }
1604
1670
  const pin = releaseEnginePin(options.bundledPin, engine.version);
1605
1671
  (0, import_node_fs7.writeFileSync)(
1606
1672
  manifestFile,
1607
- (0, import_core9.serializeManifest)({ format: import_core9.MANIFEST_FORMAT, engine: pin, extensions: {} })
1673
+ (0, import_core10.serializeManifest)({ format: import_core10.MANIFEST_FORMAT, engine: pin, extensions: {} })
1608
1674
  );
1609
- options.io.out(`\u2713 ${import_core9.MANIFEST_PATH} pins ${pin.package} ${pin.version}`);
1675
+ options.io.out(`\u2713 ${import_core10.MANIFEST_PATH} pins ${pin.package} ${pin.version}`);
1610
1676
  await ensureAdoptedEngine(
1611
1677
  options,
1612
1678
  { name: pin.package, version: pin.version, integrity: pin.integrity },
@@ -1685,13 +1751,21 @@ async function addExtension(options, project, home, argv, noDownload) {
1685
1751
  const engine = engineAt(dir, enginePin.name, enginePin.version);
1686
1752
  return delegate(options, engine, onboard, home, options.cwd);
1687
1753
  }
1688
- function delegate(options, engine, forwarded, home, cwd) {
1689
- return options.spawn({
1754
+ function isRootHelp(forwarded) {
1755
+ const first = forwarded[0];
1756
+ return first === void 0 || HELP_FLAGS.has(first);
1757
+ }
1758
+ async function delegate(options, engine, forwarded, home, cwd) {
1759
+ const code = await options.spawn({
1690
1760
  command: process.execPath,
1691
1761
  args: [engine.entry, ...forwarded],
1692
1762
  cwd,
1693
- env: { ...options.env, [PENV_HOME_VAR]: home }
1763
+ env: { ...options.env, [import_core10.PENV_HOME_VAR]: home }
1694
1764
  });
1765
+ if (code === 0 && isRootHelp(forwarded)) {
1766
+ printLauncherCommands(options.io);
1767
+ }
1768
+ return code;
1695
1769
  }
1696
1770
  // Annotate the CommonJS export names for ESM import in node:
1697
1771
  0 && (module.exports = {
@@ -1716,6 +1790,8 @@ function delegate(options, engine, forwarded, home, cwd) {
1716
1790
  EngineEntryError,
1717
1791
  EnginePinMismatchError,
1718
1792
  EnginePinUnreleasedError,
1793
+ ExtensionNotImportableError,
1794
+ ExtensionUnloadableError,
1719
1795
  HOME_META_FILE,
1720
1796
  INSTALL_COMMAND,
1721
1797
  INTEGRITY_FILE,
@@ -1726,7 +1802,6 @@ function delegate(options, engine, forwarded, home, cwd) {
1726
1802
  NPM_UPDATE_COMMAND,
1727
1803
  NoProjectError,
1728
1804
  OfficialRegistryError,
1729
- PENV_HOME_VAR,
1730
1805
  PackageCorruptError,
1731
1806
  PackageMissingError,
1732
1807
  PackageTooYoungError,
@@ -1752,9 +1827,7 @@ function delegate(options, engine, forwarded, home, cwd) {
1752
1827
  integrityOf,
1753
1828
  launcherUpdateCommand,
1754
1829
  nodeSpawner,
1755
- packageDir,
1756
1830
  packumentUrl,
1757
- penvHome,
1758
1831
  readExtensionPackage,
1759
1832
  readProviderEntries,
1760
1833
  readTarball,