@penvhq/cli 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/bin.cjs CHANGED
@@ -3023,7 +3023,7 @@ var require_jiti = __commonJS({
3023
3023
  }
3024
3024
  const isAbsolute6 = function(e3) {
3025
3025
  return ze.test(e3);
3026
- }, extname = function(e3) {
3026
+ }, extname2 = function(e3) {
3027
3027
  if (".." === e3) return "";
3028
3028
  const t3 = Ye.exec(pathe_M_eThtNZ_normalizeWindowsPath(e3));
3029
3029
  return t3 && t3[1] || "";
@@ -3725,7 +3725,7 @@ Default "index" lookups for the main are deprecated for ES modules.`, "Deprecati
3725
3725
  }
3726
3726
  const a2 = jitiResolve(e3, t3, i2);
3727
3727
  if (!a2 && i2.try) return;
3728
- const c2 = extname(a2);
3728
+ const c2 = extname2(a2);
3729
3729
  if (".json" === c2) {
3730
3730
  debug(e3, "[json]", a2);
3731
3731
  const t4 = e3.nativeRequire(a2);
@@ -3797,7 +3797,7 @@ Default "index" lookups for the main are deprecated for ES modules.`, "Deprecati
3797
3797
  return i2.startsWith("#!") && (i2 = "// " + i2), i2;
3798
3798
  }
3799
3799
  function eval_evalModule(t3, i2, n2 = {}) {
3800
- const a2 = n2.id || (n2.filename ? basename4(n2.filename) : `_jitiEval.${n2.ext || (n2.async ? "mjs" : "js")}`), c2 = n2.filename || jitiResolve(t3, a2, { async: n2.async }), l2 = n2.ext || extname(c2), y2 = n2.cache || t3.parentCache || {}, E2 = /\.[cm]?tsx?$/.test(l2), w2 = ".mjs" === l2 || ".js" === l2 && "module" === (function(e3) {
3800
+ const a2 = n2.id || (n2.filename ? basename4(n2.filename) : `_jitiEval.${n2.ext || (n2.async ? "mjs" : "js")}`), c2 = n2.filename || jitiResolve(t3, a2, { async: n2.async }), l2 = n2.ext || extname2(c2), y2 = n2.cache || t3.parentCache || {}, E2 = /\.[cm]?tsx?$/.test(l2), w2 = ".mjs" === l2 || ".js" === l2 && "module" === (function(e3) {
3801
3801
  for (; e3 && "." !== e3 && "/" !== e3; ) {
3802
3802
  e3 = pathe_M_eThtNZ_join(e3, "..");
3803
3803
  try {
@@ -3845,7 +3845,7 @@ Default "index" lookups for the main are deprecated for ES modules.`, "Deprecati
3845
3845
  (0, $e.mkdirSync)(n4, { recursive: true });
3846
3846
  } catch {
3847
3847
  }
3848
- const a4 = pathe_M_eThtNZ_join(n4, `${basename4(i6, extname(i6))}-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`);
3848
+ const a4 = pathe_M_eThtNZ_join(n4, `${basename4(i6, extname2(i6))}-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`);
3849
3849
  return (0, $e.writeFileSync)(a4, t5), a4;
3850
3850
  })(l3, n3), debug(t4, "[esm]", "[tempfile]", c4), a3(pathToFileURL3(c4))), E3 = y3 ? a3(y3).catch((e3) => {
3851
3851
  if ("ENAMETOOLONG" !== e3?.code) throw e3;
@@ -4222,7 +4222,7 @@ Did you specify these with the most recent transformation maps first?`);
4222
4222
  for (let e5 = 1; e5 < s; e5++) a += "/" + n[e5];
4223
4223
  (!a || o && !a.endsWith("/..")) && (a += "/"), e4.path = a;
4224
4224
  }
4225
- function resolve12(e4, t3) {
4225
+ function resolve13(e4, t3) {
4226
4226
  if (!e4 && !t3) return "";
4227
4227
  const r3 = parseUrl(e4);
4228
4228
  let n = r3.type;
@@ -4259,7 +4259,7 @@ Did you specify these with the most recent transformation maps first?`);
4259
4259
  return r3.scheme + "//" + r3.user + r3.host + r3.port + r3.path + s;
4260
4260
  }
4261
4261
  }
4262
- return resolve12;
4262
+ return resolve13;
4263
4263
  })();
4264
4264
  }, "./node_modules/.pnpm/@jridgewell+sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js"(e2, t2, r2) {
4265
4265
  var n;
@@ -47374,6 +47374,9 @@ var import_url = require("url");
47374
47374
  var import_crypto2 = require("crypto");
47375
47375
  var import_fs2 = require("fs");
47376
47376
  var import_path2 = require("path");
47377
+ var import_fs3 = require("fs");
47378
+ var import_os = require("os");
47379
+ var import_path3 = require("path");
47377
47380
  var REMEDY_ARROW = "\u2192";
47378
47381
  function framesOf(stack) {
47379
47382
  return (stack ?? "").split("\n").filter((line) => /^\s+at\s/.test(line));
@@ -47436,10 +47439,10 @@ var FilenameGrammarError = class extends PenvError {
47436
47439
  var ReservedTokenError = class extends PenvError {
47437
47440
  name = "ReservedTokenError";
47438
47441
  token;
47439
- constructor(subject, token, where) {
47442
+ constructor(subject, token, where2) {
47440
47443
  super(
47441
47444
  "RESERVED_TOKEN",
47442
- `The ${subject} name \`${token}\` in ${where} is a reserved token`,
47445
+ `The ${subject} name \`${token}\` in ${where2} is a reserved token`,
47443
47446
  `Filenames are split on \`.\`, so \`${token}\` cannot be a ${subject} name. Rename it.`
47444
47447
  );
47445
47448
  this.token = token;
@@ -49033,10 +49036,33 @@ function parseLocalExtensions(text) {
49033
49036
  function normalize(names) {
49034
49037
  return [...new Set(names.map((name) => name.trim()))].sort();
49035
49038
  }
49039
+ var MANIFEST_PATH2 = ".penv/state/manifest.json";
49036
49040
  var MANIFEST_FORMAT = 1;
49037
49041
  var ENGINE_PACKAGE = "@penvhq/cli";
49038
49042
  var OFFICIAL_SCOPE = "@penvhq/";
49039
49043
  var DEFAULT_REGISTRY_HOSTS = ["registry.npmjs.org", "registry.npmjs.com", "npmjs.org"];
49044
+ var ManifestError = class extends PenvError {
49045
+ name = "ManifestError";
49046
+ };
49047
+ var UnsupportedManifestFormatError = class _UnsupportedManifestFormatError extends PenvError {
49048
+ name = "UnsupportedManifestFormatError";
49049
+ /** The format the file declares. */
49050
+ found;
49051
+ /** The format this engine implements. */
49052
+ supported = MANIFEST_FORMAT;
49053
+ constructor(found, update) {
49054
+ super(
49055
+ "MANIFEST_FORMAT_UNSUPPORTED",
49056
+ `${MANIFEST_PATH2} is format ${found}, and this penv understands format ${MANIFEST_FORMAT}`,
49057
+ update === void 0 ? "Update penv, then run the command again." : `Run \`${update.updateCommand}\`, then \`${update.invokedCommand}\` again.`
49058
+ );
49059
+ this.found = found;
49060
+ }
49061
+ /** The same refusal, with the launcher's own update command and the user's command. */
49062
+ withLauncherUpdate(update) {
49063
+ return new _UnsupportedManifestFormatError(this.found, update);
49064
+ }
49065
+ };
49040
49066
  var EXACT_VERSION = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
49041
49067
  var INTEGRITY = /^sha512-[A-Za-z0-9+/]{86}==$/;
49042
49068
  var PACKAGE_NAME2 = /^(?:@[a-z0-9~-][a-z0-9._~-]*\/)?[a-z0-9~-][a-z0-9._~-]*$/;
@@ -49176,6 +49202,224 @@ var MANIFEST = external_exports.strictObject({
49176
49202
  }),
49177
49203
  extensions: EXTENSIONS2
49178
49204
  });
49205
+ var FORBIDDEN = [
49206
+ {
49207
+ pattern: /(?:^|[\s"'=(])(?:\/(?:[A-Za-z0-9._-]+\/)+|~\/|[A-Za-z]:[\\/]|\\\\[A-Za-z0-9._-]+\\)/,
49208
+ code: "MANIFEST_ABSOLUTE_PATH",
49209
+ problem: "holds an absolute path",
49210
+ remedy: "Remove it. The manifest is committed and read on every machine that clones the repo, so it names packages and versions only \u2014 never a location on one of them."
49211
+ },
49212
+ {
49213
+ pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----|\b(?:gh[pousr]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{20,}|xox[abopsr]-[A-Za-z0-9-]{10,}|AKIA[0-9A-Z]{16}|hv[sb]\.[A-Za-z0-9._-]{10,}|sk-[A-Za-z0-9]{16,}|sk_live_[A-Za-z0-9]{10,}|eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})\b|:\/\/[^/\s:@]+:[^/\s@]+@/,
49214
+ code: "MANIFEST_CREDENTIAL",
49215
+ problem: "holds something shaped like a credential",
49216
+ remedy: "Remove it and rotate that credential. Anything committed here is readable by everyone with the repository, and by everyone who ever had it."
49217
+ },
49218
+ {
49219
+ pattern: /\b[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+\s*=|\b(?:postgres|postgresql|mysql|mongodb|redis|amqps?|s3)(?:\+[a-z]+)?:\/\//,
49220
+ code: "MANIFEST_PROVIDER_CONFIG",
49221
+ problem: "holds provider configuration or a value",
49222
+ remedy: "Move it to penv.config.ts. The manifest records which extension is pinned and why it is trusted, never how it is configured or what it holds."
49223
+ }
49224
+ ];
49225
+ var UNSCANNED_KEY = "integrity";
49226
+ function pathText(path) {
49227
+ return path.map((segment, index) => {
49228
+ if (typeof segment === "number") return `[${segment}]`;
49229
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(segment)) return `[${JSON.stringify(segment)}]`;
49230
+ return index === 0 ? segment : `.${segment}`;
49231
+ }).join("");
49232
+ }
49233
+ function fieldName(path) {
49234
+ return path.length === 0 ? "root" : `\`${pathText(path)}\``;
49235
+ }
49236
+ function isPlainObject22(value2) {
49237
+ return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
49238
+ }
49239
+ function valueAt(root, path) {
49240
+ let current = root;
49241
+ for (const segment of path) {
49242
+ if (Array.isArray(current) && typeof segment === "number") {
49243
+ current = current[segment];
49244
+ continue;
49245
+ }
49246
+ if (!isPlainObject22(current)) return void 0;
49247
+ current = current[String(segment)];
49248
+ }
49249
+ return current;
49250
+ }
49251
+ function scanForbidden(value2, path) {
49252
+ if (typeof value2 === "string") {
49253
+ for (const rule of FORBIDDEN) {
49254
+ if (rule.pattern.test(value2)) {
49255
+ return new ManifestError(
49256
+ rule.code,
49257
+ `${fieldName(path)} in ${MANIFEST_PATH2} ${rule.problem}: \`${value2}\``,
49258
+ rule.remedy
49259
+ );
49260
+ }
49261
+ }
49262
+ return void 0;
49263
+ }
49264
+ if (Array.isArray(value2)) {
49265
+ for (const [index, item] of value2.entries()) {
49266
+ const found = scanForbidden(item, [...path, index]);
49267
+ if (found) return found;
49268
+ }
49269
+ return void 0;
49270
+ }
49271
+ if (isPlainObject22(value2)) {
49272
+ for (const [key, item] of Object.entries(value2)) {
49273
+ if (key === UNSCANNED_KEY) continue;
49274
+ const found = scanForbidden(item, [...path, key]);
49275
+ if (found) return found;
49276
+ }
49277
+ }
49278
+ return void 0;
49279
+ }
49280
+ function sectionRemedy(path) {
49281
+ const [section, name] = path;
49282
+ if (section === "engine") {
49283
+ return `Restore it with \`git checkout ${MANIFEST_PATH2}\`, or write the exact version and the \`integrity\` npm published for that ${ENGINE_PACKAGE} release. penv runs the bytes this pin names, so it will not guess one.`;
49284
+ }
49285
+ if (section === "extensions" && typeof name === "string") {
49286
+ return `Run \`penv add ${name}\` to rewrite that entry.`;
49287
+ }
49288
+ return `Restore it \u2014 format ${MANIFEST_FORMAT} requires it.`;
49289
+ }
49290
+ function describeType2(value2) {
49291
+ if (value2 === null) return "null";
49292
+ if (Array.isArray(value2)) return "an array";
49293
+ return `a ${typeof value2}`;
49294
+ }
49295
+ function customParams(issue2) {
49296
+ if (issue2.code !== "custom") return void 0;
49297
+ const params = issue2.params;
49298
+ if (!isPlainObject22(params)) return void 0;
49299
+ const code = params.code;
49300
+ const remedy = params.remedy;
49301
+ if (typeof code !== "string" || typeof remedy !== "string") return void 0;
49302
+ return { code, remedy };
49303
+ }
49304
+ function toManifestError(issue2, raw) {
49305
+ const path = issue2.path;
49306
+ const custom2 = customParams(issue2);
49307
+ if (custom2) {
49308
+ return new ManifestError(
49309
+ custom2.code,
49310
+ `${fieldName(path)} in ${MANIFEST_PATH2} ${issue2.message}`,
49311
+ custom2.remedy
49312
+ );
49313
+ }
49314
+ switch (issue2.code) {
49315
+ case "unrecognized_keys": {
49316
+ const key = issue2.keys[0] ?? "";
49317
+ const where2 = path.length === 0 ? "at its root" : `in ${fieldName(path)}`;
49318
+ return new ManifestError(
49319
+ "MANIFEST_UNKNOWN_KEY",
49320
+ `${MANIFEST_PATH2} has an unknown key \`${key}\` ${where2}`,
49321
+ `Remove it. Format ${MANIFEST_FORMAT} is a closed shape, so a key penv does not know is either a typo or a file a newer penv wrote.`
49322
+ );
49323
+ }
49324
+ case "invalid_key":
49325
+ return new ManifestError(
49326
+ "MANIFEST_PACKAGE_NAME",
49327
+ `\`${String(path[path.length - 1] ?? "")}\` in ${MANIFEST_PATH2} is not an npm package name`,
49328
+ "Name the package exactly as npm does, e.g. `@acme/provider-consul`."
49329
+ );
49330
+ case "invalid_union":
49331
+ return new ManifestError(
49332
+ "MANIFEST_TRUST_TIER",
49333
+ `${fieldName(path)} in ${MANIFEST_PATH2} is \`${String(valueAt(raw, path))}\`, which is not a trust tier`,
49334
+ "Use `third-party` for a package published outside your organisation, or `private` for one your own team publishes."
49335
+ );
49336
+ case "invalid_value":
49337
+ return new ManifestError(
49338
+ "MANIFEST_FIELD_VALUE",
49339
+ `${fieldName(path)} in ${MANIFEST_PATH2} is \`${String(valueAt(raw, path))}\`, and format ${MANIFEST_FORMAT} requires \`${String(issue2.values[0])}\``,
49340
+ sectionRemedy(path)
49341
+ );
49342
+ case "invalid_format": {
49343
+ if (issue2.format !== "datetime") break;
49344
+ return new ManifestError(
49345
+ "MANIFEST_TIMESTAMP",
49346
+ `${fieldName(path)} in ${MANIFEST_PATH2} is \`${String(valueAt(raw, path))}\`, which is not a UTC timestamp`,
49347
+ "Write it as ISO 8601 in UTC, e.g. `2026-08-17T00:00:00Z`."
49348
+ );
49349
+ }
49350
+ default:
49351
+ break;
49352
+ }
49353
+ const actual = valueAt(raw, path);
49354
+ if (actual === void 0) {
49355
+ return new ManifestError(
49356
+ "MANIFEST_FIELD_MISSING",
49357
+ `${MANIFEST_PATH2} is missing ${fieldName(path)}`,
49358
+ sectionRemedy(path)
49359
+ );
49360
+ }
49361
+ return new ManifestError(
49362
+ "MANIFEST_FIELD_TYPE",
49363
+ `${fieldName(path)} in ${MANIFEST_PATH2} is ${describeType2(actual)}, not what format ${MANIFEST_FORMAT} declares`,
49364
+ sectionRemedy(path)
49365
+ );
49366
+ }
49367
+ function assertFormat(root) {
49368
+ const format = root.format;
49369
+ if (typeof format === "number" && Number.isInteger(format) && format > 0) {
49370
+ if (format !== MANIFEST_FORMAT) {
49371
+ throw new UnsupportedManifestFormatError(format);
49372
+ }
49373
+ return;
49374
+ }
49375
+ const declared = format === void 0 ? "no format" : `\`${String(format)}\` as its format`;
49376
+ throw new ManifestError(
49377
+ "MANIFEST_FORMAT_INVALID",
49378
+ `${MANIFEST_PATH2} declares ${declared}, so penv cannot tell how to read it`,
49379
+ `Restore the key as \`"format": ${MANIFEST_FORMAT}\` \u2014 every manifest says its format first.`
49380
+ );
49381
+ }
49382
+ function validateManifest(value2) {
49383
+ if (!isPlainObject22(value2)) {
49384
+ throw new ManifestError(
49385
+ "MANIFEST_ROOT",
49386
+ `${MANIFEST_PATH2} holds ${describeType2(value2)} at its root, not a manifest object`,
49387
+ `Restore it with \`git checkout ${MANIFEST_PATH2}\` \u2014 penv writes this file, and every manifest is a JSON object.`
49388
+ );
49389
+ }
49390
+ assertFormat(value2);
49391
+ const forbidden = scanForbidden(value2, []);
49392
+ if (forbidden) {
49393
+ throw forbidden;
49394
+ }
49395
+ const result2 = MANIFEST.safeParse(value2);
49396
+ if (!result2.success) {
49397
+ const issue2 = result2.error.issues[0];
49398
+ if (issue2 === void 0) {
49399
+ throw new ManifestError(
49400
+ "MANIFEST_INVALID",
49401
+ `${MANIFEST_PATH2} is not a format ${MANIFEST_FORMAT} manifest`,
49402
+ `Restore it with \`git checkout ${MANIFEST_PATH2}\` \u2014 penv writes this file, and it is committed.`
49403
+ );
49404
+ }
49405
+ throw toManifestError(issue2, value2);
49406
+ }
49407
+ return result2.data;
49408
+ }
49409
+ function parseManifest(text) {
49410
+ let parsed;
49411
+ try {
49412
+ parsed = JSON.parse(text);
49413
+ } catch (cause) {
49414
+ const detail = cause instanceof Error ? cause.message : String(cause);
49415
+ throw new ManifestError(
49416
+ "MANIFEST_PARSE",
49417
+ `${MANIFEST_PATH2} is not valid JSON: ${detail}`,
49418
+ `Restore it with \`git checkout ${MANIFEST_PATH2}\` \u2014 penv writes this file, so a syntax error in it is an edit that went wrong.`
49419
+ );
49420
+ }
49421
+ return validateManifest(parsed);
49422
+ }
49179
49423
  var ENVIRONMENTS_KEY = "environments";
49180
49424
  var PRIORITY_KEYS = ["description", "owner"];
49181
49425
  function isPlainObject3(value2) {
@@ -49558,6 +49802,80 @@ function completeRotation(meta3, environment, nowIso) {
49558
49802
  };
49559
49803
  return { ...base, environments };
49560
49804
  }
49805
+ var PENV_HOME_VAR = "PENV_HOME";
49806
+ function penvHome(env) {
49807
+ const declared = env[PENV_HOME_VAR];
49808
+ if (declared !== void 0 && declared.trim() !== "") {
49809
+ return (0, import_path3.resolve)(declared);
49810
+ }
49811
+ return (0, import_path3.join)((0, import_os.homedir)(), ".penv");
49812
+ }
49813
+ function packageDir(home, kind, name, version2) {
49814
+ const bucket = (0, import_path3.resolve)(home, kind);
49815
+ const dir = (0, import_path3.resolve)(bucket, ...name.split("/"), version2);
49816
+ if (!dir.startsWith(bucket + import_path3.sep)) {
49817
+ throw new PenvError(
49818
+ "PENV_HOME_ESCAPE",
49819
+ `\`${name}\` at \`${version2}\` resolves to ${dir}, which is outside ${bucket}`,
49820
+ "Name the package exactly as npm does, e.g. `@penvhq/provider-vault`."
49821
+ );
49822
+ }
49823
+ return dir;
49824
+ }
49825
+ var IMPORTABLE = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".node"]);
49826
+ function isImportableEntry(file2) {
49827
+ return IMPORTABLE.has((0, import_path3.extname)(file2).toLowerCase());
49828
+ }
49829
+ var IMPORT_CONDITIONS = ["node", "import", "default"];
49830
+ function isPlainObject4(value2) {
49831
+ return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
49832
+ }
49833
+ function pickCondition(value2) {
49834
+ if (typeof value2 === "string") {
49835
+ return value2;
49836
+ }
49837
+ if (!isPlainObject4(value2)) {
49838
+ return void 0;
49839
+ }
49840
+ for (const condition of IMPORT_CONDITIONS) {
49841
+ const picked = pickCondition(value2[condition]);
49842
+ if (picked !== void 0) {
49843
+ return picked;
49844
+ }
49845
+ }
49846
+ return void 0;
49847
+ }
49848
+ function mainExport(exports2) {
49849
+ if (typeof exports2 === "string") {
49850
+ return exports2;
49851
+ }
49852
+ if (!isPlainObject4(exports2)) {
49853
+ return void 0;
49854
+ }
49855
+ return Object.keys(exports2).some((key) => key.startsWith(".")) ? pickCondition(exports2["."]) : pickCondition(exports2);
49856
+ }
49857
+ function stringField(source, key) {
49858
+ if (!isPlainObject4(source)) {
49859
+ return void 0;
49860
+ }
49861
+ const value2 = source[key];
49862
+ return typeof value2 === "string" && value2.trim() !== "" ? value2.trim() : void 0;
49863
+ }
49864
+ function packageEntry(dir) {
49865
+ let parsed;
49866
+ try {
49867
+ parsed = JSON.parse((0, import_fs3.readFileSync)((0, import_path3.join)(dir, "package.json"), "utf8"));
49868
+ } catch {
49869
+ return void 0;
49870
+ }
49871
+ const declared = mainExport(isPlainObject4(parsed) ? parsed.exports : void 0) ?? stringField(parsed, "main") ?? "index.js";
49872
+ const root = (0, import_path3.resolve)(dir);
49873
+ const file2 = (0, import_path3.resolve)(root, ...declared.split("/"));
49874
+ if (!file2.startsWith(root + import_path3.sep)) {
49875
+ return void 0;
49876
+ }
49877
+ return { file: file2, importable: isImportableEntry(file2) && (0, import_fs3.existsSync)(file2) };
49878
+ }
49561
49879
 
49562
49880
  // src/bin.ts
49563
49881
  var import_jiti = __toESM(require_jiti2(), 1);
@@ -50049,8 +50367,8 @@ init_cjs_shims();
50049
50367
 
50050
50368
  // ../providers/filesystem/dist/index.js
50051
50369
  init_cjs_shims();
50052
- var import_fs3 = require("fs");
50053
- var import_path3 = require("path");
50370
+ var import_fs4 = require("fs");
50371
+ var import_path4 = require("path");
50054
50372
  var META_FORMAT = "json";
50055
50373
  function isErrnoCode(error51, code) {
50056
50374
  return error51 instanceof Error && error51.code === code;
@@ -50060,7 +50378,7 @@ var FilesystemProvider = class {
50060
50378
  #root;
50061
50379
  #config;
50062
50380
  constructor(options) {
50063
- this.#root = (0, import_path3.resolve)(options.root);
50381
+ this.#root = (0, import_path4.resolve)(options.root);
50064
50382
  this.#config = options.config;
50065
50383
  }
50066
50384
  /** The records directory this provider is rooted at, absolute. */
@@ -50130,19 +50448,19 @@ var FilesystemProvider = class {
50130
50448
  /** Deletes a file that may not be there, and tidies the namespace it emptied. */
50131
50449
  #unlink(path) {
50132
50450
  try {
50133
- (0, import_fs3.unlinkSync)(path);
50451
+ (0, import_fs4.unlinkSync)(path);
50134
50452
  } catch (error51) {
50135
50453
  if (!isErrnoCode(error51, "ENOENT")) throw error51;
50136
50454
  return;
50137
50455
  }
50138
- this.#pruneEmpty((0, import_path3.dirname)(path));
50456
+ this.#pruneEmpty((0, import_path4.dirname)(path));
50139
50457
  }
50140
50458
  #pathOf(relativePosix) {
50141
- return (0, import_path3.join)(this.#root, ...relativePosix.split("/"));
50459
+ return (0, import_path4.join)(this.#root, ...relativePosix.split("/"));
50142
50460
  }
50143
50461
  #readFile(path) {
50144
50462
  try {
50145
- return (0, import_fs3.readFileSync)(path, "utf8");
50463
+ return (0, import_fs4.readFileSync)(path, "utf8");
50146
50464
  } catch (error51) {
50147
50465
  if (isErrnoCode(error51, "ENOENT") || isErrnoCode(error51, "EISDIR")) return void 0;
50148
50466
  throw error51;
@@ -50150,32 +50468,32 @@ var FilesystemProvider = class {
50150
50468
  }
50151
50469
  #writeFile(relativePosix, contents) {
50152
50470
  const path = this.#pathOf(relativePosix);
50153
- (0, import_fs3.mkdirSync)((0, import_path3.dirname)(path), { recursive: true });
50154
- (0, import_fs3.writeFileSync)(path, contents, "utf8");
50471
+ (0, import_fs4.mkdirSync)((0, import_path4.dirname)(path), { recursive: true });
50472
+ (0, import_fs4.writeFileSync)(path, contents, "utf8");
50155
50473
  }
50156
50474
  /** Namespaces are folders, so a namespace with no parameters left is no namespace. */
50157
50475
  #pruneEmpty(directory) {
50158
50476
  let current = directory;
50159
- while (current !== this.#root && current.startsWith(this.#root + import_path3.sep)) {
50477
+ while (current !== this.#root && current.startsWith(this.#root + import_path4.sep)) {
50160
50478
  try {
50161
- (0, import_fs3.rmdirSync)(current);
50479
+ (0, import_fs4.rmdirSync)(current);
50162
50480
  } catch {
50163
50481
  return;
50164
50482
  }
50165
- current = (0, import_path3.dirname)(current);
50483
+ current = (0, import_path4.dirname)(current);
50166
50484
  }
50167
50485
  }
50168
50486
  #walk(directory, namespace, out2) {
50169
50487
  let entries;
50170
50488
  try {
50171
- entries = (0, import_fs3.readdirSync)(directory, { withFileTypes: true });
50489
+ entries = (0, import_fs4.readdirSync)(directory, { withFileTypes: true });
50172
50490
  } catch (error51) {
50173
50491
  if (isErrnoCode(error51, "ENOENT")) return;
50174
50492
  throw error51;
50175
50493
  }
50176
50494
  for (const entry of [...entries].sort((a, b) => a.name < b.name ? -1 : 1)) {
50177
50495
  if (entry.isDirectory()) {
50178
- this.#walk((0, import_path3.join)(directory, entry.name), [...namespace, entry.name], out2);
50496
+ this.#walk((0, import_path4.join)(directory, entry.name), [...namespace, entry.name], out2);
50179
50497
  continue;
50180
50498
  }
50181
50499
  const relativePath = [...namespace, entry.name].join("/");
@@ -50288,7 +50606,7 @@ function inject(input) {
50288
50606
  written += 1;
50289
50607
  continue;
50290
50608
  }
50291
- const defaulted = validated === void 0 ? void 0 : valueAt(validated, accessPath(ref));
50609
+ const defaulted = validated === void 0 ? void 0 : valueAt2(validated, accessPath(ref));
50292
50610
  if (defaulted !== void 0) {
50293
50611
  target[variable] = toEnvString(defaulted);
50294
50612
  written += 1;
@@ -50304,7 +50622,7 @@ function inject(input) {
50304
50622
  function slashId(ref) {
50305
50623
  return [...ref.namespace, ref.name].join("/");
50306
50624
  }
50307
- function valueAt(root, path) {
50625
+ function valueAt2(root, path) {
50308
50626
  let node2 = root;
50309
50627
  for (const key of path) {
50310
50628
  if (typeof node2 !== "object" || node2 === null) {
@@ -50505,14 +50823,14 @@ var startChild = (invocation) => {
50505
50823
  process.off(signal, handler);
50506
50824
  }
50507
50825
  };
50508
- const ended = new Promise((resolve12, reject) => {
50826
+ const ended = new Promise((resolve13, reject) => {
50509
50827
  child.on("error", (cause) => {
50510
50828
  release();
50511
50829
  reject(cannotStart(executable, cause, invocation.purpose));
50512
50830
  });
50513
50831
  child.on("exit", (code, signal) => {
50514
50832
  release();
50515
- resolve12({ exitCode: code ?? 1, signal });
50833
+ resolve13({ exitCode: code ?? 1, signal });
50516
50834
  });
50517
50835
  });
50518
50836
  return {
@@ -50845,8 +51163,8 @@ var import_node_url = require("url");
50845
51163
 
50846
51164
  // ../providers/mock/dist/index.js
50847
51165
  init_cjs_shims();
50848
- var import_fs4 = require("fs");
50849
- var import_path4 = require("path");
51166
+ var import_fs5 = require("fs");
51167
+ var import_path5 = require("path");
50850
51168
  function scopeKey(scope) {
50851
51169
  switch (scope.kind) {
50852
51170
  case "unscoped":
@@ -50939,7 +51257,7 @@ var MockProvider = class {
50939
51257
  #load() {
50940
51258
  let contents;
50941
51259
  try {
50942
- contents = (0, import_fs4.readFileSync)(this.#storePath, "utf8");
51260
+ contents = (0, import_fs5.readFileSync)(this.#storePath, "utf8");
50943
51261
  } catch (error51) {
50944
51262
  if (error51.code === "ENOENT") return emptyStore();
50945
51263
  throw error51;
@@ -50951,8 +51269,8 @@ var MockProvider = class {
50951
51269
  };
50952
51270
  }
50953
51271
  #save(store) {
50954
- (0, import_fs4.mkdirSync)((0, import_path4.dirname)(this.#storePath), { recursive: true });
50955
- (0, import_fs4.writeFileSync)(this.#storePath, JSON.stringify(store, null, 2), "utf8");
51272
+ (0, import_fs5.mkdirSync)((0, import_path5.dirname)(this.#storePath), { recursive: true });
51273
+ (0, import_fs5.writeFileSync)(this.#storePath, JSON.stringify(store, null, 2), "utf8");
50956
51274
  }
50957
51275
  };
50958
51276
  function createMockProvider(options) {
@@ -51000,19 +51318,12 @@ async function createSourceProvider(type, context) {
51000
51318
  return loadPluginProvider(type, context);
51001
51319
  }
51002
51320
  async function loadPluginProvider(type, context) {
51003
- const resolved = resolvePlugin(type, context.root);
51004
- if (resolved === void 0) {
51005
- throw unknownProvider(type, context.environment);
51006
- }
51321
+ const path = resolveExtension(type, context.root, context.environment);
51007
51322
  let mod;
51008
51323
  try {
51009
- mod = await importPlugin(resolved);
51010
- } catch {
51011
- throw new PenvError(
51012
- "PROVIDER_PLUGIN_LOAD",
51013
- `The provider package \`${type}\` failed to load`,
51014
- "It resolved but threw while importing. Check it builds and its dependencies are installed."
51015
- );
51324
+ mod = await importPlugin(path);
51325
+ } catch (cause) {
51326
+ throw providerLoadFailed(type, path, cause);
51016
51327
  }
51017
51328
  const factory = mod[PLUGIN_FACTORY_EXPORT];
51018
51329
  if (typeof factory !== "function") {
@@ -51036,10 +51347,42 @@ function assertProvidersRegistered(config2, projectRoot, options) {
51036
51347
  if (ci && local.includes(provider.type)) {
51037
51348
  throw localExtensionInCi(provider.type, environment);
51038
51349
  }
51039
- if (resolvePlugin(provider.type, projectRoot) === void 0) {
51040
- throw unknownProvider(provider.type, environment);
51350
+ resolveExtension(provider.type, projectRoot, environment, local);
51351
+ }
51352
+ }
51353
+ function resolveExtension(type, projectRoot, environment, local = localExtensions(projectRoot)) {
51354
+ const fromProject = resolvePlugin(type, projectRoot);
51355
+ if (local.includes(type)) {
51356
+ if (fromProject === void 0) {
51357
+ throw localExtensionUnresolved(type, projectRoot, environment);
51041
51358
  }
51359
+ return fromProject;
51360
+ }
51361
+ if (fromProject !== void 0) {
51362
+ return fromProject;
51363
+ }
51364
+ const version2 = pinnedVersion(type, projectRoot);
51365
+ if (version2 === void 0) {
51366
+ throw unknownProvider(type, environment);
51367
+ }
51368
+ const stored = storedExtension(type, version2);
51369
+ if (stored === void 0) {
51370
+ throw extensionNotInstalled(type, version2, environment);
51371
+ }
51372
+ return stored;
51373
+ }
51374
+ function pinnedVersion(type, projectRoot) {
51375
+ let manifest;
51376
+ try {
51377
+ manifest = parseManifest((0, import_node_fs3.readFileSync)((0, import_node_path3.join)(projectRoot, ...MANIFEST_PATH.split("/")), "utf8"));
51378
+ } catch {
51379
+ return void 0;
51042
51380
  }
51381
+ return manifest.extensions[type]?.version;
51382
+ }
51383
+ function storedExtension(type, version2) {
51384
+ const entry = packageEntry(packageDir(penvHome(process.env), "extensions", type, version2));
51385
+ return entry?.file;
51043
51386
  }
51044
51387
  function localExtensions(projectRoot) {
51045
51388
  let text;
@@ -51091,13 +51434,37 @@ function assertSatisfiesContract(provider, specifier) {
51091
51434
  }
51092
51435
  }
51093
51436
  }
51437
+ function where(environment) {
51438
+ return environment === void 0 ? "" : ` for environment ${environment}`;
51439
+ }
51094
51440
  function unknownProvider(type, environment) {
51095
51441
  const preinstalled = [...REGISTRY.keys()].map((name) => `\`${name}\``).join(", ");
51096
- const where = environment === void 0 ? "" : ` for environment ${environment}`;
51097
51442
  return new PenvError(
51098
51443
  "UNKNOWN_PROVIDER",
51099
- `The provider \`${type}\`${where} in penv.config.ts is not installed in this project`,
51100
- `A provider's \`type\` is the package penv imports, so it has to resolve from this project: install it with \`npm i ${type}\`, or \u2014 if this repository is the one that builds it \u2014 run \`penv add --local ${type}\` once it is a dependency of the root. The CLI ships ${preinstalled} pre-installed.`
51444
+ `The provider \`${type}\`${where(environment)} in penv.config.ts is nowhere penv looks for it`,
51445
+ `Run \`penv add ${type}\` to pin a release, or \`penv add --local ${type}\` if this repository is the one that builds it. penv looks in ${LOCAL_EXTENSIONS_PATH}, this project's node_modules, and then $PENV_HOME at the version ${MANIFEST_PATH} pins. The CLI ships ${preinstalled} pre-installed.`
51446
+ );
51447
+ }
51448
+ function extensionNotInstalled(type, version2, environment) {
51449
+ return new PenvError(
51450
+ "EXTENSION_NOT_INSTALLED",
51451
+ `${MANIFEST_PATH} pins \`${type}\` ${version2}${where(environment)}, and it is not installed in ${penvHome(process.env)}`,
51452
+ "Run `penv install` \u2014 it downloads and verifies every version the manifest pins, extensions included."
51453
+ );
51454
+ }
51455
+ function localExtensionUnresolved(type, projectRoot, environment) {
51456
+ return new PenvError(
51457
+ "LOCAL_EXTENSION_UNRESOLVED",
51458
+ `${LOCAL_EXTENSIONS_PATH} records \`${type}\`${where(environment)} as a package this project develops, and it does not resolve from ${projectRoot}`,
51459
+ `Add it as a dependency of the root (a workspace link is one) and run \`pnpm install\`, or drop the name from ${LOCAL_EXTENSIONS_PATH} if this project no longer builds it.`
51460
+ );
51461
+ }
51462
+ function providerLoadFailed(type, path, cause) {
51463
+ const detail = cause instanceof Error ? cause.message : String(cause);
51464
+ return new PenvError(
51465
+ "PROVIDER_PLUGIN_LOAD",
51466
+ `The provider package \`${type}\` failed to load from ${path}: ${detail}`,
51467
+ "penv imports that file exactly as it stands, with no transform, so it has to be built JavaScript with its dependencies installed."
51101
51468
  );
51102
51469
  }
51103
51470
 
@@ -52186,7 +52553,7 @@ function assertNoActiveDotenv(project) {
52186
52553
  );
52187
52554
  }
52188
52555
  var VARIABLE2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
52189
- function isPlainObject4(value2) {
52556
+ function isPlainObject5(value2) {
52190
52557
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
52191
52558
  }
52192
52559
  function packageManifest(specifier, root) {
@@ -52201,7 +52568,7 @@ function packageManifest(specifier, root) {
52201
52568
  if ((0, import_node_fs6.existsSync)(file2)) {
52202
52569
  try {
52203
52570
  const parsed = JSON.parse((0, import_node_fs6.readFileSync)(file2, "utf8"));
52204
- if (isPlainObject4(parsed) && parsed.name === specifier) {
52571
+ if (isPlainObject5(parsed) && parsed.name === specifier) {
52205
52572
  return parsed;
52206
52573
  }
52207
52574
  } catch {
@@ -52222,7 +52589,7 @@ function declaredCredentials(config2, root) {
52222
52589
  continue;
52223
52590
  }
52224
52591
  const penv = packageManifest(provider.type, root)?.penv;
52225
- const credentials = isPlainObject4(penv) ? penv.credentials : void 0;
52592
+ const credentials = isPlainObject5(penv) ? penv.credentials : void 0;
52226
52593
  if (credentials === void 0) {
52227
52594
  continue;
52228
52595
  }
@@ -52910,8 +53277,8 @@ function lineReader(input = process.stdin, output = process.stdout) {
52910
53277
  }
52911
53278
  rl.setPrompt(question);
52912
53279
  rl.prompt();
52913
- return new Promise((resolve12) => {
52914
- waiting.push(resolve12);
53280
+ return new Promise((resolve13) => {
53281
+ waiting.push(resolve13);
52915
53282
  });
52916
53283
  },
52917
53284
  close() {
@@ -54802,9 +55169,9 @@ function assertImportable(ref, variable) {
54802
55169
  `Filenames are split on \`.\`. Rename ${variable} in the source file, then import it again.`
54803
55170
  );
54804
55171
  }
54805
- function assertNotReserved(ref, variable, where, config2) {
55172
+ function assertNotReserved(ref, variable, where2, config2) {
54806
55173
  if (isReservedToken(ref.name, config2)) {
54807
- throw new ReservedTokenError("parameter", variable, where);
55174
+ throw new ReservedTokenError("parameter", variable, where2);
54808
55175
  }
54809
55176
  }
54810
55177
  function assertRoundTrips(ref, variable, config2) {
@@ -54821,12 +55188,12 @@ function assertRoundTrips(ref, variable, config2) {
54821
55188
  `\`penv generate\` would write ${generated}, so anything reading \`process.env["${variable}"]\` would read \`undefined\`. Declare the name you want in the \`override\` block of penv.config.ts \u2014 \`override: { "${ref.name}": "${variable}" }\` \u2014 then import it again. Nothing was imported.`
54822
55189
  );
54823
55190
  }
54824
- function refsForEntries(entries, where, config2) {
55191
+ function refsForEntries(entries, where2, config2) {
54825
55192
  const refs = [];
54826
55193
  for (const entry of entries) {
54827
55194
  const ref = refFromVariable(entry.key);
54828
55195
  assertImportable(ref, entry.key);
54829
- assertNotReserved(ref, entry.key, where, config2);
55196
+ assertNotReserved(ref, entry.key, where2, config2);
54830
55197
  assertRoundTrips(ref, entry.key, config2);
54831
55198
  refs.push(ref);
54832
55199
  }
@@ -55817,7 +56184,7 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
55817
56184
  const alias = decisions.alias;
55818
56185
  const imports = alias.startsWith(IMPORTS_PREFIX);
55819
56186
  const file2 = (0, import_node_path13.join)(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
55820
- const where = imports ? PACKAGE_FILE : TSCONFIG_FILE;
56187
+ const where2 = imports ? PACKAGE_FILE : TSCONFIG_FILE;
55821
56188
  if (!(0, import_node_fs13.existsSync)(file2)) {
55822
56189
  if (imports) {
55823
56190
  return {
@@ -55840,7 +56207,7 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
55840
56207
  return {
55841
56208
  target: "tsconfig",
55842
56209
  action: "conflicted",
55843
- text: `${where} already maps ${alias} to ${edit.conflict}`,
56210
+ text: `${where2} already maps ${alias} to ${edit.conflict}`,
55844
56211
  note: `(left alone \u2014 \`import { env } from "${alias}"\` will not reach ${decisions.schemaFile})`
55845
56212
  };
55846
56213
  }
@@ -55848,14 +56215,14 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
55848
56215
  return {
55849
56216
  target: "tsconfig",
55850
56217
  action: "kept",
55851
- text: `Kept the ${alias} alias in ${where}`
56218
+ text: `Kept the ${alias} alias in ${where2}`
55852
56219
  };
55853
56220
  }
55854
56221
  (0, import_node_fs13.writeFileSync)(file2, edit.source, "utf8");
55855
56222
  return {
55856
56223
  target: "tsconfig",
55857
56224
  action: "updated",
55858
- text: `Added ${alias} alias to ${where}`
56225
+ text: `Added ${alias} alias to ${where2}`
55859
56226
  };
55860
56227
  }
55861
56228
  function writeGitignore(root, decisions = DEFAULT_DECISIONS) {
@@ -57789,10 +58156,10 @@ var watchCommand = defineCommand({
57789
58156
  }
57790
58157
  });
57791
58158
  write([`Watching .penv/ and penv.config.ts. ${out.dim("Ctrl-C to stop.")}`]);
57792
- await new Promise((resolve12) => {
58159
+ await new Promise((resolve13) => {
57793
58160
  process.once("SIGINT", () => {
57794
58161
  handle.close();
57795
- resolve12();
58162
+ resolve13();
57796
58163
  });
57797
58164
  });
57798
58165
  });