'use strict';

var Module = require('node:module');
var vendor = require('./vendor.js');
var path = require('node:path');
var path$1 = require('../external/@socketsecurity/registry/lib/path');
var constants = require('./constants.js');
var utils = require('./utils.js');
var require$$9 = require('../external/@socketsecurity/registry/lib/debug');
var logger = require('../external/@socketsecurity/registry/lib/logger');
var require$$11 = require('../external/@socketsecurity/registry/lib/objects');

var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
let _arboristPkgPath;
function getArboristPackagePath() {
  if (_arboristPkgPath === undefined) {
    const pkgName = '@npmcli/arborist';
    const mainPathWithForwardSlashes = path$1.normalizePath(utils.getNpmRequire().resolve(pkgName));
    const arboristPkgPathWithForwardSlashes = mainPathWithForwardSlashes.slice(0, mainPathWithForwardSlashes.lastIndexOf(pkgName) + pkgName.length);
    _arboristPkgPath = constants.WIN32 ? path.normalize(arboristPkgPathWithForwardSlashes) : arboristPkgPathWithForwardSlashes;
  }
  return _arboristPkgPath;
}
let _arboristClassPath;
function getArboristClassPath() {
  if (_arboristClassPath === undefined) {
    _arboristClassPath = path.join(getArboristPackagePath(), 'lib/arborist/index.js');
  }
  return _arboristClassPath;
}
let _arboristEdgeClassPath;
function getArboristEdgeClassPath() {
  if (_arboristEdgeClassPath === undefined) {
    _arboristEdgeClassPath = path.join(getArboristPackagePath(), 'lib/edge.js');
  }
  return _arboristEdgeClassPath;
}
let _arboristNodeClassPath;
function getArboristNodeClassPath() {
  if (_arboristNodeClassPath === undefined) {
    _arboristNodeClassPath = path.join(getArboristPackagePath(), 'lib/node.js');
  }
  return _arboristNodeClassPath;
}
let _arboristOverrideSetClassPath;
function getArboristOverrideSetClassPath() {
  if (_arboristOverrideSetClassPath === undefined) {
    _arboristOverrideSetClassPath = path.join(getArboristPackagePath(), 'lib/override-set.js');
  }
  return _arboristOverrideSetClassPath;
}

const DiffAction = utils.createEnum({
  add: 'ADD',
  change: 'CHANGE',
  remove: 'REMOVE'
});

function getUrlOrigin(input) {
  try {
    // TODO: URL.parse is available in Node 22.1.0. We can use it when we drop Node 18.
    // https://nodejs.org/docs/latest-v22.x/api/url.html#urlparseinput-base
    // return URL.parse(input)?.origin ?? ''
    return new URL(input).origin ?? '';
  } catch {}
  return '';
}
async function getAlertsMapFromArborist(arb, needInfoOn, options) {
  const opts = {
    __proto__: null,
    consolidate: false,
    nothrow: false,
    ...options,
    filter: utils.toFilterConfig(require$$11.getOwn(options, 'filter'))
  };
  const purls = needInfoOn.map(d => utils.idToNpmPurl(d.node.pkgid));
  let overrides;
  const overridesMap = (arb.actualTree ?? arb.idealTree ?? (await arb.loadActual()))?.overrides?.children;
  if (overridesMap) {
    overrides = Object.fromEntries(Array.from(overridesMap.entries()).map(([key, overrideSet]) => {
      return [key, overrideSet.value];
    }));
  }
  return await utils.getAlertsMapFromPurls(purls, {
    overrides,
    ...opts
  });
}
function getDetailsFromDiff(diff, options) {
  const details = [];
  // `diff` is `null` when `npm install --package-lock-only` is passed.
  if (!diff) {
    require$$9.debugFn('notice', `miss: diff is ${diff}`);
    return details;
  }
  const {
    NPM_REGISTRY_URL
  } = constants;
  const filterConfig = utils.toFilterConfig({
    existing: false,
    unknownOrigin: true,
    ...require$$11.getOwn(options, 'filter')
  });
  const queue = [...diff.children];
  let pos = 0;
  let {
    length: queueLength
  } = queue;
  while (pos < queueLength) {
    if (pos === constants.LOOP_SENTINEL) {
      throw new Error('Detected infinite loop while walking Arborist diff');
    }
    const currDiff = queue[pos++];
    const {
      action
    } = currDiff;
    if (action) {
      // The `pkgNode`, i.e. the `ideal` node, will be `undefined` if the diff
      // action is 'REMOVE'
      // The `oldNode`, i.e. the `actual` node, will be `undefined` if the diff
      // action is 'ADD'.
      const {
        actual: oldNode,
        ideal: pkgNode
      } = currDiff;
      let existing;
      let keep = false;
      if (action === DiffAction.change) {
        if (pkgNode?.package.version !== oldNode?.package.version) {
          keep = true;
          if (oldNode?.package.name && oldNode.package.name === pkgNode?.package.name) {
            existing = oldNode;
          }
        }
      } else {
        keep = action !== DiffAction.remove;
      }
      if (keep && pkgNode?.resolved && (!oldNode || oldNode.resolved)) {
        if (filterConfig.unknownOrigin || getUrlOrigin(pkgNode.resolved) === NPM_REGISTRY_URL) {
          details.push({
            node: pkgNode,
            existing
          });
        }
      }
    }
    for (const child of currDiff.children) {
      queue[queueLength++] = child;
    }
  }
  if (filterConfig.existing) {
    const {
      unchanged
    } = diff;
    for (let i = 0, {
        length
      } = unchanged; i < length; i += 1) {
      const pkgNode = unchanged[i];
      if (filterConfig.unknownOrigin || getUrlOrigin(pkgNode.resolved) === NPM_REGISTRY_URL) {
        details.push({
          node: pkgNode,
          existing: pkgNode
        });
      }
    }
  }
  return details;
}

// @ts-ignore
const {
  kInternalsSymbol,
  [kInternalsSymbol]: {
    getIpc
  }
} = constants;
const SAFE_NO_SAVE_ARBORIST_REIFY_OPTIONS_OVERRIDES = {
  __proto__: null,
  audit: false,
  dryRun: true,
  fund: false,
  ignoreScripts: true,
  progress: false,
  save: false,
  saveBundle: false,
  silent: true
};
const kCtorArgs = Symbol('ctorArgs');
const kRiskyReify = Symbol('riskyReify');
const Arborist = vendor.arboristExports;

// Implementation code not related to our custom behavior is based on
// https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/index.js:
class SafeArborist extends Arborist {
  constructor(...ctorArgs) {
    super({
      path: (ctorArgs.length ? ctorArgs[0]?.path : undefined) ?? process.cwd(),
      ...(ctorArgs.length ? ctorArgs[0] : undefined),
      ...SAFE_NO_SAVE_ARBORIST_REIFY_OPTIONS_OVERRIDES
    }, ...ctorArgs.slice(1));
    this[kCtorArgs] = ctorArgs;
  }
  async [kRiskyReify](...args) {
    const ctorArgs = this[kCtorArgs];
    const arb = new Arborist({
      ...(ctorArgs.length ? ctorArgs[0] : undefined),
      progress: false
    }, ...ctorArgs.slice(1));
    const ret = await arb.reify({
      ...(args.length ? args[0] : undefined),
      progress: false
    }, ...args.slice(1));
    Object.assign(this, arb);
    return ret;
  }

  // @ts-ignore Incorrectly typed.
  async reify(...args) {
    const options = {
      __proto__: null,
      ...(args.length ? args[0] : undefined)
    };
    const ipc = await getIpc();
    require$$9.debugDir('inspect', {
      ipc
    });
    const binName = ipc[constants.SOCKET_CLI_SHADOW_BIN];
    if (!binName) {
      return await this[kRiskyReify](...args);
    }
    await super.reify({
      ...options,
      ...SAFE_NO_SAVE_ARBORIST_REIFY_OPTIONS_OVERRIDES,
      progress: false
    },
    // @ts-ignore: TypeScript gets grumpy about rest parameters.
    ...args.slice(1));
    const shadowAcceptRisks = !!ipc[constants.SOCKET_CLI_SHADOW_ACCEPT_RISKS];
    const shadowProgress = !!ipc[constants.SOCKET_CLI_SHADOW_PROGRESS];
    const shadowSilent = !!ipc[constants.SOCKET_CLI_SHADOW_SILENT];
    const acceptRisks = shadowAcceptRisks || constants.ENV.SOCKET_CLI_ACCEPT_RISKS;
    const reportOnlyBlocking = acceptRisks || options.dryRun || options['yes'];
    const silent = !!options['silent'];
    const spinner = silent || !shadowProgress ? undefined : constants.spinner;
    const isShadowNpx = binName === 'npx';
    const hasExisting = await utils.findUp('node_modules', {
      cwd: process.cwd(),
      onlyDirectories: true
    });
    const shouldCheckExisting = reportOnlyBlocking ? true : isShadowNpx;
    const needInfoOn = getDetailsFromDiff(this.diff, {
      filter: {
        existing: shouldCheckExisting
      }
    });
    const alertsMap = await getAlertsMapFromArborist(this, needInfoOn, {
      apiToken: ipc[constants.SOCKET_CLI_SHADOW_API_TOKEN],
      spinner,
      filter: reportOnlyBlocking ? {
        actions: ['error'],
        blocked: true,
        existing: shouldCheckExisting
      } : {
        actions: ['error', 'monitor', 'warn'],
        existing: shouldCheckExisting
      }
    });
    if (alertsMap.size) {
      process.exitCode = 1;
      const viewAllRisks = constants.ENV.SOCKET_CLI_VIEW_ALL_RISKS;
      utils.logAlertsMap(alertsMap, {
        hideAt: viewAllRisks ? 'none' : 'middle',
        output: process.stderr
      });
      throw new Error(`
          Socket ${binName} exiting due to risks.${viewAllRisks ? '' : `\nView all risks - Rerun with environment variable ${constants.SOCKET_CLI_VIEW_ALL_RISKS}=1.`}${acceptRisks ? '' : `\nAccept risks - Rerun with environment variable ${constants.SOCKET_CLI_ACCEPT_RISKS}=1.`}
        `.trim());
    } else if (!silent && !shadowSilent) {
      logger.logger.success(`Socket ${binName} ${acceptRisks ? 'accepted' : 'found no'}${hasExisting ? ' new' : ''} risks`);
      if (isShadowNpx) {
        logger.logger.log(`Running ${options.add[0]}`);
      }
    }
    return await this[kRiskyReify](...args);
  }
}

const require$1 = Module.createRequire(require('node:url').pathToFileURL(__filename).href);
const Edge = vendor.edgeExports;
const Node = vendor.nodeExports;
const OverrideSet = vendor.overrideSetExports;
function installSafeArborist() {
  // Override '@npmcli/arborist' module exports with patched variants based on
  // https://github.com/npm/cli/pull/8089.
  const cache = require$1.cache;
  cache[getArboristClassPath()] = {
    exports: SafeArborist
  };
  cache[getArboristEdgeClassPath()] = {
    exports: Edge
  };
  cache[getArboristNodeClassPath()] = {
    exports: Node
  };
  cache[getArboristOverrideSetClassPath()] = {
    exports: OverrideSet
  };
}

installSafeArborist();
//# debugId=f70f8c63-2b3f-4731-b7f5-7e6cf01f055
//# sourceMappingURL=shadow-npm-inject.js.map
