fyn 3.0.5 → 3.1.1

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/fyn.mjs CHANGED
@@ -2098,7 +2098,7 @@ var require_signal_exit = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2098
2098
  return function() {};
2099
2099
  };
2100
2100
  else {
2101
- var assert$2 = __require("assert");
2101
+ var assert$3 = __require("assert");
2102
2102
  var signals$1 = require_signals$3();
2103
2103
  var isWin = /^win/i.test(process.platform);
2104
2104
  var EE$7 = __require("events");
@@ -2118,7 +2118,7 @@ var require_signal_exit = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2118
2118
  module.exports = function(cb, opts) {
2119
2119
  /* istanbul ignore if */
2120
2120
  if (!processOk(global.process)) return function() {};
2121
- assert$2.equal(typeof cb, "function", "a callback must be provided for exit handler");
2121
+ assert$3.equal(typeof cb, "function", "a callback must be provided for exit handler");
2122
2122
  if (loaded === false) load$1();
2123
2123
  var ev = "exit";
2124
2124
  if (opts && opts.alwaysLast) ev = "afterexit";
@@ -5782,7 +5782,7 @@ var file_ops_default = wrapWin32Fs(fileOps);
5782
5782
 
5783
5783
  //#endregion
5784
5784
  //#region node_modules/semver/internal/constants.js
5785
- var require_constants$8 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
5785
+ var require_constants$7 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
5786
5786
  const SEMVER_SPEC_VERSION = "2.0.0";
5787
5787
  const MAX_LENGTH = 256;
5788
5788
  const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
@@ -5820,7 +5820,7 @@ var require_debug$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
5820
5820
  //#endregion
5821
5821
  //#region node_modules/semver/internal/re.js
5822
5822
  var require_re = /* @__PURE__ */ __commonJSMin(((exports, module) => {
5823
- const { MAX_SAFE_COMPONENT_LENGTH, MAX_SAFE_BUILD_LENGTH, MAX_LENGTH } = require_constants$8();
5823
+ const { MAX_SAFE_COMPONENT_LENGTH, MAX_SAFE_BUILD_LENGTH, MAX_LENGTH } = require_constants$7();
5824
5824
  const debug = require_debug$2();
5825
5825
  exports = module.exports = {};
5826
5826
  const re = exports.re = [];
@@ -5935,7 +5935,7 @@ var require_identifiers = /* @__PURE__ */ __commonJSMin(((exports, module) => {
5935
5935
  //#region node_modules/semver/classes/semver.js
5936
5936
  var require_semver$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
5937
5937
  const debug = require_debug$2();
5938
- const { MAX_LENGTH, MAX_SAFE_INTEGER } = require_constants$8();
5938
+ const { MAX_LENGTH, MAX_SAFE_INTEGER } = require_constants$7();
5939
5939
  const { safeRe: re, t } = require_re();
5940
5940
  const parseOptions = require_parse_options();
5941
5941
  const { compareIdentifiers } = require_identifiers();
@@ -6121,7 +6121,7 @@ var require_semver$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6121
6121
 
6122
6122
  //#endregion
6123
6123
  //#region node_modules/semver/functions/parse.js
6124
- var require_parse$7 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6124
+ var require_parse$6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6125
6125
  const SemVer = require_semver$1();
6126
6126
  const parse = (version, options, throwErrors = false) => {
6127
6127
  if (version instanceof SemVer) return version;
@@ -6138,7 +6138,7 @@ var require_parse$7 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6138
6138
  //#endregion
6139
6139
  //#region node_modules/semver/functions/valid.js
6140
6140
  var require_valid$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6141
- const parse = require_parse$7();
6141
+ const parse = require_parse$6();
6142
6142
  const valid = (version, options) => {
6143
6143
  const v = parse(version, options);
6144
6144
  return v ? v.version : null;
@@ -6149,7 +6149,7 @@ var require_valid$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6149
6149
  //#endregion
6150
6150
  //#region node_modules/semver/functions/clean.js
6151
6151
  var require_clean = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6152
- const parse = require_parse$7();
6152
+ const parse = require_parse$6();
6153
6153
  const clean = (version, options) => {
6154
6154
  const s = parse(version.trim().replace(/^[=v]+/, ""), options);
6155
6155
  return s ? s.version : null;
@@ -6179,7 +6179,7 @@ var require_inc = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6179
6179
  //#endregion
6180
6180
  //#region node_modules/semver/functions/diff.js
6181
6181
  var require_diff$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6182
- const parse = require_parse$7();
6182
+ const parse = require_parse$6();
6183
6183
  const diff = (version1, version2) => {
6184
6184
  const v1 = parse(version1, null, true);
6185
6185
  const v2 = parse(version2, null, true);
@@ -6232,7 +6232,7 @@ var require_patch$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6232
6232
  //#endregion
6233
6233
  //#region node_modules/semver/functions/prerelease.js
6234
6234
  var require_prerelease = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6235
- const parse = require_parse$7();
6235
+ const parse = require_parse$6();
6236
6236
  const prerelease = (version, options) => {
6237
6237
  const parsed = parse(version, options);
6238
6238
  return parsed && parsed.prerelease.length ? parsed.prerelease : null;
@@ -6377,7 +6377,7 @@ var require_cmp = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6377
6377
  //#region node_modules/semver/functions/coerce.js
6378
6378
  var require_coerce = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6379
6379
  const SemVer = require_semver$1();
6380
- const parse = require_parse$7();
6380
+ const parse = require_parse$6();
6381
6381
  const { safeRe: re, t } = require_re();
6382
6382
  const coerce = (version, options) => {
6383
6383
  if (version instanceof SemVer) return version;
@@ -6409,8 +6409,8 @@ var require_coerce = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6409
6409
  //#endregion
6410
6410
  //#region node_modules/semver/functions/truncate.js
6411
6411
  var require_truncate = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6412
- const parse = require_parse$7();
6413
- const constants = require_constants$8();
6412
+ const parse = require_parse$6();
6413
+ const constants = require_constants$7();
6414
6414
  const SemVer = require_semver$1();
6415
6415
  const truncate = (version, truncation, options) => {
6416
6416
  if (!constants.RELEASE_TYPES.includes(truncation)) return null;
@@ -6591,7 +6591,7 @@ var require_range = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6591
6591
  const debug = require_debug$2();
6592
6592
  const SemVer = require_semver$1();
6593
6593
  const { safeRe: re, src, t, comparatorTrimReplace, tildeTrimReplace, caretTrimReplace } = require_re();
6594
- const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require_constants$8();
6594
+ const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require_constants$7();
6595
6595
  const BUILDSTRIPRE = new RegExp(src[t.BUILD], "g");
6596
6596
  const isNullSet = (c) => c.value === "<0.0.0-0";
6597
6597
  const isAny = (c) => c.value === "";
@@ -7181,10 +7181,10 @@ var require_subset = /* @__PURE__ */ __commonJSMin(((exports, module) => {
7181
7181
  //#region node_modules/semver/index.js
7182
7182
  var require_semver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
7183
7183
  const internalRe = require_re();
7184
- const constants = require_constants$8();
7184
+ const constants = require_constants$7();
7185
7185
  const SemVer = require_semver$1();
7186
7186
  const identifiers = require_identifiers();
7187
- const parse = require_parse$7();
7187
+ const parse = require_parse$6();
7188
7188
  const valid = require_valid$1();
7189
7189
  const clean = require_clean();
7190
7190
  const inc = require_inc();
@@ -10059,7 +10059,11 @@ var VisualLogger = class VisualLogger {
10059
10059
  if (this._items.indexOf(name) >= 0) return this;
10060
10060
  this._getItemKeys().forEach((k) => {
10061
10061
  const itemOpt = this._itemOptions[k];
10062
- if (itemOpt._spinning) itemOpt._spinning = SPIN_STARTED;
10062
+ if (itemOpt._spinning) {
10063
+ itemOpt._spinning = SPIN_STARTED;
10064
+ itemOpt.spinIx = 0;
10065
+ this._lines[this._items.indexOf(itemOpt.name)] = this._renderLine(itemOpt);
10066
+ }
10063
10067
  });
10064
10068
  const itemOpt = {
10065
10069
  spinInterval: DEFAULT_SPINNER_INTERVAL,
@@ -10410,6 +10414,9 @@ const FYN_LOCAL_HARD_TAG = "-fynlocal_h";
10410
10414
  function isLocal(v) {
10411
10415
  return v.indexOf(FYN_LOCAL_TAG) > 0;
10412
10416
  }
10417
+ function isLocalHard(v) {
10418
+ return v.indexOf(FYN_LOCAL_HARD_TAG) > 0;
10419
+ }
10413
10420
  function localify(v, localType, hash = "") {
10414
10421
  return `${v}${localType === "hard" ? FYN_LOCAL_HARD_TAG : FYN_LOCAL_TAG}${hash}`;
10415
10422
  }
@@ -10497,12 +10504,13 @@ function equal(v1, v2) {
10497
10504
  //#region lib/dep-item.ts
10498
10505
  var DepItem = class {
10499
10506
  name;
10500
- /** Optional dependency failure flag (truthy = failed, number for failure code) */
10507
+ /** Optional dependency failure code - truthy means failed; never assigned a boolean */
10501
10508
  optFailed;
10502
10509
  /** Version string (may be set during resolution) */
10503
10510
  version;
10504
10511
  /** Was optional check performed */
10505
10512
  optChecked;
10513
+ /** read by the dep locker's `makeDep`, so not private (FJM-154) */
10506
10514
  _semver;
10507
10515
  /** Original top level package.json dep section (dep, dev, per, opt) */
10508
10516
  src;
@@ -10676,137 +10684,108 @@ var DepItem = class {
10676
10684
  };
10677
10685
 
10678
10686
  //#endregion
10679
- //#region node_modules/item-queue/dist/inflight.js
10687
+ //#region node_modules/xflight/dist/index.js
10688
+ /** Simple assert that works in browser and Node.js */
10689
+ function assert$2(condition, message) {
10690
+ if (!condition) throw new Error(message);
10691
+ }
10680
10692
  /**
10681
- * async inflight operation memoizer
10693
+ * Keyed store of in-progress work, with start-time and last-check-time tracking.
10694
+ *
10695
+ * This is the generic core: it stores any value type `V` under any {@link RecordKey} and
10696
+ * knows nothing about promises. {@link Inflight} builds promise deduplication on top of it.
10697
+ *
10698
+ * `item-queue` uses this directly - it tracks `{ item, promise }` records keyed by an
10699
+ * incrementing number, which is neither a promise nor a string, so it needs the core rather
10700
+ * than {@link Inflight} (FJM-26).
10701
+ *
10702
+ * @template V The type of the tracked value.
10682
10703
  */
10683
- var Inflight = class {
10684
- constructor() {
10685
- this._count = 0;
10686
- this._inflights = {};
10687
- }
10704
+ var InflightStore = class {
10705
+ /** Map of inflight items by key */
10706
+ _inflights = /* @__PURE__ */ new Map();
10707
+ /**
10708
+ * Track a value under a key.
10709
+ *
10710
+ * @throws If key already exists.
10711
+ */
10688
10712
  add(key, value, now) {
10689
- assert(this._inflights[key] === void 0, `Already has inflight item ${String(key)}`);
10690
- this._count++;
10691
- now = now || Date.now();
10692
- this._inflights[key] = {
10693
- start: now,
10694
- lastXTime: now,
10713
+ assert$2(!this._inflights.has(key), `xflight: item ${String(key)} already exist`);
10714
+ const timestamp = now ?? Date.now();
10715
+ this._inflights.set(key, {
10716
+ start: timestamp,
10717
+ lastXTime: timestamp,
10695
10718
  value
10696
- };
10719
+ });
10697
10720
  return value;
10698
10721
  }
10699
- /**
10700
- * return internal inflights records object
10701
- */
10702
- get inflights() {
10703
- return this._inflights;
10704
- }
10705
- /**
10706
- * get an inflight record
10707
- *
10708
- * @param key
10709
- * @returns
10710
- */
10722
+ /** Get the tracked value for a key, or undefined. */
10711
10723
  get(key) {
10712
- const x = this._inflights[key];
10713
- return x && x.value;
10724
+ return this._inflights.get(key)?.value;
10714
10725
  }
10715
10726
  /**
10716
- * remove an inflight record
10727
+ * Remove the inflight item for a key.
10717
10728
  *
10718
- * @param key
10729
+ * @throws If key does not exist.
10719
10730
  */
10720
10731
  remove(key) {
10721
- assert(this._inflights[key] !== void 0, `Removing non-existing inflight item ${String(key)}`);
10722
- assert(this._count > 0, `Removing inflight item ${String(key)} but count is ${this._count}`);
10723
- this._count--;
10724
- if (this._count === 0) this._inflights = {};
10725
- else this._inflights[key] = void 0;
10732
+ assert$2(this._inflights.has(key), `xflight: removing non-existing item ${String(key)}`);
10733
+ this._inflights.delete(key);
10726
10734
  }
10727
10735
  /**
10728
- * Check if inflight record is empty (ie: no inflight operations)
10736
+ * Iterate the tracked items as `[key, record]` pairs.
10737
+ *
10738
+ * Prefer this over reaching for the internal map - it is what lets a caller sweep every
10739
+ * in-progress entry, which is what item-queue's overdue-work watcher does.
10729
10740
  */
10741
+ entries() {
10742
+ return this._inflights.entries();
10743
+ }
10744
+ /** Whether there are no inflight items. */
10730
10745
  get isEmpty() {
10731
- return this._count === 0;
10746
+ return this._inflights.size === 0;
10732
10747
  }
10733
- /**
10734
- * get number of operations inflight
10735
- */
10748
+ /** The number of inflight items. */
10736
10749
  get count() {
10737
- return this._count;
10750
+ return this._inflights.size;
10738
10751
  }
10739
- /**
10740
- * Get the start time of a inflight operation
10741
- *
10742
- * @param key
10743
- * @returns
10744
- */
10752
+ /** Get the start time (ms since epoch) for a key, or undefined. */
10745
10753
  getStartTime(key) {
10746
- const x = this._inflights[key];
10747
- return x && x.start;
10754
+ return this._inflights.get(key)?.start;
10748
10755
  }
10749
- /**
10750
- * Get the time an inflight operation has elapsed
10751
- * @param key
10752
- * @param now
10753
- * @returns
10754
- */
10756
+ /** Elapsed ms since the start for a key, or -1 if not found. */
10755
10757
  time(key, now) {
10756
- const x = this._inflights[key];
10757
- if (x) return (now || Date.now()) - x.start;
10758
- return -1;
10758
+ const item = this._inflights.get(key);
10759
+ return item ? (now ?? Date.now()) - item.start : -1;
10759
10760
  }
10760
- /**
10761
- * Get the time an inflight operation has elapsed
10762
- * @param key
10763
- * @param now
10764
- * @returns
10765
- */
10761
+ /** Alias for {@link time}. */
10766
10762
  elapseTime(key, now) {
10767
10763
  return this.time(key, now);
10768
10764
  }
10769
- /**
10770
- * Get the time the inflight operation was last checked
10771
- * @param key
10772
- * @returns
10773
- */
10765
+ /** Get the last check time (ms since epoch) for a key, or undefined. */
10774
10766
  getCheckTime(key) {
10775
- const x = this._inflights[key];
10776
- return x && x.lastXTime;
10767
+ return this._inflights.get(key)?.lastXTime;
10777
10768
  }
10778
- /**
10779
- * Get the time elapsed since last check
10780
- *
10781
- * @param key
10782
- * @param now
10783
- * @returns
10784
- */
10769
+ /** Elapsed ms since the last check for a key, or -1 if not found. */
10785
10770
  lastCheckTime(key, now) {
10786
- const x = this._inflights[key];
10787
- if (x) return (now || Date.now()) - x.lastXTime;
10788
- return -1;
10771
+ const item = this._inflights.get(key);
10772
+ return item ? (now ?? Date.now()) - item.lastXTime : -1;
10789
10773
  }
10790
- /**
10791
- * Get the time elapsed since last check
10792
- *
10793
- * @param key
10794
- * @param now
10795
- * @returns
10796
- */
10774
+ /** Alias for {@link lastCheckTime}. */
10797
10775
  elapseCheckTime(key, now) {
10798
10776
  return this.lastCheckTime(key, now);
10799
10777
  }
10800
10778
  /**
10801
- * Reset last check time to `now` or `Date.now()`
10779
+ * Reset the last check time for a key, or for all inflight items if no key is given.
10802
10780
  *
10803
- * @param key
10804
- * @param now
10805
- * @returns
10781
+ * @returns This instance for chaining.
10806
10782
  */
10807
10783
  resetCheckTime(key, now) {
10808
- const x = this._inflights[key];
10809
- if (x) x.lastXTime = now || Date.now();
10784
+ const timestamp = now ?? Date.now();
10785
+ if (key !== void 0) {
10786
+ const item = this._inflights.get(key);
10787
+ if (item) item.lastXTime = timestamp;
10788
+ } else for (const item of this._inflights.values()) item.lastXTime = timestamp;
10810
10789
  return this;
10811
10790
  }
10812
10791
  };
@@ -10826,7 +10805,7 @@ var ItemQueue = class ItemQueue extends EventEmitter$1 {
10826
10805
  super();
10827
10806
  this.Promise = options.Promise || ItemQueue.Promise;
10828
10807
  assert(this.Promise, "ItemQueue: No Promise implementation available");
10829
- this._pending = new Inflight();
10808
+ this._pending = new InflightStore();
10830
10809
  this._itemQ = [];
10831
10810
  if (options.itemQ) this.addItems(options.itemQ, true);
10832
10811
  this._concurrency = options.concurrency || 15;
@@ -11046,7 +11025,7 @@ var ItemQueue = class ItemQueue extends EventEmitter$1 {
11046
11025
  const watched = [];
11047
11026
  const still = [];
11048
11027
  const now = Date.now();
11049
- for (const [id, v] of Object.entries(this._pending.inflights)) if (v) {
11028
+ for (const [id, v] of this._pending.entries()) {
11050
11029
  const lastXTime = this._pending.lastCheckTime(id, now);
11051
11030
  const time = this._pending.time(id, now);
11052
11031
  const overdue = time >= this._watchTime;
@@ -12983,6 +12962,13 @@ function groupMM(mms, groups) {
12983
12962
  return groups;
12984
12963
  }
12985
12964
 
12965
+ //#endregion
12966
+ //#region node_modules/@fynpo/base/dist/util.js
12967
+ /**
12968
+ * ensure a path uses `/` for separator
12969
+ */
12970
+ const posixify$4 = Path.sep === "/" ? (x) => x : (path) => path.replace(/\\/g, "/");
12971
+
12986
12972
  //#endregion
12987
12973
  //#region node_modules/@fynpo/base/dist/packages-config.js
12988
12974
  const DEFAULT_INCLUDE = ["packages/*"];
@@ -13566,13 +13552,6 @@ function makeGitignoreMatcher(cwd) {
13566
13552
  };
13567
13553
  }
13568
13554
 
13569
- //#endregion
13570
- //#region node_modules/@fynpo/base/dist/util.js
13571
- /**
13572
- * ensure a path uses `/` for separator
13573
- */
13574
- const posixify$4 = Path.sep === "/" ? (x) => x : (path) => path.replace(/\\/g, "/");
13575
-
13576
13555
  //#endregion
13577
13556
  //#region node_modules/@fynpo/base/dist/fynpo-dep-graph.js
13578
13557
  /**
@@ -14762,6 +14741,161 @@ const spinner = VisualLogger.spinners[1];
14762
14741
  //#endregion
14763
14742
  //#region lib/util/lifecycle-script-policy.ts
14764
14743
  const TRUSTED_URL_TYPES = /* @__PURE__ */ new Set(["npm"]);
14744
+ /** The install-time lifecycle scripts this policy gates. */
14745
+ const LIFECYCLE_SCRIPTS = [
14746
+ "preinstall",
14747
+ "install",
14748
+ "postinstall"
14749
+ ];
14750
+ /** Valid `fyn.scriptPolicy` modes, loosest to strictest. */
14751
+ const SCRIPT_POLICY_MODES = [
14752
+ "all",
14753
+ "source",
14754
+ "review",
14755
+ "off"
14756
+ ];
14757
+ /**
14758
+ * The default. Nothing runs its install scripts without an approval, workspace
14759
+ * packages excepted - npm 12's model. `--script-policy=source` is the opt-out
14760
+ * back to trusting a package because of where it came from.
14761
+ */
14762
+ const DEFAULT_SCRIPT_POLICY = "review";
14763
+ /**
14764
+ * Split a range union into its parts. Accepts npm's `||` and the single `|` a
14765
+ * hand-written config may use.
14766
+ *
14767
+ * @param {string} range a semver range, possibly a union
14768
+ * @returns {string[]} the parts, trimmed
14769
+ */
14770
+ function splitRange(range) {
14771
+ return String(range).split(/\s*\|\|?\s*/).map((part) => part.trim()).filter(Boolean);
14772
+ }
14773
+ /**
14774
+ * Read an allowScripts key's spec as a semver range.
14775
+ *
14776
+ * A key's spec is a range (`^1.2.3`, `1.2.3`, `^1.2.3 || ^2.0.0`) or it is not
14777
+ * a range at all - a git/URL spec such as `github:user/repo#v1`, which matches
14778
+ * the requested spec literally instead.
14779
+ *
14780
+ * @param {string} spec the part of the key after the package name
14781
+ * @returns {(string|undefined)} the range in `||` form, or undefined
14782
+ */
14783
+ function asRange(spec) {
14784
+ if (!spec) return;
14785
+ const normalized = splitRange(spec).join(" || ");
14786
+ return import_semver.default.validRange(normalized) ? normalized : void 0;
14787
+ }
14788
+ /**
14789
+ * Normalize a `fyn.scriptPolicy` value.
14790
+ *
14791
+ * @param {*} mode the configured value
14792
+ * @param {string} [dflt] mode to use when unset
14793
+ * @returns {string} one of {@link SCRIPT_POLICY_MODES}
14794
+ * @throws {Error} when the value is set but not a known mode
14795
+ */
14796
+ function normalizeScriptPolicy(mode, dflt = DEFAULT_SCRIPT_POLICY) {
14797
+ if (mode === void 0 || mode === null || mode === "") return dflt;
14798
+ const normalized = String(mode).toLowerCase();
14799
+ if (!SCRIPT_POLICY_MODES.includes(normalized)) throw new Error(`fyn scriptPolicy "${mode}" is not valid - expected one of ${SCRIPT_POLICY_MODES.join(", ")}`);
14800
+ return normalized;
14801
+ }
14802
+ /**
14803
+ * Normalize a `fyn.scriptPolicy` value, keeping "not configured" distinct from
14804
+ * the default so scopes can be merged before the default is applied.
14805
+ *
14806
+ * @param {*} mode the configured value
14807
+ * @returns {(string|undefined)} the mode, or undefined when unset
14808
+ * @throws {Error} when the value is set but not a known mode
14809
+ */
14810
+ function normalizeScriptPolicyIfSet(mode) {
14811
+ if (mode === void 0 || mode === null || mode === "") return;
14812
+ return normalizeScriptPolicy(mode);
14813
+ }
14814
+ /**
14815
+ * Pick the stricter of two policy modes. Used to merge a package's setting with
14816
+ * the monorepo's: a package may tighten what the repo asked for, never loosen
14817
+ * it.
14818
+ *
14819
+ * @param {...string} modes modes to compare, unset values ignored
14820
+ * @returns {string} the strictest mode given, or the default when none are
14821
+ */
14822
+ function strictestScriptPolicy(...modes) {
14823
+ const given = modes.filter((mode) => mode !== void 0);
14824
+ if (given.length === 0) return DEFAULT_SCRIPT_POLICY;
14825
+ return given.reduce((strictest, mode) => SCRIPT_POLICY_MODES.indexOf(mode) > SCRIPT_POLICY_MODES.indexOf(strictest) ? mode : strictest);
14826
+ }
14827
+ /**
14828
+ * Normalize a configured allowScripts value into a map.
14829
+ *
14830
+ * A map is used as-is. A list of package names - the `--allow-scripts=a,b` CLI
14831
+ * form - becomes a blanket approval for each name, matching npm's flag.
14832
+ *
14833
+ * @param {(object|string[]|string)} value the configured value
14834
+ * @returns {object} an allowScripts map
14835
+ */
14836
+ function normalizeAllowScriptsConfig(value) {
14837
+ if (!value) return {};
14838
+ const list = Array.isArray(value) ? value : typeof value === "string" ? [value] : void 0;
14839
+ if (list === void 0) return value;
14840
+ return list.flatMap((name) => String(name).split(",")).reduce((map, name) => {
14841
+ const key = name.trim();
14842
+ if (key) map[key] = true;
14843
+ return map;
14844
+ }, {});
14845
+ }
14846
+ /**
14847
+ * Merge allowScripts maps from loosest to tightest scope - fynpo config, then
14848
+ * package.json, then CLI.
14849
+ *
14850
+ * Approvals accumulate, but a `false` at any level is final: a package cannot
14851
+ * approve what the monorepo denied. Cross-key denials (a bare-name `false`
14852
+ * against a version-pinned approval) are resolved at evaluation time, where
14853
+ * every matching key is folded together.
14854
+ *
14855
+ * @param {...object} maps allowScripts maps, loosest scope first
14856
+ * @returns {object} the merged map
14857
+ */
14858
+ function mergeAllowScripts(...maps) {
14859
+ const merged = {};
14860
+ for (const map of maps) {
14861
+ const normalized = normalizeAllowScriptsConfig(map);
14862
+ for (const key of Object.keys(normalized)) {
14863
+ if (merged[key] === false) continue;
14864
+ merged[key] = normalized[key];
14865
+ }
14866
+ }
14867
+ return merged;
14868
+ }
14869
+ /**
14870
+ * Fold the `fyn.denyScripts` map for a package.
14871
+ *
14872
+ * The map has the same shape and key grammar as `fyn.allowScripts` -
14873
+ * `{ semver, scripts }` with both fields optional, plus the shorthand forms -
14874
+ * because it answers the same two questions. The only difference is that a
14875
+ * match denies: an absent `semver` denies every version, an absent `scripts`
14876
+ * denies every install script.
14877
+ *
14878
+ * Entries need no `!` markers; every entry in this map is already negative.
14879
+ *
14880
+ * @param {object} depInfo resolved package data
14881
+ * @param {object} denyScripts the effective `fyn.denyScripts` map
14882
+ * @returns {{denyAll:boolean, scripts:Set<string>, key:(string|undefined)}} what
14883
+ * this map denies for this package
14884
+ */
14885
+ function foldDenyScripts(depInfo, denyScripts) {
14886
+ const acc = makeAcc();
14887
+ if (!denyScripts) return {
14888
+ denyAll: false,
14889
+ scripts: acc.deny,
14890
+ key: void 0
14891
+ };
14892
+ const key = foldAllowScripts(depInfo, denyScripts, acc, { version: depInfo.version });
14893
+ return {
14894
+ denyAll: acc.allowAll || acc.denyAll || acc.denied,
14895
+ scripts: /* @__PURE__ */ new Set([...acc.scripts, ...acc.deny]),
14896
+ key
14897
+ };
14898
+ }
14765
14899
  /**
14766
14900
  * Derive the effective source urlType for a dependency item.
14767
14901
  *
@@ -14799,9 +14933,28 @@ function getUrlType(depInfo) {
14799
14933
  if (spec) return analyze(spec).urlType;
14800
14934
  }
14801
14935
  /**
14936
+ * Whether a resolved package came from the workspace itself - a file:/link:
14937
+ * dependency or a fynpo sibling - rather than from a registry or a URL.
14938
+ *
14939
+ * Under `"review"` these are exempt: an allowlist is a review gate on code you
14940
+ * did not write, and monorepo source is reviewed by the PR that changed it.
14941
+ *
14942
+ * @param {object} depInfo resolved package data
14943
+ * @returns {boolean} true if the package is a workspace-local dependency
14944
+ */
14945
+ function isLocalSource(depInfo) {
14946
+ if (!depInfo) return false;
14947
+ if (depInfo.local) return true;
14948
+ const depItem = depInfo[DEP_ITEM];
14949
+ if (depItem && depItem.localType) return true;
14950
+ const spec = depItem && depItem.semver || depInfo[SEMVER];
14951
+ return Boolean(spec && analyze(spec).localType);
14952
+ }
14953
+ /**
14802
14954
  * Build the candidate whitelist keys for a package. Matching accepts BOTH the
14803
14955
  * original requested spec and the resolved version, e.g. `foo@github:user/repo`
14804
- * and `foo@2.3.0`.
14956
+ * and `foo@2.3.0`, plus the bare package name - npm's key form, where the
14957
+ * version it approved lives on the value side.
14805
14958
  *
14806
14959
  * @param {object} depInfo resolved package data
14807
14960
  * @returns {string[]} candidate keys, spec form first
@@ -14812,46 +14965,153 @@ function makeAllowKeys(depInfo) {
14812
14965
  const keys = [];
14813
14966
  if (spec) keys.push(`${depInfo.name}@${spec}`);
14814
14967
  if (depInfo.version && depInfo.version !== spec) keys.push(`${depInfo.name}@${depInfo.version}`);
14968
+ keys.push(depInfo.name);
14815
14969
  return keys;
14816
14970
  }
14817
14971
  /**
14818
- * Fold a single allowScripts entry value into an accumulator. Accepts an array
14819
- * of script names, a single script name, or the wildcard `true`/`"*"` to allow
14820
- * all scripts for the package. Script names are normalized to lowercase.
14972
+ * Classify a string allowScripts value. A lifecycle script name allows just
14973
+ * that script (fyn's form); anything that parses as a semver range is npm's
14974
+ * version pin, allowing all scripts for the matching version only.
14975
+ *
14976
+ * @param {string} value the string value
14977
+ * @returns {string} "script" or "version"
14978
+ */
14979
+ function classifyStringEntry(value) {
14980
+ if (LIFECYCLE_SCRIPTS.includes(value.toLowerCase())) return "script";
14981
+ return import_semver.default.validRange(value) ? "version" : "script";
14982
+ }
14983
+ /**
14984
+ * Fold a single allowScripts entry value into an accumulator.
14985
+ *
14986
+ * Every form is accepted, though fyn only ever writes the object one:
14987
+ *
14988
+ * | value | meaning |
14989
+ * |---|---|
14990
+ * | `{ semver, scripts }` | fyn's form - either field absent means "all" |
14991
+ * | `true` / `"*"` | all install scripts |
14992
+ * | `["install"]` / `"install"` | only those scripts |
14993
+ * | `"1.2.3"` / `"^1.2.3"` | npm's form - all scripts, only matching versions |
14994
+ * | `false` | denied outright |
14821
14995
  *
14822
14996
  * @param {(string[]|string|boolean)} value the allowScripts entry value
14823
- * @param {{allowAll:boolean, scripts:Set<string>}} acc accumulator to fold into
14824
- * @returns {{allowAll:boolean, scripts:Set<string>}} the accumulator
14997
+ * @param {{allowAll:boolean, scripts:Set<string>, denied:boolean}} acc
14998
+ * accumulator to fold into
14999
+ * @param {object} [ctx] matching context
15000
+ * @param {string} [ctx.version] the package's resolved version, for version pins
15001
+ * @returns {{allowAll:boolean, scripts:Set<string>, denied:boolean}} the accumulator
14825
15002
  */
14826
- function normalizeAllowEntry(value, acc) {
15003
+ function normalizeAllowEntry(value, acc, ctx = {}) {
15004
+ if (value === false) {
15005
+ acc.denied = true;
15006
+ return acc;
15007
+ }
14827
15008
  if (value === true || value === "*") {
14828
15009
  acc.allowAll = true;
14829
15010
  return acc;
14830
15011
  }
15012
+ if (value && typeof value === "object" && !Array.isArray(value)) {
15013
+ const range = asRange(value.semver);
15014
+ if (value.semver && !(range && ctx.version && satisfies(ctx.version, range))) return acc;
15015
+ if (value.scripts === void 0) {
15016
+ acc.allowAll = true;
15017
+ return acc;
15018
+ }
15019
+ return normalizeAllowEntry(value.scripts, acc, ctx);
15020
+ }
14831
15021
  let list = [];
14832
15022
  if (Array.isArray(value)) list = value;
14833
15023
  else if (value !== void 0) list = [value];
14834
- for (const s of list) if (s === true || s === "*") acc.allowAll = true;
14835
- else if (typeof s === "string") acc.scripts.add(s.toLowerCase());
15024
+ for (const s of list) {
15025
+ if (s === false) {
15026
+ acc.denied = true;
15027
+ continue;
15028
+ }
15029
+ if (s === true || s === "*") {
15030
+ acc.allowAll = true;
15031
+ continue;
15032
+ }
15033
+ if (typeof s !== "string") continue;
15034
+ const negated = s.startsWith("!");
15035
+ const name = negated || s.startsWith("+") ? s.slice(1) : s;
15036
+ if (negated) {
15037
+ if (name === "*") acc.denyAll = true;
15038
+ else acc.deny.add(name.toLowerCase());
15039
+ continue;
15040
+ }
15041
+ if (classifyStringEntry(name) === "version") {
15042
+ if (ctx.version && satisfies(ctx.version, name)) acc.allowAll = true;
15043
+ } else acc.scripts.add(name.toLowerCase());
15044
+ }
14836
15045
  return acc;
14837
15046
  }
14838
15047
  /**
14839
- * Fold any matching `fyn.allowScripts` entries (by spec or resolved-version key)
14840
- * into the accumulator.
15048
+ * A fresh accumulator for folding allow or deny entries.
15049
+ *
15050
+ * @returns {{allowAll:boolean, scripts:Set<string>, deny:Set<string>,
15051
+ * denyAll:boolean, denied:boolean}} the accumulator
15052
+ */
15053
+ function makeAcc() {
15054
+ return {
15055
+ allowAll: false,
15056
+ scripts: /* @__PURE__ */ new Set(),
15057
+ deny: /* @__PURE__ */ new Set(),
15058
+ denyAll: false,
15059
+ denied: false
15060
+ };
15061
+ }
15062
+ const allowIndexCache = /* @__PURE__ */ new WeakMap();
15063
+ /**
15064
+ * Group an allowScripts map by package name, parsing each key once.
14841
15065
  *
14842
- * @param {string[]} keys candidate keys from {@link makeAllowKeys}
15066
+ * @param {object} allowScripts the map
15067
+ * @returns {Map<string, object[]>} entries by package name
15068
+ */
15069
+ function indexAllowScripts(allowScripts) {
15070
+ if (!allowScripts) return /* @__PURE__ */ new Map();
15071
+ const cached = allowIndexCache.get(allowScripts);
15072
+ if (cached) return cached;
15073
+ const index = /* @__PURE__ */ new Map();
15074
+ for (const key of Object.keys(allowScripts)) {
15075
+ const at = key.indexOf("@", key.startsWith("@") ? 1 : 0);
15076
+ const name = at < 0 ? key : key.slice(0, at);
15077
+ const spec = at < 0 ? void 0 : key.slice(at + 1);
15078
+ const entries = index.get(name) || [];
15079
+ entries.push({
15080
+ key,
15081
+ spec,
15082
+ range: asRange(spec),
15083
+ value: allowScripts[key]
15084
+ });
15085
+ index.set(name, entries);
15086
+ }
15087
+ allowIndexCache.set(allowScripts, index);
15088
+ return index;
15089
+ }
15090
+ /**
15091
+ * Fold every `fyn.allowScripts` entry that matches this package into the
15092
+ * accumulator.
15093
+ *
15094
+ * A key matches when it is the bare package name (every version), when its
15095
+ * spec is a semver range the resolved version satisfies (`sharp@^0.34.0`,
15096
+ * `sharp@^0.34.0 || ^0.35.0`), or - for a git/URL dependency, where there is no
15097
+ * version to range over - when its spec is literally the requested spec.
15098
+ *
15099
+ * @param {object} depInfo resolved package data
14843
15100
  * @param {object} allowScripts the project's `fyn.allowScripts` map
14844
- * @param {{allowAll:boolean, scripts:Set<string>}} acc accumulator to fold into
15101
+ * @param {{allowAll:boolean, scripts:Set<string>, denied:boolean}} acc
15102
+ * accumulator to fold into
15103
+ * @param {object} [ctx] matching context passed to {@link normalizeAllowEntry}
14845
15104
  * @returns {(string|undefined)} the first matched key, if any
14846
15105
  */
14847
- function foldAllowScripts(keys, allowScripts, acc) {
15106
+ function foldAllowScripts(depInfo, allowScripts, acc, ctx) {
15107
+ const entries = indexAllowScripts(allowScripts).get(depInfo.name);
15108
+ if (!entries) return;
15109
+ const depItem = depInfo[DEP_ITEM];
15110
+ const requestedSpec = depItem && depItem.semver || depInfo[SEMVER];
14848
15111
  let matchedKey;
14849
- for (const key of keys) {
14850
- const value = allowScripts && allowScripts[key];
14851
- if (value !== void 0) {
14852
- if (!matchedKey) matchedKey = key;
14853
- normalizeAllowEntry(value, acc);
14854
- }
15112
+ for (const entry of entries) if (entry.spec === void 0 || entry.spec === requestedSpec || entry.range !== void 0 && Boolean(depInfo.version) && satisfies(depInfo.version, entry.range)) {
15113
+ if (!matchedKey) matchedKey = entry.key;
15114
+ normalizeAllowEntry(entry.value, acc, ctx);
14855
15115
  }
14856
15116
  return matchedKey;
14857
15117
  }
@@ -14870,42 +15130,104 @@ function isTopLevelDep(depInfo) {
14870
15130
  * Evaluate the lifecycle-script policy for a resolved package.
14871
15131
  *
14872
15132
  * @param {object} depInfo resolved package data
14873
- * @param {object} allowScripts the project's package.json `fyn.allowScripts` map
15133
+ * @param {object} allowScripts the effective `fyn.allowScripts` map
14874
15134
  * @param {object} [options] additional policy options
14875
15135
  * @param {(boolean|string|string[])} [options.allowTopLevel] the project's
14876
- * `fyn.allowTopLevelScripts` config. When truthy, non-registry packages that
14877
- * are declared directly in the top-level package.json are allowed to run the
14878
- * given lifecycle scripts (`true`/`"*"` = all, or a list of script names).
14879
- * @returns {{trusted:boolean, urlType:(string|undefined), allowAll:boolean,
15136
+ * `fyn.allowTopLevelScripts` config. When truthy, packages that are declared
15137
+ * directly in the top-level package.json are allowed to run the given
15138
+ * lifecycle scripts (`true`/`"*"` = all, or a list of script names).
15139
+ * @param {string} [options.mode] the `fyn.scriptPolicy` mode in effect
15140
+ * @param {boolean} [options.reviewLocalPackages] when true, workspace-local
15141
+ * packages lose their exemption and need an allowlist entry like any other
15142
+ * @returns {{trusted:boolean, denied:boolean, urlType:(string|undefined),
15143
+ * local:boolean, mode:string, reason:string, allowAll:boolean,
14880
15144
  * allowed:Set<string>, key:(string|undefined), topLevel:boolean}} the
14881
15145
  * resolved script policy
14882
15146
  */
14883
15147
  function evaluateScriptPolicy(depInfo, allowScripts, options = {}) {
15148
+ const { allowTopLevel, reviewLocalPackages = false, denyScripts } = options;
15149
+ const mode = normalizeScriptPolicy(options.mode);
14884
15150
  const urlType = getUrlType(depInfo);
14885
15151
  const keys = makeAllowKeys(depInfo);
14886
15152
  const topLevel = isTopLevelDep(depInfo);
14887
- if (!urlType || TRUSTED_URL_TYPES.has(urlType)) return {
14888
- trusted: true,
15153
+ const local = isLocalSource(depInfo);
15154
+ const base = {
14889
15155
  urlType,
15156
+ local,
15157
+ mode,
15158
+ topLevel,
15159
+ key: keys[0]
15160
+ };
15161
+ const nothing = {
15162
+ trusted: false,
15163
+ allowAll: false,
15164
+ allowed: /* @__PURE__ */ new Set(),
15165
+ deniedScripts: /* @__PURE__ */ new Set()
15166
+ };
15167
+ const deny = foldDenyScripts(depInfo, denyScripts);
15168
+ if (mode === "off") return {
15169
+ ...base,
15170
+ ...nothing,
15171
+ denied: true,
15172
+ reason: "off"
15173
+ };
15174
+ if (deny.denyAll) return {
15175
+ ...base,
15176
+ ...nothing,
15177
+ key: deny.key || keys[0],
15178
+ denied: true,
15179
+ reason: "denied"
15180
+ };
15181
+ const acc = makeAcc();
15182
+ const key = foldAllowScripts(depInfo, allowScripts, acc, { version: depInfo.version }) || keys[0];
15183
+ if (acc.denied || acc.denyAll) return {
15184
+ ...base,
15185
+ ...nothing,
15186
+ key,
15187
+ denied: true,
15188
+ reason: "denied"
15189
+ };
15190
+ const denied = /* @__PURE__ */ new Set([...deny.scripts, ...acc.deny]);
15191
+ if (mode === "all") return {
15192
+ ...base,
15193
+ key,
15194
+ trusted: true,
15195
+ denied: false,
14890
15196
  allowAll: true,
14891
15197
  allowed: /* @__PURE__ */ new Set(),
14892
- key: keys[0],
14893
- topLevel
15198
+ deniedScripts: denied,
15199
+ reason: "all"
14894
15200
  };
14895
- const acc = {
14896
- allowAll: false,
14897
- scripts: /* @__PURE__ */ new Set()
15201
+ if (local && !urlType && !reviewLocalPackages) return {
15202
+ ...base,
15203
+ key,
15204
+ trusted: true,
15205
+ denied: false,
15206
+ allowAll: true,
15207
+ allowed: /* @__PURE__ */ new Set(),
15208
+ deniedScripts: denied,
15209
+ reason: "local"
15210
+ };
15211
+ if (mode === "source" && (!urlType || TRUSTED_URL_TYPES.has(urlType))) return {
15212
+ ...base,
15213
+ key,
15214
+ trusted: true,
15215
+ denied: false,
15216
+ allowAll: true,
15217
+ allowed: /* @__PURE__ */ new Set(),
15218
+ deniedScripts: denied,
15219
+ reason: "registry"
14898
15220
  };
14899
- const matchedKey = foldAllowScripts(keys, allowScripts, acc);
14900
- const { allowTopLevel } = options;
14901
- if (topLevel && allowTopLevel !== void 0 && allowTopLevel !== false) normalizeAllowEntry(allowTopLevel, acc);
15221
+ if (mode === "source" && topLevel && allowTopLevel !== void 0 && allowTopLevel !== false) normalizeAllowEntry(allowTopLevel, acc, { version: depInfo.version });
14902
15222
  return {
15223
+ ...base,
14903
15224
  trusted: false,
14904
- urlType,
15225
+ denied: acc.denied,
14905
15226
  allowAll: acc.allowAll,
14906
15227
  allowed: acc.scripts,
14907
- key: matchedKey || keys[0],
14908
- topLevel
15228
+ deniedScripts: /* @__PURE__ */ new Set([...denied, ...acc.deny]),
15229
+ key,
15230
+ reason: mode === "review" ? "review" : "untrusted-source"
14909
15231
  };
14910
15232
  }
14911
15233
  /**
@@ -14914,8 +15236,11 @@ function evaluateScriptPolicy(depInfo, allowScripts, options = {}) {
14914
15236
  * @returns {boolean} whether the script is allowed to run
14915
15237
  */
14916
15238
  function isScriptAllowed(policy, scriptName) {
15239
+ if (policy.denied) return false;
15240
+ const name = String(scriptName).toLowerCase();
15241
+ if (policy.deniedScripts && policy.deniedScripts.has(name)) return false;
14917
15242
  if (policy.trusted || policy.allowAll) return true;
14918
- return policy.allowed.has(String(scriptName).toLowerCase());
15243
+ return policy.allowed.has(name);
14919
15244
  }
14920
15245
 
14921
15246
  //#endregion
@@ -14995,15 +15320,38 @@ function getPackageRawInfo(obj) {
14995
15320
  //#region lib/pkg-dep-resolver.ts
14996
15321
  const simpleSemverCompare = simpleCompare;
14997
15322
  const { checkPkgOsCpu, relativePath, unSlashNpmScope } = fyntil;
15323
+ /**
15324
+ * Decide whether a lock entry that records no package.json still needs a real meta fetch.
15325
+ *
15326
+ * `_missingJson` says the lock never captured the package's json, so its dependencies are
15327
+ * unknown rather than absent. Normally that has to be fetched for real before the package can be
15328
+ * resolved into the tree.
15329
+ *
15330
+ * The exception is a package this platform cannot use. An optional dep skipped on os/cpu is
15331
+ * recorded from its registry meta alone (pkg-dep-resolver synthesizes an os/cpu-only json for
15332
+ * it), so every such entry is `_missingJson` by construction - and on a machine that will never
15333
+ * install it, its dependencies do not matter. Fetching them anyway would cost a packument for
15334
+ * every platform variant in the tree on every install, which is the whole reason those entries
15335
+ * are in the lock (FPM-63, FPM-93).
15336
+ *
15337
+ * @param metaJson locked version metadata
15338
+ * @returns true when the real meta has to be fetched before this item can resolve
15339
+ */
15340
+ function lockedMetaNeedsFetch(metaJson) {
15341
+ if (!metaJson || !metaJson._missingJson) return false;
15342
+ return checkPkgOsCpu(metaJson) === true;
15343
+ }
14998
15344
  const failMetaMsg = (name) => `Unable to retrieve meta for package ${name} - If you've updated its version recently, try to run fyn with '--refresh-meta' again`;
14999
15345
  var PkgDepResolver = class {
15000
15346
  _options;
15001
15347
  _fyn;
15002
15348
  _pkgSrcMgr;
15349
+ /** read by `Fyn` when it starts the dist fetcher, so not private (FJM-154) */
15003
15350
  _data;
15004
15351
  _promiseQ;
15005
15352
  _defer;
15006
15353
  _optResolver;
15354
+ /** `Fyn.lockOnly` - the mode string "lock-only", or false; only read for truth */
15007
15355
  _lockOnly;
15008
15356
  _depthResolving;
15009
15357
  _localsByDepth;
@@ -15093,6 +15441,11 @@ var PkgDepResolver = class {
15093
15441
  this._defer.resolve();
15094
15442
  }
15095
15443
  }
15444
+ /**
15445
+ * @param depInfo where the peer resolutions are stashed - anything carrying a `res` bag.
15446
+ * The installer passes the whole `DepData` for the app's own peer deps ("your app"),
15447
+ * `resolvePeerDep` passes one package's info (FJM-154).
15448
+ */
15096
15449
  resolvePkgPeerDep(json, pkgId, depInfo) {
15097
15450
  const peerDepMeta = json.peerDependenciesMeta || {};
15098
15451
  import_lodash_min.default.each(json.peerDependencies || json.peerDepenencies, (semver, name) => {
@@ -15414,7 +15767,8 @@ var PkgDepResolver = class {
15414
15767
  findVersionFromDistTag(meta, semver) {
15415
15768
  if (import_semver.default.validRange(semver) === null) {
15416
15769
  const lockRsv = meta[LOCK_RSEMVERS];
15417
- if (lockRsv && lockRsv[semver]) return lockRsv[semver];
15770
+ const locked = lockRsv && lockRsv[semver];
15771
+ if (locked) return Array.isArray(locked) ? locked[0] : locked;
15418
15772
  const dtags = meta["dist-tags"];
15419
15773
  if (dtags && dtags.hasOwnProperty(semver)) return dtags[semver];
15420
15774
  }
@@ -15431,7 +15785,6 @@ var PkgDepResolver = class {
15431
15785
  if (!item.localType) item.localType = localFromMeta;
15432
15786
  if (this._fyn.enforceRegistryDeps && item.parent.depth >= 1) this._enforceRegistryDep(item, item.parent);
15433
15787
  }
15434
- const OPT_FAILED_PLATFORM = 3;
15435
15788
  const platformCheck = () => {
15436
15789
  const sysCheck = checkPkgOsCpu(metaJson);
15437
15790
  if (sysCheck !== true) return `package ${log_format_default.pkgId(item)} platform check failed: ${sysCheck}`;
@@ -15441,7 +15794,7 @@ var PkgDepResolver = class {
15441
15794
  if (item.dsrc && item.dsrc.includes("opt") && !item.optChecked) {
15442
15795
  if (sysCheck !== true) {
15443
15796
  logger$1.verbose(`optional dependencies ${sysCheck}`);
15444
- if (!item.optFailed) item.optFailed = OPT_FAILED_PLATFORM;
15797
+ if (!item.optFailed) item.optFailed = 3;
15445
15798
  item.optChecked = true;
15446
15799
  } else {
15447
15800
  logger$1.verbose("adding package to opt check:", item.name, item.semver, item.resolved);
@@ -15456,7 +15809,7 @@ var PkgDepResolver = class {
15456
15809
  logger$1.error(sysCheck);
15457
15810
  throw new Error(sysCheck);
15458
15811
  }
15459
- const pkgsData = this._data.getPkgsData(item.optFailed);
15812
+ const pkgsData = this._data.getPkgsData(Boolean(item.optFailed));
15460
15813
  let pkgV;
15461
15814
  let kpkg = pkgsData[item.name];
15462
15815
  if (kpkg) {
@@ -15501,10 +15854,17 @@ var PkgDepResolver = class {
15501
15854
  if (metaJson.hasPI || scripts.preinstall || scripts.preInstall) pkgV.hasPI = 1;
15502
15855
  if (metaJson.hasI || scripts.install || scripts.postinstall || scripts.postInstall) pkgV.hasI = 1;
15503
15856
  }
15504
- if (item.optFailed === OPT_FAILED_PLATFORM && !pkgV.json && (metaJson.os || metaJson.cpu)) pkgV.json = {
15505
- os: metaJson.os,
15506
- cpu: metaJson.cpu
15507
- };
15857
+ if (item.optFailed === 3 && !pkgV.json && (metaJson.os || metaJson.cpu)) pkgV.json = import_lodash_min.default.pick(metaJson, [
15858
+ "os",
15859
+ "cpu",
15860
+ "dependencies",
15861
+ "optionalDependencies",
15862
+ "peerDependencies",
15863
+ "bundleDependencies",
15864
+ "bundledDependencies",
15865
+ "_hasShrinkwrap",
15866
+ "_missingJson"
15867
+ ]);
15508
15868
  if (localFromMeta) {
15509
15869
  pkgV.local = item.localType;
15510
15870
  item.fullPath = pkgV.dir = pkgV.dist.fullPath;
@@ -15530,8 +15890,8 @@ var PkgDepResolver = class {
15530
15890
  }
15531
15891
  }
15532
15892
  }
15893
+ if (metaJson.deprecated) pkgV.deprecated = metaJson.deprecated;
15533
15894
  if (!optFailed) {
15534
- if (metaJson.deprecated) pkgV.deprecated = metaJson.deprecated;
15535
15895
  let deepRes = false;
15536
15896
  if (firstSeenVersion || (deepRes = this._shouldDeepResolve(pkgV))) {
15537
15897
  const pkgDepth = this._depthResolving[item.depth][item.name];
@@ -15592,7 +15952,7 @@ var PkgDepResolver = class {
15592
15952
  const getKnownSemver = () => {
15593
15953
  const find = (rsv) => {
15594
15954
  let x = rsv && rsv[item.semver];
15595
- if (!x) return x;
15955
+ if (!x) return void 0;
15596
15956
  if (Array.isArray(x)) x = x[0];
15597
15957
  if (noLocal && isLocal(x)) return false;
15598
15958
  return x;
@@ -15860,13 +16220,12 @@ ${item.depPath.join(" > ")}`);
15860
16220
  this._applyOverrides(item);
15861
16221
  this._replaceWithResolutionsData(item);
15862
16222
  return (!item.semverPath || this._fyn.preferLock ? tryLock().then((r) => r || item.semverPath && tryLocal() || false) : tryLocal().then((r) => r || tryLock())).then((r) => {
15863
- if (r && !import_lodash_min.default.get(r, [
16223
+ if (r && !lockedMetaNeedsFetch(import_lodash_min.default.get(r, [
15864
16224
  "meta",
15865
16225
  "versions",
15866
- r.resolved,
15867
- "_missingJson"
15868
- ])) return r;
15869
- if (this._lockOnly || item.localType) return;
16226
+ r.resolved
16227
+ ]))) return r;
16228
+ if (this._lockOnly || item.localType) return r || void 0;
15870
16229
  return this._pkgSrcMgr.fetchMeta(item).then((meta) => {
15871
16230
  if (!meta) throw new Error(failMetaMsg(item.name));
15872
16231
  const updated = this._fyn.depLocker.update(item, meta);
@@ -15947,7 +16306,7 @@ var Qt = Symbol("paused");
15947
16306
  var Bt = Symbol("resume");
15948
16307
  var b = Symbol("buffer");
15949
16308
  var N = Symbol("pipes");
15950
- var _$22 = Symbol("bufferLength");
16309
+ var _$23 = Symbol("bufferLength");
15951
16310
  var bi = Symbol("bufferPush");
15952
16311
  var Ie = Symbol("bufferShift");
15953
16312
  var L = Symbol("objectMode");
@@ -16007,7 +16366,7 @@ var A = class extends EventEmitter$2 {
16007
16366
  [De] = !1;
16008
16367
  [Ne] = !1;
16009
16368
  [qt] = null;
16010
- [_$22] = 0;
16369
+ [_$23] = 0;
16011
16370
  [S] = !1;
16012
16371
  [Jt];
16013
16372
  [Ce] = !1;
@@ -16023,7 +16382,7 @@ var A = class extends EventEmitter$2 {
16023
16382
  i && (this[Jt] = i, i.aborted ? this[xi]() : i.addEventListener("abort", () => this[xi]()));
16024
16383
  }
16025
16384
  get bufferLength() {
16026
- return this[_$22];
16385
+ return this[_$23];
16027
16386
  }
16028
16387
  get encoding() {
16029
16388
  return this[z];
@@ -16064,12 +16423,12 @@ var A = class extends EventEmitter$2 {
16064
16423
  else if (Vr(t)) t = Buffer.from(t);
16065
16424
  else if (typeof t != "string") throw new Error("Non-contiguous data written to non-objectMode stream");
16066
16425
  }
16067
- return this[L] ? (this[g$1] && this[_$22] !== 0 && this[Ae](!0), this[g$1] ? this.emit("data", t) : this[bi](t), this[_$22] !== 0 && this.emit("readable"), i && r(i), this[g$1]) : t.length ? (typeof t == "string" && !(e === this[z] && !this[Mt]?.lastNeed) && (t = Buffer.from(t, e)), Buffer.isBuffer(t) && this[z] && (t = this[Mt].write(t)), this[g$1] && this[_$22] !== 0 && this[Ae](!0), this[g$1] ? this.emit("data", t) : this[bi](t), this[_$22] !== 0 && this.emit("readable"), i && r(i), this[g$1]) : (this[_$22] !== 0 && this.emit("readable"), i && r(i), this[g$1]);
16426
+ return this[L] ? (this[g$1] && this[_$23] !== 0 && this[Ae](!0), this[g$1] ? this.emit("data", t) : this[bi](t), this[_$23] !== 0 && this.emit("readable"), i && r(i), this[g$1]) : t.length ? (typeof t == "string" && !(e === this[z] && !this[Mt]?.lastNeed) && (t = Buffer.from(t, e)), Buffer.isBuffer(t) && this[z] && (t = this[Mt].write(t)), this[g$1] && this[_$23] !== 0 && this[Ae](!0), this[g$1] ? this.emit("data", t) : this[bi](t), this[_$23] !== 0 && this.emit("readable"), i && r(i), this[g$1]) : (this[_$23] !== 0 && this.emit("readable"), i && r(i), this[g$1]);
16068
16427
  }
16069
16428
  read(t) {
16070
16429
  if (this[S]) return null;
16071
- if (this[C] = !1, this[_$22] === 0 || t === 0 || t && t > this[_$22]) return this[J](), null;
16072
- this[L] && (t = null), this[b].length > 1 && !this[L] && (this[b] = [this[z] ? this[b].join("") : Buffer.concat(this[b], this[_$22])]);
16430
+ if (this[C] = !1, this[_$23] === 0 || t === 0 || t && t > this[_$23]) return this[J](), null;
16431
+ this[L] && (t = null), this[b].length > 1 && !this[L] && (this[b] = [this[z] ? this[b].join("") : Buffer.concat(this[b], this[_$23])]);
16073
16432
  let e = this[Ns](t || null, this[b][0]);
16074
16433
  return this[J](), e;
16075
16434
  }
@@ -16077,7 +16436,7 @@ var A = class extends EventEmitter$2 {
16077
16436
  if (this[L]) this[Ie]();
16078
16437
  else {
16079
16438
  let i = e;
16080
- t === i.length || t === null ? this[Ie]() : typeof i == "string" ? (this[b][0] = i.slice(t), e = i.slice(0, t), this[_$22] -= t) : (this[b][0] = i.subarray(t), e = i.subarray(0, t), this[_$22] -= t);
16439
+ t === i.length || t === null ? this[Ie]() : typeof i == "string" ? (this[b][0] = i.slice(t), e = i.slice(0, t), this[_$23] -= t) : (this[b][0] = i.subarray(t), e = i.subarray(0, t), this[_$23] -= t);
16081
16440
  }
16082
16441
  return this.emit("data", e), !this[b].length && !this[Q] && this.emit("drain"), e;
16083
16442
  }
@@ -16103,10 +16462,10 @@ var A = class extends EventEmitter$2 {
16103
16462
  return this[Qt];
16104
16463
  }
16105
16464
  [bi](t) {
16106
- this[L] ? this[_$22] += 1 : this[_$22] += t.length, this[b].push(t);
16465
+ this[L] ? this[_$23] += 1 : this[_$23] += t.length, this[b].push(t);
16107
16466
  }
16108
16467
  [Ie]() {
16109
- return this[L] ? this[_$22] -= 1 : this[_$22] -= this[b][0].length, this[b].shift();
16468
+ return this[L] ? this[_$23] -= 1 : this[_$23] -= this[b][0].length, this[b].shift();
16110
16469
  }
16111
16470
  [Ae](t = !1) {
16112
16471
  do ;
@@ -16132,7 +16491,7 @@ while (this[As](this[Ie]()) && this[b].length);
16132
16491
  on(t, e) {
16133
16492
  let i = super.on(t, e);
16134
16493
  if (t === "data") this[C] = !1, this[Rt]++, !this[N].length && !this[g$1] && this[Bt]();
16135
- else if (t === "readable" && this[_$22] !== 0) super.emit("readable");
16494
+ else if (t === "readable" && this[_$23] !== 0) super.emit("readable");
16136
16495
  else if (Kr(t) && this[nt]) super.emit(t), this.removeAllListeners(t);
16137
16496
  else if (t === "error" && this[qt]) {
16138
16497
  let r = e;
@@ -16283,7 +16642,7 @@ while (this[As](this[Ie]()) && this[b].length);
16283
16642
  }
16284
16643
  destroy(t) {
16285
16644
  if (this[S]) return t ? this.emit("error", t) : this.emit(S), this;
16286
- this[S] = !0, this[C] = !0, this[b].length = 0, this[_$22] = 0;
16645
+ this[S] = !0, this[C] = !0, this[b].length = 0, this[_$23] = 0;
16287
16646
  let e = this;
16288
16647
  return typeof e.close == "function" && !this[Ne] && e.close(), t ? this.emit("error", t) : this.emit(S), this;
16289
16648
  }
@@ -18102,7 +18461,7 @@ var Ee = Symbol("processing");
18102
18461
  var ai = Symbol("processJob");
18103
18462
  var G = Symbol("jobs");
18104
18463
  var ls = Symbol("jobDone");
18105
- var ci$2 = Symbol("addFSEntry");
18464
+ var ci$3 = Symbol("addFSEntry");
18106
18465
  var or = Symbol("addTarEntry");
18107
18466
  var ds$1 = Symbol("stat");
18108
18467
  var us = Symbol("readdir");
@@ -18161,7 +18520,7 @@ var wt = class extends A {
18161
18520
  }
18162
18521
  write(t) {
18163
18522
  if (this[me]) throw new Error("write after end");
18164
- return typeof t == "string" ? this[ci$2](t) : this[or](t), this.flowing;
18523
+ return typeof t == "string" ? this[ci$3](t) : this[or](t), this.flowing;
18165
18524
  }
18166
18525
  [or](t) {
18167
18526
  let e = f(Path.resolve(this.cwd, t.path));
@@ -18172,7 +18531,7 @@ var wt = class extends A {
18172
18531
  }
18173
18532
  this[Ft]();
18174
18533
  }
18175
- [ci$2](t) {
18534
+ [ci$3](t) {
18176
18535
  let e = f(Path.resolve(this.cwd, t));
18177
18536
  this[W].push(new pi(t, e)), this[Ft]();
18178
18537
  }
@@ -18281,7 +18640,7 @@ var wt = class extends A {
18281
18640
  [di](t) {
18282
18641
  t.piped = !0, t.readdir && t.readdir.forEach((r) => {
18283
18642
  let n = t.path, o = n === "./" ? "" : n.replace(/\/*$/, "/");
18284
- this[ci$2](o + r);
18643
+ this[ci$3](o + r);
18285
18644
  });
18286
18645
  let e = t.entry, i = this.zip;
18287
18646
  if (!e) throw new Error("cannot pipe without source");
@@ -18316,7 +18675,7 @@ var kt = class extends wt {
18316
18675
  let e = t.entry, i = this.zip;
18317
18676
  if (t.readdir && t.readdir.forEach((r) => {
18318
18677
  let n = t.path, o = n === "./" ? "" : n.replace(/\/*$/, "/");
18319
- this[ci$2](o + r);
18678
+ this[ci$3](o + r);
18320
18679
  }), !e) throw new Error("Cannot pipe without source");
18321
18680
  i ? e.on("data", (r) => {
18322
18681
  i.write(r);
@@ -27372,7 +27731,7 @@ var require_scan$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
27372
27731
 
27373
27732
  //#endregion
27374
27733
  //#region node_modules/spdx-expression-parse/parse.js
27375
- var require_parse$6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
27734
+ var require_parse$5 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
27376
27735
  module.exports = function(tokens) {
27377
27736
  var index = 0;
27378
27737
  function hasMore() {
@@ -27464,7 +27823,7 @@ var require_parse$6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
27464
27823
  //#region node_modules/spdx-expression-parse/index.js
27465
27824
  var require_spdx_expression_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
27466
27825
  var scan = require_scan$1();
27467
- var parse = require_parse$6();
27826
+ var parse = require_parse$5();
27468
27827
  module.exports = function(source) {
27469
27828
  return parse(scan(source));
27470
27829
  };
@@ -27659,7 +28018,7 @@ var require_normalize_data = /* @__PURE__ */ __commonJSMin(((exports, module) =>
27659
28018
  //#region node_modules/isexe/dist/commonjs/index.min.js
27660
28019
  var require_index_min$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
27661
28020
  var a = (t, e) => () => (e || t((e = { exports: {} }).exports, e), e.exports);
27662
- var _$19 = a((i) => {
28021
+ var _$20 = a((i) => {
27663
28022
  "use strict";
27664
28023
  Object.defineProperty(i, "__esModule", { value: !0 });
27665
28024
  i.sync = i.isexe = void 0;
@@ -27772,7 +28131,7 @@ var require_index_min$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
27772
28131
  };
27773
28132
  Object.defineProperty(exports, "__esModule", { value: !0 });
27774
28133
  exports.sync = exports.isexe = exports.posix = exports.win32 = void 0;
27775
- var E = w(_$19());
28134
+ var E = w(_$20());
27776
28135
  exports.posix = E;
27777
28136
  var O = w(g());
27778
28137
  exports.win32 = O;
@@ -28188,7 +28547,7 @@ var require_lib$31 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
28188
28547
 
28189
28548
  //#endregion
28190
28549
  //#region node_modules/@npmcli/git/lib/errors.js
28191
- var require_errors$5 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
28550
+ var require_errors$4 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
28192
28551
  const maxRetry = 3;
28193
28552
  var GitError = class extends Error {
28194
28553
  shouldRetry() {
@@ -28223,7 +28582,7 @@ var require_errors$5 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
28223
28582
  //#endregion
28224
28583
  //#region node_modules/@npmcli/git/lib/make-error.js
28225
28584
  var require_make_error = /* @__PURE__ */ __commonJSMin(((exports, module) => {
28226
- const { GitConnectionError, GitPathspecError, GitUnknownError } = require_errors$5();
28585
+ const { GitConnectionError, GitPathspecError, GitUnknownError } = require_errors$4();
28227
28586
  const connectionErrorRe = new RegExp([
28228
28587
  "remote error: Internal Server Error",
28229
28588
  "The remote end hung up unexpectedly",
@@ -29478,7 +29837,7 @@ var require_clone = /* @__PURE__ */ __commonJSMin(((exports, module) => {
29478
29837
  "bitbucket.com",
29479
29838
  "bitbucket.org"
29480
29839
  ]);
29481
- const { parse: parse$2 } = __require("url");
29840
+ const { parse: parse$3 } = __require("url");
29482
29841
  const path$44 = __require("path");
29483
29842
  const getRevs = require_revs();
29484
29843
  const spawn = require_spawn();
@@ -29488,7 +29847,7 @@ var require_clone = /* @__PURE__ */ __commonJSMin(((exports, module) => {
29488
29847
  module.exports = (repo, ref = "HEAD", target = null, opts = {}) => getRevs(repo, opts).then((revs) => clone(repo, revs, ref, resolveRef(revs, ref, opts), target || defaultTarget(repo, opts.cwd), opts));
29489
29848
  const maybeShallow = (repo, opts) => {
29490
29849
  if (opts.gitShallow === false || opts.gitShallow) return opts.gitShallow;
29491
- return shallowHosts.has(parse$2(repo).host);
29850
+ return shallowHosts.has(parse$3(repo).host);
29492
29851
  };
29493
29852
  const defaultTarget = (repo, cwd = process.cwd()) => path$44.resolve(cwd, path$44.basename(repo.replace(/[/\\]?\.git$/, "")));
29494
29853
  const clone = (repo, revs, ref, revDoc, target, opts) => {
@@ -29633,7 +29992,7 @@ var require_lib$26 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
29633
29992
  is: require_is(),
29634
29993
  find: require_find$1(),
29635
29994
  isClean: require_is_clean(),
29636
- errors: require_errors$5()
29995
+ errors: require_errors$4()
29637
29996
  };
29638
29997
  }));
29639
29998
 
@@ -32547,7 +32906,7 @@ var require_node$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32547
32906
 
32548
32907
  //#endregion
32549
32908
  //#region node_modules/@npmcli/fs/lib/cp/errors.js
32550
- var require_errors$4 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32909
+ var require_errors$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32551
32910
  const { inspect } = __require("util");
32552
32911
  var SystemError = class {
32553
32912
  constructor(code, prefix, context) {
@@ -32655,10 +33014,10 @@ var require_errors$4 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32655
33014
  //#endregion
32656
33015
  //#region node_modules/@npmcli/fs/lib/cp/polyfill.js
32657
33016
  var require_polyfill = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32658
- const { ERR_FS_CP_DIR_TO_NON_DIR, ERR_FS_CP_EEXIST, ERR_FS_CP_EINVAL, ERR_FS_CP_FIFO_PIPE, ERR_FS_CP_NON_DIR_TO_DIR, ERR_FS_CP_SOCKET, ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY, ERR_FS_CP_UNKNOWN, ERR_FS_EISDIR, ERR_INVALID_ARG_TYPE } = require_errors$4();
33017
+ const { ERR_FS_CP_DIR_TO_NON_DIR, ERR_FS_CP_EEXIST, ERR_FS_CP_EINVAL, ERR_FS_CP_FIFO_PIPE, ERR_FS_CP_NON_DIR_TO_DIR, ERR_FS_CP_SOCKET, ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY, ERR_FS_CP_UNKNOWN, ERR_FS_EISDIR, ERR_INVALID_ARG_TYPE } = require_errors$3();
32659
33018
  const { constants: { errno: { EEXIST, EISDIR, EINVAL, ENOTDIR } } } = __require("os");
32660
33019
  const { chmod: chmod$3, copyFile: copyFile$1, lstat: lstat$5, mkdir: mkdir$6, readdir: readdir$5, readlink: readlink$6, stat: stat$6, symlink: symlink$2, unlink: unlink$1, utimes } = __require("fs/promises");
32661
- const { dirname: dirname$24, isAbsolute: isAbsolute$3, join: join$8, parse: parse$1, resolve: resolve$27, sep: sep$8, toNamespacedPath } = __require("path");
33020
+ const { dirname: dirname$24, isAbsolute: isAbsolute$3, join: join$8, parse: parse$2, resolve: resolve$27, sep: sep$8, toNamespacedPath } = __require("path");
32662
33021
  const { fileURLToPath: fileURLToPath$2 } = __require("url");
32663
33022
  const defaultOptions = {
32664
33023
  dereference: false,
@@ -32747,7 +33106,7 @@ var require_polyfill = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32747
33106
  async function checkParentPaths(src, srcStat, dest) {
32748
33107
  const srcParent = resolve$27(dirname$24(src));
32749
33108
  const destParent = resolve$27(dirname$24(dest));
32750
- if (destParent === srcParent || destParent === parse$1(destParent).root) return;
33109
+ if (destParent === srcParent || destParent === parse$2(destParent).root) return;
32751
33110
  let destStat;
32752
33111
  try {
32753
33112
  destStat = await stat$6(destParent, { bigint: true });
@@ -39236,7 +39595,7 @@ var require_dir = /* @__PURE__ */ __commonJSMin(((exports, module) => {
39236
39595
 
39237
39596
  //#endregion
39238
39597
  //#region node_modules/npm-registry-fetch/lib/errors.js
39239
- var require_errors$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
39598
+ var require_errors$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
39240
39599
  const { URL: URL$8 } = __require("node:url");
39241
39600
  function packageName(href) {
39242
39601
  try {
@@ -39306,7 +39665,7 @@ var require_errors$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
39306
39665
 
39307
39666
  //#endregion
39308
39667
  //#region node_modules/minizlib/dist/commonjs/constants.js
39309
- var require_constants$7 = /* @__PURE__ */ __commonJSMin(((exports) => {
39668
+ var require_constants$6 = /* @__PURE__ */ __commonJSMin(((exports) => {
39310
39669
  var __importDefault = exports && exports.__importDefault || function(mod) {
39311
39670
  return mod && mod.__esModule ? mod : { "default": mod };
39312
39671
  };
@@ -39478,8 +39837,8 @@ var require_commonjs$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
39478
39837
  const buffer_1$1 = __require("buffer");
39479
39838
  const minipass_1 = require_commonjs$5();
39480
39839
  const realZlib = __importStar(__require("zlib"));
39481
- const constants_js_1 = require_constants$7();
39482
- var constants_js_2 = require_constants$7();
39840
+ const constants_js_1 = require_constants$6();
39841
+ var constants_js_2 = require_constants$6();
39483
39842
  Object.defineProperty(exports, "constants", {
39484
39843
  enumerable: true,
39485
39844
  get: function() {
@@ -41187,7 +41546,7 @@ var require_lib$15 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
41187
41546
  //#endregion
41188
41547
  //#region node_modules/npm-registry-fetch/lib/check-response.js
41189
41548
  var require_check_response = /* @__PURE__ */ __commonJSMin(((exports, module) => {
41190
- const errors = require_errors$3();
41549
+ const errors = require_errors$2();
41191
41550
  const { Response } = require_lib$16();
41192
41551
  const defaultOpts = require_default_opts();
41193
41552
  const { log } = require_lib$39();
@@ -42862,7 +43221,7 @@ var require_policy$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
42862
43221
 
42863
43222
  //#endregion
42864
43223
  //#region node_modules/make-fetch-happen/lib/cache/errors.js
42865
- var require_errors$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
43224
+ var require_errors$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
42866
43225
  var NotCachedError = class extends Error {
42867
43226
  constructor(url) {
42868
43227
  super(`request to ${url} failed: cache mode is 'only-if-cached' but no cached response is available.`);
@@ -44688,7 +45047,7 @@ var require_smartbuffer = /* @__PURE__ */ __commonJSMin(((exports) => {
44688
45047
 
44689
45048
  //#endregion
44690
45049
  //#region node_modules/socks/build/common/constants.js
44691
- var require_constants$6 = /* @__PURE__ */ __commonJSMin(((exports) => {
45050
+ var require_constants$5 = /* @__PURE__ */ __commonJSMin(((exports) => {
44692
45051
  Object.defineProperty(exports, "__esModule", { value: true });
44693
45052
  exports.SOCKS5_NO_ACCEPTABLE_AUTH = exports.SOCKS5_CUSTOM_AUTH_END = exports.SOCKS5_CUSTOM_AUTH_START = exports.SOCKS_INCOMING_PACKET_SIZES = exports.SocksClientState = exports.Socks5Response = exports.Socks5HostType = exports.Socks5Auth = exports.Socks4Response = exports.SocksCommand = exports.ERRORS = exports.DEFAULT_TIMEOUT = void 0;
44694
45053
  const DEFAULT_TIMEOUT = 3e4;
@@ -44954,7 +45313,7 @@ var require_common$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
44954
45313
 
44955
45314
  //#endregion
44956
45315
  //#region node_modules/ip-address/dist/v4/constants.js
44957
- var require_constants$5 = /* @__PURE__ */ __commonJSMin(((exports) => {
45316
+ var require_constants$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
44958
45317
  Object.defineProperty(exports, "__esModule", { value: true });
44959
45318
  exports.SPECIAL_PURPOSE = exports.RE_SUBNET_STRING = exports.RE_ADDRESS = exports.GROUPS = exports.BITS = void 0;
44960
45319
  exports.BITS = 32;
@@ -45143,7 +45502,7 @@ var require_ipv4 = /* @__PURE__ */ __commonJSMin(((exports) => {
45143
45502
  Object.defineProperty(exports, "__esModule", { value: true });
45144
45503
  exports.Address4 = void 0;
45145
45504
  const common = __importStar(require_common$4());
45146
- const constants = __importStar(require_constants$5());
45505
+ const constants = __importStar(require_constants$4());
45147
45506
  const address_error_1 = require_address_error();
45148
45507
  const isCorrect4 = common.isCorrect(constants.BITS);
45149
45508
  /**
@@ -45651,7 +46010,7 @@ var require_ipv4 = /* @__PURE__ */ __commonJSMin(((exports) => {
45651
46010
 
45652
46011
  //#endregion
45653
46012
  //#region node_modules/ip-address/dist/v6/constants.js
45654
- var require_constants$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
46013
+ var require_constants$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
45655
46014
  Object.defineProperty(exports, "__esModule", { value: true });
45656
46015
  exports.SPECIAL_PURPOSE = exports.RE_URL_WITH_PORT = exports.RE_URL = exports.RE_ZONE_STRING = exports.RE_SUBNET_STRING = exports.RE_BAD_ADDRESS = exports.RE_BAD_CHARACTERS = exports.TYPES = exports.SCOPES = exports.GROUPS = exports.BITS = void 0;
45657
46016
  exports.BITS = 128;
@@ -45965,7 +46324,7 @@ var require_regular_expressions = /* @__PURE__ */ __commonJSMin(((exports) => {
45965
46324
  exports.padGroup = padGroup;
45966
46325
  exports.simpleRegularExpression = simpleRegularExpression;
45967
46326
  exports.possibleElisions = possibleElisions;
45968
- const v6 = __importStar(require_constants$4());
46327
+ const v6 = __importStar(require_constants$3());
45969
46328
  function groupPossibilities(possibilities) {
45970
46329
  return `(${possibilities.join("|")})`;
45971
46330
  }
@@ -46041,8 +46400,8 @@ var require_ipv6 = /* @__PURE__ */ __commonJSMin(((exports) => {
46041
46400
  Object.defineProperty(exports, "__esModule", { value: true });
46042
46401
  exports.Address6 = void 0;
46043
46402
  const common = __importStar(require_common$4());
46044
- const constants4 = __importStar(require_constants$5());
46045
- const constants6 = __importStar(require_constants$4());
46403
+ const constants4 = __importStar(require_constants$4());
46404
+ const constants6 = __importStar(require_constants$3());
46046
46405
  const helpers = __importStar(require_helpers$1());
46047
46406
  const ipv4_1 = require_ipv4();
46048
46407
  const regular_expressions_1 = require_regular_expressions();
@@ -47254,7 +47613,7 @@ var require_helpers = /* @__PURE__ */ __commonJSMin(((exports) => {
47254
47613
  Object.defineProperty(exports, "__esModule", { value: true });
47255
47614
  exports.ipToBuffer = exports.int32ToIpv4 = exports.ipv4ToInt32 = exports.validateSocksClientChainOptions = exports.validateSocksClientOptions = void 0;
47256
47615
  const util_1 = require_util$3();
47257
- const constants_1 = require_constants$6();
47616
+ const constants_1 = require_constants$5();
47258
47617
  const stream$1 = __require("stream");
47259
47618
  const ip_address_1 = require_ip_address();
47260
47619
  const net$2 = __require("net");
@@ -47421,7 +47780,7 @@ var require_socksclient = /* @__PURE__ */ __commonJSMin(((exports) => {
47421
47780
  const events_1$1 = __require("events");
47422
47781
  const net$1 = __require("net");
47423
47782
  const smart_buffer_1 = require_smartbuffer();
47424
- const constants_1 = require_constants$6();
47783
+ const constants_1 = require_constants$5();
47425
47784
  const helpers_1 = require_helpers();
47426
47785
  const receivebuffer_1 = require_receivebuffer();
47427
47786
  const util_1 = require_util$3();
@@ -48176,7 +48535,7 @@ var init_dist = __esmMin((() => {
48176
48535
 
48177
48536
  //#endregion
48178
48537
  //#region node_modules/@npmcli/agent/lib/errors.js
48179
- var require_errors$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
48538
+ var require_errors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
48180
48539
  var InvalidProxyProtocolError = class extends Error {
48181
48540
  constructor(url) {
48182
48541
  super(`Invalid protocol \`${url.protocol}\` connecting to proxy \`${url.host}\``);
@@ -48236,7 +48595,7 @@ var require_proxy = /* @__PURE__ */ __commonJSMin(((exports, module) => {
48236
48595
  const { HttpsProxyAgent } = (init_dist$1(), __toCommonJS(dist_exports$1));
48237
48596
  const { SocksProxyAgent } = (init_dist(), __toCommonJS(dist_exports));
48238
48597
  const { LRUCache } = require_index_min$2();
48239
- const { InvalidProxyProtocolError } = require_errors$1();
48598
+ const { InvalidProxyProtocolError } = require_errors();
48240
48599
  const PROXY_CACHE = new LRUCache({ max: 20 });
48241
48600
  const SOCKS_PROTOCOLS = new Set(SocksProxyAgent.protocols);
48242
48601
  const PROXY_ENV_KEYS = /* @__PURE__ */ new Set([
@@ -48291,7 +48650,7 @@ var require_agents = /* @__PURE__ */ __commonJSMin(((exports, module) => {
48291
48650
  const timers = __require("timers/promises");
48292
48651
  const { normalizeOptions, cacheOptions } = require_options();
48293
48652
  const { getProxy, getProxyAgent, proxyCache } = require_proxy();
48294
- const Errors = require_errors$1();
48653
+ const Errors = require_errors();
48295
48654
  const { Agent: AgentBase } = (init_dist$4(), __toCommonJS(dist_exports$3));
48296
48655
  module.exports = class Agent extends AgentBase {
48297
48656
  #options;
@@ -48926,7 +49285,7 @@ var require_entry$4 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
48926
49285
  //#endregion
48927
49286
  //#region node_modules/make-fetch-happen/lib/cache/index.js
48928
49287
  var require_cache = /* @__PURE__ */ __commonJSMin(((exports, module) => {
48929
- const { NotCachedError } = require_errors$2();
49288
+ const { NotCachedError } = require_errors$1();
48930
49289
  const CacheEntry = require_entry$4();
48931
49290
  const remote = require_remote$1();
48932
49291
  const cacheFetch = async (request, options) => {
@@ -49564,7 +49923,7 @@ var require_json_stream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
49564
49923
  //#endregion
49565
49924
  //#region node_modules/npm-registry-fetch/lib/index.js
49566
49925
  var require_lib$12 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
49567
- const { HttpErrorAuthOTP } = require_errors$3();
49926
+ const { HttpErrorAuthOTP } = require_errors$2();
49568
49927
  const checkResponse = require_check_response();
49569
49928
  const getAuth = require_auth$1();
49570
49929
  const fetch = require_lib$13();
@@ -51746,7 +52105,7 @@ var require_length = /* @__PURE__ */ __commonJSMin(((exports) => {
51746
52105
 
51747
52106
  //#endregion
51748
52107
  //#region node_modules/@sigstore/core/dist/asn1/parse.js
51749
- var require_parse$5 = /* @__PURE__ */ __commonJSMin(((exports) => {
52108
+ var require_parse$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
51750
52109
  Object.defineProperty(exports, "__esModule", { value: true });
51751
52110
  exports.parseInteger = parseInteger;
51752
52111
  exports.parseStringASCII = parseStringASCII;
@@ -51901,7 +52260,7 @@ var require_obj = /* @__PURE__ */ __commonJSMin(((exports) => {
51901
52260
  const stream_1 = require_stream$5();
51902
52261
  const error_1 = require_error$10();
51903
52262
  const length_1 = require_length();
51904
- const parse_1 = require_parse$5();
52263
+ const parse_1 = require_parse$4();
51905
52264
  const tag_1 = require_tag$1();
51906
52265
  var ASN1Obj = class {
51907
52266
  tag;
@@ -68295,7 +68654,7 @@ var require_line_endings = /* @__PURE__ */ __commonJSMin(((exports) => {
68295
68654
 
68296
68655
  //#endregion
68297
68656
  //#region node_modules/diff/libcjs/patch/parse.js
68298
- var require_parse$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
68657
+ var require_parse$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
68299
68658
  Object.defineProperty(exports, "__esModule", { value: true });
68300
68659
  exports.parsePatch = parsePatch;
68301
68660
  /**
@@ -68411,7 +68770,7 @@ var require_apply = /* @__PURE__ */ __commonJSMin(((exports) => {
68411
68770
  exports.applyPatches = applyPatches;
68412
68771
  var string_js_1 = require_string$2();
68413
68772
  var line_endings_js_1 = require_line_endings();
68414
- var parse_js_1 = require_parse$4();
68773
+ var parse_js_1 = require_parse$3();
68415
68774
  var distance_iterator_js_1 = require_distance_iterator();
68416
68775
  /**
68417
68776
  * attempts to apply a unified diff patch.
@@ -69012,7 +69371,7 @@ var require_libcjs = /* @__PURE__ */ __commonJSMin(((exports) => {
69012
69371
  return apply_js_1.applyPatches;
69013
69372
  }
69014
69373
  });
69015
- var parse_js_1 = require_parse$4();
69374
+ var parse_js_1 = require_parse$3();
69016
69375
  Object.defineProperty(exports, "parsePatch", {
69017
69376
  enumerable: true,
69018
69377
  get: function() {
@@ -77598,7 +77957,7 @@ async function getCacheInfoWithRefreshTime(cache, key) {
77598
77957
  //#endregion
77599
77958
  //#region lib/util/inflight.ts
77600
77959
  var import_lib$2 = /* @__PURE__ */ __toESM(require_lib$10());
77601
- var inflight_default = Inflight;
77960
+ var inflight_default = InflightStore;
77602
77961
 
77603
77962
  //#endregion
77604
77963
  //#region node_modules/pkg-preper/dist/index.js
@@ -78543,7 +78902,7 @@ var require_expand = /* @__PURE__ */ __commonJSMin(((exports, module) => {
78543
78902
 
78544
78903
  //#endregion
78545
78904
  //#region node_modules/braces/lib/constants.js
78546
- var require_constants$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
78905
+ var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
78547
78906
  module.exports = {
78548
78907
  MAX_LENGTH: 1e4,
78549
78908
  CHAR_0: "0",
@@ -78595,12 +78954,12 @@ var require_constants$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
78595
78954
 
78596
78955
  //#endregion
78597
78956
  //#region node_modules/braces/lib/parse.js
78598
- var require_parse$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
78957
+ var require_parse$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
78599
78958
  const stringify = require_stringify();
78600
78959
  /**
78601
78960
  * Constants
78602
78961
  */
78603
- const { MAX_LENGTH, CHAR_BACKSLASH, CHAR_BACKTICK, CHAR_COMMA, CHAR_DOT, CHAR_LEFT_PARENTHESES, CHAR_RIGHT_PARENTHESES, CHAR_LEFT_CURLY_BRACE, CHAR_RIGHT_CURLY_BRACE, CHAR_LEFT_SQUARE_BRACKET, CHAR_RIGHT_SQUARE_BRACKET, CHAR_DOUBLE_QUOTE, CHAR_SINGLE_QUOTE, CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE } = require_constants$3();
78962
+ const { MAX_LENGTH, CHAR_BACKSLASH, CHAR_BACKTICK, CHAR_COMMA, CHAR_DOT, CHAR_LEFT_PARENTHESES, CHAR_RIGHT_PARENTHESES, CHAR_LEFT_CURLY_BRACE, CHAR_RIGHT_CURLY_BRACE, CHAR_LEFT_SQUARE_BRACKET, CHAR_RIGHT_SQUARE_BRACKET, CHAR_DOUBLE_QUOTE, CHAR_SINGLE_QUOTE, CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE } = require_constants$2();
78604
78963
  /**
78605
78964
  * parse
78606
78965
  */
@@ -78887,7 +79246,7 @@ var require_braces = /* @__PURE__ */ __commonJSMin(((exports, module) => {
78887
79246
  const stringify = require_stringify();
78888
79247
  const compile = require_compile();
78889
79248
  const expand = require_expand();
78890
- const parse = require_parse$3();
79249
+ const parse = require_parse$2();
78891
79250
  /**
78892
79251
  * Expand the given pattern or create a regex-compatible string.
78893
79252
  *
@@ -79011,7 +79370,7 @@ var require_braces = /* @__PURE__ */ __commonJSMin(((exports, module) => {
79011
79370
 
79012
79371
  //#endregion
79013
79372
  //#region node_modules/picomatch/lib/constants.js
79014
- var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
79373
+ var require_constants$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
79015
79374
  const path$28 = __require("path");
79016
79375
  const WIN_SLASH = "\\\\/";
79017
79376
  const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
@@ -79188,7 +79547,7 @@ var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
79188
79547
  var require_utils$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
79189
79548
  const path$27 = __require("path");
79190
79549
  const win32 = process.platform === "win32";
79191
- const { REGEX_BACKSLASH, REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL } = require_constants$2();
79550
+ const { REGEX_BACKSLASH, REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL } = require_constants$1();
79192
79551
  exports.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
79193
79552
  exports.hasRegexChars = (str) => REGEX_SPECIAL_CHARS.test(str);
79194
79553
  exports.isRegexChar = (str) => str.length === 1 && exports.hasRegexChars(str);
@@ -79233,7 +79592,7 @@ var require_utils$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
79233
79592
  //#region node_modules/picomatch/lib/scan.js
79234
79593
  var require_scan = /* @__PURE__ */ __commonJSMin(((exports, module) => {
79235
79594
  const utils = require_utils$3();
79236
- const { CHAR_ASTERISK, CHAR_AT, CHAR_BACKWARD_SLASH, CHAR_COMMA, CHAR_DOT, CHAR_EXCLAMATION_MARK, CHAR_FORWARD_SLASH, CHAR_LEFT_CURLY_BRACE, CHAR_LEFT_PARENTHESES, CHAR_LEFT_SQUARE_BRACKET, CHAR_PLUS, CHAR_QUESTION_MARK, CHAR_RIGHT_CURLY_BRACE, CHAR_RIGHT_PARENTHESES, CHAR_RIGHT_SQUARE_BRACKET } = require_constants$2();
79595
+ const { CHAR_ASTERISK, CHAR_AT, CHAR_BACKWARD_SLASH, CHAR_COMMA, CHAR_DOT, CHAR_EXCLAMATION_MARK, CHAR_FORWARD_SLASH, CHAR_LEFT_CURLY_BRACE, CHAR_LEFT_PARENTHESES, CHAR_LEFT_SQUARE_BRACKET, CHAR_PLUS, CHAR_QUESTION_MARK, CHAR_RIGHT_CURLY_BRACE, CHAR_RIGHT_PARENTHESES, CHAR_RIGHT_SQUARE_BRACKET } = require_constants$1();
79237
79596
  const isPathSeparator = (code) => {
79238
79597
  return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
79239
79598
  };
@@ -79519,8 +79878,8 @@ var require_scan = /* @__PURE__ */ __commonJSMin(((exports, module) => {
79519
79878
 
79520
79879
  //#endregion
79521
79880
  //#region node_modules/picomatch/lib/parse.js
79522
- var require_parse$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
79523
- const constants = require_constants$2();
79881
+ var require_parse$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
79882
+ const constants = require_constants$1();
79524
79883
  const utils = require_utils$3();
79525
79884
  /**
79526
79885
  * Constants
@@ -80585,9 +80944,9 @@ var require_parse$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
80585
80944
  var require_picomatch$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
80586
80945
  const path$26 = __require("path");
80587
80946
  const scan = require_scan();
80588
- const parse = require_parse$2();
80947
+ const parse = require_parse$1();
80589
80948
  const utils = require_utils$3();
80590
- const constants = require_constants$2();
80949
+ const constants = require_constants$1();
80591
80950
  const isObject = (val) => val && typeof val === "object" && !Array.isArray(val);
80592
80951
  /**
80593
80952
  * Creates a matcher function from one or more glob patterns. The
@@ -81883,7 +82242,7 @@ var require_run_parallel = /* @__PURE__ */ __commonJSMin(((exports, module) => {
81883
82242
 
81884
82243
  //#endregion
81885
82244
  //#region node_modules/@nodelib/fs.scandir/out/constants.js
81886
- var require_constants$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
82245
+ var require_constants = /* @__PURE__ */ __commonJSMin(((exports) => {
81887
82246
  Object.defineProperty(exports, "__esModule", { value: true });
81888
82247
  exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = void 0;
81889
82248
  const NODE_PROCESS_VERSION_PARTS = process.versions.node.split(".");
@@ -81954,7 +82313,7 @@ var require_async$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
81954
82313
  exports.readdir = exports.readdirWithFileTypes = exports.read = void 0;
81955
82314
  const fsStat = require_out$3();
81956
82315
  const rpl = require_run_parallel();
81957
- const constants_1 = require_constants$1();
82316
+ const constants_1 = require_constants();
81958
82317
  const utils = require_utils$1();
81959
82318
  const common = require_common$3();
81960
82319
  function read(directory, settings, callback) {
@@ -82059,7 +82418,7 @@ var require_sync$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
82059
82418
  Object.defineProperty(exports, "__esModule", { value: true });
82060
82419
  exports.readdir = exports.readdirWithFileTypes = exports.read = void 0;
82061
82420
  const fsStat = require_out$3();
82062
- const constants_1 = require_constants$1();
82421
+ const constants_1 = require_constants();
82063
82422
  const utils = require_utils$1();
82064
82423
  const common = require_common$3();
82065
82424
  function read(directory, settings) {
@@ -84270,7 +84629,7 @@ var require_readShebang = /* @__PURE__ */ __commonJSMin(((exports, module) => {
84270
84629
 
84271
84630
  //#endregion
84272
84631
  //#region node_modules/cross-spawn/lib/parse.js
84273
- var require_parse$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
84632
+ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
84274
84633
  const path$17 = __require("path");
84275
84634
  const resolveCommand = require_resolveCommand();
84276
84635
  const escape = require_escape();
@@ -84374,7 +84733,7 @@ var require_enoent = /* @__PURE__ */ __commonJSMin(((exports, module) => {
84374
84733
  //#region node_modules/cross-spawn/index.js
84375
84734
  var require_cross_spawn = /* @__PURE__ */ __commonJSMin(((exports, module) => {
84376
84735
  const cp = __require("child_process");
84377
- const parse = require_parse$1();
84736
+ const parse = require_parse();
84378
84737
  const enoent = require_enoent();
84379
84738
  function spawn(command, args, options) {
84380
84739
  const parsed = parse(command, args, options);
@@ -87772,6 +88131,13 @@ var VisualExec = class {
87772
88131
  kill(signal) {
87773
88132
  this.abort(signal);
87774
88133
  }
88134
+ /**
88135
+ * Attach to an already-started child and render its output.
88136
+ *
88137
+ * Resolves with the command's `ExecOutput`, unless an `onComplete` callback returned a
88138
+ * value - that value is passed through instead. `T` names what the caller expects back;
88139
+ * it defaults to `ExecOutput`, which is what a run without an `onComplete` produces.
88140
+ */
87775
88141
  show(child) {
87776
88142
  this._stdoutKey = Symbol("visual-exec-stdout");
87777
88143
  this._stderrKey = Symbol("visual-exec-stderr");
@@ -87815,13 +88181,14 @@ var VisualExec = class {
87815
88181
  let timeoutId;
87816
88182
  const startTime = Date.now();
87817
88183
  let execPromise = child.promise.catch((err) => {
87818
- var _a, _b, _c, _d;
87819
- const output = (_a = err.output) !== null && _a !== void 0 ? _a : {
88184
+ var _a;
88185
+ var _b, _c, _d;
88186
+ const output = (_b = err.output) !== null && _b !== void 0 ? _b : {
87820
88187
  stdout: "",
87821
88188
  stderr: ""
87822
88189
  };
87823
- const exitCode = (_c = (_b = err.exitCode) !== null && _b !== void 0 ? _b : err.code) !== null && _c !== void 0 ? _c : 1;
87824
- (_d = this._onComplete) === null || _d === void 0 || _d.call(this, output, exitCode);
88190
+ const exitCode = (_d = (_c = err.exitCode) !== null && _c !== void 0 ? _c : err.code) !== null && _d !== void 0 ? _d : 1;
88191
+ (_a = this._onComplete) === null || _a === void 0 || _a.call(this, output, exitCode);
87825
88192
  this.logResult(err);
87826
88193
  throw err;
87827
88194
  }).then((output) => {
@@ -87922,6 +88289,12 @@ var VisualExec = class {
87922
88289
  logs.push(chalk.blue("\n<<<"), `End of output from ${this._outputLabel} ---`);
87923
88290
  this._logger.prefix(false)[level](...logs);
87924
88291
  }
88292
+ /**
88293
+ * Run the command and render its output.
88294
+ *
88295
+ * Resolves with `ExecOutput`, or with whatever an `onComplete` callback returned in its
88296
+ * place. `T` defaults to `ExecOutput` so the common case needs no cast at the call site.
88297
+ */
87925
88298
  execute(command) {
87926
88299
  this._startTime = Date.now();
87927
88300
  this._aborted = false;
@@ -87951,13 +88324,14 @@ var VisualExec = class {
87951
88324
  };
87952
88325
  };
87953
88326
  child.promise = child.promise.catch((err) => {
87954
- var _a, _b, _c, _d, _e;
87955
- const output = (_a = err.output) !== null && _a !== void 0 ? _a : {
88327
+ var _a;
88328
+ var _b, _c, _d, _e;
88329
+ const output = (_b = err.output) !== null && _b !== void 0 ? _b : {
87956
88330
  stdout: "",
87957
88331
  stderr: ""
87958
88332
  };
87959
- const exitCode = (_b = err.code) !== null && _b !== void 0 ? _b : 1;
87960
- const signal = (_e = (_c = err.signal) !== null && _c !== void 0 ? _c : ((_d = result.child) === null || _d === void 0 ? void 0 : _d.killed) ? "SIGTERM" : null) !== null && _e !== void 0 ? _e : null;
88333
+ const exitCode = (_c = err.code) !== null && _c !== void 0 ? _c : 1;
88334
+ const signal = (_e = (_d = err.signal) !== null && _d !== void 0 ? _d : ((_a = result.child) === null || _a === void 0 ? void 0 : _a.killed) ? "SIGTERM" : null) !== null && _e !== void 0 ? _e : null;
87961
88335
  const context = {
87962
88336
  ...baseContext(output),
87963
88337
  exitCode,
@@ -89509,7 +89883,7 @@ var PkgSrcManager = class {
89509
89883
  }
89510
89884
  pkgPreperInstallDep(dir, displayTitle) {
89511
89885
  const node = process.env.NODE || process.execPath;
89512
- const fyn = Path.join(__dirname, "../bin/fyn.js");
89886
+ const fyn = Path.join(__dirname, "../bin/fyn.mjs");
89513
89887
  return new VisualExec({
89514
89888
  displayTitle,
89515
89889
  cwd: dir,
@@ -91393,9 +91767,9 @@ var PkgDepLocker = class {
91393
91767
  const dist = vpkg.dist || {};
91394
91768
  if (vpkg.top) meta.top = 1;
91395
91769
  const scripts = json.scripts || {};
91396
- if (vpkg.optFailed) meta.optFailed = vpkg.optFailed;
91770
+ if (vpkg.optFailed && vpkg.optFailed !== 3) meta.optFailed = vpkg.optFailed;
91397
91771
  else if (scripts.preinstall || vpkg.hasPI) meta.hasPI = 1;
91398
- if (scripts.install || scripts.postinstall || scripts.postInstall) meta.hasI = 1;
91772
+ if (scripts.install || scripts.postinstall || scripts.postInstall || vpkg.hasI) meta.hasI = 1;
91399
91773
  if (vpkg.local) {
91400
91774
  meta.$ = "local";
91401
91775
  meta._ = dist.fullPath;
@@ -91404,7 +91778,7 @@ var PkgDepLocker = class {
91404
91778
  meta._ = dist.tarball;
91405
91779
  }
91406
91780
  if (!meta.optFailed) {
91407
- if (import_lodash_min.default.isEmpty(json)) meta._missingJson = true;
91781
+ if (import_lodash_min.default.isEmpty(json) || json._missingJson) meta._missingJson = true;
91408
91782
  else {
91409
91783
  if (!import_lodash_min.default.isEmpty(json.dependencies)) meta.dependencies = json.dependencies;
91410
91784
  if (!import_lodash_min.default.isEmpty(json.optionalDependencies)) meta.optionalDependencies = json.optionalDependencies;
@@ -91501,26 +91875,28 @@ var PkgDepLocker = class {
91501
91875
  const badVersionKey = !(vpkg && vpkg.$ === "local") && !import_semver.default.valid(version);
91502
91876
  const badDeps = vpkg && vpkg.dependencies && !this._depsResolvable(vpkg.dependencies);
91503
91877
  if (!import_lodash_min.default.isEmpty(vpkg) && vpkg._valid !== false && !badVersionKey && !badDeps) {
91878
+ const { $: _dollar, _: _under, top: _top, _valid: _v, ...carried } = vpkg;
91879
+ const expanded = {
91880
+ ...carried,
91881
+ fromLocked: true,
91882
+ name: item.name,
91883
+ version,
91884
+ _hasShrinkwrap: Boolean(vpkg._hasShrinkwrap)
91885
+ };
91504
91886
  if (vpkg.$ === "local") {
91505
- vpkg.local = true;
91506
- vpkg.dist = {
91887
+ expanded.local = isLocalHard(version) ? "hard" : "sym";
91888
+ expanded.dist = {
91507
91889
  integrity: "local",
91508
91890
  fullPath: vpkg._
91509
91891
  };
91510
91892
  } else {
91511
91893
  const tarballUrl = this.normalizeTarballUrl(item, vpkg._);
91512
- vpkg.dist = {
91894
+ expanded.dist = {
91513
91895
  integrity: fyntil.shaToIntegrity(vpkg.$),
91514
91896
  tarball: tarballUrl
91515
91897
  };
91516
91898
  }
91517
- vpkg.$ = void 0;
91518
- vpkg._ = void 0;
91519
- vpkg.fromLocked = true;
91520
- vpkg.name = item.name;
91521
- vpkg.version = version;
91522
- if (vpkg._hasShrinkwrap) vpkg._hasShrinkwrap = true;
91523
- versions[version] = vpkg;
91899
+ versions[version] = expanded;
91524
91900
  } else {
91525
91901
  if (badVersionKey) logger$1.error(`lockfile entry for ${item.name} has invalid version key "${version}" - ignoring and re-resolving from registry`);
91526
91902
  else if (badDeps) logger$1.error(`lockfile entry ${item.name}@${version} has dependencies not satisfiable within the lock (corrupt lock) - ignoring and re-resolving from registry`);
@@ -91558,7 +91934,10 @@ var PkgDepLocker = class {
91558
91934
  }
91559
91935
  if (Object.prototype.hasOwnProperty.call(rangeMap, spec)) return true;
91560
91936
  if (!import_semver.default.validRange(spec)) return true;
91561
- return versions.some((v) => import_semver.default.valid(v) && import_semver.default.satisfies(v, spec));
91937
+ return versions.some((v) => {
91938
+ const base = unlocalify(v);
91939
+ return import_semver.default.valid(base) && import_semver.default.satisfies(base, spec);
91940
+ });
91562
91941
  }
91563
91942
  /**
91564
91943
  * Set the package.json's dependencies items and check if they changed from
@@ -91778,7 +92157,7 @@ var DepData = class {
91778
92157
  return bad ? this.badPkgs : this.pkgs;
91779
92158
  }
91780
92159
  getPkg(item) {
91781
- return this.getPkgsData(item.optFailed)[item.name];
92160
+ return this.getPkgsData(Boolean(item.optFailed))[item.name];
91782
92161
  }
91783
92162
  getPkgById(id) {
91784
92163
  const lastAt = id.lastIndexOf("@");
@@ -94267,434 +94646,333 @@ var require_invariant = /* @__PURE__ */ __commonJSMin(((exports, module) => {
94267
94646
 
94268
94647
  //#endregion
94269
94648
  //#region node_modules/strip-bom/index.js
94270
- var strip_bom_exports = /* @__PURE__ */ __exportAll({ default: () => stripBom });
94649
+ var import_invariant = /* @__PURE__ */ __toESM(require_invariant());
94271
94650
  function stripBom(string) {
94272
94651
  if (typeof string !== "string") throw new TypeError(`Expected a string, got ${typeof string}`);
94273
94652
  if (string.charCodeAt(0) === 65279) return string.slice(1);
94274
94653
  return string;
94275
94654
  }
94276
- var init_strip_bom = __esmMin((() => {}));
94277
94655
 
94278
94656
  //#endregion
94279
- //#region yarn/lib/constants.js
94280
- var require_constants = /* @__PURE__ */ __commonJSMin(((exports) => {
94281
- Object.defineProperty(exports, "__esModule", { value: true });
94282
- exports.LOCKFILE_VERSION = void 0;
94283
- exports.LOCKFILE_VERSION = 1;
94284
- }));
94285
-
94286
- //#endregion
94287
- //#region yarn/lib/errors.js
94288
- var require_errors = /* @__PURE__ */ __commonJSMin(((exports) => {
94289
- Object.defineProperty(exports, "__esModule", { value: true });
94290
- exports.SecurityError = exports.ResponseError = exports.ProcessTermError = exports.ProcessSpawnError = exports.OneTimePasswordError = exports.MessageError = void 0;
94291
- function _defineProperty(e, r, t) {
94292
- return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
94293
- value: t,
94294
- enumerable: !0,
94295
- configurable: !0,
94296
- writable: !0
94297
- }) : e[r] = t, e;
94298
- }
94299
- function _toPropertyKey(t) {
94300
- var i = _toPrimitive(t, "string");
94301
- return "symbol" == typeof i ? i : i + "";
94302
- }
94303
- function _toPrimitive(t, r) {
94304
- if ("object" != typeof t || !t) return t;
94305
- var e = t[Symbol.toPrimitive];
94306
- if (void 0 !== e) {
94307
- var i = e.call(t, r || "default");
94308
- if ("object" != typeof i) return i;
94309
- throw new TypeError("@@toPrimitive must return a primitive value.");
94310
- }
94311
- return ("string" === r ? String : Number)(t);
94312
- }
94313
- var MessageError = class extends Error {
94314
- constructor(msg, code) {
94315
- super(msg);
94316
- _defineProperty(this, "code", void 0);
94317
- this.code = code;
94318
- }
94319
- };
94320
- exports.MessageError = MessageError;
94321
- var ProcessSpawnError = class extends MessageError {
94322
- constructor(msg, code, process) {
94323
- super(msg, code);
94324
- _defineProperty(this, "process", void 0);
94325
- this.process = process;
94326
- }
94327
- };
94328
- exports.ProcessSpawnError = ProcessSpawnError;
94329
- var SecurityError = class extends MessageError {};
94330
- exports.SecurityError = SecurityError;
94331
- var ProcessTermError = class extends MessageError {
94332
- constructor(...args) {
94333
- super(...args);
94334
- _defineProperty(this, "EXIT_CODE", void 0);
94335
- _defineProperty(this, "EXIT_SIGNAL", void 0);
94336
- }
94337
- };
94338
- exports.ProcessTermError = ProcessTermError;
94339
- var ResponseError = class extends Error {
94340
- constructor(msg, responseCode) {
94341
- super(msg);
94342
- _defineProperty(this, "responseCode", void 0);
94343
- this.responseCode = responseCode;
94344
- }
94345
- };
94346
- exports.ResponseError = ResponseError;
94347
- var OneTimePasswordError = class extends Error {};
94348
- exports.OneTimePasswordError = OneTimePasswordError;
94349
- }));
94657
+ //#region yarn/src/errors.ts
94658
+ var MessageError = class extends Error {
94659
+ constructor(msg, code) {
94660
+ super(msg);
94661
+ this.code = code;
94662
+ }
94663
+ code;
94664
+ };
94350
94665
 
94351
94666
  //#endregion
94352
- //#region yarn/lib/util/map.js
94353
- var require_map = /* @__PURE__ */ __commonJSMin(((exports) => {
94354
- Object.defineProperty(exports, "__esModule", { value: true });
94355
- exports.default = nullify;
94356
- function nullify(obj = {}) {
94357
- if (Array.isArray(obj)) for (const item of obj) nullify(item);
94358
- else if (obj !== null && typeof obj === "object" || typeof obj === "function") {
94359
- Object.setPrototypeOf(obj, null);
94360
- if (typeof obj === "object") for (const key in obj) nullify(obj[key]);
94361
- }
94362
- return obj;
94667
+ //#region yarn/src/util/map.ts
94668
+ function nullify(obj = {}) {
94669
+ if (Array.isArray(obj)) for (const item of obj) nullify(item);
94670
+ else if (obj !== null && typeof obj === "object" || typeof obj === "function") {
94671
+ Object.setPrototypeOf(obj, null);
94672
+ if (typeof obj === "object") for (const key in obj) nullify(obj[key]);
94363
94673
  }
94364
- }));
94674
+ return obj;
94675
+ }
94365
94676
 
94366
94677
  //#endregion
94367
- //#region yarn/lib/lockfile/parse.js
94368
- var require_parse = /* @__PURE__ */ __commonJSMin(((exports) => {
94369
- Object.defineProperty(exports, "__esModule", { value: true });
94370
- exports.default = _default;
94371
- var _util = _interopRequireDefault(__require("util"));
94372
- var _invariant = _interopRequireDefault(require_invariant());
94373
- var _stripBom = _interopRequireDefault((init_strip_bom(), __toCommonJS(strip_bom_exports)));
94374
- var _constants = require_constants();
94375
- var _errors = require_errors();
94376
- var _map = _interopRequireDefault(require_map());
94377
- var _yamljs = _interopRequireDefault(require_Yaml());
94378
- function _interopRequireDefault(e) {
94379
- return e && e.__esModule ? e : { default: e };
94380
- }
94381
- function _defineProperty(e, r, t) {
94382
- return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
94383
- value: t,
94384
- enumerable: !0,
94385
- configurable: !0,
94386
- writable: !0
94387
- }) : e[r] = t, e;
94388
- }
94389
- function _toPropertyKey(t) {
94390
- var i = _toPrimitive(t, "string");
94391
- return "symbol" == typeof i ? i : i + "";
94392
- }
94393
- function _toPrimitive(t, r) {
94394
- if ("object" != typeof t || !t) return t;
94395
- var e = t[Symbol.toPrimitive];
94396
- if (void 0 !== e) {
94397
- var i = e.call(t, r || "default");
94398
- if ("object" != typeof i) return i;
94399
- throw new TypeError("@@toPrimitive must return a primitive value.");
94400
- }
94401
- return ("string" === r ? String : Number)(t);
94402
- }
94403
- const VERSION_REGEX = /^yarn lockfile v(\d+)$/;
94404
- const TOKEN_TYPES = {
94405
- boolean: "BOOLEAN",
94406
- string: "STRING",
94407
- identifier: "IDENTIFIER",
94408
- eof: "EOF",
94409
- colon: "COLON",
94410
- newline: "NEWLINE",
94411
- comment: "COMMENT",
94412
- indent: "INDENT",
94413
- invalid: "INVALID",
94414
- number: "NUMBER",
94415
- comma: "COMMA"
94416
- };
94417
- const VALID_PROP_VALUE_TOKENS = [
94418
- TOKEN_TYPES.boolean,
94419
- TOKEN_TYPES.string,
94420
- TOKEN_TYPES.number
94421
- ];
94422
- function isValidPropValueToken(token) {
94423
- return VALID_PROP_VALUE_TOKENS.indexOf(token.type) >= 0;
94678
+ //#region yarn/src/lockfile/parse.ts
94679
+ const VERSION_REGEX = /^yarn lockfile v(\d+)$/;
94680
+ const TOKEN_TYPES = {
94681
+ boolean: "BOOLEAN",
94682
+ string: "STRING",
94683
+ identifier: "IDENTIFIER",
94684
+ eof: "EOF",
94685
+ colon: "COLON",
94686
+ newline: "NEWLINE",
94687
+ comment: "COMMENT",
94688
+ indent: "INDENT",
94689
+ invalid: "INVALID",
94690
+ number: "NUMBER",
94691
+ comma: "COMMA"
94692
+ };
94693
+ const VALID_PROP_VALUE_TOKENS = [
94694
+ TOKEN_TYPES.boolean,
94695
+ TOKEN_TYPES.string,
94696
+ TOKEN_TYPES.number
94697
+ ];
94698
+ function isValidPropValueToken(token) {
94699
+ return VALID_PROP_VALUE_TOKENS.indexOf(token.type) >= 0;
94700
+ }
94701
+ function* tokenise(input) {
94702
+ let lastNewline = false;
94703
+ let line = 1;
94704
+ let col = 0;
94705
+ function buildToken(type, value) {
94706
+ return {
94707
+ line,
94708
+ col,
94709
+ type,
94710
+ value
94711
+ };
94424
94712
  }
94425
- function* tokenise(input) {
94426
- let lastNewline = false;
94427
- let line = 1;
94428
- let col = 0;
94429
- function buildToken(type, value) {
94430
- return {
94431
- line,
94432
- col,
94433
- type,
94434
- value
94435
- };
94436
- }
94437
- while (input.length) {
94438
- let chop = 0;
94439
- if (input[0] === "\n" || input[0] === "\r") {
94440
- chop++;
94441
- if (input[1] === "\n") chop++;
94442
- line++;
94443
- col = 0;
94444
- yield buildToken(TOKEN_TYPES.newline);
94445
- } else if (input[0] === "#") {
94446
- chop++;
94447
- let nextNewline = input.indexOf("\n", chop);
94448
- if (nextNewline === -1) nextNewline = input.length;
94449
- const val = input.substring(chop, nextNewline);
94450
- chop = nextNewline;
94451
- yield buildToken(TOKEN_TYPES.comment, val);
94452
- } else if (input[0] === " ") {
94453
- if (lastNewline) {
94454
- let indentSize = 1;
94455
- for (let i = 1; input[i] === " "; i++) indentSize++;
94456
- if (indentSize % 2) throw new TypeError("Invalid number of spaces");
94457
- else {
94458
- chop = indentSize;
94459
- yield buildToken(TOKEN_TYPES.indent, indentSize / 2);
94460
- }
94461
- } else chop++;
94462
- } else if (input[0] === "\"") {
94463
- let i = 1;
94464
- for (; i < input.length; i++) if (input[i] === "\"") {
94465
- if (!(input[i - 1] === "\\" && input[i - 2] !== "\\")) {
94466
- i++;
94467
- break;
94468
- }
94713
+ while (input.length) {
94714
+ let chop = 0;
94715
+ if (input[0] === "\n" || input[0] === "\r") {
94716
+ chop++;
94717
+ if (input[1] === "\n") chop++;
94718
+ line++;
94719
+ col = 0;
94720
+ yield buildToken(TOKEN_TYPES.newline);
94721
+ } else if (input[0] === "#") {
94722
+ chop++;
94723
+ let nextNewline = input.indexOf("\n", chop);
94724
+ if (nextNewline === -1) nextNewline = input.length;
94725
+ const val = input.substring(chop, nextNewline);
94726
+ chop = nextNewline;
94727
+ yield buildToken(TOKEN_TYPES.comment, val);
94728
+ } else if (input[0] === " ") {
94729
+ if (lastNewline) {
94730
+ let indentSize = 1;
94731
+ for (let i = 1; input[i] === " "; i++) indentSize++;
94732
+ if (indentSize % 2) throw new TypeError("Invalid number of spaces");
94733
+ else {
94734
+ chop = indentSize;
94735
+ yield buildToken(TOKEN_TYPES.indent, indentSize / 2);
94736
+ }
94737
+ } else chop++;
94738
+ } else if (input[0] === "\"") {
94739
+ let i = 1;
94740
+ for (; i < input.length; i++) if (input[i] === "\"") {
94741
+ if (!(input[i - 1] === "\\" && input[i - 2] !== "\\")) {
94742
+ i++;
94743
+ break;
94469
94744
  }
94470
- const val = input.substring(0, i);
94471
- chop = i;
94472
- try {
94473
- yield buildToken(TOKEN_TYPES.string, JSON.parse(val));
94474
- } catch (err) {
94475
- if (err instanceof SyntaxError) yield buildToken(TOKEN_TYPES.invalid);
94476
- else throw err;
94477
- }
94478
- } else if (/^[0-9]/.test(input)) {
94479
- const val = /^[0-9]+/.exec(input)[0];
94480
- chop = val.length;
94481
- yield buildToken(TOKEN_TYPES.number, +val);
94482
- } else if (/^true/.test(input)) {
94483
- yield buildToken(TOKEN_TYPES.boolean, true);
94484
- chop = 4;
94485
- } else if (/^false/.test(input)) {
94486
- yield buildToken(TOKEN_TYPES.boolean, false);
94487
- chop = 5;
94488
- } else if (input[0] === ":") {
94489
- yield buildToken(TOKEN_TYPES.colon);
94490
- chop++;
94491
- } else if (input[0] === ",") {
94492
- yield buildToken(TOKEN_TYPES.comma);
94493
- chop++;
94494
- } else if (/^[a-zA-Z\/.-]/g.test(input)) {
94495
- let i = 0;
94496
- for (; i < input.length; i++) {
94497
- const char = input[i];
94498
- if (char === ":" || char === " " || char === "\n" || char === "\r" || char === ",") break;
94499
- }
94500
- const name = input.substring(0, i);
94501
- chop = i;
94502
- yield buildToken(TOKEN_TYPES.string, name);
94503
- } else yield buildToken(TOKEN_TYPES.invalid);
94504
- if (!chop) yield buildToken(TOKEN_TYPES.invalid);
94505
- col += chop;
94506
- lastNewline = input[0] === "\n" || input[0] === "\r" && input[1] === "\n";
94507
- input = input.slice(chop);
94508
- }
94509
- yield buildToken(TOKEN_TYPES.eof);
94510
- }
94511
- var Parser = class {
94512
- constructor(input, fileLoc = "lockfile") {
94513
- _defineProperty(this, "fileLoc", void 0);
94514
- _defineProperty(this, "token", void 0);
94515
- _defineProperty(this, "tokens", void 0);
94516
- _defineProperty(this, "comments", void 0);
94517
- this.comments = [];
94518
- this.tokens = tokenise(input);
94519
- this.fileLoc = fileLoc;
94520
- }
94521
- onComment(token) {
94522
- const value = token.value;
94523
- (0, _invariant.default)(typeof value === "string", "expected token value to be a string");
94524
- const comment = value.trim();
94525
- const versionMatch = comment.match(VERSION_REGEX);
94526
- if (versionMatch) {
94527
- const version = +versionMatch[1];
94528
- if (version > _constants.LOCKFILE_VERSION) throw new _errors.MessageError(`Can't install from a lockfile of version ${version} as you're on an old yarn version that only supports versions up to ${_constants.LOCKFILE_VERSION}. Run \`$ yarn self-update\` to upgrade to the latest version.`);
94529
- }
94530
- this.comments.push(comment);
94531
- }
94532
- next() {
94533
- const item = this.tokens.next();
94534
- (0, _invariant.default)(item, "expected a token");
94535
- const { done, value } = item;
94536
- if (done || !value) throw new Error("No more tokens");
94537
- else if (value.type === TOKEN_TYPES.comment) {
94538
- this.onComment(value);
94539
- return this.next();
94540
- } else return this.token = value;
94541
- }
94542
- unexpected(msg = "Unexpected token") {
94543
- throw new SyntaxError(`${msg} ${this.token.line}:${this.token.col} in ${this.fileLoc}`);
94544
- }
94545
- expect(tokType) {
94546
- if (this.token.type === tokType) this.next();
94547
- else this.unexpected();
94548
- }
94549
- eat(tokType) {
94550
- if (this.token.type === tokType) {
94745
+ }
94746
+ const val = input.substring(0, i);
94747
+ chop = i;
94748
+ try {
94749
+ yield buildToken(TOKEN_TYPES.string, JSON.parse(val));
94750
+ } catch (err) {
94751
+ if (err instanceof SyntaxError) yield buildToken(TOKEN_TYPES.invalid);
94752
+ else throw err;
94753
+ }
94754
+ } else if (/^[0-9]/.test(input)) {
94755
+ const val = /^[0-9]+/.exec(input)[0];
94756
+ chop = val.length;
94757
+ yield buildToken(TOKEN_TYPES.number, +val);
94758
+ } else if (/^true/.test(input)) {
94759
+ yield buildToken(TOKEN_TYPES.boolean, true);
94760
+ chop = 4;
94761
+ } else if (/^false/.test(input)) {
94762
+ yield buildToken(TOKEN_TYPES.boolean, false);
94763
+ chop = 5;
94764
+ } else if (input[0] === ":") {
94765
+ yield buildToken(TOKEN_TYPES.colon);
94766
+ chop++;
94767
+ } else if (input[0] === ",") {
94768
+ yield buildToken(TOKEN_TYPES.comma);
94769
+ chop++;
94770
+ } else if (/^[a-zA-Z\/.-]/g.test(input)) {
94771
+ let i = 0;
94772
+ for (; i < input.length; i++) {
94773
+ const char = input[i];
94774
+ if (char === ":" || char === " " || char === "\n" || char === "\r" || char === ",") break;
94775
+ }
94776
+ const name = input.substring(0, i);
94777
+ chop = i;
94778
+ yield buildToken(TOKEN_TYPES.string, name);
94779
+ } else yield buildToken(TOKEN_TYPES.invalid);
94780
+ if (!chop) yield buildToken(TOKEN_TYPES.invalid);
94781
+ col += chop;
94782
+ lastNewline = input[0] === "\n" || input[0] === "\r" && input[1] === "\n";
94783
+ input = input.slice(chop);
94784
+ }
94785
+ yield buildToken(TOKEN_TYPES.eof);
94786
+ }
94787
+ var Parser$1 = class {
94788
+ constructor(input, fileLoc = "lockfile") {
94789
+ this.comments = [];
94790
+ this.tokens = tokenise(input);
94791
+ this.fileLoc = fileLoc;
94792
+ }
94793
+ fileLoc;
94794
+ token;
94795
+ tokens;
94796
+ comments;
94797
+ onComment(token) {
94798
+ const value = token.value;
94799
+ (0, import_invariant.default)(typeof value === "string", "expected token value to be a string");
94800
+ const comment = value.trim();
94801
+ const versionMatch = comment.match(VERSION_REGEX);
94802
+ if (versionMatch) {
94803
+ const version = +versionMatch[1];
94804
+ if (version > 1) throw new MessageError(`Can't install from a lockfile of version ${version} as you're on an old yarn version that only supports versions up to ${1}. Run \`$ yarn self-update\` to upgrade to the latest version.`);
94805
+ }
94806
+ this.comments.push(comment);
94807
+ }
94808
+ next() {
94809
+ const item = this.tokens.next();
94810
+ (0, import_invariant.default)(item, "expected a token");
94811
+ const { done, value } = item;
94812
+ if (done || !value) throw new Error("No more tokens");
94813
+ else if (value.type === TOKEN_TYPES.comment) {
94814
+ this.onComment(value);
94815
+ return this.next();
94816
+ } else return this.token = value;
94817
+ }
94818
+ unexpected(msg = "Unexpected token") {
94819
+ throw new SyntaxError(`${msg} ${this.token.line}:${this.token.col} in ${this.fileLoc}`);
94820
+ }
94821
+ expect(tokType) {
94822
+ if (this.token.type === tokType) this.next();
94823
+ else this.unexpected();
94824
+ }
94825
+ eat(tokType) {
94826
+ if (this.token.type === tokType) {
94827
+ this.next();
94828
+ return true;
94829
+ } else return false;
94830
+ }
94831
+ parse(indent = 0) {
94832
+ const obj = nullify();
94833
+ while (true) {
94834
+ const propToken = this.token;
94835
+ if (propToken.type === TOKEN_TYPES.newline) {
94836
+ const nextToken = this.next();
94837
+ if (!indent) continue;
94838
+ if (nextToken.type !== TOKEN_TYPES.indent) break;
94839
+ if (nextToken.value === indent) this.next();
94840
+ else break;
94841
+ } else if (propToken.type === TOKEN_TYPES.indent) {
94842
+ if (propToken.value === indent) this.next();
94843
+ else break;
94844
+ } else if (propToken.type === TOKEN_TYPES.eof) break;
94845
+ else if (propToken.type === TOKEN_TYPES.string) {
94846
+ const key = propToken.value;
94847
+ (0, import_invariant.default)(key, "Expected a key");
94848
+ const keys = [key];
94551
94849
  this.next();
94552
- return true;
94553
- } else return false;
94554
- }
94555
- parse(indent = 0) {
94556
- const obj = (0, _map.default)();
94557
- while (true) {
94558
- const propToken = this.token;
94559
- if (propToken.type === TOKEN_TYPES.newline) {
94560
- const nextToken = this.next();
94561
- if (!indent) continue;
94562
- if (nextToken.type !== TOKEN_TYPES.indent) break;
94563
- if (nextToken.value === indent) this.next();
94564
- else break;
94565
- } else if (propToken.type === TOKEN_TYPES.indent) {
94566
- if (propToken.value === indent) this.next();
94567
- else break;
94568
- } else if (propToken.type === TOKEN_TYPES.eof) break;
94569
- else if (propToken.type === TOKEN_TYPES.string) {
94570
- const key = propToken.value;
94571
- (0, _invariant.default)(key, "Expected a key");
94572
- const keys = [key];
94850
+ while (this.token.type === TOKEN_TYPES.comma) {
94851
+ this.next();
94852
+ const keyToken = this.token;
94853
+ if (keyToken.type !== TOKEN_TYPES.string) this.unexpected("Expected string");
94854
+ const key = keyToken.value;
94855
+ (0, import_invariant.default)(key, "Expected a key");
94856
+ keys.push(key);
94573
94857
  this.next();
94574
- while (this.token.type === TOKEN_TYPES.comma) {
94575
- this.next();
94576
- const keyToken = this.token;
94577
- if (keyToken.type !== TOKEN_TYPES.string) this.unexpected("Expected string");
94578
- const key = keyToken.value;
94579
- (0, _invariant.default)(key, "Expected a key");
94580
- keys.push(key);
94581
- this.next();
94582
- }
94583
- const wasColon = this.token.type === TOKEN_TYPES.colon;
94584
- if (wasColon) this.next();
94585
- if (isValidPropValueToken(this.token)) {
94586
- for (const key of keys) obj[key] = this.token.value;
94587
- this.next();
94588
- } else if (wasColon) {
94589
- const val = this.parse(indent + 1);
94590
- for (const key of keys) obj[key] = val;
94591
- if (indent && this.token.type !== TOKEN_TYPES.indent) break;
94592
- } else this.unexpected("Invalid value type");
94593
- } else this.unexpected(`Unknown token: ${_util.default.inspect(propToken)}`);
94594
- }
94595
- return obj;
94596
- }
94597
- };
94598
- const MERGE_CONFLICT_ANCESTOR = "|||||||";
94599
- const MERGE_CONFLICT_END = ">>>>>>>";
94600
- const MERGE_CONFLICT_SEP = "=======";
94601
- const MERGE_CONFLICT_START = "<<<<<<<";
94602
- /**
94603
- * Extract the two versions of the lockfile from a merge conflict.
94604
- */
94605
- function extractConflictVariants(str) {
94606
- const variants = [[], []];
94607
- const lines = str.split(/\r?\n/g);
94608
- let skip = false;
94609
- while (lines.length) {
94610
- const line = lines.shift();
94611
- if (line.startsWith(MERGE_CONFLICT_START)) {
94612
- while (lines.length) {
94613
- const conflictLine = lines.shift();
94614
- if (conflictLine === MERGE_CONFLICT_SEP) {
94615
- skip = false;
94616
- break;
94617
- } else if (skip || conflictLine.startsWith(MERGE_CONFLICT_ANCESTOR)) {
94618
- skip = true;
94619
- continue;
94620
- } else variants[0].push(conflictLine);
94621
- }
94622
- while (lines.length) {
94623
- const conflictLine = lines.shift();
94624
- if (conflictLine.startsWith(MERGE_CONFLICT_END)) break;
94625
- else variants[1].push(conflictLine);
94626
94858
  }
94627
- } else {
94628
- variants[0].push(line);
94629
- variants[1].push(line);
94630
- }
94859
+ const wasColon = this.token.type === TOKEN_TYPES.colon;
94860
+ if (wasColon) this.next();
94861
+ if (isValidPropValueToken(this.token)) {
94862
+ for (const key of keys) obj[key] = this.token.value;
94863
+ this.next();
94864
+ } else if (wasColon) {
94865
+ const val = this.parse(indent + 1);
94866
+ for (const key of keys) obj[key] = val;
94867
+ if (indent && this.token.type !== TOKEN_TYPES.indent) break;
94868
+ } else this.unexpected("Invalid value type");
94869
+ } else this.unexpected(`Unknown token: ${Util.inspect(propToken)}`);
94631
94870
  }
94632
- return [variants[0].join("\n"), variants[1].join("\n")];
94633
- }
94634
- /**
94635
- * Check if a lockfile has merge conflicts.
94636
- */
94637
- function hasMergeConflicts(str) {
94638
- return str.includes(MERGE_CONFLICT_START) && str.includes(MERGE_CONFLICT_SEP) && str.includes(MERGE_CONFLICT_END);
94871
+ return obj;
94639
94872
  }
94640
- /**
94641
- * Parse the lockfile.
94642
- */
94643
- function parse(str, fileLoc) {
94644
- const parser = new Parser(str, fileLoc);
94645
- parser.next();
94646
- if (!fileLoc.endsWith(`.yml`)) try {
94647
- return parser.parse();
94648
- } catch (error1) {
94649
- try {
94650
- return _yamljs.default.parse(str);
94651
- } catch (error2) {
94652
- throw error1;
94873
+ };
94874
+ const MERGE_CONFLICT_ANCESTOR = "|||||||";
94875
+ const MERGE_CONFLICT_END = ">>>>>>>";
94876
+ const MERGE_CONFLICT_SEP = "=======";
94877
+ const MERGE_CONFLICT_START = "<<<<<<<";
94878
+ /**
94879
+ * Extract the two versions of the lockfile from a merge conflict.
94880
+ */
94881
+ function extractConflictVariants(str) {
94882
+ const variants = [[], []];
94883
+ const lines = str.split(/\r?\n/g);
94884
+ let skip = false;
94885
+ while (lines.length) {
94886
+ const line = lines.shift();
94887
+ if (line.startsWith(MERGE_CONFLICT_START)) {
94888
+ while (lines.length) {
94889
+ const conflictLine = lines.shift();
94890
+ if (conflictLine === MERGE_CONFLICT_SEP) {
94891
+ skip = false;
94892
+ break;
94893
+ } else if (skip || conflictLine.startsWith(MERGE_CONFLICT_ANCESTOR)) {
94894
+ skip = true;
94895
+ continue;
94896
+ } else variants[0].push(conflictLine);
94653
94897
  }
94654
- }
94655
- else {
94656
- const result = _yamljs.default.parse(str);
94657
- if (typeof result === "object") return result;
94658
- else return {};
94898
+ while (lines.length) {
94899
+ const conflictLine = lines.shift();
94900
+ if (conflictLine.startsWith(MERGE_CONFLICT_END)) break;
94901
+ else variants[1].push(conflictLine);
94902
+ }
94903
+ } else {
94904
+ variants[0].push(line);
94905
+ variants[1].push(line);
94659
94906
  }
94660
94907
  }
94661
- /**
94662
- * Parse and merge the two variants in a conflicted lockfile.
94663
- */
94664
- function parseWithConflict(str, fileLoc) {
94665
- const variants = extractConflictVariants(str);
94908
+ return [variants[0].join("\n"), variants[1].join("\n")];
94909
+ }
94910
+ /**
94911
+ * Check if a lockfile has merge conflicts.
94912
+ */
94913
+ function hasMergeConflicts(str) {
94914
+ return str.includes(MERGE_CONFLICT_START) && str.includes(MERGE_CONFLICT_SEP) && str.includes(MERGE_CONFLICT_END);
94915
+ }
94916
+ /**
94917
+ * Parse the lockfile.
94918
+ */
94919
+ function parse$1(str, fileLoc) {
94920
+ const parser = new Parser$1(str, fileLoc);
94921
+ parser.next();
94922
+ if (!fileLoc.endsWith(`.yml`)) try {
94923
+ return parser.parse();
94924
+ } catch (error1) {
94666
94925
  try {
94667
- return {
94668
- type: "merge",
94669
- object: Object.assign({}, parse(variants[0], fileLoc), parse(variants[1], fileLoc))
94670
- };
94671
- } catch (err) {
94672
- if (err instanceof SyntaxError) return {
94673
- type: "conflict",
94674
- object: {}
94675
- };
94676
- else throw err;
94926
+ return import_Yaml.default.parse(str);
94927
+ } catch (error2) {
94928
+ throw error1;
94677
94929
  }
94678
94930
  }
94679
- function _default(str, fileLoc = "lockfile") {
94680
- str = (0, _stripBom.default)(str);
94681
- return hasMergeConflicts(str) ? parseWithConflict(str, fileLoc) : {
94682
- type: "success",
94683
- object: parse(str, fileLoc)
94931
+ else {
94932
+ const result = import_Yaml.default.parse(str);
94933
+ if (typeof result === "object") return result;
94934
+ else return {};
94935
+ }
94936
+ }
94937
+ /**
94938
+ * Parse and merge the two variants in a conflicted lockfile.
94939
+ */
94940
+ function parseWithConflict(str, fileLoc) {
94941
+ const variants = extractConflictVariants(str);
94942
+ try {
94943
+ return {
94944
+ type: "merge",
94945
+ object: Object.assign({}, parse$1(variants[0], fileLoc), parse$1(variants[1], fileLoc))
94684
94946
  };
94947
+ } catch (err) {
94948
+ if (err instanceof SyntaxError) return {
94949
+ type: "conflict",
94950
+ object: {}
94951
+ };
94952
+ else throw err;
94685
94953
  }
94686
- }));
94954
+ }
94955
+ function parse_default(str, fileLoc = "lockfile") {
94956
+ str = stripBom(str);
94957
+ return hasMergeConflicts(str) ? parseWithConflict(str, fileLoc) : {
94958
+ type: "success",
94959
+ object: parse$1(str, fileLoc)
94960
+ };
94961
+ }
94687
94962
 
94688
94963
  //#endregion
94689
- //#region yarn/index.js
94690
- var require_yarn = /* @__PURE__ */ __commonJSMin(((exports, module) => {
94691
- const parse = require_parse().default;
94692
- module.exports = { parseYarnLock: (str, filename) => parse(str, filename).object };
94693
- }));
94964
+ //#region yarn/index.ts
94965
+ /**
94966
+ * @param {string} str the yarn.lock contents
94967
+ * @param {string} [filename] name to report in parse errors
94968
+ * @returns {object} the lockfile as a map of `name@spec` to its resolution
94969
+ */
94970
+ function parseYarnLock(str, filename) {
94971
+ return parse_default(str, filename).object;
94972
+ }
94694
94973
 
94695
94974
  //#endregion
94696
94975
  //#region lib/util/make-npm-env.ts
94697
- var import_yarn = require_yarn();
94698
94976
  /**
94699
94977
  * Helper functions for creating npm environment variables.
94700
94978
  * Matches npm 11+ minimal approach.
@@ -94715,8 +94993,9 @@ function initEnv(fromEnv, production) {
94715
94993
  }
94716
94994
  /**
94717
94995
  * Create npm_package_* and npm_config_* environment variables
94718
- * Matches npm 11's minimal approach - only adds npm_package_name, npm_package_version, npm_package_json
94719
- * and minimal npm_config_* variables
94996
+ * Matches npm 11's minimal approach - only adds npm_package_name and npm_package_version
94997
+ * (npm_package_json is added by the caller, which knows the manifest path) and minimal
94998
+ * npm_config_* variables
94720
94999
  *
94721
95000
  * @param {Object} data - Package.json or config object
94722
95001
  * @param {Object} opts - Options object
@@ -95114,8 +95393,31 @@ const readPkgJson$1 = (dir) => {
95114
95393
  return {};
95115
95394
  });
95116
95395
  };
95117
- const fynInstalledDir = Path.dirname(optionalRequire.resolve("../package.json"));
95118
- const fynCli = createRequire(Path.join(fynInstalledDir, "_")).resolve("./bin/fyn.js");
95396
+ let fynCliPath;
95397
+ const findFynCli = () => {
95398
+ if (fynCliPath) return fynCliPath;
95399
+ const candidates = [];
95400
+ const resolveFrom = (dir, spec) => {
95401
+ try {
95402
+ return createRequire(Path.join(dir, "_")).resolve(spec);
95403
+ } catch {
95404
+ return;
95405
+ }
95406
+ };
95407
+ const ownDir = optionalRequire.resolve("../package.json");
95408
+ if (ownDir) candidates.push(Path.dirname(ownDir));
95409
+ const asDep = resolveFrom(Path.dirname(fileURLToPath(import.meta.url)), "fyn/package.json");
95410
+ if (asDep) candidates.push(Path.dirname(asDep));
95411
+ for (const dir of candidates) {
95412
+ const cli = resolveFrom(dir, "./bin/fyn.mjs");
95413
+ if (cli) {
95414
+ fynCliPath = cli;
95415
+ return cli;
95416
+ }
95417
+ }
95418
+ fynCliPath = Path.join(candidates[0] || process.cwd(), "bin", "fyn.mjs");
95419
+ return fynCliPath;
95420
+ };
95119
95421
  const ONE_MB = 1048576;
95120
95422
  var LifecycleScripts = class {
95121
95423
  _fyn;
@@ -95139,7 +95441,8 @@ var LifecycleScripts = class {
95139
95441
  if (this._appDir) xsh.envPath.addToFront(Path.join(this._appDir, "node_modules/.bin"), env);
95140
95442
  xsh.envPath.addToFront(Path.join(this._pkgDir, "node_modules/.bin"), env);
95141
95443
  env.npm_node_execpath = env.NODE = env.NODE || process.execPath;
95142
- env.npm_execpath = fynCli;
95444
+ env.npm_execpath = findFynCli();
95445
+ env.npm_package_json = Path.join(this._pkgDir, "package.json");
95143
95446
  env.INIT_CWD = this._fyn.initCwd || this._fyn.cwd || process.cwd();
95144
95447
  return env;
95145
95448
  }
@@ -95170,7 +95473,11 @@ var LifecycleScripts = class {
95170
95473
  const scriptName = chalk.magenta(name);
95171
95474
  const script = `"${chalk.cyan(scriptCommand)}"`;
95172
95475
  const pkgDir = log_format_default.pkgPath(this._pkg.name, this._pkgDir.replace(this._fyn.cwd || "", "."));
95173
- const env = this.makeEnv({ PWD: this._pkgDir });
95476
+ const env = this.makeEnv({
95477
+ PWD: this._pkgDir,
95478
+ npm_lifecycle_event: name,
95479
+ npm_lifecycle_script: scriptCommand
95480
+ });
95174
95481
  logger$1.verbose(`running npm script '${scriptName}' of ${dimPkgName}: ${script} - at dir ${pkgDir}`);
95175
95482
  const child = xsh.exec({
95176
95483
  silent,
@@ -95416,8 +95723,6 @@ var PkgDepLinker = class {
95416
95723
  //#endregion
95417
95724
  //#region lib/pkg-opt-resolver.ts
95418
95725
  const { readPkgJson } = fyntil;
95419
- /** `optFailed` value meaning the package's os/cpu did not match the machine that checked it */
95420
- const OPT_FAILED_PLATFORM = 3;
95421
95726
  /**
95422
95727
  * Decide what a lockfile's `optFailed` record means for the machine running *this* install.
95423
95728
  *
@@ -95446,6 +95751,7 @@ var PkgOptResolver = class {
95446
95751
  _resolving;
95447
95752
  _failedChecks;
95448
95753
  _failedPkgs;
95754
+ /** assigned by `PkgDepResolver` right after it is constructed, so not private (FJM-154) */
95449
95755
  _depResolver;
95450
95756
  _inflights;
95451
95757
  _fyn;
@@ -95512,7 +95818,7 @@ var PkgOptResolver = class {
95512
95818
  version,
95513
95819
  top: !import_lodash_min.default.get(item, ["parent", "depth"])
95514
95820
  };
95515
- const policy = evaluateScriptPolicy(optDepInfo, this._fyn.allowScripts, { allowTopLevel: this._fyn.allowTopLevelScripts });
95821
+ const policy = evaluateScriptPolicy(optDepInfo, this._fyn.allowScripts, this._fyn.scriptPolicyOptions);
95516
95822
  return {
95517
95823
  allowed: isScriptAllowed(policy, "preinstall"),
95518
95824
  policy
@@ -95798,7 +96104,7 @@ var LocalPkgBuilder = class {
95798
96104
  });
95799
96105
  const cliFynJs = Path.join(__dirname, "../cli/fyn.js");
95800
96106
  if (await file_ops_default.exists(cliFynJs)) this._fynJs = cliFynJs;
95801
- else this._fynJs = Path.join(__dirname, "../bin/fyn.js");
96107
+ else this._fynJs = Path.join(__dirname, "../bin/fyn.mjs");
95802
96108
  const { localsByDepth } = this._options;
95803
96109
  this._promiseQ.on("doneItem", (data) => {
95804
96110
  this._waitItems[data.item.fullPath].resolve({});
@@ -96341,7 +96647,7 @@ async function syncLocalExports(options) {
96341
96647
  const createLock$1 = Util.promisify(import_lockfile.lock);
96342
96648
  const unlock$1 = Util.promisify(import_lockfile.unlock);
96343
96649
  const { posixify } = fyntil;
96344
- var Fyn = class {
96650
+ var Fyn = class Fyn {
96345
96651
  _cliSource;
96346
96652
  _shownMissingFiles;
96347
96653
  _options;
@@ -96356,9 +96662,12 @@ var Fyn = class {
96356
96662
  _pkgFyn;
96357
96663
  _pkgSrcMgr;
96358
96664
  _depLocker;
96665
+ /** set in `resolveDependencies`, before the collaborators that read it exist */
96359
96666
  _distFetcher;
96667
+ /** set in `resolveDependencies`, before the collaborators that read it exist */
96360
96668
  _depResolver;
96361
96669
  _optResolver;
96670
+ /** set in `resolveDependencies`, before any collaborator that reads it exists */
96362
96671
  _data;
96363
96672
  _central;
96364
96673
  _npmLockData;
@@ -96375,9 +96684,14 @@ var Fyn = class {
96375
96684
  _npmConfigEnv;
96376
96685
  _shortPkgDir;
96377
96686
  _allowScripts;
96687
+ _denyScripts;
96378
96688
  _allowTopLevelScripts;
96379
96689
  _enforceRegistryDeps;
96380
- /** Local packages with nested dependencies */
96690
+ _scriptPolicy;
96691
+ _reviewLocalPackages;
96692
+ _blockedScripts;
96693
+ _pendingScripts;
96694
+ /** Local packages with nested dependencies - written by the dep linker, never read back */
96381
96695
  localPkgWithNestedDep;
96382
96696
  constructor({ opts = {}, _cliSource = {}, _fynpo = true }) {
96383
96697
  this._cliSource = { ..._cliSource };
@@ -96444,7 +96758,7 @@ var Fyn = class {
96444
96758
  if (file_ops_default.existsSync(yarnLockName)) {
96445
96759
  logger$1.info("Reading yarn.lock");
96446
96760
  const yarnLockData = await file_ops_default.readFile(yarnLockName, "utf-8");
96447
- this._yarnLock = (0, import_yarn.parseYarnLock)(yarnLockData);
96761
+ this._yarnLock = parseYarnLock(yarnLockData);
96448
96762
  }
96449
96763
  return false;
96450
96764
  }
@@ -96529,15 +96843,60 @@ var Fyn = class {
96529
96843
  if (this._fynpo?.config) return this._fynpo.dir === this._cwd;
96530
96844
  return false;
96531
96845
  }
96846
+ /**
96847
+ * Options a fynpo config must never supply. `cwd`/`initCwd` are where fyn was
96848
+ * invoked; a monorepo config that relocated the install would be changing
96849
+ * which package.json is being installed.
96850
+ */
96851
+ static FYNPO_OPTIONS_DENY = ["cwd", "initCwd"];
96852
+ /**
96853
+ * Options with precedence rules of their own - allowlists union across scopes
96854
+ * with a denial winning at any level, the blacklist only ever unions, and the
96855
+ * policy mode may only be tightened by a package. The `allowScripts` /
96856
+ * `denyScripts` / `scriptPolicy` / `reviewLocalPackages` getters read the
96857
+ * fynpo config directly, so the generic merge must leave these alone rather
96858
+ * than flattening them here.
96859
+ */
96860
+ static FYNPO_OPTIONS_OWN_MERGE = [
96861
+ "allowScripts",
96862
+ "denyScripts",
96863
+ "allowTopLevelScripts",
96864
+ "scriptPolicy",
96865
+ "reviewLocalPackages"
96866
+ ];
96867
+ /**
96868
+ * Merge the monorepo's `fyn.options` (from fynpo.json / fynpo.config.js) into
96869
+ * this instance's options.
96870
+ *
96871
+ * Precedence is CLI > rc > fynpo config: `_cliSource[key]` records where each
96872
+ * option came from, and anything other than `"default"` means the user asked
96873
+ * for that value, so the monorepo config must not override it.
96874
+ */
96875
+ mergeFynpoOptions() {
96876
+ const fynpoOptions = import_lodash_min.default.get(this, [
96877
+ "_fynpo",
96878
+ "config",
96879
+ "fyn",
96880
+ "options"
96881
+ ]);
96882
+ if (import_lodash_min.default.isEmpty(fynpoOptions)) return;
96883
+ for (const key of Object.keys(fynpoOptions)) {
96884
+ if (Fyn.FYNPO_OPTIONS_DENY.includes(key) || Fyn.FYNPO_OPTIONS_OWN_MERGE.includes(key)) continue;
96885
+ const source = this._cliSource[key];
96886
+ if (source && source !== "default") {
96887
+ logger$1.debug(`fynpo config option ${key} ignored - already set by ${source}`);
96888
+ continue;
96889
+ }
96890
+ const value = fynpoOptions[key];
96891
+ const existing = this._options[key];
96892
+ this._options[key] = import_lodash_min.default.isPlainObject(value) && import_lodash_min.default.isPlainObject(existing) ? import_lodash_min.default.merge({}, value, existing) : value;
96893
+ }
96894
+ logger$1.debug("applied fynpo fyn.options", JSON.stringify(fynpoOptions));
96895
+ }
96532
96896
  async _initializePkg() {
96533
96897
  if (!this._fynpo) {
96534
96898
  this._fynpo = await fyntil.loadFynpo(this._cwd);
96535
- const savedCwd = this._options.cwd;
96536
- import_lodash_min.default.merge(this._options, import_lodash_min.default.get(this, "_fynpo.fyn.options"));
96537
- if (savedCwd) {
96538
- this._options.cwd = savedCwd;
96539
- this._cwd = savedCwd;
96540
- }
96899
+ this.mergeFynpoOptions();
96541
96900
  }
96542
96901
  this.checkLayoutOption();
96543
96902
  if (!this._pkg) {
@@ -96571,6 +96930,7 @@ var Fyn = class {
96571
96930
  }
96572
96931
  }
96573
96932
  this.checkProductionMode();
96933
+ logger$1.debug("install script policy", this.scriptPolicy);
96574
96934
  let fynpoNpmRun;
96575
96935
  if (this._fynpo?.config) {
96576
96936
  if (this._fynpo.graph?.getPackageAtDir(this._cwd)) {
@@ -96755,7 +97115,9 @@ var Fyn = class {
96755
97115
  centralDir,
96756
97116
  production: this.production,
96757
97117
  layout: this._options.layout,
96758
- shortPkgDir: this._shortPkgDir
97118
+ shortPkgDir: this._shortPkgDir,
97119
+ blockedScripts: this._blockedScripts || this._installConfig.blockedScripts || [],
97120
+ pendingScripts: this._pendingScripts || this._installConfig.pendingScripts || []
96759
97121
  };
96760
97122
  await file_ops_default.writeFile(filename, `${JSON.stringify(outputConfig, null, 2)}\n`);
96761
97123
  } catch (err) {
@@ -96774,7 +97136,8 @@ var Fyn = class {
96774
97136
  const options = _options || this._options;
96775
97137
  if (options.pkgFile) {
96776
97138
  let pkgFile;
96777
- if (options.pkgFile === "package.json") {
97139
+ const cwdIsExplicit = Boolean(this._cliSource.cwd) && this._cliSource.cwd !== "default";
97140
+ if (options.pkgFile === "package.json" && !cwdIsExplicit) {
96778
97141
  let foundDir = null;
96779
97142
  pathUpEach(this._cwd, (path) => {
96780
97143
  const testPath = Path.join(path, "package.json");
@@ -96796,6 +97159,7 @@ var Fyn = class {
96796
97159
  } catch (err) {
96797
97160
  logger$1.error("failed to read package.json file", pkgFile);
96798
97161
  logger$1.error(err.message);
97162
+ if (cwdIsExplicit) logger$1.error(`cwd was set explicitly by ${this._cliSource.cwd}, so fyn did not search parent directories for a package.json`);
96799
97163
  fyntil.exit(err);
96800
97164
  }
96801
97165
  const pkgFyn = await this.loadPkgFyn();
@@ -96857,14 +97221,112 @@ var Fyn = class {
96857
97221
  get showDeprecated() {
96858
97222
  return this._options.showDeprecated && "show-deprecated";
96859
97223
  }
97224
+ /**
97225
+ * The monorepo's `fyn.options` block, when installing inside a fynpo repo.
97226
+ * This is the repo-wide baseline for the script policy - one allowlist,
97227
+ * reviewed once, instead of a copy in every package.
97228
+ *
97229
+ * @returns {object} the fynpo `fyn.options`, or an empty object
97230
+ */
97231
+ get fynpoFynOptions() {
97232
+ return import_lodash_min.default.get(this, [
97233
+ "_fynpo",
97234
+ "config",
97235
+ "fyn",
97236
+ "options"
97237
+ ]) || {};
97238
+ }
96860
97239
  get allowScripts() {
96861
- if (this._allowScripts === void 0 && this._pkg) this._allowScripts = import_lodash_min.default.get(this._pkg, ["fyn", "allowScripts"]) || {};
97240
+ if (this._allowScripts === void 0 && this._pkg) this._allowScripts = mergeAllowScripts(this.fynpoFynOptions.allowScripts, import_lodash_min.default.get(this._pkg, ["fyn", "allowScripts"]), this._options.allowScripts);
96862
97241
  return this._allowScripts || {};
96863
97242
  }
97243
+ get denyScripts() {
97244
+ if (this._denyScripts === void 0 && this._pkg) this._denyScripts = mergeAllowScripts(this.fynpoFynOptions.denyScripts, import_lodash_min.default.get(this._pkg, ["fyn", "denyScripts"]), this._options.denyScripts);
97245
+ return this._denyScripts || {};
97246
+ }
96864
97247
  get allowTopLevelScripts() {
96865
- if (this._allowTopLevelScripts === void 0 && this._pkg) this._allowTopLevelScripts = import_lodash_min.default.get(this._pkg, ["fyn", "allowTopLevelScripts"]) || false;
97248
+ if (this._allowTopLevelScripts === void 0 && this._pkg) {
97249
+ const values = [
97250
+ this._options.allowTopLevelScripts,
97251
+ import_lodash_min.default.get(this._pkg, ["fyn", "allowTopLevelScripts"]),
97252
+ this.fynpoFynOptions.allowTopLevelScripts
97253
+ ];
97254
+ this._allowTopLevelScripts = values.find((v) => v !== void 0) || false;
97255
+ }
96866
97256
  return this._allowTopLevelScripts || false;
96867
97257
  }
97258
+ get scriptPolicy() {
97259
+ if (this._scriptPolicy === void 0) {
97260
+ const cliOpt = this._options.scriptPolicy;
97261
+ if (cliOpt) this._scriptPolicy = normalizeScriptPolicy(cliOpt);
97262
+ else if (this._pkg) this._scriptPolicy = strictestScriptPolicy(normalizeScriptPolicyIfSet(this.fynpoFynOptions.scriptPolicy), normalizeScriptPolicyIfSet(import_lodash_min.default.get(this._pkg, ["fyn", "scriptPolicy"])));
97263
+ }
97264
+ return this._scriptPolicy || "review";
97265
+ }
97266
+ get reviewLocalPackages() {
97267
+ if (this._reviewLocalPackages === void 0 && this._pkg) this._reviewLocalPackages = Boolean(this._options.reviewLocalPackages || import_lodash_min.default.get(this._pkg, ["fyn", "reviewLocalPackages"]) || this.fynpoFynOptions.reviewLocalPackages);
97268
+ return Boolean(this._reviewLocalPackages);
97269
+ }
97270
+ /**
97271
+ * The options every lifecycle-script policy evaluation needs, so the two
97272
+ * enforcement points (installer and optional-dep probe) cannot drift.
97273
+ *
97274
+ * @returns {object} options for `evaluateScriptPolicy`
97275
+ */
97276
+ get scriptPolicyOptions() {
97277
+ return {
97278
+ mode: this.scriptPolicy,
97279
+ allowTopLevel: this.allowTopLevelScripts,
97280
+ reviewLocalPackages: this.reviewLocalPackages,
97281
+ denyScripts: this.denyScripts
97282
+ };
97283
+ }
97284
+ get allowScriptsPin() {
97285
+ const opt = this._options.allowScriptsPin;
97286
+ return opt === void 0 ? true : Boolean(opt);
97287
+ }
97288
+ /**
97289
+ * Forget the merged allowlist, so it is rebuilt from disk. Used after the
97290
+ * install-time review prompt writes new approvals.
97291
+ *
97292
+ * @returns {void}
97293
+ */
97294
+ resetAllowScripts() {
97295
+ this._allowScripts = void 0;
97296
+ this._denyScripts = void 0;
97297
+ this._pkg = this._pkg && { ...this._pkg };
97298
+ }
97299
+ get allowScriptsPending() {
97300
+ return Boolean(this._options.allowScriptsPending);
97301
+ }
97302
+ /**
97303
+ * Record the packages whose install scripts were blocked, and those that
97304
+ * would need approval under `"review"`, so they are saved with the install
97305
+ * config and `fyn install-scripts ls` can list them without re-resolving the
97306
+ * tree.
97307
+ *
97308
+ * @param {object[]} blocked blocked-scripts records
97309
+ * @param {object[]} [pending] would-be-blocked-under-review records
97310
+ * @returns {void}
97311
+ */
97312
+ setBlockedScripts(blocked, pending) {
97313
+ this._blockedScripts = blocked || [];
97314
+ if (pending !== void 0) this._pendingScripts = pending;
97315
+ }
97316
+ /**
97317
+ * @returns {object[]} packages whose install scripts were blocked - from this
97318
+ * run when there was one, else what the last install recorded
97319
+ */
97320
+ get blockedScripts() {
97321
+ return this._blockedScripts || this._installConfig.blockedScripts || [];
97322
+ }
97323
+ /**
97324
+ * @returns {object[]} packages that would need approval under `"review"` -
97325
+ * only populated when the install ran with `--allow-scripts-pending`
97326
+ */
97327
+ get pendingScripts() {
97328
+ return this._pendingScripts || this._installConfig.pendingScripts || [];
97329
+ }
96868
97330
  get enforceRegistryDeps() {
96869
97331
  const cliOpt = this._options.enforceRegistryDeps;
96870
97332
  if (cliOpt !== void 0) return Boolean(cliOpt);
@@ -97517,6 +97979,696 @@ var PkgBinLinker$1 = class extends PkgBinLinkerBase {
97517
97979
  /* istanbul ignore next */
97518
97980
  const PkgBinLinker = process.platform === "win32" ? PkgBinLinkerWin32 : PkgBinLinker$1;
97519
97981
 
97982
+ //#endregion
97983
+ //#region lib/util/script-policy-report.ts
97984
+ /**
97985
+ * Build the record of one package whose install scripts were blocked.
97986
+ *
97987
+ * @param {object} depInfo resolved package data
97988
+ * @param {object} policy result of `evaluateScriptPolicy`
97989
+ * @param {string[]} blocked the lifecycle scripts that were skipped
97990
+ * @returns {object} a blocked-scripts record
97991
+ */
97992
+ function makeBlockedRecord(depInfo, policy, blocked) {
97993
+ return {
97994
+ name: depInfo.name,
97995
+ version: depInfo.version,
97996
+ key: policy.key,
97997
+ scripts: [...blocked],
97998
+ urlType: policy.urlType,
97999
+ reason: policy.reason,
98000
+ topLevel: Boolean(policy.topLevel),
98001
+ local: Boolean(policy.local)
98002
+ };
98003
+ }
98004
+ /**
98005
+ * The allowScripts key to write for a record: always the bare package name.
98006
+ *
98007
+ * The constraints live in the value, so one package is one entry however many
98008
+ * versions and scripts it accumulates - see {@link makeAllowEntry}.
98009
+ *
98010
+ * @param {object} record a blocked-scripts record
98011
+ * @returns {string} the allowScripts key
98012
+ */
98013
+ function allowScriptsKey(record) {
98014
+ return record.name;
98015
+ }
98016
+ /**
98017
+ * The allowScripts value fyn writes: `{ semver, scripts }`, with either field
98018
+ * omitted when it would not narrow anything.
98019
+ *
98020
+ * Pinned (the default) `semver` is a caret range of the reviewed version, so
98021
+ * the approval follows that release line and a jump past it comes back for
98022
+ * review. Unpinned there is no `semver`, which covers every version. `scripts`
98023
+ * is omitted when every install script is approved.
98024
+ *
98025
+ * @param {object} record a blocked-scripts record
98026
+ * @param {boolean} [pin] whether to scope the approval to the reviewed version
98027
+ * @returns {object} the allowScripts value
98028
+ */
98029
+ function makeAllowEntry(record, pin = true) {
98030
+ const entry = {};
98031
+ if (pin && record.version) entry.semver = caretRange(record.version);
98032
+ const scripts = normalizeScriptsValue(record.scripts);
98033
+ if (scripts.length > 0 && scripts[0] !== "*") entry.scripts = scripts;
98034
+ return entry;
98035
+ }
98036
+ /**
98037
+ * Fold a record into an entry a previous approval already wrote.
98038
+ *
98039
+ * One key per package means a second version widens `semver` rather than adding
98040
+ * a near-duplicate key. When the two approvals cover different scripts the
98041
+ * union is taken - with a single entry there is nowhere to record "these
98042
+ * scripts for that range, those for this one".
98043
+ *
98044
+ * @param {*} existing the value already in the map, any accepted form
98045
+ * @param {object} record a blocked-scripts record
98046
+ * @param {boolean} [pin] whether to scope the approval to the reviewed version
98047
+ * @returns {object} the merged entry
98048
+ */
98049
+ function mergeAllowEntry(existing, record, pin = true) {
98050
+ const added = makeAllowEntry(record, pin);
98051
+ if (existing === void 0) return added;
98052
+ const prior = toAllowEntry(existing);
98053
+ const entry = {};
98054
+ if (prior.semver !== void 0 && added.semver !== void 0) entry.semver = addVersionToRange(prior.semver, record.version);
98055
+ if (prior.scripts !== void 0 && added.scripts !== void 0) {
98056
+ const scripts = normalizeScriptsValue([...prior.scripts, ...added.scripts]);
98057
+ if (scripts[0] !== "*") entry.scripts = scripts;
98058
+ }
98059
+ return entry;
98060
+ }
98061
+ /**
98062
+ * Read any accepted allowScripts value as the object form, so a map written by
98063
+ * hand - or by npm - can be merged into.
98064
+ *
98065
+ * @param {*} value an allowScripts value
98066
+ * @returns {object} the value as `{ semver?, scripts? }`
98067
+ */
98068
+ function toAllowEntry(value) {
98069
+ if (value && typeof value === "object" && !Array.isArray(value)) return value;
98070
+ if (value === true || value === "*" || value === void 0) return {};
98071
+ const strings = (Array.isArray(value) ? value : [value]).filter((item) => typeof item === "string");
98072
+ if (strings.length === 1 && !LIFECYCLE_SCRIPTS.includes(strings[0].toLowerCase())) {
98073
+ const range = asRange(strings[0]);
98074
+ if (range) return { semver: range };
98075
+ }
98076
+ const scripts = normalizeScriptsValue(strings);
98077
+ return scripts[0] === "*" ? {} : { scripts };
98078
+ }
98079
+ /**
98080
+ * The range an approval of one reviewed version covers.
98081
+ *
98082
+ * @param {string} version the resolved version
98083
+ * @returns {string} a caret range, or the version itself when it is not semver
98084
+ */
98085
+ function caretRange(version) {
98086
+ const clean = unlocalify(String(version));
98087
+ return asRange(`^${clean}`) ? `^${clean}` : clean;
98088
+ }
98089
+ /**
98090
+ * Widen a range so it also covers a version, leaving it alone when it already
98091
+ * does. This is how a second approval of the same package becomes
98092
+ * `pkg@^1.2.3 || ^2.0.0` instead of a second key.
98093
+ *
98094
+ * @param {string} range the existing range
98095
+ * @param {string} version the version to cover
98096
+ * @returns {string} the range
98097
+ */
98098
+ function addVersionToRange(range, version) {
98099
+ const parts = splitRange(range);
98100
+ const added = caretRange(version);
98101
+ if (parts.includes(added) || parts.some((part) => satisfies(version, part))) return parts.join(" || ");
98102
+ return [...parts, added].join(" || ");
98103
+ }
98104
+ /**
98105
+ * Normalize a list of script names into the value fyn writes: `["*"]` when it
98106
+ * covers every install script, otherwise the names in lifecycle order.
98107
+ *
98108
+ * @param {(string[]|string|boolean)} scripts script names, or a wildcard
98109
+ * @returns {string[]} the value to write
98110
+ */
98111
+ function normalizeScriptsValue(scripts) {
98112
+ const names = new Set((Array.isArray(scripts) ? scripts : scripts === void 0 ? [] : [scripts]).map((name) => String(name).toLowerCase()));
98113
+ if (names.has("*") || names.has("true") || LIFECYCLE_SCRIPTS.every((name) => names.has(name))) return ["*"];
98114
+ const known = LIFECYCLE_SCRIPTS.filter((name) => names.has(name));
98115
+ const extra = [...names].filter((name) => !LIFECYCLE_SCRIPTS.includes(name));
98116
+ return [...known, ...extra];
98117
+ }
98118
+ /**
98119
+ * Build the `fyn.allowScripts` fragment that would let these packages run the
98120
+ * scripts they were blocked from.
98121
+ *
98122
+ * fyn's per-script form is kept rather than npm's blanket `"pkg": "1.2.3"`:
98123
+ * it approves the same version but only the scripts the package actually has,
98124
+ * so a later release that adds a `preinstall` still comes back for review.
98125
+ *
98126
+ * @param {object[]} records blocked-scripts records
98127
+ * @param {object} [options] options
98128
+ * @param {boolean} [options.pin] pin each approval to the resolved version
98129
+ * @returns {object} an allowScripts map
98130
+ */
98131
+ function buildAllowScriptsPatch(records, { pin = true } = {}) {
98132
+ const patch = {};
98133
+ for (const record of records) {
98134
+ const key = allowScriptsKey(record);
98135
+ patch[key] = mergeAllowEntry(patch[key], record, pin);
98136
+ }
98137
+ return patch;
98138
+ }
98139
+ /**
98140
+ * @param {object[]} records blocked-scripts records
98141
+ * @returns {object[]} the records, deduplicated by name@version and with their
98142
+ * script lists merged
98143
+ */
98144
+ function dedupeBlockedRecords(records) {
98145
+ const byId = /* @__PURE__ */ new Map();
98146
+ for (const record of records) {
98147
+ const id = `${record.name}@${record.version}`;
98148
+ const existing = byId.get(id);
98149
+ if (existing) existing.scripts = [.../* @__PURE__ */ new Set([...existing.scripts, ...record.scripts])];
98150
+ else byId.set(id, {
98151
+ ...record,
98152
+ scripts: [...record.scripts]
98153
+ });
98154
+ }
98155
+ return [...byId.values()].sort((a, b) => a.name.localeCompare(b.name));
98156
+ }
98157
+ /**
98158
+ * Why a package was blocked, in a few words for the summary table.
98159
+ *
98160
+ * @param {object} record a blocked-scripts record
98161
+ * @returns {string} the reason
98162
+ */
98163
+ function blockedReasonText(record) {
98164
+ switch (record.reason) {
98165
+ case "off": return "scriptPolicy is off";
98166
+ case "denied": return "denied (denyScripts / allowScripts false)";
98167
+ case "review": return "not reviewed";
98168
+ default: return `non-registry (${record.urlType})`;
98169
+ }
98170
+ }
98171
+ /**
98172
+ * Render the end-of-install summary for the packages that were blocked.
98173
+ *
98174
+ * @param {object[]} records blocked-scripts records
98175
+ * @param {object} [options] options
98176
+ * @param {string} [options.mode] the scriptPolicy mode in effect
98177
+ * @param {boolean} [options.pin] pin suggested approvals to the resolved version
98178
+ * @param {boolean} [options.anyTopLevel] whether any record is a direct dep
98179
+ * @returns {string[]} lines to log, empty when nothing was blocked
98180
+ */
98181
+ function formatBlockedScriptsSummary(records, { mode = "source", pin = true } = {}) {
98182
+ const blocked = dedupeBlockedRecords(records);
98183
+ if (blocked.length === 0) return [];
98184
+ const lines = [`${chalk.black.bgYellow("WARN")} ${chalk.magenta("install scripts blocked")} ` + chalk.yellow(`${blocked.length} package${blocked.length > 1 ? "s" : ""} did not run their install scripts`)];
98185
+ const width = blocked.reduce((w, r) => Math.max(w, `${r.name}@${r.version}`.length), 0);
98186
+ for (const record of blocked) {
98187
+ const id = `${record.name}@${record.version}`.padEnd(width);
98188
+ lines.push(` ${chalk.cyan(id)} ${chalk.yellow(record.scripts.join(", "))} ${chalk.gray(blockedReasonText(record))}`);
98189
+ }
98190
+ if (mode !== "off") {
98191
+ const patch = buildAllowScriptsPatch(blocked.filter((r) => r.reason !== "denied"), { pin });
98192
+ if (Object.keys(patch).length > 0) lines.push(chalk.blue(" To allow them, add to package.json:"), ` ${chalk.cyan(JSON.stringify({ fyn: { allowScripts: patch } }))}`, chalk.blue(` Or run: ${chalk.cyan("fyn install-scripts approve <package>")}`));
98193
+ if (blocked.some((r) => r.topLevel && r.reason !== "denied")) lines.push(chalk.blue(" Direct dependencies can also be trusted as a group with:"), ` ${chalk.cyan(`{ "fyn": { "allowTopLevelScripts": true } }`)}`);
98194
+ }
98195
+ return lines;
98196
+ }
98197
+ /**
98198
+ * Render the summary for packages that would need approval under `"review"`,
98199
+ * shown with `--allow-scripts-pending` while the install still runs under the
98200
+ * mode in effect. This is what switching to `"review"` would cost.
98201
+ *
98202
+ * @param {object[]} records pending-review records
98203
+ * @param {object} [options] options
98204
+ * @param {string} [options.mode] the scriptPolicy mode in effect
98205
+ * @param {boolean} [options.pin] pin suggested approvals to the resolved version
98206
+ * @returns {string[]} lines to log, empty when nothing is pending
98207
+ */
98208
+ function formatPendingScriptsSummary(records, { mode = "source", pin = true } = {}) {
98209
+ const pending = dedupeBlockedRecords(records);
98210
+ if (pending.length === 0) return [];
98211
+ const lines = [chalk.blue(`${pending.length} package${pending.length > 1 ? "s" : ""} would need approval under ${chalk.cyan("scriptPolicy: \"review\"")} (currently ${chalk.cyan(mode)})`)];
98212
+ const width = pending.reduce((w, r) => Math.max(w, `${r.name}@${r.version}`.length), 0);
98213
+ for (const record of pending) lines.push(` ${chalk.cyan(`${record.name}@${record.version}`.padEnd(width))} ` + chalk.yellow(record.scripts.join(", ")));
98214
+ const patch = buildAllowScriptsPatch(pending.filter((r) => r.reason !== "denied"), { pin });
98215
+ if (Object.keys(patch).length > 0) lines.push(chalk.blue(" To approve them now:"), ` ${chalk.cyan(JSON.stringify({ fyn: { allowScripts: patch } }))}`);
98216
+ return lines;
98217
+ }
98218
+
98219
+ //#endregion
98220
+ //#region lib/install-scripts.ts
98221
+ /**
98222
+ * Split an allowScripts key into its package name and optional spec.
98223
+ *
98224
+ * Keys are `name`, `name@spec` or `@scope/name@spec`, so the separator is the
98225
+ * `@` after the scope, not the first one.
98226
+ *
98227
+ * @param {string} key an allowScripts key
98228
+ * @returns {{name:string, spec:(string|undefined)}} the parts
98229
+ */
98230
+ function parseAllowKey(key) {
98231
+ const at = key.indexOf("@", key.startsWith("@") ? 1 : 0);
98232
+ if (at < 0) return {
98233
+ name: key,
98234
+ spec: void 0
98235
+ };
98236
+ return {
98237
+ name: key.slice(0, at),
98238
+ spec: key.slice(at + 1)
98239
+ };
98240
+ }
98241
+ /**
98242
+ * Add approvals to an allowScripts map.
98243
+ *
98244
+ * A package already denied is left alone: a denial is a decision someone made,
98245
+ * and an approve - `--all` included - must not quietly undo it. Both spellings
98246
+ * of a denial count, the `denyScripts` list and a `false` in the map.
98247
+ *
98248
+ * One package is one entry, keyed by its bare name, so approving a second
98249
+ * version widens the entry's `semver` instead of adding a near-duplicate key.
98250
+ *
98251
+ * @param {object} allowScripts the current map
98252
+ * @param {object[]} records packages to approve, as blocked-scripts records
98253
+ * @param {object} [options] options
98254
+ * @param {boolean} [options.pin] scope each approval to the reviewed version
98255
+ * @param {string[]} [options.denyScripts] the configured deny list
98256
+ * @returns {{allowScripts:object, approved:string[], skipped:string[]}} result
98257
+ */
98258
+ function approveEntries(allowScripts, records, { pin = true, denyScripts = {} } = {}) {
98259
+ const updated = { ...allowScripts };
98260
+ const approved = [];
98261
+ const skipped = [];
98262
+ const denyNames = new Set(Object.keys(denyScripts || {}).map((key) => parseAllowKey(key).name));
98263
+ for (const record of records) {
98264
+ const key = allowScriptsKey(record);
98265
+ if (denyNames.has(record.name) || Object.keys(updated).some((k) => updated[k] === false && parseAllowKey(k).name === record.name)) {
98266
+ skipped.push(key);
98267
+ continue;
98268
+ }
98269
+ updated[key] = mergeAllowEntry(updated[key], record, pin);
98270
+ approved.push(key);
98271
+ }
98272
+ return {
98273
+ allowScripts: updated,
98274
+ approved,
98275
+ skipped
98276
+ };
98277
+ }
98278
+ /**
98279
+ * Record explicit denials.
98280
+ *
98281
+ * A denial is written against the bare package name so it covers every version,
98282
+ * with an empty entry - `{}` - meaning "every version, every script", the same
98283
+ * defaults the allowlist uses for an absent `semver` / `scripts`. A hand-edited
98284
+ * entry can narrow either.
98285
+ *
98286
+ * @param {object} denyScripts the current deny map
98287
+ * @param {string[]} names package names to deny
98288
+ * @returns {{denyScripts:object, denied:string[], already:string[]}} result
98289
+ */
98290
+ function denyEntries(denyScripts, names) {
98291
+ const updated = { ...denyScripts || {} };
98292
+ const denied = [];
98293
+ const already = [];
98294
+ for (const name of names) {
98295
+ const { name: pkgName } = parseAllowKey(name);
98296
+ if (updated[pkgName] !== void 0) {
98297
+ already.push(pkgName);
98298
+ continue;
98299
+ }
98300
+ updated[pkgName] = {};
98301
+ denied.push(pkgName);
98302
+ }
98303
+ return {
98304
+ denyScripts: updated,
98305
+ denied,
98306
+ already
98307
+ };
98308
+ }
98309
+ /**
98310
+ * Drop entries for packages that are no longer installed.
98311
+ *
98312
+ * @param {object} allowScripts the current map
98313
+ * @param {string[]} installedNames names of the installed packages
98314
+ * @returns {{allowScripts:object, removed:string[]}} result
98315
+ */
98316
+ function pruneEntries(allowScripts, installedNames) {
98317
+ const installed = new Set(installedNames);
98318
+ const updated = {};
98319
+ const removed = [];
98320
+ for (const key of Object.keys(allowScripts)) if (installed.has(parseAllowKey(key).name)) updated[key] = allowScripts[key];
98321
+ else removed.push(key);
98322
+ return {
98323
+ allowScripts: updated,
98324
+ removed
98325
+ };
98326
+ }
98327
+ /**
98328
+ * Match the packages named on the command line against the records an install
98329
+ * recorded, so an approval can be pinned to the version that was reviewed.
98330
+ *
98331
+ * @param {string[]} names package names, optionally `name@version`
98332
+ * @param {object[]} records blocked / pending records from the last install
98333
+ * @returns {{matched:object[], unknown:string[]}} the records to act on
98334
+ */
98335
+ function selectRecords(names, records) {
98336
+ const byName = /* @__PURE__ */ new Map();
98337
+ for (const record of records) if (!byName.has(record.name)) byName.set(record.name, record);
98338
+ const matched = [];
98339
+ const unknown = [];
98340
+ for (const arg of names) {
98341
+ const { name, spec } = parseAllowKey(arg);
98342
+ const record = byName.get(name);
98343
+ if (spec) matched.push({
98344
+ name,
98345
+ version: spec,
98346
+ scripts: record ? record.scripts : [
98347
+ "preinstall",
98348
+ "install",
98349
+ "postinstall"
98350
+ ]
98351
+ });
98352
+ else if (record) matched.push(record);
98353
+ else unknown.push(arg);
98354
+ }
98355
+ return {
98356
+ matched,
98357
+ unknown
98358
+ };
98359
+ }
98360
+ /**
98361
+ * Where an allowlist change should be written.
98362
+ *
98363
+ * In a fynpo repo the monorepo root is the default: one allowlist, reviewed
98364
+ * once. `--local` writes to the package's own package.json instead.
98365
+ *
98366
+ * @param {object} fyn the Fyn instance
98367
+ * @param {boolean} [local] force the package's own package.json
98368
+ * @returns {{file:string, fynpo:boolean}} the target
98369
+ */
98370
+ function resolveTarget(fyn, local = false) {
98371
+ const fynpoDir = import_lodash_min.default.get(fyn, ["_fynpo", "dir"]);
98372
+ if (!local && fynpoDir) return {
98373
+ file: Path.join(fynpoDir, "fynpo.json"),
98374
+ fynpo: true
98375
+ };
98376
+ return {
98377
+ file: Path.join(fyn.cwd, "package.json"),
98378
+ fynpo: false
98379
+ };
98380
+ }
98381
+ /**
98382
+ * Where one fyn option lives in a target file. A fynpo.json keeps fyn's options
98383
+ * under `fyn.options`, a package.json directly under `fyn`.
98384
+ *
98385
+ * @param {object} target from {@link resolveTarget}
98386
+ * @param {string} key the option name
98387
+ * @returns {string[]} the path into the file's JSON
98388
+ */
98389
+ function targetOptionPath(target, key) {
98390
+ return target.fynpo ? [
98391
+ "fyn",
98392
+ "options",
98393
+ key
98394
+ ] : ["fyn", key];
98395
+ }
98396
+ /**
98397
+ * Whether this run can stop and ask a person.
98398
+ *
98399
+ * @returns {boolean} true when there is a terminal and no CI environment
98400
+ */
98401
+ function canPrompt() {
98402
+ return Boolean(!import_ci_info.isCI && process.stdin.isTTY && process.stdout.isTTY);
98403
+ }
98404
+ /**
98405
+ * Ask a question on the terminal.
98406
+ *
98407
+ * @param {string} question the prompt
98408
+ * @returns {Promise<string>} the trimmed, lowercased answer
98409
+ */
98410
+ function ask(question) {
98411
+ const rl = readline.createInterface({
98412
+ input: process.stdin,
98413
+ output: process.stdout
98414
+ });
98415
+ return new Promise((resolve) => {
98416
+ rl.question(question, (answer) => {
98417
+ rl.close();
98418
+ resolve(String(answer).trim().toLowerCase());
98419
+ });
98420
+ });
98421
+ }
98422
+ /**
98423
+ * `fyn install-scripts` - list, approve, deny and prune install-script
98424
+ * approvals.
98425
+ */
98426
+ var InstallScripts = class {
98427
+ /**
98428
+ * @param {object} options options
98429
+ * @param {object} options.fyn the Fyn instance
98430
+ */
98431
+ constructor({ fyn }) {
98432
+ this._fyn = fyn;
98433
+ }
98434
+ /**
98435
+ * The packages awaiting review: what the last install blocked, plus what it
98436
+ * recorded as pending when run with `--allow-scripts-pending`.
98437
+ *
98438
+ * @returns {object[]} deduplicated records
98439
+ */
98440
+ get records() {
98441
+ return dedupeBlockedRecords([...this._fyn.blockedScripts, ...this._fyn.pendingScripts]);
98442
+ }
98443
+ /**
98444
+ * Read the script-policy lists from the file a change would be written to.
98445
+ *
98446
+ * The deny list is normalized on the way in, so an entry written by hand in
98447
+ * any of the accepted forms is still recognized as already denied.
98448
+ *
98449
+ * @param {object} target from {@link resolveTarget}
98450
+ * @returns {Promise<{json:object, allowScripts:object, denyScripts:object}>} the file contents
98451
+ */
98452
+ async readTarget(target) {
98453
+ let json = {};
98454
+ try {
98455
+ json = JSON.parse(await file_ops_default.readFile(target.file));
98456
+ } catch (err) {
98457
+ if (target.fynpo) throw new Error(`cannot read ${target.file} - the monorepo allowlist needs a fynpo.json. Use --local to write to this package's package.json instead.`);
98458
+ throw err;
98459
+ }
98460
+ return {
98461
+ json,
98462
+ allowScripts: import_lodash_min.default.get(json, targetOptionPath(target, "allowScripts")) || {},
98463
+ denyScripts: import_lodash_min.default.get(json, targetOptionPath(target, "denyScripts")) || {}
98464
+ };
98465
+ }
98466
+ /**
98467
+ * Write script-policy lists back to their file. Only the keys named in
98468
+ * `values` are touched, so approving does not rewrite the deny list.
98469
+ *
98470
+ * @param {object} target from {@link resolveTarget}
98471
+ * @param {object} read result of {@link readTarget}
98472
+ * @param {object} values the option values to write, keyed by option name
98473
+ * @returns {Promise<void>} nothing
98474
+ */
98475
+ async writeTarget(target, read, values) {
98476
+ for (const key of Object.keys(values)) import_lodash_min.default.set(read.json, targetOptionPath(target, key), values[key]);
98477
+ await file_ops_default.writeFile(target.file, `${JSON.stringify(read.json, null, 2)}\n`);
98478
+ logger$1.info(`updated ${chalk.cyan(target.file)}`);
98479
+ }
98480
+ /**
98481
+ * List the packages awaiting review.
98482
+ *
98483
+ * @param {object} [options] options
98484
+ * @param {boolean} [options.json] emit JSON instead of a table
98485
+ * @returns {Promise<object[]>} the records listed
98486
+ */
98487
+ async ls({ json = false } = {}) {
98488
+ const records = this.records;
98489
+ if (json) {
98490
+ process.stdout.write(`${JSON.stringify(records, null, 2)}\n`);
98491
+ return records;
98492
+ }
98493
+ if (records.length === 0) {
98494
+ logger$1.info(chalk.green("No packages are awaiting install-script review."));
98495
+ if (this._fyn.scriptPolicy !== "review") logger$1.info(`Run ${chalk.cyan("fyn install --allow-scripts-pending")} to see what ${chalk.cyan("scriptPolicy: \"review\"")} would ask you to approve.`);
98496
+ return records;
98497
+ }
98498
+ logger$1.info(`${records.length} package${records.length > 1 ? "s" : ""} awaiting install-script review:`);
98499
+ const width = records.reduce((w, r) => Math.max(w, `${r.name}@${r.version}`.length), 0);
98500
+ for (const record of records) logger$1.info(` ${chalk.cyan(`${record.name}@${record.version}`.padEnd(width))} ${chalk.yellow(record.scripts.join(", "))} ${chalk.gray(blockedReasonText(record))}`);
98501
+ logger$1.info(`Approve with ${chalk.cyan("fyn install-scripts approve <package>")}`);
98502
+ return records;
98503
+ }
98504
+ /**
98505
+ * Approve packages to run their install scripts.
98506
+ *
98507
+ * @param {string[]} names packages to approve, optionally `name@version`
98508
+ * @param {object} [options] options
98509
+ * @param {boolean} [options.all] approve everything awaiting review
98510
+ * @param {boolean} [options.local] write to this package's package.json
98511
+ * @returns {Promise<string[]>} the keys written
98512
+ */
98513
+ async approve(names = [], { all = false, local = false } = {}) {
98514
+ const pin = this._fyn.allowScriptsPin;
98515
+ let toApprove;
98516
+ if (all) toApprove = this.records;
98517
+ else {
98518
+ const { matched, unknown } = selectRecords(names, this.records);
98519
+ if (unknown.length > 0) {
98520
+ logger$1.error(`not awaiting review: ${unknown.join(", ")} - run ${chalk.cyan("fyn install-scripts ls")} to see what is, or name an explicit version`);
98521
+ return [];
98522
+ }
98523
+ toApprove = matched;
98524
+ }
98525
+ if (toApprove.length === 0) {
98526
+ logger$1.info("nothing to approve");
98527
+ return [];
98528
+ }
98529
+ const target = resolveTarget(this._fyn, local);
98530
+ const read = await this.readTarget(target);
98531
+ const { allowScripts, approved, skipped } = approveEntries(read.allowScripts, toApprove, {
98532
+ pin,
98533
+ denyScripts: {
98534
+ ...read.denyScripts || {},
98535
+ ...this._fyn.denyScripts || {}
98536
+ }
98537
+ });
98538
+ if (skipped.length > 0) logger$1.warn(`${chalk.magenta("denied, not approved")}: ${skipped.join(", ")} - remove the ${chalk.cyan("denyScripts")} entry, or the ${chalk.cyan("allowScripts")} ${chalk.cyan("false")}, first if that was not intended`);
98539
+ if (approved.length === 0) return [];
98540
+ await this.writeTarget(target, read, { allowScripts });
98541
+ logger$1.info(`approved ${chalk.cyan(approved.join(", "))}`);
98542
+ logger$1.info(`Install scripts do not run retroactively - run ${chalk.cyan("fyn install")} to run them.`);
98543
+ return approved;
98544
+ }
98545
+ /**
98546
+ * Deny packages outright, on the `denyScripts` blacklist. The denial wins
98547
+ * over any approval, at any scope.
98548
+ *
98549
+ * @param {string[]} names packages to deny
98550
+ * @param {object} [options] options
98551
+ * @param {boolean} [options.local] write to this package's package.json
98552
+ * @returns {Promise<string[]>} the names newly denied
98553
+ */
98554
+ async deny(names = [], { local = false } = {}) {
98555
+ if (names.length === 0) {
98556
+ logger$1.error("no packages named to deny");
98557
+ return [];
98558
+ }
98559
+ const target = resolveTarget(this._fyn, local);
98560
+ const read = await this.readTarget(target);
98561
+ const { denyScripts, denied, already } = denyEntries(read.denyScripts, names);
98562
+ if (already.length > 0) logger$1.info(`already denied ${chalk.cyan(already.join(", "))}`);
98563
+ if (denied.length === 0) return [];
98564
+ await this.writeTarget(target, read, { denyScripts });
98565
+ logger$1.info(`denied ${chalk.cyan(denied.join(", "))}`);
98566
+ return denied;
98567
+ }
98568
+ /**
98569
+ * Drop approvals for packages that are no longer installed.
98570
+ *
98571
+ * @param {object} [options] options
98572
+ * @param {boolean} [options.local] write to this package's package.json
98573
+ * @returns {Promise<string[]>} the keys removed
98574
+ */
98575
+ /**
98576
+ * Every package this project actually has installed.
98577
+ *
98578
+ * Not `loadFvVersions()` alone, which reads `<node_modules>/.f/_` - the store
98579
+ * for versions that had to be isolated. Under the default hoisted layout that
98580
+ * is a small slice of the tree, and pruning against it deleted the approvals
98581
+ * of every package sitting in `node_modules` proper.
98582
+ *
98583
+ * @returns {Promise<Set<string>>} installed package names, scoped names included
98584
+ */
98585
+ async installedPackageNames() {
98586
+ const names = new Set(Object.keys(await this._fyn.loadFvVersions()));
98587
+ const outputDir = this._fyn.getOutputDir();
98588
+ const readDir = async (dir) => {
98589
+ try {
98590
+ return await file_ops_default.readdir(dir);
98591
+ } catch {
98592
+ return [];
98593
+ }
98594
+ };
98595
+ for (const entry of await readDir(outputDir)) {
98596
+ if (entry.startsWith(".")) continue;
98597
+ if (entry.startsWith("@")) {
98598
+ for (const scoped of await readDir(Path.join(outputDir, entry))) if (!scoped.startsWith(".")) names.add(`${entry}/${scoped}`);
98599
+ } else names.add(entry);
98600
+ }
98601
+ return names;
98602
+ }
98603
+ async prune({ local = false } = {}) {
98604
+ const installed = [...await this.installedPackageNames()];
98605
+ if (installed.length === 0) {
98606
+ logger$1.error(`no installed packages found - run ${chalk.cyan("fyn install")} before pruning, otherwise every approval would look unused`);
98607
+ return [];
98608
+ }
98609
+ const target = resolveTarget(this._fyn, local);
98610
+ const read = await this.readTarget(target);
98611
+ const { allowScripts, removed } = pruneEntries(read.allowScripts, installed);
98612
+ if (removed.length === 0) {
98613
+ logger$1.info(chalk.green("nothing to prune"));
98614
+ return [];
98615
+ }
98616
+ await this.writeTarget(target, read, { allowScripts });
98617
+ logger$1.info(`pruned ${chalk.cyan(removed.join(", "))}`);
98618
+ return removed;
98619
+ }
98620
+ /**
98621
+ * Stop an install that wants to run install scripts nobody has approved.
98622
+ *
98623
+ * On a terminal this asks; anywhere else - CI, a pipe, a hook - there is
98624
+ * nobody to ask, so the install fails rather than quietly producing a tree
98625
+ * whose native packages were never built. `--script-policy=source` is the
98626
+ * documented way out.
98627
+ *
98628
+ * @param {object[]} records blocked-scripts records from this install
98629
+ * @returns {Promise<object[]>} the records approved, empty when none were
98630
+ * @throws {Error} when there is no terminal to ask on
98631
+ */
98632
+ async review(records) {
98633
+ const pending = dedupeBlockedRecords(records);
98634
+ if (pending.length === 0) return [];
98635
+ if (!canPrompt()) {
98636
+ const err = /* @__PURE__ */ new Error(`${pending.length} package(s) need approval to run their install scripts, and there is no terminal to ask on: ${pending.map((r) => `${r.name}@${r.version}`).join(", ")}.\n Approve them where you can review the code:\n ${chalk.cyan("fyn install-scripts approve <package>")}\n Or record the approvals in your package.json / fynpo.json before installing.\n To go back to trusting packages by where they came from, install with ${chalk.cyan("--script-policy=source")}.`);
98637
+ err._fynAlreadyLogged = true;
98638
+ logger$1.error(err.message);
98639
+ throw err;
98640
+ }
98641
+ logger$1.info(`${pending.length} package${pending.length > 1 ? "s" : ""} want to run install scripts that have not been approved:`);
98642
+ const width = pending.reduce((w, r) => Math.max(w, `${r.name}@${r.version}`.length), 0);
98643
+ for (const record of pending) logger$1.info(` ${chalk.cyan(`${record.name}@${record.version}`.padEnd(width))} ` + chalk.yellow(record.scripts.join(", ")));
98644
+ const answer = await ask(`Approve? ${chalk.cyan("[a]")}ll / ${chalk.cyan("[s]")}elect / ${chalk.cyan("[n]")}one (default) `);
98645
+ let approve = [];
98646
+ if (answer === "a" || answer === "all" || answer === "y" || answer === "yes") approve = pending;
98647
+ else if (answer === "s" || answer === "select") for (const record of pending) {
98648
+ const yn = await ask(` ${record.name}@${record.version} (${record.scripts.join(", ")})? [y/N] `);
98649
+ if (yn === "y" || yn === "yes") approve.push(record);
98650
+ }
98651
+ if (approve.length === 0) {
98652
+ logger$1.info("no approvals recorded - install scripts will not run");
98653
+ return [];
98654
+ }
98655
+ const target = resolveTarget(this._fyn, false);
98656
+ const read = await this.readTarget(target);
98657
+ const { allowScripts, approved, skipped } = approveEntries(read.allowScripts, approve, {
98658
+ pin: this._fyn.allowScriptsPin,
98659
+ denyScripts: {
98660
+ ...read.denyScripts || {},
98661
+ ...this._fyn.denyScripts || {}
98662
+ }
98663
+ });
98664
+ if (skipped.length > 0) logger$1.warn(`${chalk.magenta("denied, not approved")}: ${skipped.join(", ")} - remove the ${chalk.cyan("denyScripts")} entry, or the ${chalk.cyan("allowScripts")} ${chalk.cyan("false")}, first if that was not intended`);
98665
+ if (approved.length === 0) return [];
98666
+ await this.writeTarget(target, read, { allowScripts });
98667
+ logger$1.info(`approved ${chalk.cyan(approved.join(", "))}`);
98668
+ return approve.filter((record) => approved.includes(record.name));
98669
+ }
98670
+ };
98671
+
97520
98672
  //#endregion
97521
98673
  //#region lib/pkg-installer.ts
97522
98674
  var PkgInstaller = class {
@@ -97531,6 +98683,12 @@ var PkgInstaller = class {
97531
98683
  preInstall;
97532
98684
  postInstall;
97533
98685
  toLink;
98686
+ /** packages whose install scripts the policy blocked, reported once at the end */
98687
+ blockedScripts = [];
98688
+ /** packages that would need approval under "review" - only with --allow-scripts-pending */
98689
+ pendingScripts = [];
98690
+ /** the blocked packages themselves, so an approval at the prompt can queue their scripts */
98691
+ _blockedDeps = [];
97534
98692
  constructor(options) {
97535
98693
  this._fyn = options.fyn;
97536
98694
  this._data = this._fyn._data;
@@ -97548,6 +98706,9 @@ var PkgInstaller = class {
97548
98706
  this.preInstall = [];
97549
98707
  this.postInstall = [];
97550
98708
  this.toLink = [];
98709
+ this.blockedScripts = [];
98710
+ this.pendingScripts = [];
98711
+ this._blockedDeps = [];
97551
98712
  this._data.cleanLinked();
97552
98713
  this._fyn._depResolver.resolvePkgPeerDep(this._fyn._pkg, "your app", this._data);
97553
98714
  const pkgsData = this._data.getPkgsData();
@@ -97557,6 +98718,7 @@ var PkgInstaller = class {
97557
98718
  logger$1.debug("queuing", depInfo.name, depInfo.version, "for install");
97558
98719
  await this._gatherPkg(depInfo);
97559
98720
  }
98721
+ await this._reviewBlockedScripts();
97560
98722
  return this._doInstall().finally(() => {
97561
98723
  this.preInstall = void 0;
97562
98724
  this.postInstall = void 0;
@@ -97587,11 +98749,14 @@ var PkgInstaller = class {
97587
98749
  const pkgJson = depInfo.json;
97588
98750
  logger$1.debug("linked dependencies for", pkgJson.name, pkgJson.version);
97589
98751
  }
97590
- if (depInfo.str.trim() === outputStr.trim()) continue;
98752
+ const hardLocalPkgJson = depInfo.local === "hard" ? Path.join(this._fyn.getInstalledPkgDir(depInfo.name, depInfo.version, depInfo), "package.json") : void 0;
98753
+ if (depInfo.str.trim() === outputStr.trim()) {
98754
+ if (hardLocalPkgJson) await this._detachPkgJsonLink(hardLocalPkgJson);
98755
+ continue;
98756
+ }
97591
98757
  let pkgJsonFp;
97592
- if (depInfo.local === "hard") {
97593
- const vdir = this._fyn.getInstalledPkgDir(depInfo.name, depInfo.version, depInfo);
97594
- pkgJsonFp = Path.join(vdir, "package.json");
98758
+ if (hardLocalPkgJson) {
98759
+ pkgJsonFp = hardLocalPkgJson;
97595
98760
  await xaa_default.try(() => file_ops_default.unlink(pkgJsonFp));
97596
98761
  } else pkgJsonFp = Path.join(depInfo.dir, "package.json");
97597
98762
  depInfo.str = outputStr;
@@ -97606,6 +98771,24 @@ var PkgInstaller = class {
97606
98771
  }
97607
98772
  }
97608
98773
  }
98774
+ /**
98775
+ * Give an installed package.json its own inode if it is still hardlinked to the local
98776
+ * package it was replicated from.
98777
+ *
98778
+ * Same content, new physical file: unlink the directory entry and write the bytes back.
98779
+ * Cheap - it only touches manifests that actually still share an inode.
98780
+ *
98781
+ * @param pkgJsonFp - installed package.json to detach
98782
+ */
98783
+ async _detachPkgJsonLink(pkgJsonFp) {
98784
+ const stat = await xaa_default.try(() => file_ops_default.stat(pkgJsonFp));
98785
+ if (!stat || stat.nlink < 2) return;
98786
+ const data = await xaa_default.try(() => file_ops_default.readFile(pkgJsonFp));
98787
+ if (data === void 0) return;
98788
+ logger$1.debug("detaching hardlinked package.json from its local source", pkgJsonFp);
98789
+ await xaa_default.try(() => file_ops_default.unlink(pkgJsonFp));
98790
+ await file_ops_default.writeFile(pkgJsonFp, data);
98791
+ }
97609
98792
  _isDepSrcOptionalOnly(depInfo) {
97610
98793
  if ((!depInfo.src || depInfo.src === "opt") && (!depInfo.dsrc || depInfo.dsrc === "opt")) return true;
97611
98794
  return depInfo.requests.filter((req) => req.find((r) => r.startsWith("opt"))).length === depInfo.requests.length;
@@ -97748,6 +98931,7 @@ var PkgInstaller = class {
97748
98931
  if (this._fyn.showDeprecated && import_lodash_min.default.isEmpty(warned)) logger$1.info(chalk.green("HOORAY!!! None of your dependencies are marked deprecated."));
97749
98932
  }).then(() => this._installLocalExports()).then(() => this._saveLockData()).then(() => {
97750
98933
  this._fyn._depResolver._logConsolidatedPeerDepWarnings();
98934
+ this._reportBlockedScripts();
97751
98935
  logger$1.info(`${chalk.green("done install")} ${log_format_default.time(Date.now() - start)}`);
97752
98936
  }).finally(() => {
97753
98937
  logger$1.removeItem(INSTALL_PACKAGE);
@@ -97805,37 +98989,157 @@ var PkgInstaller = class {
97805
98989
  }
97806
98990
  json._fyn = {};
97807
98991
  const scripts = json.scripts || {};
97808
- const scriptPolicy = evaluateScriptPolicy(depInfo, this._fyn.allowScripts, { allowTopLevel: this._fyn.allowTopLevelScripts });
97809
- const blockedScripts = [];
97810
- const isAllowed = (scriptName) => {
97811
- if (isScriptAllowed(scriptPolicy, scriptName)) return true;
97812
- blockedScripts.push(scriptName);
97813
- return false;
97814
- };
98992
+ const scriptPolicy = evaluateScriptPolicy(depInfo, this._fyn.allowScripts, this._fyn.scriptPolicyOptions);
97815
98993
  const hasPI = json.hasPI || Boolean(scripts.preinstall);
97816
- if (!Boolean(depInfo.preinstall) && hasPI) {
98994
+ const piExed = Boolean(depInfo.preinstall);
98995
+ const candidates = [];
98996
+ if (!piExed && hasPI) {
97817
98997
  if (depInfo.preInstalled) json._fyn.preinstall = true;
97818
- else if (isAllowed("preinstall")) {
97819
- logger$1.debug("adding preinstall step for", depInfo.dir);
97820
- this.preInstall.push(depInfo);
97821
- }
98998
+ else candidates.push("preinstall");
97822
98999
  }
99000
+ for (const name of ["install", "postinstall"]) if (Boolean(scripts[name]) && !json._fyn[name]) candidates.push(name);
97823
99001
  this.toLink.push(depInfo);
97824
- const install = ["install", "postinstall"].filter((x) => {
97825
- return Boolean(scripts[x]) && !json._fyn[x] && isAllowed(x);
99002
+ const blockedScripts = candidates.filter((name) => !isScriptAllowed(scriptPolicy, name));
99003
+ this._queueScripts(depInfo, candidates.filter((name) => isScriptAllowed(scriptPolicy, name)));
99004
+ if (blockedScripts.length > 0) {
99005
+ const byName = scriptPolicy.deniedScripts || /* @__PURE__ */ new Set();
99006
+ const denied = blockedScripts.filter((name) => byName.has(name));
99007
+ const pending = blockedScripts.filter((name) => !byName.has(name));
99008
+ if (denied.length > 0) this._recordBlockedScripts(depInfo, {
99009
+ ...scriptPolicy,
99010
+ reason: "denied"
99011
+ }, denied);
99012
+ if (pending.length > 0) {
99013
+ this._recordBlockedScripts(depInfo, scriptPolicy, pending);
99014
+ this._blockedDeps.push({
99015
+ depInfo,
99016
+ candidates: pending
99017
+ });
99018
+ }
99019
+ }
99020
+ this._recordPendingReview(depInfo, {
99021
+ hasPI,
99022
+ ...scripts
97826
99023
  });
99024
+ }
99025
+ /**
99026
+ * Queue a package's allowed install scripts.
99027
+ *
99028
+ * Idempotent per package, because the review prompt re-queues after an
99029
+ * approval and a package may already have had some of its scripts allowed.
99030
+ *
99031
+ * @param {object} depInfo resolved package data
99032
+ * @param {string[]} names the lifecycle scripts to run
99033
+ * @returns {void}
99034
+ */
99035
+ _queueScripts(depInfo, names) {
99036
+ if (names.includes("preinstall") && !this.preInstall.includes(depInfo)) {
99037
+ logger$1.debug("adding preinstall step for", depInfo.dir);
99038
+ this.preInstall.push(depInfo);
99039
+ }
99040
+ const install = ["install", "postinstall"].filter((name) => names.includes(name));
97827
99041
  if (install.length > 0) {
97828
99042
  logger$1.debug("adding install step for", depInfo.dir, install);
97829
- depInfo.install = install;
97830
- this.postInstall.push(depInfo);
99043
+ depInfo.install = import_lodash_min.default.union(depInfo.install || [], install);
99044
+ if (!this.postInstall.includes(depInfo)) this.postInstall.push(depInfo);
97831
99045
  }
97832
- if (blockedScripts.length > 0) this._warnBlockedScripts(depInfo, scriptPolicy, blockedScripts);
97833
99046
  }
97834
- _warnBlockedScripts(depInfo, policy, blocked) {
97835
- const id = log_format_default.pkgId(depInfo);
97836
- logger$1.warn(`${chalk.black.bgYellow("WARN")} ${chalk.magenta("scripts blocked")} ${id}`, chalk.yellow(`skipped lifecycle [${blocked.join(", ")}] for non-registry (${policy.urlType}) package - not in fyn.allowScripts`));
97837
- logger$1.verbose(chalk.blue(" To allow, add to package.json:"), chalk.cyan(`"fyn": { "allowScripts": { "${policy.key}": [${blocked.map((s) => `"${s}"`).join(", ")}] } }`));
97838
- if (policy.topLevel) logger$1.verbose(chalk.blue(" Or trust all direct deps' scripts with:"), chalk.cyan(`"fyn": { "allowTopLevelScripts": true }`));
99047
+ /**
99048
+ * Stop before running anything when packages want to run install scripts
99049
+ * nobody has approved. On a terminal this asks and re-queues whatever the
99050
+ * person approves; anywhere else it throws.
99051
+ *
99052
+ * Only under `"review"`: `"source"` is the documented opt-out and keeps
99053
+ * warning-and-continuing, and under `"off"` nothing running is the point.
99054
+ *
99055
+ * @returns {Promise<void>} nothing
99056
+ */
99057
+ async _reviewBlockedScripts() {
99058
+ if (this._fyn.scriptPolicy !== "review" || this._blockedDeps.length === 0) return;
99059
+ const pending = this.blockedScripts.filter((record) => record.reason !== "denied");
99060
+ if (pending.length === 0) return;
99061
+ if ((await new InstallScripts({ fyn: this._fyn }).review(pending)).length === 0) return;
99062
+ this._fyn.resetAllowScripts();
99063
+ this._requeueApproved();
99064
+ }
99065
+ /**
99066
+ * Re-evaluate the packages that were blocked, after the allowlist on disk
99067
+ * changed, and queue whatever is now allowed.
99068
+ *
99069
+ * @returns {void}
99070
+ */
99071
+ _requeueApproved() {
99072
+ const blockedDeps = this._blockedDeps;
99073
+ const stillBlocked = [];
99074
+ this._blockedDeps = [];
99075
+ this.blockedScripts = [];
99076
+ for (const { depInfo, candidates } of blockedDeps) {
99077
+ const policy = evaluateScriptPolicy(depInfo, this._fyn.allowScripts, this._fyn.scriptPolicyOptions);
99078
+ const blocked = candidates.filter((name) => !isScriptAllowed(policy, name));
99079
+ this._queueScripts(depInfo, candidates.filter((name) => isScriptAllowed(policy, name)));
99080
+ if (blocked.length > 0) {
99081
+ this._recordBlockedScripts(depInfo, policy, blocked);
99082
+ stillBlocked.push({
99083
+ depInfo,
99084
+ candidates
99085
+ });
99086
+ }
99087
+ }
99088
+ this._blockedDeps = stillBlocked;
99089
+ }
99090
+ /**
99091
+ * With `--allow-scripts-pending`, also answer the question `"review"` would
99092
+ * ask - which packages have install scripts that nobody has approved - while
99093
+ * still running the install under the mode in effect. That is how a project
99094
+ * sees what switching to `"review"` would cost before switching.
99095
+ *
99096
+ * @param {object} depInfo resolved package data
99097
+ * @param {object} scripts the package's scripts, plus `hasPI`
99098
+ * @returns {void}
99099
+ */
99100
+ _recordPendingReview(depInfo, scripts) {
99101
+ if (!this._fyn.allowScriptsPending) return;
99102
+ const has = LIFECYCLE_SCRIPTS.filter((name) => Boolean(scripts[name]) || name === "preinstall" && scripts.hasPI);
99103
+ if (has.length === 0) return;
99104
+ const policy = evaluateScriptPolicy(depInfo, this._fyn.allowScripts, {
99105
+ ...this._fyn.scriptPolicyOptions,
99106
+ mode: strictestScriptPolicy(this._fyn.scriptPolicy, "review")
99107
+ });
99108
+ const pending = has.filter((name) => !isScriptAllowed(policy, name));
99109
+ if (pending.length > 0) this.pendingScripts.push(makeBlockedRecord(depInfo, policy, pending));
99110
+ }
99111
+ /**
99112
+ * Record a package whose install scripts the policy blocked. Reported as one
99113
+ * summary at the end of the install rather than a warning per package - under
99114
+ * "review" mode every native dependency blocks on a first install.
99115
+ *
99116
+ * @param {object} depInfo resolved package data
99117
+ * @param {object} policy result of `evaluateScriptPolicy`
99118
+ * @param {string[]} blocked the lifecycle scripts that were skipped
99119
+ * @returns {void}
99120
+ */
99121
+ _recordBlockedScripts(depInfo, policy, blocked) {
99122
+ logger$1.debug("scripts blocked", log_format_default.pkgId(depInfo), `[${blocked.join(", ")}]`, policy.reason);
99123
+ this.blockedScripts.push(makeBlockedRecord(depInfo, policy, blocked));
99124
+ }
99125
+ /**
99126
+ * Report every package whose install scripts were blocked, with the exact
99127
+ * config to paste to allow them, and hand the same records to fyn so they are
99128
+ * saved for `fyn install-scripts ls`.
99129
+ *
99130
+ * @returns {void}
99131
+ */
99132
+ _reportBlockedScripts() {
99133
+ const records = this.blockedScripts;
99134
+ this._fyn.setBlockedScripts(records, this.pendingScripts);
99135
+ for (const line of formatBlockedScriptsSummary(records, {
99136
+ mode: this._fyn.scriptPolicy,
99137
+ pin: this._fyn.allowScriptsPin
99138
+ })) logger$1.warn(line);
99139
+ for (const line of formatPendingScriptsSummary(this.pendingScripts, {
99140
+ mode: this._fyn.scriptPolicy,
99141
+ pin: this._fyn.allowScriptsPin
99142
+ })) logger$1.info(line);
97839
99143
  }
97840
99144
  _cleanBin() {
97841
99145
  logger$1.updateItem(INSTALL_PACKAGE, "cleaning node_modules/.bin");
@@ -98475,6 +99779,18 @@ const OMIT_SRC = {
98475
99779
  optional: "opt",
98476
99780
  peer: "per"
98477
99781
  };
99782
+ /**
99783
+ * How long to wait on npm's bulk advisories endpoint before giving up, in ms.
99784
+ *
99785
+ * That endpoint intermittently accepts the connection and never answers. npm-registry-fetch
99786
+ * would apply its own defaults here - a 5 minute timeout over 3 attempts - which turns a
99787
+ * stalled endpoint into a ~15 minute silent install hang. Audit is advisory and its caller
99788
+ * already degrades to cached data or a warning, so bound it tightly instead: one attempt,
99789
+ * ten seconds, then fall through.
99790
+ */
99791
+ const AUDIT_FETCH_TIMEOUT = 1e4;
99792
+ /** No retries on the audit fetch - an endpoint that just stalled is unlikely to answer the retry. */
99793
+ const AUDIT_FETCH_RETRY = { retries: 0 };
98478
99794
  var AuditReport = class {
98479
99795
  _fyn;
98480
99796
  _depData;
@@ -98574,7 +99890,9 @@ var AuditReport = class {
98574
99890
  ...fetchOpts,
98575
99891
  method: "POST",
98576
99892
  body: payload,
98577
- gzip: true
99893
+ gzip: true,
99894
+ timeout: AUDIT_FETCH_TIMEOUT,
99895
+ retry: AUDIT_FETCH_RETRY
98578
99896
  })).json();
98579
99897
  const result = {
98580
99898
  advisories,
@@ -99740,6 +101058,38 @@ var FynCli = class {
99740
101058
  return this._opts.saveLogs && this.saveLogs(this._opts.saveLogs);
99741
101059
  });
99742
101060
  }
101061
+ /**
101062
+ * `fyn install-scripts <action>` - review and record which packages may run
101063
+ * their install scripts.
101064
+ *
101065
+ * @param {string} action one of ls / approve / deny / prune
101066
+ * @param {object} argv parsed command args and options
101067
+ * @returns {Promise<unknown>} the action's result
101068
+ */
101069
+ async installScripts(action, argv = {}) {
101070
+ const opts = argv.opts || {};
101071
+ const packages = (argv.args || {}).packages || [];
101072
+ await this.fyn._initializePkg();
101073
+ const cmd = new InstallScripts({ fyn: this.fyn });
101074
+ try {
101075
+ switch (action) {
101076
+ case "ls": return await cmd.ls({ json: Boolean(opts.json) });
101077
+ case "approve": return await cmd.approve(packages, {
101078
+ all: Boolean(opts.all),
101079
+ local: Boolean(opts.local)
101080
+ });
101081
+ case "deny": return await cmd.deny(packages, { local: Boolean(opts.local) });
101082
+ case "prune": return await cmd.prune({ local: Boolean(opts.local) });
101083
+ default: throw new Error(`unknown install-scripts action "${action}"`);
101084
+ }
101085
+ } catch (err) {
101086
+ logger$1.error(err.message);
101087
+ fyntil.exit(1);
101088
+ return;
101089
+ } finally {
101090
+ if (this._opts.saveLogs) await this.saveLogs(this._opts.saveLogs);
101091
+ }
101092
+ }
99743
101093
  audit(argv) {
99744
101094
  const opts = {
99745
101095
  json: argv.opts?.json || false,
@@ -99784,7 +101134,7 @@ var FynCli = class {
99784
101134
  if (this._config._fynpo.config) xsh.envPath.addToFront(Path.join(this._config._fynpo.dir, "node_modules/.bin"), env);
99785
101135
  setupNodeGypEnv(env);
99786
101136
  env.npm_node_execpath = env.NODE = env.NODE || process.execPath;
99787
- env.npm_execpath = fileURLToPath$1(new URL("../bin/fyn.js", import.meta.url));
101137
+ env.npm_execpath = findFynCli();
99788
101138
  env.INIT_CWD = this.fyn.cwd;
99789
101139
  const mainScript = scripts[scripts.length - 1];
99790
101140
  return dist_default.each(_scripts, (s) => {
@@ -100053,6 +101403,85 @@ function getTerminalWidth() {
100053
101403
  return process.stdout.columns || 80;
100054
101404
  }
100055
101405
 
101406
+ //#endregion
101407
+ //#region node_modules/@fynjs/cli-args/dist/symbols.js
101408
+ const _PARENT = Symbol("parent");
101409
+ const _PREV = Symbol("prev");
101410
+ const _NEXT = Symbol("next");
101411
+ /** commands whose sync `exec` returned a promise - see NixClap.runExec */
101412
+ const _ASYNC_EXEC = Symbol("asyncExec");
101413
+
101414
+ //#endregion
101415
+ //#region node_modules/@fynjs/cli-args/dist/clap-node.js
101416
+ /**
101417
+ * Represents a node in a command-line application parser.
101418
+ *
101419
+ * @remarks
101420
+ * This class is used to represent commands or options in a CLI application. Each node can have a parent node,
101421
+ * which represents a hierarchical relationship (e.g., command -> sub-command, option).
101422
+ *
101423
+ * @example
101424
+ * ```typescript
101425
+ * const rootNode = new ClapNode('root', 'rootAlias');
101426
+ * const childNode = new ClapNode('child', 'childAlias', rootNode);
101427
+ * rootNode.addArg('arg1');
101428
+ * childNode.addError(new Error('An error occurred'));
101429
+ * ```
101430
+ *
101431
+ * @public
101432
+ */
101433
+ var ClapNode = class {
101434
+ /**
101435
+ *
101436
+ * @param name - idiomatic name of the command
101437
+ * @param inputName - name that was specified by user to trigger this command
101438
+ * @param parent
101439
+ */
101440
+ constructor(name, alias, parent) {
101441
+ this.name = name;
101442
+ this.alias = alias;
101443
+ this.argsList = [];
101444
+ this.argv = [];
101445
+ this.argsMap = {};
101446
+ if (parent) Object.defineProperty(this, _PARENT, {
101447
+ value: parent,
101448
+ configurable: true,
101449
+ enumerable: false,
101450
+ writable: true
101451
+ });
101452
+ this._errors = [];
101453
+ }
101454
+ /**
101455
+ * Add an arg to this node
101456
+ * @param arg
101457
+ * @returns
101458
+ */
101459
+ addArg(arg) {
101460
+ this.argsList.push(arg);
101461
+ }
101462
+ addVerbatimArg(arg) {
101463
+ this.argv.push(arg);
101464
+ }
101465
+ addError(e) {
101466
+ this._errors.push(e);
101467
+ }
101468
+ get hasErrors() {
101469
+ return this._errors.length > 0;
101470
+ }
101471
+ get error() {
101472
+ return this._errors[0];
101473
+ }
101474
+ get errors() {
101475
+ return this._errors;
101476
+ }
101477
+ get parent() {
101478
+ return this[_PARENT];
101479
+ }
101480
+ getParent() {
101481
+ return this.parent;
101482
+ }
101483
+ };
101484
+
100056
101485
  //#endregion
100057
101486
  //#region node_modules/@fynjs/cli-args/dist/base.js
100058
101487
  const SUPPORT_TYPES = [
@@ -100183,336 +101612,6 @@ var CliBase = class {
100183
101612
  }
100184
101613
  };
100185
101614
 
100186
- //#endregion
100187
- //#region node_modules/@fynjs/cli-args/dist/symbols.js
100188
- const _PARENT = Symbol("parent");
100189
- const _PREV = Symbol("prev");
100190
- const _NEXT = Symbol("next");
100191
- /** commands whose sync `exec` returned a promise - see NixClap.runExec */
100192
- const _ASYNC_EXEC = Symbol("asyncExec");
100193
-
100194
- //#endregion
100195
- //#region node_modules/@fynjs/cli-args/dist/clap-node.js
100196
- /**
100197
- * Represents a node in a command-line application parser.
100198
- *
100199
- * @remarks
100200
- * This class is used to represent commands or options in a CLI application. Each node can have a parent node,
100201
- * which represents a hierarchical relationship (e.g., command -> sub-command, option).
100202
- *
100203
- * @example
100204
- * ```typescript
100205
- * const rootNode = new ClapNode('root', 'rootAlias');
100206
- * const childNode = new ClapNode('child', 'childAlias', rootNode);
100207
- * rootNode.addArg('arg1');
100208
- * childNode.addError(new Error('An error occurred'));
100209
- * ```
100210
- *
100211
- * @public
100212
- */
100213
- var ClapNode = class {
100214
- /**
100215
- *
100216
- * @param name - idiomatic name of the command
100217
- * @param inputName - name that was specified by user to trigger this command
100218
- * @param parent
100219
- */
100220
- constructor(name, alias, parent) {
100221
- this.name = name;
100222
- this.alias = alias;
100223
- this.argsList = [];
100224
- this.argv = [];
100225
- this.argsMap = {};
100226
- if (parent) Object.defineProperty(this, _PARENT, {
100227
- value: parent,
100228
- configurable: true,
100229
- enumerable: false,
100230
- writable: true
100231
- });
100232
- this._errors = [];
100233
- }
100234
- /**
100235
- * Add an arg to this node
100236
- * @param arg
100237
- * @returns
100238
- */
100239
- addArg(arg) {
100240
- this.argsList.push(arg);
100241
- }
100242
- addVerbatimArg(arg) {
100243
- this.argv.push(arg);
100244
- }
100245
- addError(e) {
100246
- this._errors.push(e);
100247
- }
100248
- get hasErrors() {
100249
- return this._errors.length > 0;
100250
- }
100251
- get error() {
100252
- return this._errors[0];
100253
- }
100254
- get errors() {
100255
- return this._errors;
100256
- }
100257
- get parent() {
100258
- return this[_PARENT];
100259
- }
100260
- getParent() {
100261
- return this.parent;
100262
- }
100263
- };
100264
-
100265
- //#endregion
100266
- //#region node_modules/@fynjs/cli-args/dist/command-node.js
100267
- /**
100268
- * Object representation for an instance of a command on the CLI
100269
- */
100270
- var CommandNode = class extends ClapNode {
100271
- constructor(name, alias, cmdBase) {
100272
- super(name, alias);
100273
- this.argv.push(name);
100274
- this.cmdBase = cmdBase;
100275
- this.subCmdNodes = {};
100276
- this.optNodes = {};
100277
- this.optCount = {};
100278
- this.isGreedy = false;
100279
- }
100280
- /** get the options with full values for this command */
100281
- get options() {
100282
- return this.jsonMeta.optsFull;
100283
- }
100284
- /** get the options for this command */
100285
- get opts() {
100286
- return this.jsonMeta.opts;
100287
- }
100288
- /** get the arguments for this command */
100289
- get args() {
100290
- return this.jsonMeta.args;
100291
- }
100292
- get source() {
100293
- return this.jsonMeta.source;
100294
- }
100295
- get optsFull() {
100296
- return this.jsonMeta.optsFull;
100297
- }
100298
- get subCommands() {
100299
- return this.jsonMeta.subCommands;
100300
- }
100301
- getParent() {
100302
- return super.getParent();
100303
- }
100304
- /**
100305
- * Get the root command by traversing up the parent chain until no more parent exists.
100306
- * @returns The root CommandNode
100307
- */
100308
- get rootCmd() {
100309
- let current = this;
100310
- while (current.getParent()) current = current.getParent();
100311
- return current;
100312
- }
100313
- /**
100314
- * Get array of all commands that have exec callback
100315
- *
100316
- * Root command execution is handled specially:
100317
- * - This method filters out the root command (returns false for isRootCommand)
100318
- * - Root command execution is determined by _shouldExecuteRootCommand() in nix-clap.ts
100319
- * - Root command is executed in runExec/runExecAsync methods when conditions are met
100320
- *
100321
- * @param cmds - Array to accumulate command nodes with exec handlers
100322
- * @param includeSubCommands - Whether to recursively include sub-commands
100323
- * @returns Array of command nodes that have exec handlers (excluding root command)
100324
- */
100325
- getExecCommands(cmds, includeSubCommands) {
100326
- if (this.cmdBase.exec && !isRootCommand(this.alias)) cmds.push(this);
100327
- if (includeSubCommands) for (const _key in this.subCmdNodes) this.subCmdNodes[_key].getExecCommands(cmds, includeSubCommands);
100328
- return cmds;
100329
- }
100330
- /**
100331
- * Get the command chain (sequence from root to this command, including itself)
100332
- * @returns Array of CommandNodes from root to this command
100333
- */
100334
- get cmdChain() {
100335
- let parent = this;
100336
- const nodes = [];
100337
- while (parent && (parent = parent.getParent())) nodes.push(parent);
100338
- return nodes.reverse().concat(this);
100339
- }
100340
- /**
100341
- * Invoke the command's exec handler
100342
- *
100343
- * @param includeSubCommands - if true, then also do sub commands
100344
- * @param parsed - The parsed result containing remaining args after --
100345
- */
100346
- invokeExec(includeSubCommands, parsed) {
100347
- let count = 0;
100348
- const cmds = this.getExecCommands([], includeSubCommands);
100349
- for (const cmd of cmds) {
100350
- const result = cmd.cmdBase.exec(cmd, parsed);
100351
- if (parsed) {
100352
- if (isThenable(result)) (parsed[_ASYNC_EXEC] || (parsed[_ASYNC_EXEC] = [])).push(cmd.name);
100353
- if (!parsed.execCmd) parsed.execCmd = cmd;
100354
- }
100355
- count++;
100356
- }
100357
- return count;
100358
- }
100359
- /**
100360
- *
100361
- * @param includeSubCommands
100362
- * @param parsed - The parsed result containing remaining args after --
100363
- * @returns
100364
- */
100365
- async invokeExecAsync(includeSubCommands, parsed) {
100366
- let count = 0;
100367
- const cmds = this.getExecCommands([], includeSubCommands);
100368
- for (const cmd of cmds) {
100369
- await cmd.cmdBase.exec(cmd, parsed);
100370
- if (parsed && !parsed.execCmd) parsed.execCmd = cmd;
100371
- count++;
100372
- }
100373
- return count;
100374
- }
100375
- /**
100376
- * Add a sub command to this node
100377
- * @param name
100378
- * @returns node for the new command
100379
- */
100380
- addCommandNode(node) {
100381
- node[_PARENT] = this;
100382
- this.subCmdNodes[node.name] = node;
100383
- return node;
100384
- }
100385
- getErrorNodes(errorNodes = []) {
100386
- if (this.hasErrors) errorNodes.push(this);
100387
- for (const _key in this.optNodes) {
100388
- const _optNode = this.optNodes[_key];
100389
- if (_optNode.hasErrors) errorNodes.push(_optNode);
100390
- }
100391
- for (const _key in this.subCmdNodes) this.subCmdNodes[_key].getErrorNodes(errorNodes);
100392
- return errorNodes;
100393
- }
100394
- /**
100395
- * Add a option to this node
100396
- * @param name
100397
- * @returns node for the new option
100398
- */
100399
- addOptionNode(node) {
100400
- node[_PARENT] = this;
100401
- const name = node.option.name || node.name;
100402
- this.optNodes[name] = node;
100403
- if (!this.optCount[name]) this.optCount[name] = 0;
100404
- this.optCount[name]++;
100405
- return node;
100406
- }
100407
- removeOptionNode(name) {
100408
- if (this.optNodes[name]) {
100409
- delete this.optNodes[name];
100410
- delete this.optCount[name];
100411
- }
100412
- }
100413
- /**
100414
- *
100415
- */
100416
- applyDefaults() {
100417
- for (const optName in this.optNodes) this.optNodes[optName].applyDefaults();
100418
- const options = this.cmdBase.options._options;
100419
- for (const optName in options) {
100420
- const opt = options[optName];
100421
- if (opt.spec.hasOwnProperty("argDefault") && !this.optNodes[optName]) new ClapNodeGenerator(this).addOptionWithArgs(optName, [].concat(opt.spec.argDefault), opt);
100422
- }
100423
- for (const subCmdName in this.subCmdNodes) this.subCmdNodes[subCmdName].applyDefaults();
100424
- }
100425
- /**
100426
- * Allow you to apply extra config to the parsed object, overriding any `opts` with `source` not start with `cli`.
100427
- *
100428
- * For example, you can allow user to specify options in their `package.json` file, and apply those after the command line is parsed.
100429
- * @param config - Config object containing user options config
100430
- * @param src - Name of the source that provided the config. Default to `user`
100431
- * @returns
100432
- */
100433
- applyConfig(config, src = "user") {
100434
- for (const key in config) {
100435
- const data = {
100436
- name: key,
100437
- value: config[key],
100438
- verbatim: config[key],
100439
- arg: config[key],
100440
- dashes: 0
100441
- };
100442
- const matchOpt = this.cmdBase.options.match(data);
100443
- if (matchOpt) {
100444
- const optNode = this.optNodes[matchOpt.name];
100445
- if (!optNode || !optNode.source.startsWith("cli")) {
100446
- this.removeOptionNode(matchOpt.name);
100447
- new ClapNodeGenerator(this).addOptionWithArgs(matchOpt.name, [].concat(data.arg), matchOpt.option, src);
100448
- }
100449
- } else if (!this.optNodes[key]) new ClapNodeGenerator(this).addOptionWithArgs(key, [].concat(data.arg), void 0, src);
100450
- }
100451
- }
100452
- /**
100453
- * For options that has names with - in them, add it using its name converted to camelCase.
100454
- */
100455
- makeCamelCaseOptions() {
100456
- for (const key in this.optNodes) {
100457
- const camelCaseKey = camelCase(key);
100458
- if (camelCaseKey !== key && !this.optNodes[camelCaseKey]) {
100459
- this.optNodes[camelCaseKey] = this.optNodes[key];
100460
- this.optCount[camelCaseKey] = this.optCount[key];
100461
- }
100462
- }
100463
- for (const key in this.subCmdNodes) this.subCmdNodes[key].makeCamelCaseOptions();
100464
- }
100465
- /**
100466
- * check for missing options that are required
100467
- *
100468
- * @param missing
100469
- * @returns
100470
- */
100471
- checkRequiredOptions(missing = []) {
100472
- const _opts = this.cmdBase.options._options;
100473
- for (const name in _opts) if (_opts[name].spec.required) {
100474
- if (!this.optNodes[name]) missing.push(name);
100475
- }
100476
- for (const kCmd in this.subCmdNodes) this.subCmdNodes[kCmd].checkRequiredOptions(missing);
100477
- return missing;
100478
- }
100479
- get jsonMeta() {
100480
- if (this._jsonMeta) return this._jsonMeta;
100481
- const opts = {};
100482
- const optsFull = {};
100483
- const source = {};
100484
- for (const name in this.optNodes) {
100485
- const variants = [name];
100486
- const node = this.optNodes[name];
100487
- if (node.alias && name !== node.alias) variants.push(node.alias);
100488
- for (const _name of variants) {
100489
- if (node.option.isSingleArg) opts[_name] = node.argsMap[0];
100490
- else if (node.option.isCounting) opts[_name] = this.optCount[node.option.name];
100491
- else if (!node.option.hasArgs) opts[_name] = node.argsMap[0];
100492
- else opts[_name] = node.argsMap;
100493
- optsFull[_name] = node.argsMap;
100494
- source[_name] = this.optNodes[name].source;
100495
- }
100496
- }
100497
- const subCommands = {};
100498
- for (const name in this.subCmdNodes) subCommands[name] = this.subCmdNodes[name].jsonMeta;
100499
- const meta = {
100500
- name: this.name,
100501
- alias: this.alias,
100502
- argList: this.argsList,
100503
- args: this.argsMap,
100504
- opts,
100505
- optsFull,
100506
- optsCount: this.optCount,
100507
- source,
100508
- verbatim: {},
100509
- subCommands
100510
- };
100511
- this._jsonMeta = meta;
100512
- return meta;
100513
- }
100514
- };
100515
-
100516
101615
  //#endregion
100517
101616
  //#region node_modules/@fynjs/cli-args/dist/option-base.js
100518
101617
  /**
@@ -100568,35 +101667,6 @@ var UnknownOption = class extends OptionBase {
100568
101667
  };
100569
101668
  const optUnknown = new UnknownOption("", {});
100570
101669
 
100571
- //#endregion
100572
- //#region node_modules/@fynjs/cli-args/dist/option-node.js
100573
- /**
100574
- * Represents an option node in the command-line argument parser.
100575
- * Extends the `ClapNode` class to include specific option-related data.
100576
- *
100577
- * @remarks
100578
- * This class is used to handle options parsed from the command-line input.
100579
- * It includes the option itself and its source.
100580
- *
100581
- * @example
100582
- * ```typescript
100583
- * const optionMatch: OptionMatch = { name: 'verbose', alias: 'v', value: true, option: someOption };
100584
- * const optionNode = new OptionNode(optionMatch);
100585
- * ```
100586
- *
100587
- */
100588
- var OptionNode = class extends ClapNode {
100589
- constructor(data, parent) {
100590
- super(data.name, data.alias, parent);
100591
- this.argv.push(data.arg);
100592
- if (data.value !== void 0) this.addArg(data.value);
100593
- this.option = data.option || optUnknown;
100594
- }
100595
- applyDefaults() {
100596
- if (!this.option.spec.argDefault) return;
100597
- }
100598
- };
100599
-
100600
101670
  //#endregion
100601
101671
  //#region node_modules/@fynjs/cli-args/dist/options.js
100602
101672
  /**
@@ -100944,6 +102014,286 @@ const unknownCommandBaseNoOptions = new CommandBase("unknown no unknown options"
100944
102014
  alias: []
100945
102015
  });
100946
102016
 
102017
+ //#endregion
102018
+ //#region node_modules/@fynjs/cli-args/dist/option-node.js
102019
+ /**
102020
+ * Represents an option node in the command-line argument parser.
102021
+ * Extends the `ClapNode` class to include specific option-related data.
102022
+ *
102023
+ * @remarks
102024
+ * This class is used to handle options parsed from the command-line input.
102025
+ * It includes the option itself and its source.
102026
+ *
102027
+ * @example
102028
+ * ```typescript
102029
+ * const optionMatch: OptionMatch = { name: 'verbose', alias: 'v', value: true, option: someOption };
102030
+ * const optionNode = new OptionNode(optionMatch);
102031
+ * ```
102032
+ *
102033
+ */
102034
+ var OptionNode = class extends ClapNode {
102035
+ constructor(data, parent) {
102036
+ super(data.name, data.alias, parent);
102037
+ this.argv.push(data.arg);
102038
+ if (data.value !== void 0) this.addArg(data.value);
102039
+ this.option = data.option || optUnknown;
102040
+ }
102041
+ applyDefaults() {
102042
+ if (!this.option.spec.argDefault) return;
102043
+ }
102044
+ };
102045
+
102046
+ //#endregion
102047
+ //#region node_modules/@fynjs/cli-args/dist/command-node.js
102048
+ /**
102049
+ * Object representation for an instance of a command on the CLI
102050
+ */
102051
+ var CommandNode = class extends ClapNode {
102052
+ constructor(name, alias, cmdBase) {
102053
+ super(name, alias);
102054
+ this.argv.push(name);
102055
+ this.cmdBase = cmdBase;
102056
+ this.subCmdNodes = {};
102057
+ this.optNodes = {};
102058
+ this.optCount = {};
102059
+ this.isGreedy = false;
102060
+ }
102061
+ /** get the options with full values for this command */
102062
+ get options() {
102063
+ return this.jsonMeta.optsFull;
102064
+ }
102065
+ /** get the options for this command */
102066
+ get opts() {
102067
+ return this.jsonMeta.opts;
102068
+ }
102069
+ /** get the arguments for this command */
102070
+ get args() {
102071
+ return this.jsonMeta.args;
102072
+ }
102073
+ get source() {
102074
+ return this.jsonMeta.source;
102075
+ }
102076
+ get optsFull() {
102077
+ return this.jsonMeta.optsFull;
102078
+ }
102079
+ get subCommands() {
102080
+ return this.jsonMeta.subCommands;
102081
+ }
102082
+ getParent() {
102083
+ return super.getParent();
102084
+ }
102085
+ /**
102086
+ * Get the root command by traversing up the parent chain until no more parent exists.
102087
+ * @returns The root CommandNode
102088
+ */
102089
+ get rootCmd() {
102090
+ let current = this;
102091
+ while (current.getParent()) current = current.getParent();
102092
+ return current;
102093
+ }
102094
+ /**
102095
+ * Get array of all commands that have exec callback
102096
+ *
102097
+ * Root command execution is handled specially:
102098
+ * - This method filters out the root command (returns false for isRootCommand)
102099
+ * - Root command execution is determined by _shouldExecuteRootCommand() in nix-clap.ts
102100
+ * - Root command is executed in runExec/runExecAsync methods when conditions are met
102101
+ *
102102
+ * @param cmds - Array to accumulate command nodes with exec handlers
102103
+ * @param includeSubCommands - Whether to recursively include sub-commands
102104
+ * @returns Array of command nodes that have exec handlers (excluding root command)
102105
+ */
102106
+ getExecCommands(cmds, includeSubCommands) {
102107
+ if (this.cmdBase.exec && !isRootCommand(this.alias)) cmds.push(this);
102108
+ if (includeSubCommands) for (const _key in this.subCmdNodes) this.subCmdNodes[_key].getExecCommands(cmds, includeSubCommands);
102109
+ return cmds;
102110
+ }
102111
+ /**
102112
+ * Get the command chain (sequence from root to this command, including itself)
102113
+ * @returns Array of CommandNodes from root to this command
102114
+ */
102115
+ get cmdChain() {
102116
+ let parent = this;
102117
+ const nodes = [];
102118
+ while (parent && (parent = parent.getParent())) nodes.push(parent);
102119
+ return nodes.reverse().concat(this);
102120
+ }
102121
+ /**
102122
+ * Invoke the command's exec handler
102123
+ *
102124
+ * @param includeSubCommands - if true, then also do sub commands
102125
+ * @param parsed - The parsed result containing remaining args after --
102126
+ */
102127
+ invokeExec(includeSubCommands, parsed) {
102128
+ let count = 0;
102129
+ const cmds = this.getExecCommands([], includeSubCommands);
102130
+ for (const cmd of cmds) {
102131
+ const result = cmd.cmdBase.exec(cmd, parsed);
102132
+ if (parsed) {
102133
+ if (isThenable(result)) (parsed[_ASYNC_EXEC] || (parsed[_ASYNC_EXEC] = [])).push(cmd.name);
102134
+ if (!parsed.execCmd) parsed.execCmd = cmd;
102135
+ }
102136
+ count++;
102137
+ }
102138
+ return count;
102139
+ }
102140
+ /**
102141
+ *
102142
+ * @param includeSubCommands
102143
+ * @param parsed - The parsed result containing remaining args after --
102144
+ * @returns
102145
+ */
102146
+ async invokeExecAsync(includeSubCommands, parsed) {
102147
+ let count = 0;
102148
+ const cmds = this.getExecCommands([], includeSubCommands);
102149
+ for (const cmd of cmds) {
102150
+ await cmd.cmdBase.exec(cmd, parsed);
102151
+ if (parsed && !parsed.execCmd) parsed.execCmd = cmd;
102152
+ count++;
102153
+ }
102154
+ return count;
102155
+ }
102156
+ /**
102157
+ * Add a sub command to this node
102158
+ * @param name
102159
+ * @returns node for the new command
102160
+ */
102161
+ addCommandNode(node) {
102162
+ node[_PARENT] = this;
102163
+ this.subCmdNodes[node.name] = node;
102164
+ return node;
102165
+ }
102166
+ getErrorNodes(errorNodes = []) {
102167
+ if (this.hasErrors) errorNodes.push(this);
102168
+ for (const _key in this.optNodes) {
102169
+ const _optNode = this.optNodes[_key];
102170
+ if (_optNode.hasErrors) errorNodes.push(_optNode);
102171
+ }
102172
+ for (const _key in this.subCmdNodes) this.subCmdNodes[_key].getErrorNodes(errorNodes);
102173
+ return errorNodes;
102174
+ }
102175
+ /**
102176
+ * Add a option to this node
102177
+ * @param name
102178
+ * @returns node for the new option
102179
+ */
102180
+ addOptionNode(node) {
102181
+ node[_PARENT] = this;
102182
+ const name = node.option.name || node.name;
102183
+ this.optNodes[name] = node;
102184
+ if (!this.optCount[name]) this.optCount[name] = 0;
102185
+ this.optCount[name]++;
102186
+ return node;
102187
+ }
102188
+ removeOptionNode(name) {
102189
+ if (this.optNodes[name]) {
102190
+ delete this.optNodes[name];
102191
+ delete this.optCount[name];
102192
+ }
102193
+ }
102194
+ /**
102195
+ *
102196
+ */
102197
+ applyDefaults() {
102198
+ for (const optName in this.optNodes) this.optNodes[optName].applyDefaults();
102199
+ const options = this.cmdBase.options._options;
102200
+ for (const optName in options) {
102201
+ const opt = options[optName];
102202
+ if (opt.spec.hasOwnProperty("argDefault") && !this.optNodes[optName]) new ClapNodeGenerator(this).addOptionWithArgs(optName, [].concat(opt.spec.argDefault), opt);
102203
+ }
102204
+ for (const subCmdName in this.subCmdNodes) this.subCmdNodes[subCmdName].applyDefaults();
102205
+ }
102206
+ /**
102207
+ * Allow you to apply extra config to the parsed object, overriding any `opts` with `source` not start with `cli`.
102208
+ *
102209
+ * For example, you can allow user to specify options in their `package.json` file, and apply those after the command line is parsed.
102210
+ * @param config - Config object containing user options config
102211
+ * @param src - Name of the source that provided the config. Default to `user`
102212
+ * @returns
102213
+ */
102214
+ applyConfig(config, src = "user") {
102215
+ for (const key in config) {
102216
+ const data = {
102217
+ name: key,
102218
+ value: config[key],
102219
+ verbatim: config[key],
102220
+ arg: config[key],
102221
+ dashes: 0
102222
+ };
102223
+ const matchOpt = this.cmdBase.options.match(data);
102224
+ if (matchOpt) {
102225
+ const optNode = this.optNodes[matchOpt.name];
102226
+ if (!optNode || !optNode.source.startsWith("cli")) {
102227
+ this.removeOptionNode(matchOpt.name);
102228
+ new ClapNodeGenerator(this).addOptionWithArgs(matchOpt.name, [].concat(data.arg), matchOpt.option, src);
102229
+ }
102230
+ } else if (!this.optNodes[key]) new ClapNodeGenerator(this).addOptionWithArgs(key, [].concat(data.arg), void 0, src);
102231
+ }
102232
+ }
102233
+ /**
102234
+ * For options that has names with - in them, add it using its name converted to camelCase.
102235
+ */
102236
+ makeCamelCaseOptions() {
102237
+ for (const key in this.optNodes) {
102238
+ const camelCaseKey = camelCase(key);
102239
+ if (camelCaseKey !== key && !this.optNodes[camelCaseKey]) {
102240
+ this.optNodes[camelCaseKey] = this.optNodes[key];
102241
+ this.optCount[camelCaseKey] = this.optCount[key];
102242
+ }
102243
+ }
102244
+ for (const key in this.subCmdNodes) this.subCmdNodes[key].makeCamelCaseOptions();
102245
+ }
102246
+ /**
102247
+ * check for missing options that are required
102248
+ *
102249
+ * @param missing
102250
+ * @returns
102251
+ */
102252
+ checkRequiredOptions(missing = []) {
102253
+ const _opts = this.cmdBase.options._options;
102254
+ for (const name in _opts) if (_opts[name].spec.required) {
102255
+ if (!this.optNodes[name]) missing.push(name);
102256
+ }
102257
+ for (const kCmd in this.subCmdNodes) this.subCmdNodes[kCmd].checkRequiredOptions(missing);
102258
+ return missing;
102259
+ }
102260
+ get jsonMeta() {
102261
+ if (this._jsonMeta) return this._jsonMeta;
102262
+ const opts = {};
102263
+ const optsFull = {};
102264
+ const source = {};
102265
+ for (const name in this.optNodes) {
102266
+ const variants = [name];
102267
+ const node = this.optNodes[name];
102268
+ if (node.alias && name !== node.alias) variants.push(node.alias);
102269
+ for (const _name of variants) {
102270
+ if (node.option.isSingleArg) opts[_name] = node.argsMap[0];
102271
+ else if (node.option.isCounting) opts[_name] = this.optCount[node.option.name];
102272
+ else if (!node.option.hasArgs) opts[_name] = node.argsMap[0];
102273
+ else opts[_name] = node.argsMap;
102274
+ optsFull[_name] = node.argsMap;
102275
+ source[_name] = this.optNodes[name].source;
102276
+ }
102277
+ }
102278
+ const subCommands = {};
102279
+ for (const name in this.subCmdNodes) subCommands[name] = this.subCmdNodes[name].jsonMeta;
102280
+ const meta = {
102281
+ name: this.name,
102282
+ alias: this.alias,
102283
+ argList: this.argsList,
102284
+ args: this.argsMap,
102285
+ opts,
102286
+ optsFull,
102287
+ optsCount: this.optCount,
102288
+ source,
102289
+ verbatim: {},
102290
+ subCommands
102291
+ };
102292
+ this._jsonMeta = meta;
102293
+ return meta;
102294
+ }
102295
+ };
102296
+
100947
102297
  //#endregion
100948
102298
  //#region node_modules/@fynjs/cli-args/dist/node-generator.js
100949
102299
  const BUILDER_STATUS_GATHER_END = 1;
@@ -107931,8 +109281,9 @@ var FynGlobal = class {
107931
109281
  /**
107932
109282
  * Show PATH setup instructions
107933
109283
  */
107934
- showPathSetup() {
107935
- const binPath = Path.join(this.globalRoot, "current", "bin");
109284
+ async showPathSetup() {
109285
+ const stableBin = Path.join(this.globalRoot, "bin");
109286
+ const binPath = await file_ops_default.exists(stableBin) ? stableBin : this.globalBinDir;
107936
109287
  const isWindows = process.platform === "win32";
107937
109288
  console.log("\nTo use globally installed packages, add the bin directory to your PATH:\n");
107938
109289
  if (isWindows) {
@@ -109027,6 +110378,23 @@ const options = {
109027
110378
  desc: "fetch package dist tarball during dep resolving",
109028
110379
  argDefault: "false"
109029
110380
  },
110381
+ "script-policy": {
110382
+ args: "<mode string>",
110383
+ desc: "install script policy: all, source, review (default), or off"
110384
+ },
110385
+ "allow-scripts": {
110386
+ args: "[packages string..]",
110387
+ desc: "let these packages run install scripts, for this run only"
110388
+ },
110389
+ "deny-scripts": {
110390
+ args: "[packages string..]",
110391
+ desc: "block these packages from running install scripts, whatever approved them"
110392
+ },
110393
+ "allow-scripts-pin": {
110394
+ args: "<flag boolean>",
110395
+ desc: "pin script approvals to the reviewed version",
110396
+ argDefault: "true"
110397
+ },
109030
110398
  "central-store": {
109031
110399
  args: "<flag boolean>",
109032
110400
  alias: ["central", "cs"],
@@ -109214,6 +110582,10 @@ const commands = {
109214
110582
  "audit-file": {
109215
110583
  desc: "write audit report JSON to a file after install",
109216
110584
  args: "<path string>"
110585
+ },
110586
+ "allow-scripts-pending": {
110587
+ desc: "also report which packages would need approval under script policy review",
110588
+ args: "<flag boolean>"
109217
110589
  }
109218
110590
  }
109219
110591
  },
@@ -109262,6 +110634,11 @@ const commands = {
109262
110634
  args: "<flag boolean>",
109263
110635
  desc: "save fyn section to package-fyn.json",
109264
110636
  argDefault: "false"
110637
+ },
110638
+ audit: {
110639
+ desc: "run security audit after install (use --no-audit to skip)",
110640
+ args: "<flag boolean>",
110641
+ default: true
109265
110642
  }
109266
110643
  }
109267
110644
  },
@@ -109282,14 +110659,21 @@ const commands = {
109282
110659
  pickOpts.noStartupInfo = true;
109283
110660
  fyntil.resetFynpo();
109284
110661
  logger$1.info("installing...");
109285
- return await new FynCli(pickOpts).install();
110662
+ return await new FynCli(pickOpts).install({ opts: { audit: meta.opts.audit } });
109286
110663
  }
109287
110664
  },
109288
- options: { install: {
109289
- args: "<flag boolean>",
109290
- argDefault: "true",
109291
- desc: "Run install after removed"
109292
- } }
110665
+ options: {
110666
+ install: {
110667
+ args: "<flag boolean>",
110668
+ argDefault: "true",
110669
+ desc: "Run install after removed"
110670
+ },
110671
+ audit: {
110672
+ desc: "run security audit after install (use --no-audit to skip)",
110673
+ args: "<flag boolean>",
110674
+ default: true
110675
+ }
110676
+ }
109293
110677
  },
109294
110678
  stat: {
109295
110679
  desc: "Show stats of installed packages",
@@ -109375,6 +110759,60 @@ const commands = {
109375
110759
  args: "<flag boolean>"
109376
110760
  } }
109377
110761
  },
110762
+ "install-scripts": {
110763
+ desc: "Review which packages may run install scripts",
110764
+ subCommands: {
110765
+ ls: {
110766
+ desc: "List packages awaiting install-script review",
110767
+ alias: "list",
110768
+ options: { json: {
110769
+ args: "<flag boolean>",
110770
+ desc: "emit the list as JSON"
110771
+ } },
110772
+ async exec(cmd) {
110773
+ return new FynCli(await pickOptions(cmd)).installScripts("ls", cmd.jsonMeta);
110774
+ }
110775
+ },
110776
+ approve: {
110777
+ desc: "Allow packages to run their install scripts",
110778
+ args: "[packages string..]",
110779
+ options: {
110780
+ all: {
110781
+ args: "<flag boolean>",
110782
+ desc: "approve every package awaiting review"
110783
+ },
110784
+ local: {
110785
+ args: "<flag boolean>",
110786
+ desc: "write to this package's package.json instead of the monorepo's fynpo.json"
110787
+ }
110788
+ },
110789
+ async exec(cmd) {
110790
+ return new FynCli(await pickOptions(cmd)).installScripts("approve", cmd.jsonMeta);
110791
+ }
110792
+ },
110793
+ deny: {
110794
+ desc: "Deny packages outright - wins over any approval",
110795
+ args: "[packages string..]",
110796
+ options: { local: {
110797
+ args: "<flag boolean>",
110798
+ desc: "write to this package's package.json instead of the monorepo's fynpo.json"
110799
+ } },
110800
+ async exec(cmd) {
110801
+ return new FynCli(await pickOptions(cmd)).installScripts("deny", cmd.jsonMeta);
110802
+ }
110803
+ },
110804
+ prune: {
110805
+ desc: "Drop approvals for packages that are no longer installed",
110806
+ options: { local: {
110807
+ args: "<flag boolean>",
110808
+ desc: "write to this package's package.json instead of the monorepo's fynpo.json"
110809
+ } },
110810
+ async exec(cmd) {
110811
+ return new FynCli(await pickOptions(cmd)).installScripts("prune", cmd.jsonMeta);
110812
+ }
110813
+ }
110814
+ }
110815
+ },
109378
110816
  "sync-local": {
109379
110817
  desc: "Refresh locally linked package files",
109380
110818
  alias: "sl",
@@ -109529,7 +110967,7 @@ const commands = {
109529
110967
  desc: "Directory for global packages (default: ~/.fyn/global)"
109530
110968
  } },
109531
110969
  async exec(cmd) {
109532
- (await makeFynGlobal(cmd)).showPathSetup();
110970
+ await (await makeFynGlobal(cmd)).showPathSetup();
109533
110971
  }
109534
110972
  }
109535
110973
  }