socket 1.1.39 → 1.1.40
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/CHANGELOG.md +8 -0
- package/dist/cli.js +15 -6
- package/dist/cli.js.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
- package/dist/utils.js +100 -100
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../src/utils/debug.mts","../src/utils/errors.mts","../src/utils/config.mts","../src/utils/requirements.mts","../src/utils/sdk.mts","../src/utils/api.mts","../src/utils/fail-msg-with-badge.mts","../src/utils/markdown.mts","../src/utils/serialize-result-json.mts","../src/utils/terminal-link.mts","../src/utils/check-input.mts","../src/utils/get-output-kind.mts","../src/utils/strings.mts","../src/utils/output-formatting.mts","../src/utils/tildify.mts","../src/utils/meow-with-subcommands.mts","../src/utils/ms-at-home.mts","../src/commands/organization/fetch-organization-list.mts","../src/commands/scan/suggest-org-slug.mts","../src/commands/scan/suggest-to-persist-orgslug.mts","../src/utils/determine-org-slug.mts","../src/commands/ci/fetch-default-org-slug.mts","../src/utils/extract-names.mts","../src/utils/git.mts","../src/utils/purl.mts","../src/utils/socket-url.mts","../src/utils/map-to-object.mts","../src/utils/walk-nested-map.mts","../src/utils/coana.mts","../src/utils/fs.mts","../src/utils/glob.mts","../src/utils/path-resolve.mts","../src/utils/yarn-paths.mts","../src/utils/yarn-version.mts","../src/utils/dlx.mts","../src/utils/organization.mts","../src/utils/socket-json.mts","../src/utils/github.mts","../src/utils/cmd.mts","../src/utils/cve-to-ghsa.mts","../src/utils/purl-to-ghsa.mts","../src/utils/semver.mts","../src/utils/completion.mts","../src/utils/npm-package-arg.mts","../src/utils/npm-paths.mts","../src/shadow/npm/install.mts","../src/utils/agent.mts","../src/utils/package-environment.mts","../src/utils/ecosystem.mts","../src/utils/dlx-detection.mts","../src/utils/pnpm-paths.mts","../src/utils/shadow-links.mts","../src/utils/filter-config.mts","../src/utils/spec.mts","../src/utils/pnpm.mts","../src/utils/alert/artifact.mts","../src/utils/objects.mts","../src/utils/alert/fix.mts","../src/utils/alert/severity.mts","../src/utils/color-or-markdown.mts","../src/utils/translations.mts","../src/utils/socket-package-alert.mts","../src/utils/alerts-map.mts","../src/utils/npm-spec.mts"],"sourcesContent":["/**\n * Debug utilities for Socket CLI.\n * Provides structured debugging with categorized levels and helpers.\n *\n * Debug Categories:\n * DEFAULT (shown with SOCKET_CLI_DEBUG=1):\n * - 'error': Critical errors that prevent operation\n * - 'warn': Important warnings that may affect behavior\n * - 'notice': Notable events and state changes\n * - 'silly': Very verbose debugging info\n *\n * OPT-IN ONLY (require explicit DEBUG='category' even with SOCKET_CLI_DEBUG=1):\n * - 'inspect': Detailed object inspection (DEBUG='inspect' or DEBUG='*')\n * - 'stdio': Command execution logs (DEBUG='stdio' or DEBUG='*')\n *\n * These opt-in categories are intentionally excluded from default debug output\n * to reduce noise. Enable them explicitly when needed for deep debugging.\n */\n\nimport { debugDir, debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../constants.mts'\n\n/**\n * Debug an API request start.\n * Logs essential info without exposing sensitive data.\n */\nexport function debugApiRequest(\n method: string,\n endpoint: string,\n timeout?: number | undefined,\n): void {\n if (constants.ENV.SOCKET_CLI_DEBUG) {\n const timeoutStr = timeout !== undefined ? ` (timeout: ${timeout}ms)` : ''\n logger.info(\n `[DEBUG] ${new Date().toISOString()} request started: ${method} ${endpoint}${timeoutStr}`,\n )\n }\n}\n\n/**\n * Debug an API response end.\n * Logs essential info without exposing sensitive data.\n */\nexport function debugApiResponse(\n method: string,\n endpoint: string,\n status?: number | undefined,\n error?: unknown | undefined,\n duration?: number | undefined,\n headers?: Record<string, string> | undefined,\n): void {\n if (!constants.ENV.SOCKET_CLI_DEBUG) {\n return\n }\n\n if (error) {\n logger.fail(\n `[DEBUG] ${new Date().toISOString()} request error: ${method} ${endpoint} - ${error instanceof Error ? error.message : 'Unknown error'}${duration !== undefined ? ` (${duration}ms)` : ''}`,\n )\n if (headers) {\n logger.info(\n `[DEBUG] response headers: ${JSON.stringify(headers, null, 2)}`,\n )\n }\n } else {\n const durationStr = duration !== undefined ? ` (${duration}ms)` : ''\n logger.info(\n `[DEBUG] ${new Date().toISOString()} request ended: ${method} ${endpoint}: HTTP ${status}${durationStr}`,\n )\n if (headers && status && status >= 400) {\n logger.info(\n `[DEBUG] response headers: ${JSON.stringify(headers, null, 2)}`,\n )\n }\n }\n}\n\n/**\n * Debug file operation.\n * Logs file operations with appropriate level.\n */\nexport function debugFileOp(\n operation: 'read' | 'write' | 'delete' | 'create',\n filepath: string,\n error?: unknown | undefined,\n): void {\n if (error) {\n debugDir('warn', {\n operation,\n filepath,\n error: error instanceof Error ? error.message : 'Unknown error',\n })\n } else if (isDebug('silly')) {\n debugFn('silly', `File ${operation}: ${filepath}`)\n }\n}\n\n/**\n * Debug package scanning.\n * Provides insight into security scanning.\n */\nexport function debugScan(\n phase: 'start' | 'progress' | 'complete' | 'error',\n packageCount?: number | undefined,\n details?: unknown | undefined,\n): void {\n switch (phase) {\n case 'start':\n if (packageCount) {\n debugFn('notice', `Scanning ${packageCount} packages`)\n }\n break\n case 'progress':\n if (isDebug('silly') && packageCount) {\n debugFn('silly', `Scan progress: ${packageCount} packages processed`)\n }\n break\n case 'complete':\n debugFn(\n 'notice',\n `Scan complete${packageCount ? `: ${packageCount} packages` : ''}`,\n )\n break\n case 'error':\n debugDir('error', {\n phase: 'scan_error',\n details,\n })\n break\n }\n}\n\n/**\n * Debug configuration loading.\n */\nexport function debugConfig(\n source: string,\n found: boolean,\n error?: unknown | undefined,\n): void {\n if (error) {\n debugDir('warn', {\n source,\n error: error instanceof Error ? error.message : 'Unknown error',\n })\n } else if (found) {\n debugFn('notice', `Config loaded: ${source}`)\n } else if (isDebug('silly')) {\n debugFn('silly', `Config not found: ${source}`)\n }\n}\n\n/**\n * Debug git operations.\n * Only logs important git operations, not every command.\n */\nexport function debugGit(\n operation: string,\n success: boolean,\n details?: Record<string, unknown> | undefined,\n): void {\n if (!success) {\n debugDir('warn', {\n git_op: operation,\n ...details,\n })\n } else if (\n (isDebug('notice') && operation.includes('push')) ||\n operation.includes('commit')\n ) {\n // Only log important operations like push and commit.\n debugFn('notice', `Git ${operation} succeeded`)\n } else if (isDebug('silly')) {\n debugFn('silly', `Git ${operation}`)\n }\n}\n\nexport { debugDir, debugFn, isDebug }\n","/**\n * Error utilities for Socket CLI.\n * Provides consistent error handling, formatting, and message extraction.\n *\n * Key Classes:\n * - AuthError: Authentication failures (401/403 responses)\n * - InputError: User input validation failures\n *\n * Key Functions:\n * - captureException: Send errors to Sentry for monitoring\n * - formatErrorWithDetail: Format errors with detailed context\n * - getErrorCause: Get error cause with fallback to UNKNOWN_ERROR\n * - getErrorMessage: Extract error message from any thrown value\n *\n * Error Handling Strategy:\n * - Always prefer specific error types over generic errors\n * - Use formatErrorWithDetail for user-facing error messages\n * - Log errors to Sentry in production for monitoring\n */\n\nimport { setTimeout as wait } from 'node:timers/promises'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants, { UNKNOWN_ERROR } from '../constants.mts'\n\nconst {\n kInternalsSymbol,\n [kInternalsSymbol as unknown as 'Symbol(kInternalsSymbol)']: { getSentry },\n} = constants\n\ntype EventHintOrCaptureContext = { [key: string]: any } | Function\n\nexport class AuthError extends Error {}\n\nexport class InputError extends Error {\n public body: string | undefined\n\n constructor(message: string, body?: string | undefined) {\n super(message)\n this.body = body\n }\n}\n\nexport async function captureException(\n exception: unknown,\n hint?: EventHintOrCaptureContext | undefined,\n): Promise<string> {\n const result = captureExceptionSync(exception, hint)\n // \"Sleep\" for a second, just in case, hopefully enough time to initiate fetch.\n await wait(1000)\n return result\n}\n\nexport function captureExceptionSync(\n exception: unknown,\n hint?: EventHintOrCaptureContext | undefined,\n): string {\n const Sentry = getSentry()\n if (!Sentry) {\n return ''\n }\n debugFn('notice', 'send: exception to Sentry')\n return Sentry.captureException(exception, hint) as string\n}\n\nexport function isErrnoException(\n value: unknown,\n): value is NodeJS.ErrnoException {\n if (!(value instanceof Error)) {\n return false\n }\n return (value as NodeJS.ErrnoException).code !== undefined\n}\n\n/**\n * Extracts an error message from an unknown value.\n * Returns the message if it's an Error object, otherwise returns undefined.\n *\n * @param error - The error object to extract message from\n * @returns The error message or undefined\n */\nexport function getErrorMessage(error: unknown): string | undefined {\n return (error as Error)?.message\n}\n\n/**\n * Extracts an error message from an unknown value with a fallback.\n * Returns the message if it's an Error object, otherwise returns the fallback.\n *\n * @param error - The error object to extract message from\n * @param fallback - The fallback message if no error message is found\n * @returns The error message or fallback\n *\n * @example\n * getErrorMessageOr(error, 'Unknown error occurred')\n * // Returns: \"ENOENT: no such file or directory\" or \"Unknown error occurred\"\n */\nexport function getErrorMessageOr(error: unknown, fallback: string): string {\n return getErrorMessage(error) || fallback\n}\n\n/**\n * Extracts an error cause from an unknown value.\n * Returns the error message if available, otherwise UNKNOWN_ERROR.\n * Commonly used for creating CResult error causes.\n *\n * @param error - The error object to extract message from\n * @returns The error message or UNKNOWN_ERROR constant\n *\n * @example\n * return { ok: false, message: 'Operation failed', cause: getErrorCause(e) }\n */\nexport function getErrorCause(error: unknown): string {\n return getErrorMessageOr(error, UNKNOWN_ERROR)\n}\n\n/**\n * Formats an error message with an optional error detail appended.\n * Extracts the message from an unknown error value and appends it\n * to the base message if available.\n *\n * @param baseMessage - The base message to display\n * @param error - The error object to extract message from\n * @returns Formatted message with error detail if available\n *\n * @example\n * formatErrorWithDetail('Failed to delete file', error)\n * // Returns: \"Failed to delete file: ENOENT: no such file or directory\"\n * // Or just: \"Failed to delete file\" if no error message\n */\nexport function formatErrorWithDetail(\n baseMessage: string,\n error: unknown,\n): string {\n const errorMessage = getErrorMessage(error)\n return `${baseMessage}${errorMessage ? `: ${errorMessage}` : ''}`\n}\n","/**\n * Configuration utilities for Socket CLI.\n * Manages CLI configuration including API tokens, org settings, and preferences.\n *\n * Configuration Hierarchy (highest priority first):\n * 1. Environment variables (SOCKET_CLI_*)\n * 2. Command-line --config flag\n * 3. Persisted config file (base64 encoded JSON)\n *\n * Supported Config Keys:\n * - apiBaseUrl: Socket API endpoint URL\n * - apiProxy: Proxy for API requests\n * - apiToken: Authentication token for Socket API\n * - defaultOrg/org: Default organization slug\n * - enforcedOrgs: Organizations with enforced security policies\n *\n * Key Functions:\n * - findSocketYmlSync: Locate socket.yml configuration file\n * - getConfigValue: Retrieve configuration value by key\n * - overrideCachedConfig: Apply temporary config overrides\n * - updateConfigValue: Persist configuration changes\n */\n\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\n\nimport config from '@socketsecurity/config'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { safeReadFileSync } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\n\nimport { debugConfig } from './debug.mts'\nimport constants, {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_DEFAULT_ORG,\n CONFIG_KEY_ENFORCED_ORGS,\n CONFIG_KEY_ORG,\n SOCKET_YAML,\n SOCKET_YML,\n} from '../constants.mts'\nimport { getErrorCause } from './errors.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { SocketYml } from '@socketsecurity/config'\n\nexport interface LocalConfig {\n apiBaseUrl?: string | null | undefined\n // @deprecated ; use apiToken. when loading a config, if this prop exists it\n // is deleted and set to apiToken instead, and then persisted.\n // should only happen once for legacy users.\n apiKey?: string | null | undefined\n apiProxy?: string | null | undefined\n apiToken?: string | null | undefined\n defaultOrg?: string | undefined\n enforcedOrgs?: string[] | readonly string[] | null | undefined\n skipAskToPersistDefaultOrg?: boolean | undefined\n // Convenience alias for defaultOrg.\n org?: string | undefined\n}\n\nconst sensitiveConfigKeyLookup: Set<keyof LocalConfig> = new Set([\n CONFIG_KEY_API_TOKEN,\n])\n\nconst supportedConfig: Map<keyof LocalConfig, string> = new Map([\n [CONFIG_KEY_API_BASE_URL, 'Base URL of the Socket API endpoint'],\n [CONFIG_KEY_API_PROXY, 'A proxy through which to access the Socket API'],\n [\n CONFIG_KEY_API_TOKEN,\n 'The Socket API token required to access most Socket API endpoints',\n ],\n [\n CONFIG_KEY_DEFAULT_ORG,\n 'The default org slug to use; usually the org your Socket API token has access to. When set, all orgSlug arguments are implied to be this value.',\n ],\n [\n CONFIG_KEY_ENFORCED_ORGS,\n 'Orgs in this list have their security policies enforced on this machine',\n ],\n [\n 'skipAskToPersistDefaultOrg',\n 'This flag prevents the Socket CLI from asking you to persist the org slug when you selected one interactively',\n ],\n [CONFIG_KEY_ORG, 'Alias for defaultOrg'],\n])\n\nconst supportedConfigEntries = [...supportedConfig.entries()].sort((a, b) =>\n naturalCompare(a[0], b[0]),\n)\nconst supportedConfigKeys = supportedConfigEntries.map(p => p[0])\n\nfunction getConfigValues(): LocalConfig {\n if (_cachedConfig === undefined) {\n // Order: env var > --config flag > file\n _cachedConfig = {} as LocalConfig\n const { socketAppDataPath } = constants\n if (socketAppDataPath) {\n const raw = safeReadFileSync(socketAppDataPath)\n if (raw) {\n try {\n Object.assign(\n _cachedConfig,\n JSON.parse(Buffer.from(raw, 'base64').toString()),\n )\n debugConfig(socketAppDataPath, true)\n } catch (e) {\n logger.warn(`Failed to parse config at ${socketAppDataPath}`)\n debugConfig(socketAppDataPath, false, e)\n }\n // Normalize apiKey to apiToken and persist it.\n // This is a one time migration per user.\n if (_cachedConfig['apiKey']) {\n const token = _cachedConfig['apiKey']\n delete _cachedConfig['apiKey']\n updateConfigValue(CONFIG_KEY_API_TOKEN, token)\n }\n } else {\n mkdirSync(path.dirname(socketAppDataPath), { recursive: true })\n }\n }\n }\n return _cachedConfig\n}\n\nfunction normalizeConfigKey(\n key: keyof LocalConfig,\n): CResult<keyof LocalConfig> {\n // Note: apiKey was the old name of the token. When we load a config with\n // property apiKey, we'll copy that to apiToken and delete the old property.\n // We added `org` as a convenience alias for `defaultOrg`\n const normalizedKey =\n key === 'apiKey'\n ? CONFIG_KEY_API_TOKEN\n : key === CONFIG_KEY_ORG\n ? CONFIG_KEY_DEFAULT_ORG\n : key\n if (!isSupportedConfigKey(normalizedKey)) {\n return {\n ok: false,\n message: `Invalid config key: ${normalizedKey}`,\n data: undefined,\n }\n }\n return { ok: true, data: normalizedKey }\n}\n\nexport type FoundSocketYml = {\n path: string\n parsed: SocketYml\n}\n\nexport function findSocketYmlSync(\n dir = process.cwd(),\n): CResult<FoundSocketYml | undefined> {\n let prevDir = null\n while (dir !== prevDir) {\n let ymlPath = path.join(dir, SOCKET_YML)\n let yml = safeReadFileSync(ymlPath)\n if (yml === undefined) {\n ymlPath = path.join(dir, SOCKET_YAML)\n yml = safeReadFileSync(ymlPath)\n }\n if (typeof yml === 'string') {\n try {\n return {\n ok: true,\n data: {\n path: ymlPath,\n parsed: config.parseSocketConfig(yml),\n },\n }\n } catch (e) {\n debugFn('error', `Failed to parse config file: ${ymlPath}`)\n debugDir('error', e)\n return {\n ok: false,\n message: `Found file but was unable to parse ${ymlPath}`,\n cause: getErrorCause(e),\n }\n }\n }\n prevDir = dir\n dir = path.join(dir, '..')\n }\n return { ok: true, data: undefined }\n}\n\nexport function getConfigValue<Key extends keyof LocalConfig>(\n key: Key,\n): CResult<LocalConfig[Key]> {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(key)\n if (!keyResult.ok) {\n return keyResult\n }\n return { ok: true, data: localConfig[keyResult.data as Key] }\n}\n\n// This version squashes errors, returning undefined instead.\n// Should be used when we can reasonably predict the call can't fail.\nexport function getConfigValueOrUndef<Key extends keyof LocalConfig>(\n key: Key,\n): LocalConfig[Key] | undefined {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(key)\n if (!keyResult.ok) {\n return undefined\n }\n return localConfig[keyResult.data as Key]\n}\n\n// Ensure export because dist/utils.js is required in src/constants.mts.\n// eslint-disable-next-line n/exports-style\nif (typeof exports === 'object' && exports !== null) {\n // eslint-disable-next-line n/exports-style\n exports.getConfigValueOrUndef = getConfigValueOrUndef\n}\n\nexport function getSupportedConfigEntries() {\n return [...supportedConfigEntries]\n}\n\nexport function getSupportedConfigKeys() {\n return [...supportedConfigKeys]\n}\n\nexport function isConfigFromFlag() {\n return _configFromFlag\n}\n\nexport function isSensitiveConfigKey(key: string): key is keyof LocalConfig {\n return sensitiveConfigKeyLookup.has(key as keyof LocalConfig)\n}\n\nexport function isSupportedConfigKey(key: string): key is keyof LocalConfig {\n return supportedConfig.has(key as keyof LocalConfig)\n}\n\nlet _cachedConfig: LocalConfig | undefined\n// When using --config or SOCKET_CLI_CONFIG, do not persist the config.\nlet _configFromFlag = false\n\nexport function overrideCachedConfig(jsonConfig: unknown): CResult<undefined> {\n debugFn('notice', 'override: full config (not stored)')\n\n let config\n try {\n config = JSON.parse(String(jsonConfig))\n if (!config || typeof config !== 'object') {\n // `null` is valid json, so are primitive values.\n // They're not valid config objects :)\n return {\n ok: false,\n message: 'Could not parse Config as JSON',\n cause:\n \"Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again.\",\n }\n }\n } catch {\n // Force set an empty config to prevent accidentally using system settings.\n _cachedConfig = {} as LocalConfig\n _configFromFlag = true\n\n return {\n ok: false,\n message: 'Could not parse Config as JSON',\n cause:\n \"Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again.\",\n }\n }\n\n // @ts-ignore Override an illegal object.\n _cachedConfig = config as LocalConfig\n _configFromFlag = true\n\n // Normalize apiKey to apiToken.\n if (_cachedConfig['apiKey']) {\n if (_cachedConfig['apiToken']) {\n logger.warn(\n 'Note: The config override had both apiToken and apiKey. Using the apiToken value. Remove the apiKey to get rid of this message.',\n )\n }\n _cachedConfig['apiToken'] = _cachedConfig['apiKey']\n delete _cachedConfig['apiKey']\n }\n\n return { ok: true, data: undefined }\n}\n\nexport function overrideConfigApiToken(apiToken: unknown) {\n debugFn('notice', 'override: Socket API token (not stored)')\n // Set token to the local cached config and mark it read-only so it doesn't persist.\n _cachedConfig = {\n ...config,\n ...(apiToken === undefined ? {} : { apiToken: String(apiToken) }),\n } as LocalConfig\n _configFromFlag = true\n}\n\nlet _pendingSave = false\nexport function updateConfigValue<Key extends keyof LocalConfig>(\n configKey: keyof LocalConfig,\n value: LocalConfig[Key],\n): CResult<undefined | string> {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(configKey)\n if (!keyResult.ok) {\n return keyResult\n }\n const key: Key = keyResult.data as Key\n // Implicitly deleting when serializing.\n let wasDeleted = value === undefined\n if (key === 'skipAskToPersistDefaultOrg') {\n if (value === 'true' || value === 'false') {\n localConfig['skipAskToPersistDefaultOrg'] = value === 'true'\n } else {\n delete localConfig['skipAskToPersistDefaultOrg']\n wasDeleted = true\n }\n } else {\n if (value === 'undefined' || value === 'true' || value === 'false') {\n logger.warn(\n `Note: The value is set to \"${value}\", as a string (!). Use \\`socket config unset\\` to reset a key.`,\n )\n }\n localConfig[key] = value\n }\n if (_configFromFlag) {\n return {\n ok: true,\n message: `Config key '${key}' was ${wasDeleted ? 'deleted' : `updated`}`,\n data: 'Change applied but not persisted; current config is overridden through env var or flag',\n }\n }\n\n if (!_pendingSave) {\n _pendingSave = true\n process.nextTick(() => {\n _pendingSave = false\n const { socketAppDataPath } = constants\n if (socketAppDataPath) {\n writeFileSync(\n socketAppDataPath,\n Buffer.from(JSON.stringify(localConfig)).toString('base64'),\n )\n }\n })\n }\n\n return {\n ok: true,\n message: `Config key '${key}' was ${wasDeleted ? 'deleted' : `updated`}`,\n data: undefined,\n }\n}\n","/**\n * Requirements configuration utilities for Socket CLI.\n * Manages API permissions and quota requirements for commands.\n *\n * Key Functions:\n * - getRequirements: Load requirements configuration\n * - getRequirementsKey: Convert command path to requirements key\n *\n * Configuration:\n * - Loads from requirements.json\n * - Maps command paths to permission requirements\n * - Used for permission validation and help text\n */\n\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nconst require = createRequire(import.meta.url)\n\nlet _requirements:\n | Readonly<typeof import('../../requirements.json')>\n | undefined\n\nexport function getRequirements() {\n if (_requirements === undefined) {\n _requirements = /*@__PURE__*/ require(\n path.join(constants.rootPath, 'requirements.json'),\n )\n }\n return _requirements!\n}\n\n/**\n * Convert command path to requirements key.\n */\nexport function getRequirementsKey(cmdPath: string): string {\n return cmdPath.replace(/^socket[: ]/, '').replace(/ +/g, ':')\n}\n","/**\n * Socket SDK utilities for Socket CLI.\n * Manages SDK initialization and configuration for API communication.\n *\n * Authentication:\n * - Interactive password prompt for missing tokens\n * - Supports environment variable (SOCKET_CLI_API_TOKEN)\n * - Validates token format and presence\n *\n * Proxy Support:\n * - Automatic proxy agent selection\n * - HTTP/HTTPS proxy configuration\n * - Respects SOCKET_CLI_API_PROXY environment variable\n *\n * SDK Setup:\n * - createSocketSdk: Create configured SDK instance\n * - getDefaultApiToken: Retrieve API token from config/env\n * - getDefaultProxyUrl: Retrieve proxy URL from config/env\n * - getPublicApiToken: Get public API token constant\n * - setupSdk: Initialize Socket SDK with authentication\n *\n * User Agent:\n * - Automatic user agent generation from package.json\n * - Includes CLI version and platform information\n */\n\nimport { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'\n\nimport isInteractive from '@socketregistry/is-interactive/index.cjs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { password } from '@socketsecurity/registry/lib/prompts'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\nimport { isUrl } from '@socketsecurity/registry/lib/url'\nimport { SocketSdk, createUserAgentFromPkgJson } from '@socketsecurity/sdk'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport { debugApiRequest, debugApiResponse } from './debug.mts'\nimport constants, {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n} from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { RequestInfo, ResponseInfo } from '@socketsecurity/sdk'\n\nconst TOKEN_PREFIX = 'sktsec_'\nconst TOKEN_PREFIX_LENGTH = TOKEN_PREFIX.length\nconst TOKEN_VISIBLE_LENGTH = 5\n\n// The Socket API server that should be used for operations.\nexport function getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n constants.ENV.SOCKET_CLI_API_BASE_URL ||\n getConfigValueOrUndef(CONFIG_KEY_API_BASE_URL)\n return isUrl(baseUrl) ? baseUrl : undefined\n}\n\n// The Socket API server that should be used for operations.\nexport function getDefaultProxyUrl(): string | undefined {\n const apiProxy =\n constants.ENV.SOCKET_CLI_API_PROXY ||\n getConfigValueOrUndef(CONFIG_KEY_API_PROXY)\n return isUrl(apiProxy) ? apiProxy : undefined\n}\n\n// This Socket API token should be stored globally for the duration of the CLI execution.\nlet _defaultToken: string | undefined\nexport function getDefaultApiToken(): string | undefined {\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n _defaultToken = undefined\n return _defaultToken\n }\n\n const key =\n constants.ENV.SOCKET_CLI_API_TOKEN ||\n getConfigValueOrUndef(CONFIG_KEY_API_TOKEN) ||\n _defaultToken\n\n _defaultToken = isNonEmptyString(key) ? key : undefined\n return _defaultToken\n}\n\nexport function getPublicApiToken(): string {\n return (\n getDefaultApiToken() ||\n constants.ENV.SOCKET_CLI_API_TOKEN ||\n constants.SOCKET_PUBLIC_API_TOKEN\n )\n}\n\nexport function getVisibleTokenPrefix(): string {\n const apiToken = getDefaultApiToken()\n return apiToken\n ? apiToken.slice(\n TOKEN_PREFIX_LENGTH,\n TOKEN_PREFIX_LENGTH + TOKEN_VISIBLE_LENGTH,\n )\n : ''\n}\n\nexport function hasDefaultApiToken(): boolean {\n return !!getDefaultApiToken()\n}\n\nexport type SetupSdkOptions = {\n apiBaseUrl?: string | undefined\n apiProxy?: string | undefined\n apiToken?: string | undefined\n}\n\nexport async function setupSdk(\n options?: SetupSdkOptions | undefined,\n): Promise<CResult<SocketSdk>> {\n const opts = { __proto__: null, ...options } as SetupSdkOptions\n let { apiToken = getDefaultApiToken() } = opts\n\n if (typeof apiToken !== 'string' && isInteractive()) {\n apiToken = await password({\n message:\n 'Enter your Socket.dev API token (not saved, use socket login to persist)',\n })\n _defaultToken = apiToken\n }\n\n if (!apiToken) {\n return {\n ok: false,\n message: 'Auth Error',\n cause: 'You need to provide an API token. Run `socket login` first.',\n }\n }\n\n let { apiProxy } = opts\n if (!isUrl(apiProxy)) {\n apiProxy = getDefaultProxyUrl()\n }\n\n const { apiBaseUrl = getDefaultApiBaseUrl() } = opts\n\n // Usage of HttpProxyAgent vs. HttpsProxyAgent based on the chart at:\n // https://github.com/delvedor/hpagent?tab=readme-ov-file#usage\n const ProxyAgent = apiBaseUrl?.startsWith('http:')\n ? HttpProxyAgent\n : HttpsProxyAgent\n\n const sdkOptions = {\n ...(apiProxy ? { agent: new ProxyAgent({ proxy: apiProxy }) } : {}),\n ...(apiBaseUrl ? { baseUrl: apiBaseUrl } : {}),\n timeout: constants.ENV.SOCKET_CLI_API_TIMEOUT,\n userAgent: createUserAgentFromPkgJson({\n name: constants.ENV.INLINED_SOCKET_CLI_NAME,\n version: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n homepage: constants.ENV.INLINED_SOCKET_CLI_HOMEPAGE,\n }),\n // Add HTTP request hooks for debugging if SOCKET_CLI_DEBUG is enabled.\n ...(constants.ENV.SOCKET_CLI_DEBUG\n ? {\n hooks: {\n onRequest: (info: RequestInfo) => {\n debugApiRequest(info.method, info.url, info.timeout)\n },\n onResponse: (info: ResponseInfo) => {\n debugApiResponse(\n info.method,\n info.url,\n info.status,\n info.error,\n info.duration,\n info.headers,\n )\n },\n },\n }\n : {}),\n }\n\n if (constants.ENV.SOCKET_CLI_DEBUG) {\n logger.info(\n `[DEBUG] ${new Date().toISOString()} SDK options: ${JSON.stringify(sdkOptions)}`,\n )\n }\n\n const sdk = new SocketSdk(apiToken, sdkOptions)\n\n return {\n ok: true,\n data: sdk,\n }\n}\n","/**\n * API utilities for Socket CLI.\n * Provides consistent API communication with error handling and permissions management.\n *\n * Key Functions:\n * - getDefaultApiBaseUrl: Get configured API endpoint\n * - getErrorMessageForHttpStatusCode: User-friendly HTTP error messages\n * - handleApiCall: Execute Socket SDK API calls with error handling\n * - handleApiCallNoSpinner: Execute API calls without UI spinner\n * - queryApi: Execute raw API queries with text response\n *\n * Error Handling:\n * - Automatic permission requirement logging for 403 errors\n * - Detailed error messages for common HTTP status codes\n * - Integration with debug helpers for API response logging\n *\n * Configuration:\n * - Respects SOCKET_CLI_API_BASE_URL environment variable\n * - Falls back to configured apiBaseUrl or default API_V0_URL\n */\n\nimport { messageWithCauses } from 'pony-cause'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport { debugApiRequest, debugApiResponse } from './debug.mts'\nimport constants, {\n CONFIG_KEY_API_BASE_URL,\n EMPTY_VALUE,\n HTTP_STATUS_BAD_REQUEST,\n HTTP_STATUS_FORBIDDEN,\n HTTP_STATUS_INTERNAL_SERVER_ERROR,\n HTTP_STATUS_NOT_FOUND,\n HTTP_STATUS_UNAUTHORIZED,\n} from '../constants.mts'\nimport { getRequirements, getRequirementsKey } from './requirements.mts'\nimport { getDefaultApiToken } from './sdk.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type {\n SocketSdkErrorResult,\n SocketSdkOperations,\n SocketSdkResult,\n SocketSdkSuccessResult,\n} from '@socketsecurity/sdk'\n\nconst NO_ERROR_MESSAGE = 'No error message returned'\n\nexport type CommandRequirements = {\n permissions?: string[] | undefined\n quota?: number | undefined\n}\n\n/**\n * Get command requirements from requirements.json based on command path.\n */\nfunction getCommandRequirements(\n cmdPath?: string | undefined,\n): CommandRequirements | undefined {\n if (!cmdPath) {\n return undefined\n }\n\n const requirements = getRequirements()\n const key = getRequirementsKey(cmdPath)\n return (requirements.api as any)[key] || undefined\n}\n\n/**\n * Log required permissions for a command when encountering 403 errors.\n */\nfunction logPermissionsFor403(cmdPath?: string | undefined): void {\n const requirements = getCommandRequirements(cmdPath)\n if (!requirements?.permissions?.length) {\n return\n }\n\n logger.error('This command requires the following API permissions:')\n for (const permission of requirements.permissions) {\n logger.error(` - ${permission}`)\n }\n logger.error('Please ensure your API token has the required permissions.')\n}\n\n// The Socket API server that should be used for operations.\nexport function getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n constants.ENV.SOCKET_CLI_API_BASE_URL ||\n getConfigValueOrUndef(CONFIG_KEY_API_BASE_URL)\n if (isNonEmptyString(baseUrl)) {\n return baseUrl\n }\n const API_V0_URL = constants.API_V0_URL\n return API_V0_URL\n}\n\n/**\n * Get user-friendly error message for HTTP status codes.\n */\nexport async function getErrorMessageForHttpStatusCode(code: number) {\n if (code === HTTP_STATUS_BAD_REQUEST) {\n return 'One of the options passed might be incorrect'\n }\n if (code === HTTP_STATUS_FORBIDDEN || code === HTTP_STATUS_UNAUTHORIZED) {\n return 'Your Socket API token may not have the required permissions for this command or you might be trying to access (data from) an organization that is not linked to the API token you are logged in with'\n }\n if (code === HTTP_STATUS_NOT_FOUND) {\n return 'The requested Socket API endpoint was not found (404) or there was no result for the requested parameters. If unexpected, this could be a temporary problem caused by an incident or a bug in the CLI. If the problem persists please let us know.'\n }\n if (code === HTTP_STATUS_INTERNAL_SERVER_ERROR) {\n return 'There was an unknown server side problem with your request. This ought to be temporary. Please let us know if this problem persists.'\n }\n return `Server responded with status code ${code}`\n}\n\nexport type HandleApiCallOptions = {\n description?: string | undefined\n spinner?: Spinner | undefined\n commandPath?: string | undefined\n}\n\nexport type ApiCallResult<T extends SocketSdkOperations> = CResult<\n SocketSdkSuccessResult<T>['data']\n>\n\n/**\n * Handle Socket SDK API calls with error handling and permission logging.\n */\nexport async function handleApiCall<T extends SocketSdkOperations>(\n value: Promise<SocketSdkResult<T>>,\n options?: HandleApiCallOptions | undefined,\n): Promise<ApiCallResult<T>> {\n const { commandPath, description, spinner } = {\n __proto__: null,\n ...options,\n } as HandleApiCallOptions\n\n if (description) {\n spinner?.start(`Requesting ${description} from API...`)\n } else {\n spinner?.start()\n }\n\n let sdkResult: SocketSdkResult<T>\n try {\n sdkResult = await value\n spinner?.stop()\n if (description) {\n const message = `Received Socket API response (after requesting ${description}).`\n if (sdkResult.success) {\n logger.success(message)\n } else {\n logger.info(message)\n }\n }\n } catch (e) {\n spinner?.stop()\n const socketSdkErrorResult: ApiCallResult<T> = {\n ok: false,\n message: 'Socket API error',\n cause: messageWithCauses(e as Error),\n }\n if (description) {\n logger.fail(`An error was thrown while requesting ${description}`)\n }\n debugDir('inspect', { socketSdkErrorResult })\n return socketSdkErrorResult\n }\n\n // Note: TS can't narrow down the type of result due to generics.\n if (sdkResult.success === false) {\n const endpoint = description || 'Socket API'\n debugApiResponse('API', endpoint, sdkResult.status as number)\n debugDir('inspect', { sdkResult })\n\n const errCResult = sdkResult as SocketSdkErrorResult<T>\n const errStr = errCResult.error ? String(errCResult.error).trim() : ''\n const message = errStr || NO_ERROR_MESSAGE\n const reason = errCResult.cause || NO_ERROR_MESSAGE\n const cause =\n reason && message !== reason ? `${message} (reason: ${reason})` : message\n const socketSdkErrorResult: ApiCallResult<T> = {\n ok: false,\n message: 'Socket API error',\n cause,\n data: {\n code: sdkResult.status,\n },\n }\n\n // Log required permissions for 403 errors when in a command context.\n if (commandPath && sdkResult.status === 403) {\n logPermissionsFor403(commandPath)\n }\n\n return socketSdkErrorResult\n }\n const socketSdkSuccessResult: ApiCallResult<T> = {\n ok: true,\n data: (sdkResult as SocketSdkSuccessResult<T>).data,\n }\n return socketSdkSuccessResult\n}\n\nexport async function handleApiCallNoSpinner<T extends SocketSdkOperations>(\n value: Promise<SocketSdkResult<T>>,\n description: string,\n): Promise<CResult<SocketSdkSuccessResult<T>['data']>> {\n let sdkResult: SocketSdkResult<T>\n try {\n sdkResult = await value\n } catch (e) {\n debugFn('error', `API request failed: ${description}`)\n debugDir('error', e)\n\n const errStr = e ? String(e).trim() : ''\n const message = 'Socket API error'\n const rawCause = errStr || NO_ERROR_MESSAGE\n const cause = message !== rawCause ? rawCause : ''\n\n return {\n ok: false,\n message,\n ...(cause ? { cause } : {}),\n }\n }\n\n // Note: TS can't narrow down the type of result due to generics\n if (sdkResult.success === false) {\n debugFn('error', `fail: ${description} bad response`)\n debugDir('inspect', { sdkResult })\n\n const sdkErrorResult = sdkResult as SocketSdkErrorResult<T>\n const errStr = sdkErrorResult.error\n ? String(sdkErrorResult.error).trim()\n : ''\n const message = errStr || NO_ERROR_MESSAGE\n const reason = sdkErrorResult.cause || NO_ERROR_MESSAGE\n const cause =\n reason && message !== reason ? `${message} (reason: ${reason})` : message\n\n return {\n ok: false,\n message: 'Socket API error',\n cause,\n data: {\n code: sdkResult.status,\n },\n }\n } else {\n const sdkSuccessResult = sdkResult as SocketSdkSuccessResult<T>\n return {\n ok: true,\n data: sdkSuccessResult.data,\n }\n }\n}\n\nasync function queryApi(path: string, apiToken: string) {\n const baseUrl = getDefaultApiBaseUrl()\n if (!baseUrl) {\n throw new Error('Socket API base URL is not configured.')\n }\n\n const url = `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`\n const result = await fetch(url, {\n method: 'GET',\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:`)}`,\n },\n })\n return result\n}\n\n/**\n * Query Socket API endpoint and return text response with error handling.\n */\nexport async function queryApiSafeText(\n path: string,\n description?: string | undefined,\n commandPath?: string | undefined,\n): Promise<CResult<string>> {\n const apiToken = getDefaultApiToken()\n if (!apiToken) {\n return {\n ok: false,\n message: 'Authentication Error',\n cause:\n 'User must be authenticated to run this command. Run `socket login` and enter your Socket API token.',\n }\n }\n\n const { spinner } = constants\n\n if (description) {\n spinner.start(`Requesting ${description} from API...`)\n debugApiRequest('GET', path, constants.ENV.SOCKET_CLI_API_TIMEOUT)\n }\n\n let result\n const startTime = Date.now()\n try {\n result = await queryApi(path, apiToken)\n const duration = Date.now() - startTime\n debugApiResponse(\n 'GET',\n path,\n result.status,\n undefined,\n duration,\n Object.fromEntries(result.headers.entries()),\n )\n if (description) {\n spinner.successAndStop(\n `Received Socket API response (after requesting ${description}).`,\n )\n }\n } catch (e) {\n const duration = Date.now() - startTime\n if (description) {\n spinner.failAndStop(\n `An error was thrown while requesting ${description}.`,\n )\n debugApiResponse('GET', path, undefined, e, duration)\n }\n\n debugFn('error', 'Query API request failed')\n debugDir('error', e)\n\n const errStr = e ? String(e).trim() : ''\n const message = 'API request failed'\n const rawCause = errStr || NO_ERROR_MESSAGE\n const cause = message !== rawCause ? rawCause : ''\n\n return {\n ok: false,\n message,\n ...(cause ? { cause } : {}),\n }\n }\n\n if (!result.ok) {\n const { status } = result\n // Log required permissions for 403 errors when in a command context.\n if (commandPath && status === 403) {\n logPermissionsFor403(commandPath)\n }\n return {\n ok: false,\n message: 'Socket API error',\n cause: `${result.statusText} (reason: ${await getErrorMessageForHttpStatusCode(status)})`,\n data: {\n code: status,\n },\n }\n }\n\n try {\n const data = await result.text()\n return {\n ok: true,\n data,\n }\n } catch (e) {\n debugFn('error', 'Failed to read API response text')\n debugDir('error', e)\n\n return {\n ok: false,\n message: 'API request failed',\n cause: 'Unexpected error reading response text',\n }\n }\n}\n\n/**\n * Query Socket API endpoint and return parsed JSON response.\n */\nexport async function queryApiSafeJson<T>(\n path: string,\n description = '',\n): Promise<CResult<T>> {\n const result = await queryApiSafeText(path, description)\n\n if (!result.ok) {\n return result\n }\n\n try {\n return {\n ok: true,\n data: JSON.parse(result.data) as T,\n }\n } catch (e) {\n return {\n ok: false,\n message: 'Server returned invalid JSON',\n cause: `Please report this. JSON.parse threw an error over the following response: \\`${(result.data?.slice?.(0, 100) || EMPTY_VALUE).trim() + (result.data?.length > 100 ? '...' : '')}\\``,\n }\n }\n}\n\nexport type SendApiRequestOptions = {\n method: 'POST' | 'PUT'\n body?: unknown | undefined\n description?: string | undefined\n commandPath?: string | undefined\n}\n\n/**\n * Send POST/PUT request to Socket API with JSON response handling.\n */\nexport async function sendApiRequest<T>(\n path: string,\n options?: SendApiRequestOptions | undefined,\n): Promise<CResult<T>> {\n const apiToken = getDefaultApiToken()\n if (!apiToken) {\n return {\n ok: false,\n message: 'Authentication Error',\n cause:\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your Socket API token.',\n }\n }\n\n const baseUrl = getDefaultApiBaseUrl()\n if (!baseUrl) {\n return {\n ok: false,\n message: 'Configuration Error',\n cause:\n 'Socket API endpoint is not configured. Please check your environment configuration.',\n }\n }\n\n const { body, commandPath, description, method } = {\n __proto__: null,\n ...options,\n } as SendApiRequestOptions\n const { spinner } = constants\n\n if (description) {\n spinner.start(`Requesting ${description} from API...`)\n }\n\n let result\n try {\n const fetchOptions = {\n method,\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:`)}`,\n 'Content-Type': 'application/json',\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n }\n\n result = await fetch(\n `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`,\n fetchOptions,\n )\n if (description) {\n spinner.successAndStop(\n `Received Socket API response (after requesting ${description}).`,\n )\n }\n } catch (e) {\n if (description) {\n spinner.failAndStop(\n `An error was thrown while requesting ${description}.`,\n )\n }\n\n debugFn('error', `API ${method} request failed`)\n debugDir('error', e)\n\n const errStr = e ? String(e).trim() : ''\n const message = 'API request failed'\n const rawCause = errStr || NO_ERROR_MESSAGE\n const cause = message !== rawCause ? rawCause : ''\n\n return {\n ok: false,\n message,\n ...(cause ? { cause } : {}),\n }\n }\n\n if (!result.ok) {\n const { status } = result\n // Log required permissions for 403 errors when in a command context.\n if (commandPath && status === 403) {\n logPermissionsFor403(commandPath)\n }\n return {\n ok: false,\n message: 'Socket API error',\n cause: `${result.statusText} (reason: ${await getErrorMessageForHttpStatusCode(status)})`,\n data: {\n code: status,\n },\n }\n }\n\n try {\n const data = await result.json()\n return {\n ok: true,\n data: data as T,\n }\n } catch (e) {\n debugFn('error', 'Failed to parse API response JSON')\n debugDir('error', e)\n return {\n ok: false,\n message: 'API request failed',\n cause: 'Unexpected error parsing response JSON',\n }\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nexport function failMsgWithBadge(\n badge: string,\n message: string | undefined,\n): string {\n const prefix = colors.bgRedBright(\n colors.bold(colors.white(` ${badge}${message ? ': ' : ''}`)),\n )\n const postfix = message ? ` ${colors.bold(message)}` : ''\n return `${prefix}${postfix}`\n}\n","/**\n * Markdown utilities for Socket CLI.\n * Generates formatted markdown output for reports and documentation.\n *\n * Key Functions:\n * - mdTableStringNumber: Create markdown table with string keys and number values\n *\n * Table Features:\n * - Auto-sizing columns based on content\n * - Proper alignment for headers and data\n * - Clean markdown-compliant formatting\n *\n * Usage:\n * - Analytics reports\n * - Scan result tables\n * - Statistical summaries\n */\n\nexport function mdTableStringNumber(\n title1: string,\n title2: string,\n obj: Record<string, number | string>,\n): string {\n // | Date | Counts |\n // | ----------- | ------ |\n // | Header | 201464 |\n // | Paragraph | 18 |\n let mw1 = title1.length\n let mw2 = title2.length\n for (const { 0: key, 1: value } of Object.entries(obj)) {\n mw1 = Math.max(mw1, key.length)\n mw2 = Math.max(mw2, String(value ?? '').length)\n }\n\n const lines = []\n lines.push(`| ${title1.padEnd(mw1, ' ')} | ${title2.padEnd(mw2)} |`)\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n for (const { 0: key, 1: value } of Object.entries(obj)) {\n lines.push(\n `| ${key.padEnd(mw1, ' ')} | ${String(value ?? '').padStart(mw2, ' ')} |`,\n )\n }\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n\n return lines.join('\\n')\n}\n\nexport function mdTable<T extends Array<Record<string, string>>>(\n logs: T,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: Array<string & keyof T[number]>,\n titles: string[] = cols,\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const log of logs) {\n for (let i = 0, { length } = cols; i < length; i += 1) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n cws[i] = Math.max(\n cws[i] ?? 0,\n String(val).length,\n (titles[i] || '').length,\n )\n }\n }\n\n let div = '|'\n for (const cw of cws) {\n div += ' ' + '-'.repeat(cw) + ' |'\n }\n\n let header = '|'\n for (let i = 0, { length } = titles; i < length; i += 1) {\n header += ' ' + String(titles[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n\n let body = ''\n for (const log of logs) {\n body += '|'\n for (let i = 0, { length } = cols; i < length; i += 1) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => s.trim()).join('\\n')\n}\n\nexport function mdTableOfPairs(\n arr: Array<[string, string]>,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: string[],\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const [key, val] of arr) {\n cws[0] = Math.max(cws[0] ?? 0, String(key).length)\n cws[1] = Math.max(cws[1] ?? 0, String(val ?? '').length)\n }\n\n let div = '|'\n for (const cw of cws) {\n div += ' ' + '-'.repeat(cw) + ' |'\n }\n\n let header = '|'\n for (let i = 0, { length } = cols; i < length; i += 1) {\n header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n\n let body = ''\n for (const [key, val] of arr) {\n body += '|'\n body += ' ' + String(key).padEnd(cws[0] ?? 0, ' ') + ' |'\n body += ' ' + String(val ?? '').padEnd(cws[1] ?? 0, ' ') + ' |'\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => s.trim()).join('\\n')\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isObject } from '@socketsecurity/registry/lib/objects'\n\nimport type { CResult } from '../types.mts'\n\n// Serialize the final result object before printing it\n// All commands that support the --json flag should call this before printing\nexport function serializeResultJson(data: CResult<unknown>): string {\n if (!isObject(data)) {\n process.exitCode = 1\n\n debugFn('inspect', { data })\n\n // We should not allow the JSON value to be \"null\", or a boolean/number/string,\n // even if they are valid \"json\".\n return `${JSON.stringify({\n ok: false,\n message: 'Unable to serialize JSON',\n cause:\n 'There was a problem converting the data set to JSON. The JSON was not an object. Please try again without --json',\n }).trim()}\\n`\n }\n\n try {\n return `${JSON.stringify(data, null, 2).trim()}\\n`\n } catch (e) {\n process.exitCode = 1\n\n const message =\n 'There was a problem converting the data set to JSON. Please try again without --json'\n\n logger.fail(message)\n debugFn('error', 'JSON serialization failed')\n debugDir('error', e)\n\n // This could be caused by circular references, which is an \"us\" problem.\n return `${JSON.stringify({\n ok: false,\n message: 'Unable to serialize JSON',\n cause: message,\n }).trim()}\\n`\n }\n}\n","import path from 'node:path'\n\nimport terminalLink from 'terminal-link'\n\nimport { SOCKET_WEBSITE_URL } from '../constants.mts'\n\n/**\n * Creates a terminal link to a local file.\n * @param filePath The file path to link to\n * @param text Optional display text (defaults to the file path itself)\n * @returns A terminal link to the file\n */\nexport function fileLink(filePath: string, text?: string | undefined): string {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(filePath)\n return terminalLink(text ?? filePath, `file://${absolutePath}`)\n}\n\n/**\n * Creates a terminal link to an email address.\n * @param email The email address\n * @param text Optional display text (defaults to the email address itself)\n * @returns A terminal link to compose an email\n */\nexport function mailtoLink(email: string, text?: string | undefined): string {\n return terminalLink(text ?? email, `mailto:${email}`)\n}\n\n/**\n * Creates a terminal link to the Socket.dev dashboard.\n * @param path The path within the dashboard (e.g., '/org/YOURORG/alerts')\n * @param text Optional display text\n * @returns A terminal link to the Socket.dev dashboard URL\n */\nexport function socketDashboardLink(\n dashPath: string,\n text?: string | undefined,\n): string {\n const url = `https://socket.dev/dashboard${dashPath.startsWith('/') ? dashPath : `/${dashPath}`}`\n return terminalLink(text ?? url, url)\n}\n\n/**\n * Creates a terminal link to the Socket.dev website.\n * @param text Display text for the link (defaults to 'Socket.dev')\n * @param urlPath Optional path to append to the base URL (e.g., '/pricing')\n * @returns A terminal link to Socket.dev\n */\nexport function socketDevLink(\n text?: string | undefined,\n urlPath?: string | undefined,\n): string {\n return terminalLink(\n text ?? 'Socket.dev',\n `${SOCKET_WEBSITE_URL}${urlPath || ''}`,\n )\n}\n\n/**\n * Creates a terminal link to Socket.dev documentation.\n * @param docPath The documentation path (e.g., '/docs/api-keys')\n * @param text Optional display text\n * @returns A terminal link to the Socket.dev documentation\n */\nexport function socketDocsLink(\n docPath: string,\n text?: string | undefined,\n): string {\n const url = `https://docs.socket.dev${docPath.startsWith('/') ? docPath : `/${docPath}`}`\n return terminalLink(text ?? url, url)\n}\n\n/**\n * Creates a terminal link to Socket.dev package page.\n * @param ecosystem The package ecosystem (e.g., 'npm')\n * @param packageName The package name\n * @param version Optional package version or path (e.g., 'files/1.0.0/CHANGELOG.md')\n * @param text Optional display text\n * @returns A terminal link to the Socket.dev package page\n */\nexport function socketPackageLink(\n ecosystem: string,\n packageName: string,\n version?: string | undefined,\n text?: string | undefined,\n): string {\n let url: string\n if (version) {\n // Check if version contains a path like 'files/1.0.0/CHANGELOG.md'.\n if (version.includes('/')) {\n url = `https://socket.dev/${ecosystem}/package/${packageName}/${version}`\n } else {\n url = `https://socket.dev/${ecosystem}/package/${packageName}/overview/${version}`\n }\n } else {\n url = `https://socket.dev/${ecosystem}/package/${packageName}`\n }\n return terminalLink(text ?? url, url)\n}\n\n/**\n * Creates a terminal link to a web URL.\n * @param url The web URL to link to\n * @param text Optional display text (defaults to the URL itself)\n * @returns A terminal link to the URL\n */\nexport function webLink(url: string, text?: string | undefined): string {\n return terminalLink(text ?? url, url)\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { LOG_SYMBOLS, logger } from '@socketsecurity/registry/lib/logger'\nimport { stripAnsi } from '@socketsecurity/registry/lib/strings'\n\nimport { failMsgWithBadge } from './fail-msg-with-badge.mts'\nimport { serializeResultJson } from './serialize-result-json.mts'\n\nimport type { OutputKind } from '../types.mts'\n\nexport function checkCommandInput(\n outputKind: OutputKind,\n ...checks: Array<{\n fail: string\n message: string\n test: boolean\n nook?: boolean | undefined\n pass?: string | undefined\n }>\n): boolean {\n if (checks.every(d => d.test)) {\n return true\n }\n\n const msg = ['Please review the input requirements and try again', '']\n for (const d of checks) {\n // If nook, then ignore when test is ok\n if (d.nook && d.test) {\n continue\n }\n const lines = d.message.split('\\n')\n const { length: lineCount } = lines\n if (!lineCount) {\n continue\n }\n // If the message has newlines then format the first line with the input\n // expectation and the rest indented below it.\n const logSymbol = d.test ? LOG_SYMBOLS.success : LOG_SYMBOLS.fail\n const reason = d.test ? d.pass : d.fail\n let listItem = ` ${logSymbol} ${lines[0]}`\n if (reason) {\n const styledReason = d.test ? colors.green(reason) : colors.red(reason)\n listItem += ` (${styledReason})`\n }\n msg.push(listItem)\n if (lineCount > 1) {\n msg.push(...lines.slice(1).map(str => ` ${str}`))\n }\n }\n\n // Use exit status of 2 to indicate incorrect usage, generally invalid\n // options or missing arguments.\n // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html\n process.exitCode = 2\n\n if (outputKind === 'json') {\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'Input error',\n data: stripAnsi(msg.join('\\n')),\n }),\n )\n } else {\n logger.fail(failMsgWithBadge('Input error', msg.join('\\n')))\n }\n\n return false\n}\n","/**\n * Output format detection utilities for Socket CLI.\n * Determines output format based on command flags.\n *\n * Key Functions:\n * - getOutputKind: Determine output format from flags\n *\n * Supported Formats:\n * - JSON: Machine-readable JSON output\n * - Markdown: Formatted markdown for reports\n * - Text: Plain text for terminal display\n *\n * Usage:\n * - Processes --json and --markdown flags\n * - Returns appropriate output format constant\n * - Defaults to text format for terminal display\n */\n\nimport { OUTPUT_JSON, OUTPUT_MARKDOWN, OUTPUT_TEXT } from '../constants.mts'\n\nimport type { OutputKind } from '../types.mts'\n\nexport function getOutputKind(json: unknown, markdown: unknown): OutputKind {\n if (json) {\n return OUTPUT_JSON\n }\n if (markdown) {\n return OUTPUT_MARKDOWN\n }\n return OUTPUT_TEXT\n}\n","/**\n * String manipulation utilities for Socket CLI.\n * Provides common string transformations and formatting.\n *\n * Key Functions:\n * - camelToKebab: Convert camelCase to kebab-case\n *\n * Usage:\n * - Command name transformations\n * - Flag name conversions\n * - Consistent string formatting\n */\n\nexport function camelToKebab(str: string): string {\n return str === '' ? '' : str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()\n}\n","/**\n * Output formatting utilities for Socket CLI.\n * Provides consistent formatting for help text and command output.\n *\n * Key Functions:\n * - getFlagApiRequirementsOutput: Format API requirements for flags\n * - getHelpListOutput: Format help text lists with descriptions\n * - getFlagsHelpOutput: Generate formatted help for command flags\n *\n * Formatting Features:\n * - Automatic indentation and alignment\n * - Flag description formatting\n * - Requirements and permissions display\n * - Hidden flag filtering\n *\n * Usage:\n * - Used by command help systems\n * - Provides consistent terminal output formatting\n * - Handles kebab-case conversion for flags\n */\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { isObject } from '@socketsecurity/registry/lib/objects'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { indentString } from '@socketsecurity/registry/lib/strings'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { getRequirements, getRequirementsKey } from './requirements.mts'\nimport { camelToKebab } from './strings.mts'\n\nimport type { MeowFlags } from '../flags.mts'\n\ntype ApiRequirementsOptions = {\n indent?: number | undefined\n}\n\ntype HelpListOptions = {\n indent?: number | undefined\n keyPrefix?: string | undefined\n padName?: number | undefined\n}\n\ntype ListDescription =\n | { description: string }\n | { description: string; hidden: boolean }\n\nexport function getFlagApiRequirementsOutput(\n cmdPath: string,\n options?: ApiRequirementsOptions | undefined,\n): string {\n const { indent = 6 } = {\n __proto__: null,\n ...options,\n } as ApiRequirementsOptions\n const key = getRequirementsKey(cmdPath)\n const requirements = getRequirements()\n const data = (requirements.api as any)[key]\n let result = ''\n if (data) {\n const quota: number = data?.quota\n const rawPerms: string[] = data?.permissions\n const padding = ''.padEnd(indent)\n const lines = []\n if (Number.isFinite(quota) && quota > 0) {\n lines.push(`${padding}- Quota: ${quota} ${pluralize('unit', quota)}`)\n }\n if (Array.isArray(rawPerms) && rawPerms.length) {\n const perms = rawPerms.slice().sort(naturalCompare)\n lines.push(`${padding}- Permissions: ${joinAnd(perms)}`)\n }\n result += lines.join('\\n')\n }\n return result.trim() || '(none)'\n}\n\nexport function getFlagListOutput(\n list: MeowFlags,\n options?: HelpListOptions | undefined,\n): string {\n const { keyPrefix = '--' } = {\n __proto__: null,\n ...options,\n } as HelpListOptions\n return getHelpListOutput(\n {\n ...list,\n },\n { ...options, keyPrefix },\n )\n}\n\nexport function getHelpListOutput(\n list: Record<string, ListDescription>,\n options?: HelpListOptions | undefined,\n): string {\n const {\n indent = 6,\n keyPrefix = '',\n padName = 20,\n } = {\n __proto__: null,\n ...options,\n } as HelpListOptions\n let result = ''\n const names = Object.keys(list).sort(naturalCompare)\n for (const name of names) {\n const entry = list[name]\n const entryIsObj = isObject(entry)\n if (entryIsObj && 'hidden' in entry && entry?.hidden) {\n continue\n }\n const printedName = `${keyPrefix}${camelToKebab(name)}`\n const preDescription = `${''.padEnd(indent)}${printedName.padEnd(Math.max(printedName.length + 2, padName))}`\n\n result += preDescription\n\n const description = entryIsObj ? entry.description : String(entry)\n if (description) {\n result += indentString(description, preDescription.length).trimStart()\n }\n result += '\\n'\n }\n return result.trim() || '(none)'\n}\n","/**\n * Path tildification utilities for Socket CLI.\n * Abbreviates home directory paths with tilde notation.\n *\n * Key Functions:\n * - tildify: Replace home directory with ~ in paths\n *\n * Usage:\n * - Shortens absolute paths for display\n * - Converts /Users/name/... to ~/...\n * - Common Unix convention for home directory\n */\n\nimport path from 'node:path'\n\nimport { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport constants from '../constants.mts'\n\nexport function tildify(cwd: string) {\n return cwd.replace(\n new RegExp(`^${escapeRegExp(constants.homePath)}(?:${path.sep}|$)`, 'i'),\n '~/',\n )\n}\n","import meow from 'meow'\nimport terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport {\n getOwn,\n hasOwn,\n toSortedObject,\n} from '@socketsecurity/registry/lib/objects'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { getCliSpinners } from '@socketsecurity/registry/lib/spinner'\nimport {\n indentString,\n trimNewlines,\n} from '@socketsecurity/registry/lib/strings'\n\nimport {\n getConfigValueOrUndef,\n isConfigFromFlag,\n overrideCachedConfig,\n overrideConfigApiToken,\n} from './config.mts'\nimport { isDebug } from './debug.mts'\nimport { getFlagListOutput, getHelpListOutput } from './output-formatting.mts'\nimport { socketPackageLink } from './terminal-link.mts'\nimport constants, {\n API_V0_URL,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_DEFAULT_ORG,\n FLAG_HELP_FULL,\n FLAG_JSON,\n FLAG_MARKDOWN,\n FLAG_ORG,\n NPM,\n NPX,\n // PNPM,\n // YARN,\n} from '../constants.mts'\nimport { commonFlags } from '../flags.mts'\nimport { getVisibleTokenPrefix } from './sdk.mts'\nimport { tildify } from './tildify.mts'\n\nimport type { MeowFlag, MeowFlags } from '../flags.mts'\nimport type { Options, Result } from 'meow'\n\nexport interface CliAlias {\n description: string\n argv: readonly string[]\n hidden?: boolean | undefined\n}\n\nexport type CliAliases = Record<string, CliAlias>\n\nexport type CliSubcommandRun = (\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: { parentName: string; rawArgv?: readonly string[] },\n) => Promise<void> | void\n\nexport interface CliSubcommand {\n description: string\n hidden?: boolean | undefined\n run: CliSubcommandRun\n}\n\n// Property names are picked such that the name is at the top when the props\n// get ordered by alphabet while flags is near the bottom and the help text\n// at the bottom, because they tend ot occupy the most lines of code.\nexport interface CliCommandConfig {\n commandName: string\n description: string\n hidden: boolean\n flags: MeowFlags\n help: (command: string, config: CliCommandConfig) => string\n}\n\nexport interface CliCommandContext {\n parentName: string\n rawArgv?: string[] | readonly string[]\n}\n\nexport interface MeowConfig {\n name: string\n argv: string[] | readonly string[]\n importMeta: ImportMeta\n subcommands: Record<string, CliSubcommand>\n}\n\nexport interface MeowOptions extends Omit<Options<any>, 'argv' | 'importMeta'> {\n aliases?: CliAliases | undefined\n // When no sub-command is given, default to this sub-command.\n defaultSub?: string | undefined\n}\n\nconst HELP_INDENT = 2\n\nconst HELP_PAD_NAME = 28\n\n/**\n * Format a command description for help output.\n */\nfunction description(command: CliSubcommand | undefined): string {\n const description = command?.description\n const str =\n typeof description === 'string' ? description : String(description)\n return indentString(str, HELP_PAD_NAME).trimStart()\n}\n\n/**\n * Find the best matching command name for a typo.\n */\nfunction findBestCommandMatch(\n input: string,\n subcommands: Record<string, unknown>,\n aliases: Record<string, unknown>,\n): string | null {\n let bestMatch = null\n let bestScore = Infinity\n const allCommands = [...Object.keys(subcommands), ...Object.keys(aliases)]\n for (const command of allCommands) {\n const distance = levenshteinDistance(\n input.toLowerCase(),\n command.toLowerCase(),\n )\n const maxLength = Math.max(input.length, command.length)\n // Only suggest if the similarity is reasonable (more than 50% similar).\n if (distance < maxLength * 0.5 && distance < bestScore) {\n bestScore = distance\n bestMatch = command\n }\n }\n return bestMatch\n}\n\n/**\n * Determine the origin of the API token.\n */\nfunction getTokenOrigin(): string {\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n return ''\n }\n if (constants.ENV.SOCKET_CLI_API_TOKEN) {\n return '(env)'\n }\n const configToken = getConfigValueOrUndef(CONFIG_KEY_API_TOKEN)\n if (configToken) {\n return isConfigFromFlag() ? '(--config flag)' : '(config)'\n }\n return ''\n}\n\n/**\n * Generate the ASCII banner header for Socket CLI commands.\n */\nfunction getAsciiHeader(\n command: string,\n orgFlag: string | undefined,\n compactMode: boolean = false,\n) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n const { REDACTED } = constants\n const redacting = constants.ENV.VITEST\n\n // Version display: show hash in debug mode, otherwise show semantic version.\n const fullVersion = constants.ENV.INLINED_SOCKET_CLI_VERSION\n const versionHash = constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH\n const cliVersion = redacting\n ? REDACTED\n : isDebug()\n ? versionHash\n : `v${fullVersion}`\n\n const nodeVersion = redacting ? REDACTED : process.version\n const showNodeVersion = !redacting && isDebug()\n const defaultOrg = getConfigValueOrUndef(CONFIG_KEY_DEFAULT_ORG)\n const configFromFlagDot = isConfigFromFlag() ? '*' : '.'\n\n // Token display with origin indicator.\n const tokenPrefix = getVisibleTokenPrefix()\n const tokenOrigin = redacting ? '' : getTokenOrigin()\n const noApiToken = constants.ENV.SOCKET_CLI_NO_API_TOKEN\n const shownToken = redacting\n ? REDACTED\n : noApiToken\n ? colors.red('(disabled)')\n : tokenPrefix\n ? `${colors.green(tokenPrefix)}***${tokenOrigin ? ` ${tokenOrigin}` : ''}`\n : colors.yellow('(not set)')\n\n const relCwd = redacting ? REDACTED : normalizePath(tildify(process.cwd()))\n\n // Consolidated org display format.\n const orgPart = redacting\n ? `org: ${REDACTED}`\n : orgFlag\n ? `org: ${colors.cyan(orgFlag)} (${FLAG_ORG} flag)`\n : defaultOrg && defaultOrg !== 'null'\n ? `org: ${colors.cyan(defaultOrg)} (config)`\n : colors.yellow('org: (not set)')\n\n // Compact mode for CI/automation.\n if (compactMode) {\n const compactToken = noApiToken\n ? '(disabled)'\n : tokenPrefix\n ? `${tokenPrefix}***${tokenOrigin ? ` ${tokenOrigin}` : ''}`\n : '(not set)'\n const compactOrg =\n orgFlag ||\n (defaultOrg && defaultOrg !== 'null' ? defaultOrg : '(not set)')\n return `CLI: ${cliVersion} | cmd: ${command} | org: ${compactOrg} | token: ${compactToken}`\n }\n\n // Note: We could draw these with ascii box art instead but I worry about\n // portability and paste-ability. \"simple\" ascii chars just work.\n const body = `\n _____ _ _ /---------------\n | __|___ ___| |_ ___| |_ | CLI: ${cliVersion}\n |__ | ${configFromFlagDot} | _| '_| -_| _| | ${showNodeVersion ? `Node: ${nodeVersion}, ` : ''}token: ${shownToken}, ${orgPart}\n |_____|___|___|_,_|___|_|.dev | Command: \\`${command}\\`, cwd: ${relCwd}\n `.trim()\n // Note: logger will auto-append a newline.\n return ` ${body}`\n}\n\n/**\n * Calculate Levenshtein distance between two strings for fuzzy matching.\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const matrix = Array.from({ length: a.length + 1 }, () =>\n Array(b.length + 1).fill(0),\n )\n for (let i = 0; i <= a.length; i++) {\n matrix[i]![0] = i\n }\n for (let j = 0; j <= b.length; j++) {\n matrix[0]![j] = j\n }\n for (let i = 1; i <= a.length; i++) {\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n matrix[i]![j] = Math.min(\n // Deletion.\n matrix[i - 1]![j]! + 1,\n // Insertion.\n matrix[i]![j - 1]! + 1,\n // Substitution.\n matrix[i - 1]![j - 1]! + cost,\n )\n }\n }\n return matrix[a.length]![b.length]!\n}\n\n/**\n * Determine if the banner should be suppressed based on output flags.\n */\nfunction shouldSuppressBanner(flags: Record<string, unknown>): boolean {\n return Boolean(\n flags['json'] || flags['markdown'] || flags['banner'] === false,\n )\n}\n\n/**\n * Emit the Socket CLI banner to stderr for branding and debugging.\n */\nexport function emitBanner(\n name: string,\n orgFlag: string | undefined,\n compactMode: boolean = false,\n) {\n // Print a banner at the top of each command.\n // This helps with brand recognition and marketing.\n // It also helps with debugging since it contains version and command details.\n // Note: print over stderr to preserve stdout for flags like --json and\n // --markdown. If we don't do this, you can't use --json in particular\n // and pipe the result to other tools. By emitting the banner over stderr\n // you can do something like `socket scan view xyz | jq | process`.\n // The spinner also emits over stderr for example.\n logger.error(getAsciiHeader(name, orgFlag, compactMode))\n}\n\n// For debugging. Whenever you call meowOrExit it will store the command here\n// This module exports a getter that returns the current value.\nlet lastSeenCommand = ''\n\n/**\n * Get the last command that was processed by meowOrExit (for debugging).\n */\nexport function getLastSeenCommand(): string {\n return lastSeenCommand\n}\n\n/**\n * Main function for handling CLI with subcommands using meow.\n * @param config Configuration object with name, argv, importMeta, and subcommands.\n * @param options Optional settings like aliases and defaultSub.\n * @example\n * meowWithSubcommands(\n * { name, argv, importMeta, subcommands },\n * { aliases, defaultSub }\n * )\n */\nexport async function meowWithSubcommands(\n config: MeowConfig,\n options?: MeowOptions | undefined,\n): Promise<void> {\n const { argv, importMeta, name, subcommands } = {\n __proto__: null,\n ...config,\n } as MeowConfig\n const {\n aliases = {},\n defaultSub,\n ...additionalOptions\n } = { __proto__: null, ...options } as MeowOptions\n const flags: MeowFlags = {\n ...commonFlags,\n version: {\n type: 'boolean',\n hidden: true,\n description: 'Print the app version',\n },\n ...getOwn(additionalOptions, 'flags'),\n }\n\n const [commandOrAliasName_, ...rawCommandArgv] = argv\n let commandOrAliasName = commandOrAliasName_\n if (!commandOrAliasName && defaultSub) {\n commandOrAliasName = defaultSub\n }\n\n // No further args or first arg is a flag (shrug).\n const isRootCommand =\n name === 'socket' &&\n (!commandOrAliasName || commandOrAliasName?.startsWith('-'))\n\n // Try to support `socket <purl>` as a shorthand for `socket package score <purl>`.\n if (!isRootCommand) {\n if (commandOrAliasName?.startsWith('pkg:')) {\n logger.info('Invoking `socket package score`.')\n return await meowWithSubcommands(\n { name, argv: ['package', 'deep', ...argv], importMeta, subcommands },\n options,\n )\n }\n // Support `socket npm/lodash` or whatever as a shorthand, too.\n // Accept any ecosystem and let the remote sort it out.\n if (/^[a-z]+\\//.test(commandOrAliasName || '')) {\n logger.info('Invoking `socket package score`.')\n return await meowWithSubcommands(\n {\n name,\n argv: [\n 'package',\n 'deep',\n `pkg:${commandOrAliasName}`,\n ...rawCommandArgv,\n ],\n importMeta,\n subcommands,\n },\n options,\n )\n }\n }\n\n if (isRootCommand) {\n const hiddenDebugFlag = !isDebug()\n\n flags['compactHeader'] = {\n ...flags['compactHeader'],\n hidden: false,\n } as MeowFlag\n\n flags['config'] = {\n ...flags['config'],\n hidden: false,\n } as MeowFlag\n\n flags['dryRun'] = {\n ...flags['dryRun'],\n hidden: false,\n } as MeowFlag\n\n flags['help'] = {\n ...flags['help'],\n hidden: false,\n } as MeowFlag\n\n flags['helpFull'] = {\n ...flags['helpFull'],\n hidden: false,\n } as MeowFlag\n\n flags['maxOldSpaceSize'] = {\n ...flags['maxOldSpaceSize'],\n hidden: hiddenDebugFlag,\n } as MeowFlag\n\n flags['maxSemiSpaceSize'] = {\n ...flags['maxSemiSpaceSize'],\n hidden: hiddenDebugFlag,\n } as MeowFlag\n\n flags['version'] = {\n ...flags['version'],\n hidden: false,\n } as MeowFlag\n\n delete flags['json']\n delete flags['markdown']\n } else {\n delete flags['help']\n delete flags['helpFull']\n delete flags['version']\n }\n\n // This is basically a dry-run parse of cli args and flags. We use this to\n // determine config overrides and expected output mode.\n const cli1 = meow({\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n // Ensure we don't check unknown flags.\n allowUnknownFlags: true,\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n })\n\n const {\n compactHeader: compactHeaderFlag,\n config: configFlag,\n org: orgFlag,\n spinner: spinnerFlag,\n } = cli1.flags as {\n compactHeader: boolean\n config: string\n org: string\n spinner: boolean\n }\n\n const compactMode =\n compactHeaderFlag || (constants.ENV.CI && !constants.ENV.VITEST)\n const noSpinner = spinnerFlag === false || isDebug()\n\n // Use CI spinner style when --no-spinner is passed or debug mode is enabled.\n // This prevents the spinner from interfering with debug output.\n if (noSpinner) {\n constants.spinner.spinner = getCliSpinners('ci')!\n }\n // Hard override the config if instructed to do so.\n // The env var overrides the --flag, which overrides the persisted config\n // Also, when either of these are used, config updates won't persist.\n let configOverrideResult\n if (constants.ENV.SOCKET_CLI_CONFIG) {\n configOverrideResult = overrideCachedConfig(constants.ENV.SOCKET_CLI_CONFIG)\n } else if (configFlag) {\n configOverrideResult = overrideCachedConfig(configFlag)\n }\n\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n // This overrides the config override and even the explicit token env var.\n // The config will be marked as readOnly to prevent persisting it.\n overrideConfigApiToken(undefined)\n } else {\n const tokenOverride = constants.ENV.SOCKET_CLI_API_TOKEN\n if (tokenOverride) {\n // This will set the token (even if there was a config override) and\n // set it to readOnly, making sure the temp token won't be persisted.\n overrideConfigApiToken(tokenOverride)\n }\n }\n\n if (configOverrideResult?.ok === false) {\n if (!shouldSuppressBanner(cli1.flags)) {\n emitBanner(name, orgFlag, compactMode)\n // Add newline in stderr.\n logger.error('')\n }\n logger.fail(configOverrideResult.message)\n process.exitCode = 2\n return\n }\n\n // If we have got some args, then lets find out if we can find a command.\n if (commandOrAliasName) {\n const alias = aliases[commandOrAliasName]\n // First: Resolve argv data from alias if its an alias that's been given.\n const [commandName, ...commandArgv] = alias\n ? [...alias.argv, ...rawCommandArgv]\n : [commandOrAliasName, ...rawCommandArgv]\n // Second: Find a command definition using that data.\n const commandDefinition = commandName ? subcommands[commandName] : undefined\n // Third: If a valid command has been found, then we run it...\n if (commandDefinition) {\n // Extract the original command arguments from the full argv\n // by skipping the command name\n return await commandDefinition.run(commandArgv, importMeta, {\n parentName: name,\n })\n }\n\n // Suggest similar commands for typos.\n if (commandName && !commandDefinition) {\n const suggestion = findBestCommandMatch(commandName, subcommands, aliases)\n if (suggestion) {\n process.exitCode = 2\n logger.fail(\n `Unknown command \"${commandName}\". Did you mean \"${suggestion}\"?`,\n )\n return\n }\n }\n }\n\n const lines = ['', 'Usage', ` $ ${name} <command>`]\n if (isRootCommand) {\n lines.push(\n ` $ ${name} scan create ${FLAG_JSON}`,\n ` $ ${name} package score ${NPM} lodash ${FLAG_MARKDOWN}`,\n )\n }\n lines.push('')\n if (isRootCommand) {\n // \"Bucket\" some commands for easier usage.\n const commands = new Set([\n 'analytics',\n 'audit-log',\n 'ci',\n 'cdxgen',\n 'config',\n 'dependencies',\n 'fix',\n 'install',\n //'json',\n 'license',\n 'login',\n 'logout',\n 'manifest',\n NPM,\n NPX,\n 'optimize',\n 'organization',\n 'package',\n //'patch',\n // PNPM,\n 'raw-npm',\n 'raw-npx',\n 'repository',\n 'scan',\n //'security',\n 'threat-feed',\n 'uninstall',\n 'wrapper',\n // YARN,\n ])\n Object.entries(subcommands)\n .filter(([_name, subcommand]) => !subcommand.hidden)\n .map(([name]) => name)\n .forEach(name => {\n if (commands.has(name)) {\n commands.delete(name)\n } else {\n logger.fail('Received an unknown command:', name)\n }\n })\n if (commands.size) {\n logger.fail(\n 'Found commands in the list that were not marked as public or not defined at all:',\n // Node < 22 will print 'Object (n)' before the array. So to have consistent\n // test snapshots we use joinAnd.\n joinAnd(\n Array.from(commands)\n .sort(naturalCompare)\n .map(c => `'${c}'`),\n ),\n )\n }\n lines.push(\n 'Note: All commands have their own --help',\n '',\n 'Main commands',\n ` socket login ${description(subcommands['login'])}`,\n ` socket scan create Create a new Socket scan and report`,\n ` socket npm/lodash@4.17.21 Request the Socket score of a package`,\n ` socket fix ${description(subcommands['fix'])}`,\n ` socket optimize ${description(subcommands['optimize'])}`,\n ` socket cdxgen ${description(subcommands['cdxgen'])}`,\n ` socket ci ${description(subcommands['ci'])}`,\n ``,\n 'Socket API',\n ` analytics ${description(subcommands['analytics'])}`,\n ` audit-log ${description(subcommands['audit-log'])}`,\n ` organization ${description(subcommands['organization'])}`,\n ` package ${description(subcommands['package'])}`,\n ` repository ${description(subcommands['repository'])}`,\n ` scan ${description(subcommands['scan'])}`,\n ` threat-feed ${description(subcommands['threat-feed'])}`,\n ``,\n 'Local tools',\n ` manifest ${description(subcommands['manifest'])}`,\n ` npm ${description(subcommands[NPM])}`,\n ` npx ${description(subcommands[NPX])}`,\n ` raw-npm ${description(subcommands['raw-npm'])}`,\n ` raw-npx ${description(subcommands['raw-npx'])}`,\n '',\n 'CLI configuration',\n ` config ${description(subcommands['config'])}`,\n ` install ${description(subcommands['install'])}`,\n ` login Socket API login and CLI setup`,\n ` logout ${description(subcommands['logout'])}`,\n ` uninstall ${description(subcommands['uninstall'])}`,\n ` wrapper ${description(subcommands['wrapper'])}`,\n )\n } else {\n lines.push('Commands')\n lines.push(\n ` ${getHelpListOutput(\n {\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(subcommands).filter(\n ({ 1: subcommand }) => !subcommand.hidden,\n ),\n ),\n ),\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(aliases).filter(({ 1: alias }) => {\n const { hidden } = alias\n const cmdName = hidden ? '' : alias.argv[0]\n const subcommand = cmdName ? subcommands[cmdName] : undefined\n return subcommand && !subcommand.hidden\n }),\n ),\n ),\n },\n { indent: HELP_INDENT, padName: HELP_PAD_NAME },\n )}`,\n )\n }\n\n lines.push('', 'Options')\n if (isRootCommand) {\n lines.push(\n ' Note: All commands have these flags even when not displayed in their help',\n '',\n )\n } else {\n lines.push('')\n }\n lines.push(\n ` ${getFlagListOutput(\n {\n ...flags,\n // Explicitly document the negated --no-banner variant.\n noBanner: {\n ...flags['banner'],\n hidden: false,\n } as MeowFlag,\n // Explicitly document the negated --no-spinner variant.\n noSpinner: {\n ...flags['spinner'],\n hidden: false,\n } as MeowFlag,\n },\n { indent: HELP_INDENT, padName: HELP_PAD_NAME },\n )}`,\n )\n if (isRootCommand) {\n // Check if we should show full help with environment variables.\n const showFullHelp = argv.includes(FLAG_HELP_FULL)\n\n if (showFullHelp) {\n // Show full help with environment variables.\n lines.push(\n '',\n 'Environment variables',\n ' SOCKET_CLI_API_TOKEN Set the Socket API token',\n ' SOCKET_CLI_CONFIG A JSON stringified Socket configuration object',\n ' SOCKET_CLI_GITHUB_API_URL Change the base URL for GitHub REST API calls',\n ' SOCKET_CLI_GIT_USER_EMAIL The git config `user.email` used by Socket CLI',\n ` ${colors.italic('Defaults:')} github-actions[bot]@users.noreply.github.com`,\n ' SOCKET_CLI_GIT_USER_NAME The git config `user.name` used by Socket CLI',\n ` ${colors.italic('Defaults:')} github-actions[bot]`,\n ` SOCKET_CLI_GITHUB_TOKEN A classic or fine-grained ${terminalLink('GitHub personal access token', 'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens')}`,\n ` ${colors.italic('Aliases:')} GITHUB_TOKEN`,\n ' SOCKET_CLI_NO_API_TOKEN Make the default API token `undefined`',\n ' SOCKET_CLI_NPM_PATH The absolute location of the npm directory',\n ' SOCKET_CLI_ORG_SLUG Specify the Socket organization slug',\n '',\n ' SOCKET_CLI_ACCEPT_RISKS Accept risks of a Socket wrapped npm/npx run',\n ' SOCKET_CLI_VIEW_ALL_RISKS View all risks of a Socket wrapped npm/npx run',\n '',\n 'Environment variables for development',\n ' SOCKET_CLI_API_BASE_URL Change the base URL for Socket API calls',\n ` ${colors.italic('Defaults:')} The \"apiBaseUrl\" value of socket/settings local app data`,\n ` if present, else ${API_V0_URL}`,\n ' SOCKET_CLI_API_PROXY Set the proxy Socket API requests are routed through, e.g. if set to',\n ` ${terminalLink('http://127.0.0.1:9090', 'https://docs.proxyman.io/troubleshooting/couldnt-see-any-requests-from-3rd-party-network-libraries')} then all request are passed through that proxy`,\n ` ${colors.italic('Aliases:')} HTTPS_PROXY, https_proxy, HTTP_PROXY, and http_proxy`,\n ' SOCKET_CLI_API_TIMEOUT Set the timeout in milliseconds for Socket API requests',\n ' SOCKET_CLI_DEBUG Enable debug logging in Socket CLI',\n ` DEBUG Enable debug logging based on the ${socketPackageLink('npm', 'debug', undefined, 'debug')} package`,\n )\n } else {\n // Show condensed help with hint about --help-full.\n lines.push(\n '',\n 'Environment variables [more...]',\n ` Use ${colors.bold(FLAG_HELP_FULL)} to view all environment variables`,\n )\n }\n }\n\n // Parse it again. Config overrides should now be applied (may affect help).\n // Note: this is displayed as help screen if the command does not override it\n // (which is the case for most sub-commands with sub-commands).\n const cli2 = meow({\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n // Do not strictly check for flags here.\n allowUnknownFlags: true,\n // We will emit help when we're ready.\n // Plus, if we allow this then meow may exit here.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n help: lines.map(l => indentString(l, HELP_INDENT)).join('\\n'),\n })\n\n const { dryRun, help: helpFlag } = cli2.flags as {\n dryRun: boolean\n help: boolean\n }\n\n // ...else we provide basic instructions and help.\n if (!shouldSuppressBanner(cli2.flags)) {\n emitBanner(name, orgFlag, compactMode)\n // Meow will add newline so don't add stderr spacing here.\n }\n if (!helpFlag && dryRun) {\n process.exitCode = 0\n logger.log(`${constants.DRY_RUN_LABEL}: No-op, call a sub-command; ok`)\n } else {\n // When you explicitly request --help, the command should be successful\n // so we exit(0). If we do it because we need more input, we exit(2).\n cli2.showHelp(helpFlag ? 0 : 2)\n }\n}\n\nexport interface MeowOrExitConfig {\n argv: string[] | readonly string[]\n config: CliCommandConfig\n parentName: string\n importMeta: ImportMeta\n}\n\nexport type MeowOrExitOptions = {\n allowUnknownFlags?: boolean | undefined\n}\n\n/**\n * Create meow CLI instance or exit with help/error (meow will exit immediately\n * if it calls .showHelp()).\n * @param config Configuration object with argv, config, parentName, and importMeta.\n * @param options Optional settings like allowUnknownFlags.\n * @example\n * meowOrExit(\n * { argv, config, parentName, importMeta },\n * { allowUnknownFlags: false }\n * )\n */\nexport function meowOrExit(\n config: MeowOrExitConfig,\n options?: MeowOrExitOptions | undefined,\n): Result<MeowFlags> {\n const {\n argv,\n config: cliConfig,\n importMeta,\n parentName,\n } = { __proto__: null, ...config } as MeowOrExitConfig\n const { allowUnknownFlags = true } = {\n __proto__: null,\n ...options,\n } as MeowOrExitOptions\n const command = `${parentName} ${cliConfig.commandName}`\n lastSeenCommand = command\n\n // This exits if .printHelp() is called either by meow itself or by us.\n const cli = meow({\n argv,\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n collectUnknownFlags: true,\n description: cliConfig.description,\n flags: cliConfig.flags,\n help: trimNewlines(cliConfig.help(command, cliConfig)),\n importMeta,\n })\n\n const {\n compactHeader: compactHeaderFlag,\n help: helpFlag,\n org: orgFlag,\n spinner: spinnerFlag,\n version: versionFlag,\n } = cli.flags as {\n compactHeader: boolean\n help: boolean\n org: string\n spinner: boolean\n version: boolean | undefined\n }\n\n const compactMode =\n compactHeaderFlag || (constants.ENV.CI && !constants.ENV.VITEST)\n const noSpinner = spinnerFlag === false || isDebug()\n\n // Use CI spinner style when --no-spinner is passed.\n // This prevents the spinner from interfering with debug output.\n if (noSpinner) {\n constants.spinner.spinner = getCliSpinners('ci')!\n }\n\n if (!shouldSuppressBanner(cli.flags)) {\n emitBanner(command, orgFlag, compactMode)\n // Add newline in stderr.\n // Meow help adds a newline too so we do it here.\n logger.error('')\n }\n\n // As per https://github.com/sindresorhus/meow/issues/178\n // Setting `allowUnknownFlags: false` makes it reject camel cased flags.\n // if (!allowUnknownFlags) {\n // // Run meow specifically with the flag setting. It will exit(2) if an\n // // invalid flag is set and print a message.\n // meow({\n // argv,\n // allowUnknownFlags: false,\n // // Prevent meow from potentially exiting early.\n // autoHelp: false,\n // autoVersion: false,\n // description: config.description,\n // flags: config.flags,\n // help: trimNewlines(config.help(command, config)),\n // importMeta,\n // })\n // }\n\n if (helpFlag) {\n cli.showHelp(0)\n }\n\n // Meow doesn't detect 'version' as an unknown flag, so we do the leg work here.\n if (versionFlag && !hasOwn(cliConfig.flags, 'version')) {\n // Use `console.error` here instead of `logger.error` to match Meow behavior.\n console.error('Unknown flag\\n--version')\n // eslint-disable-next-line n/no-process-exit\n process.exit(2)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n }\n\n // Now test for help state. Run Meow again. If it exits now, it must be due\n // to wanting to print the help screen. But it would exit(0) and we want a\n // consistent exit(2) for that case (missing input).\n process.exitCode = 2\n meow({\n argv,\n // As per https://github.com/sindresorhus/meow/issues/178\n // Setting `allowUnknownFlags: false` makes it reject camel cased flags.\n allowUnknownFlags: Boolean(allowUnknownFlags),\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n description: cliConfig.description,\n help: trimNewlines(cliConfig.help(command, cliConfig)),\n importMeta,\n flags: cliConfig.flags,\n })\n // Ok, no help, reset to default.\n process.exitCode = 0\n\n return cli\n}\n","export function msAtHome(isoTimeStamp: string): string {\n const timeStart = Date.parse(isoTimeStamp)\n const timeEnd = Date.now()\n\n const rtf = new Intl.RelativeTimeFormat('en', {\n numeric: 'always',\n style: 'short',\n })\n\n const delta = timeEnd - timeStart\n if (delta < 60 * 60 * 1000) {\n return rtf.format(-Math.round(delta / (60 * 1000)), 'minute')\n // return Math.round(delta / (60 * 1000)) + ' min ago'\n } else if (delta < 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (60 * 60 * 1000)).toFixed(1), 'hour')\n // return (delta / (60 * 60 * 1000)).toFixed(1) + ' hr ago'\n } else if (delta < 7 * 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (24 * 60 * 60 * 1000)).toFixed(1), 'day')\n // return (delta / (24 * 60 * 60 * 1000)).toFixed(1) + ' day ago'\n } else {\n return isoTimeStamp.slice(0, 10)\n }\n}\n","import { handleApiCall } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\nimport type { SocketSdk, SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchOrganizationOptions = {\n description?: string | undefined\n sdk?: SocketSdk | undefined\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport type EnterpriseOrganization = Omit<Organization, 'plan'> & {\n plan: `enterprise${string}`\n}\n\nexport type EnterpriseOrganizations = EnterpriseOrganization[]\n\nexport type Organization =\n SocketSdkSuccessResult<'getOrganizations'>['data']['organizations'][string]\n\nexport type Organizations = Organization[]\n\nexport type OrganizationsData = { organizations: Organizations }\n\nexport type OrganizationsCResult = CResult<OrganizationsData>\n\nexport async function fetchOrganization(\n options?: FetchOrganizationOptions | undefined,\n): Promise<OrganizationsCResult> {\n const {\n description = 'organization list',\n sdk,\n sdkOpts,\n } = {\n __proto__: null,\n ...options,\n } as FetchOrganizationOptions\n\n let sockSdk = sdk\n if (!sockSdk) {\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n sockSdk = sockSdkCResult.data\n }\n\n const orgsCResult = await handleApiCall(sockSdk.getOrganizations(), {\n description,\n })\n if (!orgsCResult.ok) {\n return orgsCResult\n }\n\n return {\n ...orgsCResult,\n data: {\n organizations: Object.values(orgsCResult.data.organizations),\n },\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nexport async function suggestOrgSlug(): Promise<string | void> {\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n logger.fail(\n 'Failed to lookup organization list from API, unable to suggest',\n )\n return undefined\n }\n\n // Ignore a failed request here. It was not the primary goal of\n // running this command and reporting it only leads to end-user confusion.\n const { organizations } = orgsCResult.data\n const proceed = await select<string>({\n message:\n 'Missing org name; do you want to use any of these orgs for this scan?',\n choices: [\n ...organizations.map(o => {\n const name = o.name ?? o.slug\n return {\n name: `Yes [${name}]`,\n value: name,\n description: `Use \"${name}\" as the organization`,\n }\n }),\n {\n name: 'No',\n value: '',\n description:\n 'Do not use any of these organizations (will end in a no-op)',\n },\n ],\n })\n\n if (proceed) {\n return proceed\n }\n return undefined\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { getConfigValue, updateConfigValue } from '../../utils/config.mts'\n\nexport async function suggestToPersistOrgSlug(orgSlug: string): Promise<void> {\n const skipAsk = getConfigValue('skipAskToPersistDefaultOrg')\n if (!skipAsk.ok || skipAsk.data) {\n // Don't ask to store it when disabled before, or when reading config fails.\n return\n }\n\n const result = await select<string>({\n message: `Would you like to use this org (${orgSlug}) as the default org for future calls?`,\n choices: [\n {\n name: 'Yes',\n value: 'yes',\n description: 'Stores it in your config',\n },\n {\n name: 'No',\n value: 'no',\n description: 'Do not persist this org as default org',\n },\n {\n name: \"No and don't ask again\",\n value: 'sush',\n description:\n 'Do not store as default org and do not ask again to persist it',\n },\n ],\n })\n if (result === 'yes') {\n const updateResult = updateConfigValue('defaultOrg', orgSlug)\n if (updateResult.ok) {\n logger.success('Updated default org config to:', orgSlug)\n } else {\n logger.fail(\n '(Non blocking) Failed to update default org in config:',\n updateResult.cause,\n )\n }\n } else if (result === 'sush') {\n const updateResult = updateConfigValue('skipAskToPersistDefaultOrg', true)\n if (updateResult.ok) {\n logger.info('Default org not changed. Will not ask to persist again.')\n } else {\n logger.fail(\n `(Non blocking) Failed to store preference; will ask to persist again next time. Reason: ${updateResult.cause}`,\n )\n }\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport {\n CONFIG_KEY_DEFAULT_ORG,\n V1_MIGRATION_GUIDE_URL,\n} from '../constants.mts'\nimport { getConfigValueOrUndef } from './config.mts'\nimport { webLink } from './terminal-link.mts'\nimport { suggestOrgSlug } from '../commands/scan/suggest-org-slug.mts'\nimport { suggestToPersistOrgSlug } from '../commands/scan/suggest-to-persist-orgslug.mts'\n\nexport async function determineOrgSlug(\n orgFlag: string,\n interactive: boolean,\n dryRun: boolean,\n): Promise<[string, string | undefined]> {\n const defaultOrgSlug = getConfigValueOrUndef(CONFIG_KEY_DEFAULT_ORG)\n let orgSlug = String(orgFlag || defaultOrgSlug || '')\n if (!orgSlug) {\n if (!interactive) {\n logger.warn(\n 'Note: This command requires an org slug because the Socket API endpoint does.',\n )\n logger.warn('')\n logger.warn(\n 'It seems no default org was setup and the `--org` flag was not used.',\n )\n logger.warn(\n \"Additionally, `--no-interactive` was set so we can't ask for it.\",\n )\n logger.warn(\n 'Since v1.0.0 the org _argument_ for all commands was dropped in favor of an',\n )\n logger.warn(\n 'implicit default org setting, which will be setup when you run `socket login`.',\n )\n logger.warn('')\n logger.warn(\n 'Note: When running in CI, you probably want to set the `--org` flag.',\n )\n logger.warn('')\n logger.warn(\n `For details, see the ${webLink(V1_MIGRATION_GUIDE_URL, 'v1 migration guide')}`,\n )\n logger.warn('')\n logger.warn(\n 'This command will exit now because the org slug is required to proceed.',\n )\n return ['', undefined]\n }\n\n logger.warn(\n 'Unable to determine the target org. Trying to auto-discover it now...',\n )\n logger.info('Note: Run `socket login` to set a default org.')\n logger.error(' Use the --org flag to override the default org.')\n logger.error('')\n if (dryRun) {\n logger.fail('Skipping auto-discovery of org in dry-run mode')\n } else {\n orgSlug = (await suggestOrgSlug()) || ''\n if (orgSlug) {\n await suggestToPersistOrgSlug(orgSlug)\n }\n }\n }\n\n return [orgSlug, defaultOrgSlug]\n}\n","import { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../../constants.mts'\nimport { getConfigValueOrUndef } from '../../utils/config.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { CResult } from '../../types.mts'\n\n// Use the config defaultOrg when set, otherwise discover from remote.\nexport async function getDefaultOrgSlug(): Promise<CResult<string>> {\n const defaultOrgResult = getConfigValueOrUndef('defaultOrg')\n if (defaultOrgResult) {\n debugFn(\n 'notice',\n 'use: org from \"defaultOrg\" value of socket/settings local app data',\n defaultOrgResult,\n )\n return { ok: true, data: defaultOrgResult }\n }\n\n const envOrgSlug = constants.ENV.SOCKET_CLI_ORG_SLUG\n if (envOrgSlug) {\n debugFn(\n 'notice',\n 'use: org from SOCKET_CLI_ORG_SLUG environment variable',\n envOrgSlug,\n )\n return { ok: true, data: envOrgSlug }\n }\n\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return orgsCResult\n }\n\n const { organizations } = orgsCResult.data\n const keys = Object.keys(organizations)\n if (!keys.length) {\n return {\n ok: false,\n message: 'Failed to establish identity',\n data: `No organization associated with the Socket API token. Unable to continue.`,\n }\n }\n\n const slug = (organizations as any)[keys[0]!]?.name ?? undefined\n if (!slug) {\n return {\n ok: false,\n message: 'Failed to establish identity',\n data: `Cannot determine the default organization for the API token. Unable to continue.`,\n }\n }\n\n debugFn('notice', 'resolve: org from Socket API', slug)\n\n return {\n ok: true,\n message: 'Retrieved default org from server',\n data: slug,\n }\n}\n","import constants from '../constants.mts'\n\n/**\n * Sanitizes a name to comply with repository naming constraints.\n * Constraints: 100 or less A-Za-z0-9 characters only with non-repeating,\n * non-leading or trailing ., _ or - only.\n *\n * @param name - The name to sanitize\n * @returns Sanitized name that complies with repository naming rules, or empty string if no valid characters\n */\nfunction sanitizeName(name: string): string {\n if (!name) {\n return ''\n }\n\n // Replace sequences of illegal characters with underscores.\n const sanitized = name\n // Replace any sequence of non-alphanumeric characters (except ., _, -) with underscore.\n .replace(/[^A-Za-z0-9._-]+/g, '_')\n // Replace sequences of multiple allowed special chars with single underscore.\n .replace(/[._-]{2,}/g, '_')\n // Remove leading special characters.\n .replace(/^[._-]+/, '')\n // Remove trailing special characters.\n .replace(/[._-]+$/, '')\n // Truncate to 100 characters max.\n .slice(0, 100)\n\n return sanitized\n}\n\n/**\n * Extracts and sanitizes a repository name.\n *\n * @param name - The repository name to extract and sanitize\n * @returns Sanitized repository name, or default repository name if empty\n */\nexport function extractName(name: string): string {\n const sanitized = sanitizeName(name)\n return sanitized || constants.SOCKET_DEFAULT_REPOSITORY\n}\n\n/**\n * Extracts and sanitizes a repository owner name.\n *\n * @param owner - The repository owner name to extract and sanitize\n * @returns Sanitized repository owner name, or undefined if input is empty\n */\nexport function extractOwner(owner: string): string | undefined {\n if (!owner) {\n return undefined\n }\n const sanitized = sanitizeName(owner)\n return sanitized || undefined\n}\n","/**\n * Git utilities for Socket CLI.\n * Provides git operations for repository management, branch handling, and commits.\n *\n * Branch Operations:\n * - gitCheckoutBranch: Switch to branch\n * - gitCreateBranch: Create new local branch\n * - gitDeleteBranch: Delete local branch\n * - gitDeleteRemoteBranch: Delete remote branch\n * - gitPushBranch: Push branch to remote with --force\n *\n * Commit Operations:\n * - gitCleanFdx: Remove untracked files\n * - gitCommit: Stage files and create commit\n * - gitEnsureIdentity: Configure git user.name/email\n * - gitResetHard: Reset to branch/commit\n *\n * Remote URL Parsing:\n * - parseGitRemoteUrl: Extract owner/repo from SSH or HTTPS URLs\n *\n * Repository Information:\n * - detectDefaultBranch: Find default branch (main/master/develop/etc)\n * - getBaseBranch: Determine base branch (respects GitHub Actions env)\n * - getRepoInfo: Extract owner/repo from git remote URL\n * - gitBranch: Get current branch or commit hash\n */\n\nimport { debugDir, debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { isSpawnError, spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants, { FLAG_QUIET } from '../constants.mts'\nimport { debugGit } from './debug.mts'\nimport { extractName, extractOwner } from './extract-names.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { SpawnOptions } from '@socketsecurity/registry/lib/spawn'\n\n// Listed in order of check preference.\nconst COMMON_DEFAULT_BRANCH_NAMES = [\n // Modern default (GitHub, GitLab, Bitbucket have switched to this).\n 'main',\n // Historic default in Git (pre-2020, still used in many repos).\n 'master',\n // Common in Git Flow workflows (main for stable, develop for ongoing work).\n 'develop',\n // Used by teams adopting trunk-based development practices.\n 'trunk',\n // Used in some older enterprise setups and tools.\n 'default',\n]\n\nexport async function getBaseBranch(cwd = process.cwd()): Promise<string> {\n const { GITHUB_BASE_REF, GITHUB_REF_NAME, GITHUB_REF_TYPE } = constants.ENV\n // 1. In a pull request, this is always the base branch.\n if (GITHUB_BASE_REF) {\n return GITHUB_BASE_REF\n }\n // 2. If it's a branch (not a tag), GITHUB_REF_TYPE should be 'branch'.\n if (GITHUB_REF_TYPE === 'branch' && GITHUB_REF_NAME) {\n return GITHUB_REF_NAME\n }\n // 3. Try to resolve the default remote branch using 'git remote show origin'.\n // This handles detached HEADs or workflows triggered by tags/releases.\n try {\n const originDetails = (\n await spawn('git', ['remote', 'show', 'origin'], { cwd })\n ).stdout\n\n const match = /(?<=HEAD branch: ).+/.exec(originDetails)\n if (match?.[0]) {\n return match[0].trim()\n }\n } catch {}\n // GitHub and GitLab default to branch name \"main\"\n // https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches#about-the-default-branch\n return 'main'\n}\n\nexport type RepoInfo = {\n owner: string\n repo: string\n}\n\nexport async function getRepoInfo(\n cwd = process.cwd(),\n): Promise<RepoInfo | undefined> {\n let info\n try {\n const remoteUrl = (\n await spawn('git', ['remote', 'get-url', 'origin'], { cwd })\n ).stdout\n info = parseGitRemoteUrl(remoteUrl)\n if (!info) {\n debugFn('warn', `Unmatched git remote URL format: ${remoteUrl}`)\n debugDir('warn', { remoteUrl })\n }\n } catch (e) {\n // Expected failure when not in a git repo.\n debugDir('inspect', { message: 'git remote get-url failed', error: e })\n }\n return info\n}\n\nexport async function getRepoName(cwd = process.cwd()): Promise<string> {\n const repoInfo = await getRepoInfo(cwd)\n return repoInfo?.repo\n ? extractName(repoInfo.repo)\n : constants.SOCKET_DEFAULT_REPOSITORY\n}\n\nexport async function getRepoOwner(\n cwd = process.cwd(),\n): Promise<string | undefined> {\n const repoInfo = await getRepoInfo(cwd)\n return repoInfo?.owner ? extractOwner(repoInfo.owner) : undefined\n}\n\nexport async function gitBranch(\n cwd = process.cwd(),\n): Promise<string | undefined> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n // Try symbolic-ref first which returns the branch name or fails in a\n // detached HEAD state.\n try {\n const gitSymbolicRefResult = await spawn(\n 'git',\n ['symbolic-ref', '--short', 'HEAD'],\n stdioPipeOptions,\n )\n return gitSymbolicRefResult.stdout\n } catch (e) {\n // Expected in detached HEAD state, fallback to rev-parse.\n debugDir('inspect', { message: 'In detached HEAD state', error: e })\n }\n // Fallback to using rev-parse to get the short commit hash in a\n // detached HEAD state.\n try {\n const gitRevParseResult = await spawn(\n 'git',\n ['rev-parse', '--short', 'HEAD'],\n stdioPipeOptions,\n )\n return gitRevParseResult.stdout\n } catch (e) {\n // Both methods failed, likely not in a git repo.\n debugDir('inspect', { message: 'Unable to determine git branch', error: e })\n }\n return undefined\n}\n\n/**\n * Try to detect the default branch name by checking common patterns.\n * Returns the first branch that exists in the repository.\n */\nexport async function detectDefaultBranch(\n cwd = process.cwd(),\n): Promise<string> {\n // First pass: check all local branches\n for (const branch of COMMON_DEFAULT_BRANCH_NAMES) {\n // eslint-disable-next-line no-await-in-loop\n if (await gitLocalBranchExists(branch, cwd)) {\n return branch\n }\n }\n // Second pass: check remote branches only if no local branch found\n for (const branch of COMMON_DEFAULT_BRANCH_NAMES) {\n // eslint-disable-next-line no-await-in-loop\n if (await gitRemoteBranchExists(branch, cwd)) {\n return branch\n }\n }\n return constants.SOCKET_DEFAULT_BRANCH\n}\n\nexport type GitCreateAndPushBranchOptions = {\n cwd?: string | undefined\n email?: string | undefined\n user?: string | undefined\n}\n\nexport async function gitCleanFdx(cwd = process.cwd()): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['clean', '-fdx'], stdioIgnoreOptions)\n debugGit('clean -fdx', true)\n return true\n } catch (e) {\n debugGit('clean -fdx', false, { error: e })\n }\n return false\n}\n\nexport async function gitCheckoutBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['checkout', branch], stdioIgnoreOptions)\n debugGit(`checkout ${branch}`, true)\n return true\n } catch (e) {\n debugGit(`checkout ${branch}`, false, { error: e })\n }\n return false\n}\n\nexport async function gitCreateBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n if (await gitLocalBranchExists(branch)) {\n return true\n }\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['branch', branch], stdioIgnoreOptions)\n debugGit(`branch ${branch}`, true)\n return true\n } catch (e) {\n debugGit(`branch ${branch}`, false, { error: e })\n }\n return false\n}\n\nexport async function gitPushBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn(\n 'git',\n ['push', '--force', '--set-upstream', 'origin', branch],\n stdioIgnoreOptions,\n )\n debugGit(`push ${branch}`, true)\n return true\n } catch (e) {\n if (isSpawnError(e) && e.code === 128) {\n debugFn(\n 'error',\n \"Push denied: token requires write permissions for 'contents' and 'pull-requests'\",\n )\n debugDir('error', e)\n debugDir('inspect', { branch })\n } else {\n debugGit(`push ${branch}`, false, { error: e })\n }\n }\n return false\n}\n\nexport async function gitCommit(\n commitMsg: string,\n filepaths: string[],\n options?: GitCreateAndPushBranchOptions | undefined,\n): Promise<boolean> {\n if (!filepaths.length) {\n debugFn('notice', `miss: no filepaths to add`)\n return false\n }\n const {\n cwd = process.cwd(),\n email = constants.ENV.SOCKET_CLI_GIT_USER_EMAIL,\n user = constants.ENV.SOCKET_CLI_GIT_USER_NAME,\n } = { __proto__: null, ...options } as GitCreateAndPushBranchOptions\n\n await gitEnsureIdentity(user, email, cwd)\n\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['add', ...filepaths], stdioIgnoreOptions)\n debugGit('add', true, { count: filepaths.length })\n } catch (e) {\n debugGit('add', false, { error: e })\n debugDir('inspect', { filepaths })\n return false\n }\n\n try {\n await spawn('git', ['commit', '-m', commitMsg], stdioIgnoreOptions)\n debugGit('commit', true)\n return true\n } catch (e) {\n debugGit('commit', false, { error: e })\n debugDir('inspect', { commitMsg })\n }\n return false\n}\n\nexport async function gitDeleteBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n // Will throw with exit code 1 if branch does not exist.\n await spawn('git', ['branch', '-D', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n // Expected failure when branch doesn't exist.\n debugDir('inspect', {\n message: `Branch deletion failed (may not exist): ${branch}`,\n error: e,\n })\n }\n return false\n}\n\nexport async function gitDeleteRemoteBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n // Will throw with exit code 1 if branch does not exist.\n await spawn(\n 'git',\n ['push', 'origin', '--delete', branch],\n stdioIgnoreOptions,\n )\n return true\n } catch (e) {\n // Expected failure when remote branch doesn't exist.\n debugDir('inspect', {\n message: `Remote branch deletion failed (may not exist): ${branch}`,\n error: e,\n })\n }\n return false\n}\n\nexport async function gitEnsureIdentity(\n name: string,\n email: string,\n cwd = process.cwd(),\n): Promise<void> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n const identEntries: Array<[string, string]> = [\n ['user.email', email],\n ['user.name', name],\n ]\n await Promise.all(\n identEntries.map(async ({ 0: prop, 1: value }) => {\n let configValue\n try {\n // Will throw with exit code 1 if the config property is not set.\n const gitConfigResult = await spawn(\n 'git',\n ['config', '--get', prop],\n stdioPipeOptions,\n )\n configValue = gitConfigResult.stdout\n } catch (e) {\n // Expected when config property is not set.\n debugDir('inspect', {\n message: `Git config property not set: ${prop}`,\n error: e,\n })\n }\n if (configValue !== value) {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['config', prop, value], stdioIgnoreOptions)\n } catch (e) {\n debugFn('warn', `Failed to set git config: ${prop}`)\n debugDir('warn', e)\n debugDir('inspect', { value })\n }\n }\n }),\n )\n}\n\nexport async function gitLocalBranchExists(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n // Will throw with exit code 1 if the branch does not exist.\n await spawn(\n 'git',\n ['show-ref', FLAG_QUIET, `refs/heads/${branch}`],\n stdioIgnoreOptions,\n )\n return true\n } catch {\n // Expected when branch doesn't exist - no logging needed.\n }\n return false\n}\n\nexport async function gitRemoteBranchExists(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n try {\n const lsRemoteResult = await spawn(\n 'git',\n ['ls-remote', '--heads', 'origin', branch],\n stdioPipeOptions,\n )\n return lsRemoteResult.stdout.length > 0\n } catch (e) {\n // Expected when remote is not accessible or branch doesn't exist.\n debugDir('inspect', {\n message: `Remote branch check failed: ${branch}`,\n error: e,\n })\n }\n return false\n}\n\nexport async function gitResetAndClean(\n branch = 'HEAD',\n cwd = process.cwd(),\n): Promise<void> {\n // Discards tracked changes.\n await gitResetHard(branch, cwd)\n // Deletes all untracked files and directories.\n await gitCleanFdx(cwd)\n}\n\nexport async function gitResetHard(\n branch = 'HEAD',\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['reset', '--hard', branch], stdioIgnoreOptions)\n debugGit(`reset --hard ${branch}`, true)\n return true\n } catch (e) {\n debugGit(`reset --hard ${branch}`, false, { error: e })\n }\n return false\n}\n\nexport async function gitUnstagedModifiedFiles(\n cwd = process.cwd(),\n): Promise<CResult<string[]>> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n try {\n const gitDiffResult = await spawn(\n 'git',\n ['diff', '--name-only'],\n stdioPipeOptions,\n )\n const changedFilesDetails = gitDiffResult.stdout\n const relPaths = changedFilesDetails.split('\\n')\n return {\n ok: true,\n data: relPaths.map(p => normalizePath(p)),\n }\n } catch (e) {\n debugFn('error', 'Failed to get unstaged modified files')\n debugDir('error', e)\n return {\n ok: false,\n message: 'Git Error',\n cause: 'Unexpected error while trying to ask git whether repo is dirty',\n }\n }\n}\n\nconst parsedGitRemoteUrlCache = new Map<string, RepoInfo | undefined>()\n\nexport function parseGitRemoteUrl(remoteUrl: string): RepoInfo | undefined {\n let result = parsedGitRemoteUrlCache.get(remoteUrl)\n if (result) {\n return { ...result }\n }\n // Handle SSH-style\n const sshMatch = /^git@[^:]+:([^/]+)\\/(.+?)(?:\\.git)?$/.exec(remoteUrl)\n // 1. Handle SSH-style, e.g. git@github.com:owner/repo.git\n if (sshMatch) {\n result = { owner: sshMatch[1]!, repo: sshMatch[2]! }\n } else {\n // 2. Handle HTTPS/URL-style, e.g. https://github.com/owner/repo.git\n try {\n const parsed = new URL(remoteUrl)\n // Remove leading slashes from pathname and split by \"/\" to extract segments.\n const segments = parsed.pathname.replace(/^\\/+/, '').split('/')\n // The second-to-last segment is expected to be the owner (e.g., \"owner\" in /owner/repo.git).\n const owner = segments.at(-2)\n // The last segment is expected to be the repo name, so we remove the \".git\" suffix if present.\n const repo = segments.at(-1)?.replace(/\\.git$/, '')\n if (owner && repo) {\n result = { owner, repo }\n }\n } catch {}\n }\n parsedGitRemoteUrlCache.set(remoteUrl, result)\n return result ? { ...result } : result\n}\n","/**\n * Package URL (PURL) utilities for Socket CLI.\n * Implements the PURL specification for universal package identification.\n *\n * PURL Format:\n * pkg:type/namespace/name@version?qualifiers#subpath\n *\n * Key Functions:\n * - createPurlObject: Create PURL from components\n * - isPurl: Check if string is valid PURL\n * - normalizePurl: Normalize PURL format\n * - parsePurl: Parse PURL string to object\n * - purlToString: Convert PURL object to string\n *\n * Supported Types:\n * - cargo: Rust packages\n * - gem: Ruby packages\n * - go: Go modules\n * - maven: Java packages\n * - npm: Node.js packages\n * - pypi: Python packages\n *\n * See: https://github.com/package-url/purl-spec\n */\n\nimport { PackageURL, type PurlQualifiers } from '@socketregistry/packageurl-js'\nimport { isObjectObject } from '@socketsecurity/registry/lib/objects'\n\nimport type { SocketArtifact } from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\n\nexport type PurlObject<T> = T & { type: PURL_Type }\n\nexport type PurlLike = string | PackageURL | SocketArtifact\n\nexport type CreatePurlObjectOptions = {\n type?: string | undefined\n namespace?: string | undefined\n name?: string | undefined\n version?: string | undefined\n qualifiers?: PurlQualifiers | undefined\n subpath?: string | undefined\n throws?: boolean | undefined\n}\n\nexport type CreatePurlOptionsWithThrows = CreatePurlObjectOptions & {\n throws?: true | undefined\n}\n\nexport type CreatePurlOptionsNoThrows = CreatePurlObjectOptions & {\n throws: false\n}\n\nexport function createPurlObject(\n options: CreatePurlOptionsWithThrows,\n): PurlObject<PackageURL>\nexport function createPurlObject(\n options: CreatePurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function createPurlObject(\n type: string | CreatePurlObjectOptions,\n options?: CreatePurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function createPurlObject(\n type: string | CreatePurlObjectOptions,\n options: CreatePurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function createPurlObject(\n type: string | CreatePurlObjectOptions,\n options?: CreatePurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function createPurlObject(\n type: string,\n name: string,\n options: CreatePurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function createPurlObject(\n type: string,\n name: string,\n options?: CreatePurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function createPurlObject(\n type: string | CreatePurlObjectOptions,\n name?: string | CreatePurlObjectOptions | undefined,\n options?: CreatePurlObjectOptions | undefined,\n): PurlObject<PackageURL> | undefined {\n let opts: CreatePurlObjectOptions | undefined\n if (isObjectObject(type)) {\n opts = { __proto__: null, ...type } as CreatePurlObjectOptions\n type = opts.type as string\n name = opts.name as string\n } else if (isObjectObject(name)) {\n opts = { __proto__: null, ...name } as CreatePurlObjectOptions\n name = opts.name as string\n } else {\n opts = { __proto__: null, ...options } as CreatePurlObjectOptions\n if (typeof name !== 'string') {\n name = opts.name as string\n }\n }\n const { namespace, qualifiers, subpath, throws, version } = opts\n const shouldThrow = throws === undefined || !!throws\n try {\n return new PackageURL(\n type,\n namespace,\n name,\n version,\n qualifiers,\n subpath,\n ) as PurlObject<PackageURL>\n } catch (e) {\n if (shouldThrow) {\n throw e\n }\n }\n return undefined\n}\n\nexport type PurlObjectOptions = {\n throws?: boolean | undefined\n}\n\nexport type PurlOptionsWithThrows = PurlObjectOptions & {\n throws?: true | undefined\n}\n\nexport type PurlOptionsNoThrows = PurlObjectOptions & { throws: false }\n\nexport function getPurlObject(\n purl: string,\n options?: PurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function getPurlObject(\n purl: string,\n options: PurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function getPurlObject(\n purl: PackageURL,\n options?: PurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function getPurlObject(\n purl: PackageURL,\n options: PurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function getPurlObject(\n purl: SocketArtifact,\n options?: PurlOptionsWithThrows | undefined,\n): PurlObject<SocketArtifact>\nexport function getPurlObject(\n purl: SocketArtifact,\n options: PurlOptionsNoThrows,\n): PurlObject<SocketArtifact> | undefined\nexport function getPurlObject(\n purl: PurlLike,\n options?: PurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL | SocketArtifact>\nexport function getPurlObject(\n purl: PurlLike,\n options?: PurlObjectOptions | undefined,\n): PurlObject<PackageURL | SocketArtifact> | undefined {\n const { throws } = { __proto__: null, ...options } as PurlObjectOptions\n const shouldThrow = throws === undefined || !!throws\n try {\n return typeof purl === 'string'\n ? (PackageURL.fromString(normalizePurl(purl)) as PurlObject<PackageURL>)\n : (purl as PurlObject<PackageURL | SocketArtifact>)\n } catch (e) {\n if (shouldThrow) {\n throw e\n }\n return undefined\n }\n}\n\nexport function normalizePurl(rawPurl: string): string {\n return rawPurl.startsWith('pkg:') ? rawPurl : `pkg:${rawPurl}`\n}\n","/**\n * Socket.dev URL utilities for Socket CLI.\n * Generates URLs for Socket.dev website features and resources.\n *\n * Key Functions:\n * - getPkgFullNameFromPurl: Extract full package name from PURL\n * - getSocketDevAlertUrl: Generate alert type documentation URL\n * - getSocketDevPackageOverviewUrl: Generate package overview URL\n * - getSocketDevPackageOverviewUrlFromPurl: Generate overview URL from PURL\n * - getSocketDevPackageUrl: Generate package detail URL\n * - getSocketDevPackageUrlFromPurl: Generate package URL from PURL\n * - getSocketDevReportUrl: Generate scan report URL\n *\n * URL Generation:\n * - Package overview and detail pages\n * - Security alert documentation\n * - Scan report links\n * - Ecosystem-specific URL formatting\n */\n\nimport constants from '../constants.mts'\nimport { getPurlObject } from './purl.mts'\n\nimport type { SocketArtifact } from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\nimport type { PackageURL } from '@socketregistry/packageurl-js'\n\nexport function getPkgFullNameFromPurl(\n purl: string | PackageURL | SocketArtifact,\n): string {\n const purlObj = getPurlObject(purl)\n const { name, namespace } = purlObj\n return namespace\n ? `${namespace}${purlObj.type === 'maven' ? ':' : '/'}${name}`\n : name!\n}\n\nexport function getSocketDevAlertUrl(alertType: string): string {\n return `${constants.SOCKET_WEBSITE_URL}/alerts/${alertType}`\n}\n\nexport function getSocketDevPackageOverviewUrlFromPurl(\n purl: string | PackageURL | SocketArtifact,\n): string {\n const purlObj = getPurlObject(purl)\n const fullName = getPkgFullNameFromPurl(purlObj)\n return getSocketDevPackageOverviewUrl(purlObj.type, fullName, purlObj.version)\n}\n\nexport function getSocketDevPackageOverviewUrl(\n ecosystem: PURL_Type,\n fullName: string,\n version?: string | undefined,\n): string {\n const url = `${constants.SOCKET_WEBSITE_URL}/${ecosystem}/package/${fullName}`\n return ecosystem === 'golang'\n ? `${url}${version ? `?section=overview&version=${version}` : ''}`\n : `${url}${version ? `/overview/${version}` : ''}`\n}\n","interface NestedRecord<T> {\n [key: string]: T | NestedRecord<T>\n}\n\n/**\n * Convert a Map<string, Map|string> to a nested object of similar shape.\n * The goal is to serialize it with JSON.stringify, which Map can't do.\n */\nexport function mapToObject<T>(\n map: Map<string, T | Map<string, T | Map<string, T>>>,\n): NestedRecord<T> {\n return Object.fromEntries(\n Array.from(map.entries()).map(([k, v]) => [\n k,\n v instanceof Map ? mapToObject(v) : v,\n ]),\n )\n}\n","type NestedMap<T> = Map<string, T | NestedMap<T>>\n\nexport function* walkNestedMap<T>(\n map: NestedMap<T>,\n keys: string[] = [],\n): Generator<{ keys: string[]; value: T }> {\n for (const { 0: key, 1: value } of map.entries()) {\n if (value instanceof Map) {\n yield* walkNestedMap(value as NestedMap<T>, [...keys, key])\n } else {\n yield { keys: [...keys, key], value: value }\n }\n }\n}\n","/**\n * Coana integration utilities for Socket CLI.\n * Manages reachability analysis via Coana tech CLI.\n *\n * Key Functions:\n * - extractTier1ReachabilityScanId: Extract scan ID from socket facts file\n *\n * Integration:\n * - Works with @coana-tech/cli for reachability analysis\n * - Processes socket facts JSON files\n * - Extracts tier 1 reachability scan identifiers\n */\n\nimport { readJsonSync } from '@socketsecurity/registry/lib/fs'\n\nexport function extractTier1ReachabilityScanId(\n socketFactsFile: string,\n): string | undefined {\n const json = readJsonSync(socketFactsFile, { throws: false })\n const tier1ReachabilityScanId = String(\n json?.['tier1ReachabilityScanId'] ?? '',\n ).trim()\n return tier1ReachabilityScanId.length > 0\n ? tier1ReachabilityScanId\n : undefined\n}\n","/**\n * File system utilities for Socket CLI.\n * Provides file and directory search functionality.\n *\n * Key Functions:\n * - findUp: Search for files/directories up the directory tree\n *\n * Features:\n * - Upward directory traversal\n * - Supports file and directory searching\n * - Abort signal support for cancellation\n * - Multiple name search support\n *\n * Usage:\n * - Finding configuration files (package.json, lockfiles)\n * - Locating project root directories\n * - Searching for specific files in parent directories\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nexport type FindUpOptions = {\n cwd?: string | undefined\n onlyDirectories?: boolean | undefined\n onlyFiles?: boolean | undefined\n signal?: AbortSignal | undefined\n}\n\nexport async function findUp(\n name: string | string[],\n options?: FindUpOptions | undefined,\n): Promise<string | undefined> {\n const opts = { __proto__: null, ...options }\n const { cwd = process.cwd(), signal = constants.abortSignal } = opts\n let { onlyDirectories = false, onlyFiles = true } = opts\n if (onlyDirectories) {\n onlyFiles = false\n }\n if (onlyFiles) {\n onlyDirectories = false\n }\n let dir = path.resolve(cwd)\n const { root } = path.parse(dir)\n const names = [name].flat()\n while (dir && dir !== root) {\n for (const name of names) {\n if (signal?.aborted) {\n return undefined\n }\n const thePath = path.join(dir, name)\n try {\n // eslint-disable-next-line no-await-in-loop\n const stats = await fs.stat(thePath)\n if (!onlyDirectories && stats.isFile()) {\n return thePath\n }\n if (!onlyFiles && stats.isDirectory()) {\n return thePath\n }\n } catch {}\n }\n dir = path.dirname(dir)\n }\n return undefined\n}\n","import path from 'node:path'\n\nimport fastGlob from 'fast-glob'\nimport ignore from 'ignore'\nimport micromatch from 'micromatch'\nimport { parse as yamlParse } from 'yaml'\n\nimport { isDirSync, safeReadFile } from '@socketsecurity/registry/lib/fs'\nimport { defaultIgnore } from '@socketsecurity/registry/lib/globs'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { transform } from '@socketsecurity/registry/lib/streams'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { NODE_MODULES, PNPM } from '../constants.mts'\n\nimport type { Agent } from './package-environment.mts'\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\nimport type { Options as GlobOptions } from 'fast-glob'\n\nconst DEFAULT_IGNORE_FOR_GIT_IGNORE = defaultIgnore.filter(\n p => !p.endsWith('.gitignore'),\n)\n\nconst IGNORED_DIRS = [\n // Taken from ignore-by-default:\n // https://github.com/novemberborn/ignore-by-default/blob/v2.1.0/index.js\n '.git', // Git repository files, see <https://git-scm.com/>\n '.log', // Log files emitted by tools such as `tsserver`, see <https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29>\n '.nyc_output', // Temporary directory where nyc stores coverage data, see <https://github.com/bcoe/nyc>\n '.sass-cache', // Cache folder for node-sass, see <https://github.com/sass/node-sass>\n '.yarn', // Where node modules are installed when using Yarn, see <https://yarnpkg.com/>\n 'bower_components', // Where Bower packages are installed, see <http://bower.io/>\n 'coverage', // Standard output directory for code coverage reports, see <https://github.com/gotwarlost/istanbul>\n NODE_MODULES, // Where Node modules are installed, see <https://nodejs.org/>\n // Taken from globby:\n // https://github.com/sindresorhus/globby/blob/v14.0.2/ignore.js#L11-L16\n 'flow-typed',\n] as const\n\nconst IGNORED_DIR_PATTERNS = IGNORED_DIRS.map(i => `**/${i}`)\n\nasync function getWorkspaceGlobs(\n agent: Agent,\n cwd = process.cwd(),\n): Promise<string[]> {\n let workspacePatterns\n if (agent === PNPM) {\n const workspacePath = path.join(cwd, 'pnpm-workspace.yaml')\n const yml = await safeReadFile(workspacePath)\n if (yml) {\n try {\n workspacePatterns = yamlParse(yml)?.packages\n } catch {}\n }\n } else {\n workspacePatterns = (await readPackageJson(cwd, { throws: false }))?.[\n 'workspaces'\n ]\n }\n return Array.isArray(workspacePatterns)\n ? workspacePatterns\n .filter(isNonEmptyString)\n .map(workspacePatternToGlobPattern)\n : []\n}\n\nfunction ignoreFileLinesToGlobPatterns(\n lines: string[] | readonly string[],\n filepath: string,\n cwd: string,\n): string[] {\n const base = path.relative(cwd, path.dirname(filepath)).replace(/\\\\/g, '/')\n const patterns = []\n for (let i = 0, { length } = lines; i < length; i += 1) {\n const pattern = lines[i]!.trim()\n if (pattern.length > 0 && pattern.charCodeAt(0) !== 35 /*'#'*/) {\n patterns.push(\n ignorePatternToMinimatch(\n pattern.length && pattern.charCodeAt(0) === 33 /*'!'*/\n ? `!${path.posix.join(base, pattern.slice(1))}`\n : path.posix.join(base, pattern),\n ),\n )\n }\n }\n return patterns\n}\n\nfunction ignoreFileToGlobPatterns(\n content: string,\n filepath: string,\n cwd: string,\n): string[] {\n return ignoreFileLinesToGlobPatterns(content.split(/\\r?\\n/), filepath, cwd)\n}\n\n// Based on `@eslint/compat` convertIgnorePatternToMinimatch.\n// Apache v2.0 licensed\n// Copyright Nicholas C. Zakas\n// https://github.com/eslint/rewrite/blob/compat-v1.2.1/packages/compat/src/ignore-file.js#L28\nfunction ignorePatternToMinimatch(pattern: string): string {\n const isNegated = pattern.startsWith('!')\n const negatedPrefix = isNegated ? '!' : ''\n const patternToTest = (isNegated ? pattern.slice(1) : pattern).trimEnd()\n // Special cases.\n if (\n patternToTest === '' ||\n patternToTest === '**' ||\n patternToTest === '/**' ||\n patternToTest === '**'\n ) {\n return `${negatedPrefix}${patternToTest}`\n }\n const firstIndexOfSlash = patternToTest.indexOf('/')\n const matchEverywherePrefix =\n firstIndexOfSlash === -1 || firstIndexOfSlash === patternToTest.length - 1\n ? '**/'\n : ''\n const patternWithoutLeadingSlash =\n firstIndexOfSlash === 0 ? patternToTest.slice(1) : patternToTest\n // Escape `{` and `(` because in gitignore patterns they are just\n // literal characters without any specific syntactic meaning,\n // while in minimatch patterns they can form brace expansion or extglob syntax.\n //\n // For example, gitignore pattern `src/{a,b}.js` ignores file `src/{a,b}.js`.\n // But, the same minimatch pattern `src/{a,b}.js` ignores files `src/a.js` and `src/b.js`.\n // Minimatch pattern `src/\\{a,b}.js` is equivalent to gitignore pattern `src/{a,b}.js`.\n const escapedPatternWithoutLeadingSlash =\n patternWithoutLeadingSlash.replaceAll(\n /(?=((?:\\\\.|[^{(])*))\\1([{(])/guy,\n '$1\\\\$2',\n )\n const matchInsideSuffix = patternToTest.endsWith('/**') ? '/*' : ''\n return `${negatedPrefix}${matchEverywherePrefix}${escapedPatternWithoutLeadingSlash}${matchInsideSuffix}`\n}\n\nfunction workspacePatternToGlobPattern(workspace: string): string {\n const { length } = workspace\n if (!length) {\n return ''\n }\n // If the workspace ends with \"/\"\n if (workspace.charCodeAt(length - 1) === 47 /*'/'*/) {\n return `${workspace}/*/package.json`\n }\n // If the workspace ends with \"/**\"\n if (\n workspace.charCodeAt(length - 1) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 2) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 3) === 47 /*'/'*/\n ) {\n return `${workspace}/*/**/package.json`\n }\n // Things like \"packages/a\" or \"packages/*\"\n return `${workspace}/package.json`\n}\n\nexport function filterBySupportedScanFiles(\n filepaths: string[] | readonly string[],\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n): string[] {\n const patterns = getSupportedFilePatterns(supportedFiles)\n return filepaths.filter(p => micromatch.some(p, patterns))\n}\n\nexport function getSupportedFilePatterns(\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n): string[] {\n const patterns: string[] = []\n for (const key of Object.keys(supportedFiles)) {\n const supported = supportedFiles[key]\n if (supported) {\n patterns.push(...Object.values(supported).map(p => `**/${p.pattern}`))\n }\n }\n return patterns\n}\n\ntype GlobWithGitIgnoreOptions = GlobOptions & {\n socketConfig?: SocketYml | undefined\n}\n\nexport async function globWithGitIgnore(\n patterns: string[] | readonly string[],\n options: GlobWithGitIgnoreOptions,\n): Promise<string[]> {\n const {\n cwd = process.cwd(),\n socketConfig,\n ...additionalOptions\n } = { __proto__: null, ...options } as GlobWithGitIgnoreOptions\n\n const ignores = new Set<string>(IGNORED_DIR_PATTERNS)\n\n const projectIgnorePaths = socketConfig?.projectIgnorePaths\n if (Array.isArray(projectIgnorePaths)) {\n const ignorePatterns = ignoreFileLinesToGlobPatterns(\n projectIgnorePaths,\n path.join(cwd, '.gitignore'),\n cwd,\n )\n for (const pattern of ignorePatterns) {\n ignores.add(pattern)\n }\n }\n\n const gitIgnoreStream = fastGlob.globStream(['**/.gitignore'], {\n absolute: true,\n cwd,\n ignore: DEFAULT_IGNORE_FOR_GIT_IGNORE,\n })\n for await (const ignorePatterns of transform(\n gitIgnoreStream,\n async (filepath: string) =>\n ignoreFileToGlobPatterns(\n (await safeReadFile(filepath)) ?? '',\n filepath,\n cwd,\n ),\n { concurrency: 8 },\n )) {\n for (const p of ignorePatterns) {\n ignores.add(p)\n }\n }\n\n let hasNegatedPattern = false\n for (const p of ignores) {\n if (p.charCodeAt(0) === 33 /*'!'*/) {\n hasNegatedPattern = true\n break\n }\n }\n\n const globOptions = {\n __proto__: null,\n absolute: true,\n cwd,\n dot: true,\n ignore: hasNegatedPattern ? defaultIgnore : [...ignores],\n ...additionalOptions,\n } as GlobOptions\n\n if (!hasNegatedPattern) {\n return await fastGlob.glob(patterns as string[], globOptions)\n }\n\n // Add support for negated \"ignore\" patterns which many globbing libraries,\n // including 'fast-glob', 'globby', and 'tinyglobby', lack support for.\n const filtered: string[] = []\n const ig = ignore().add([...ignores])\n const stream = fastGlob.globStream(\n patterns as string[],\n globOptions,\n ) as AsyncIterable<string>\n for await (const p of stream) {\n // Note: the input files must be INSIDE the cwd. If you get strange looking\n // relative path errors here, most likely your path is outside the given cwd.\n const relPath = globOptions.absolute ? path.relative(cwd, p) : p\n if (!ig.ignores(relPath)) {\n filtered.push(p)\n }\n }\n return filtered\n}\n\nexport async function globWorkspace(\n agent: Agent,\n cwd = process.cwd(),\n): Promise<string[]> {\n const workspaceGlobs = await getWorkspaceGlobs(agent, cwd)\n return workspaceGlobs.length\n ? await fastGlob.glob(workspaceGlobs, {\n absolute: true,\n cwd,\n ignore: defaultIgnore,\n })\n : []\n}\n\nexport function isReportSupportedFile(\n filepath: string,\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n) {\n const patterns = getSupportedFilePatterns(supportedFiles)\n return micromatch.some(filepath, patterns)\n}\n\nexport function pathsToGlobPatterns(\n paths: string[] | readonly string[],\n cwd?: string | undefined,\n): string[] {\n // TODO: Does not support `~/` paths.\n return paths.map(p => {\n // Convert current directory references to glob patterns.\n if (p === '.' || p === './') {\n return '**/*'\n }\n const absolutePath = path.isAbsolute(p)\n ? p\n : path.resolve(cwd ?? process.cwd(), p)\n // If the path is a directory, scan it recursively for all files.\n if (isDirSync(absolutePath)) {\n return `${p}/**/*`\n }\n return p\n })\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport {\n resolveBinPathSync,\n whichBinSync,\n} from '@socketsecurity/registry/lib/bin'\nimport { isDirSync } from '@socketsecurity/registry/lib/fs'\n\nimport constants, { NODE_MODULES, NPM } from '../constants.mts'\nimport {\n filterBySupportedScanFiles,\n globWithGitIgnore,\n pathsToGlobPatterns,\n} from './glob.mts'\n\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport function findBinPathDetailsSync(binName: string): {\n name: string\n path: string | undefined\n shadowed: boolean\n} {\n const rawBinPaths =\n whichBinSync(binName, {\n all: true,\n nothrow: true,\n }) ?? []\n // whichBinSync may return a string when only one result is found, even with all: true.\n // This handles both the current published version and future versions.\n const binPaths = Array.isArray(rawBinPaths)\n ? rawBinPaths\n : typeof rawBinPaths === 'string'\n ? [rawBinPaths]\n : []\n const { shadowBinPath } = constants\n let shadowIndex = -1\n let theBinPath: string | undefined\n for (let i = 0, { length } = binPaths; i < length; i += 1) {\n const binPath = binPaths[i]!\n // Skip our bin directory if it's in the front.\n if (path.dirname(binPath) === shadowBinPath) {\n shadowIndex = i\n } else {\n theBinPath = resolveBinPathSync(binPath)\n break\n }\n }\n return { name: binName, path: theBinPath, shadowed: shadowIndex !== -1 }\n}\n\nexport function findNpmDirPathSync(npmBinPath: string): string | undefined {\n const { WIN32 } = constants\n let thePath = npmBinPath\n while (true) {\n const libNmNpmPath = path.join(thePath, `lib/${NODE_MODULES}/${NPM}`)\n // mise, which uses opaque binaries, puts its npm bin in a path like:\n // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/bin/npm.\n // HOWEVER, the location of the npm install is:\n // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/lib/node_modules/npm.\n if (\n // Use existsSync here because statsSync, even with { throwIfNoEntry: false },\n // will throw an ENOTDIR error for paths like ./a-file-that-exists/a-directory-that-does-not.\n // See https://github.com/nodejs/node/issues/56993.\n isDirSync(libNmNpmPath)\n ) {\n thePath = libNmNpmPath\n }\n const hasNmInCurrPath = isDirSync(path.join(thePath, NODE_MODULES))\n const hasNmInParentPath =\n !hasNmInCurrPath && isDirSync(path.join(thePath, `../${NODE_MODULES}`))\n if (\n // npm bin paths may look like:\n // /usr/local/share/npm/bin/npm\n // /Users/SomeUsername/.nvm/versions/node/vX.X.X/bin/npm\n // C:\\Users\\SomeUsername\\AppData\\Roaming\\npm\\bin\\npm.cmd\n // OR\n // C:\\Program Files\\nodejs\\npm.cmd\n //\n // In practically all cases the npm path contains a node_modules folder:\n // /usr/local/share/npm/bin/npm/node_modules\n // C:\\Program Files\\nodejs\\node_modules\n (hasNmInCurrPath ||\n // In some bespoke cases the node_modules folder is in the parent directory.\n hasNmInParentPath) &&\n // Optimistically look for the default location.\n (path.basename(thePath) === NPM ||\n // Chocolatey installs npm bins in the same directory as node bins.\n (WIN32 && existsSync(path.join(thePath, `${NPM}.cmd`))))\n ) {\n return hasNmInParentPath ? path.dirname(thePath) : thePath\n }\n const parent = path.dirname(thePath)\n if (parent === thePath) {\n return undefined\n }\n thePath = parent\n }\n}\n\nexport type PackageFilesForScanOptions = {\n cwd?: string | undefined\n config?: SocketYml | undefined\n}\n\nexport async function getPackageFilesForScan(\n inputPaths: string[],\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n options?: PackageFilesForScanOptions | undefined,\n): Promise<string[]> {\n const { config: socketConfig, cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as PackageFilesForScanOptions\n\n const filepaths = await globWithGitIgnore(\n pathsToGlobPatterns(inputPaths, options?.cwd),\n {\n cwd,\n socketConfig,\n },\n )\n\n return filterBySupportedScanFiles(filepaths!, supportedFiles)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { YARN } from '../constants.mts'\nimport { findBinPathDetailsSync } from './path-resolve.mts'\n\nfunction exitWithBinPathError(binName: string): never {\n logger.fail(\n `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`,\n )\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n}\n\nlet _yarnBinPath: string | undefined\nexport function getYarnBinPath(): string {\n if (_yarnBinPath === undefined) {\n _yarnBinPath = getYarnBinPathDetails().path\n if (!_yarnBinPath) {\n exitWithBinPathError(YARN)\n }\n }\n return _yarnBinPath\n}\n\nlet _yarnBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nexport function getYarnBinPathDetails(): ReturnType<\n typeof findBinPathDetailsSync\n> {\n if (_yarnBinPathDetails === undefined) {\n _yarnBinPathDetails = findBinPathDetailsSync(YARN)\n }\n return _yarnBinPathDetails\n}\n\nexport function isYarnBinPathShadowed(): boolean {\n return getYarnBinPathDetails().shadowed\n}\n","import { spawnSync } from '@socketsecurity/registry/lib/spawn'\n\nimport { getYarnBinPath } from './yarn-paths.mts'\nimport constants, { FLAG_VERSION, UTF8 } from '../constants.mts'\n\nlet _isYarnBerry: boolean | undefined\nexport function isYarnBerry(): boolean {\n if (_isYarnBerry === undefined) {\n try {\n const yarnBinPath = getYarnBinPath()\n const result = spawnSync(yarnBinPath, [FLAG_VERSION], {\n encoding: UTF8,\n // On Windows, yarn is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\n shell: constants.WIN32,\n })\n\n if (result.status === 0 && result.stdout) {\n const version = result.stdout\n // Yarn Berry starts from version 2.x\n const majorVersion = parseInt(version.split('.')[0]!, 10)\n _isYarnBerry = majorVersion >= 2\n } else {\n _isYarnBerry = false\n }\n } catch {\n _isYarnBerry = false\n }\n }\n return _isYarnBerry\n}\n","/**\n * DLX execution utilities for Socket CLI.\n * Manages package execution via npx/pnpm dlx/yarn dlx commands.\n *\n * Key Functions:\n * - spawnCdxgenDlx: Execute CycloneDX generator via dlx\n * - spawnCoanaDlx: Execute Coana CLI tool via dlx\n * - spawnDlx: Execute packages using dlx-style commands\n * - spawnSynpDlx: Execute Synp converter via dlx\n *\n * Package Manager Detection:\n * - Auto-detects npm, pnpm, or yarn based on lockfiles\n * - Supports force-refresh and silent execution modes\n *\n * Integration:\n * - Works with shadow binaries for security scanning\n * - Handles version pinning and cache management\n * - Configures environment for third-party tools\n */\n\nimport { createRequire } from 'node:module'\n\nimport { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport { getDefaultOrgSlug } from '../commands/ci/fetch-default-org-slug.mts'\nimport constants, {\n FLAG_QUIET,\n FLAG_SILENT,\n NPM,\n PNPM,\n YARN,\n} from '../constants.mts'\nimport { getErrorCause } from './errors.mts'\nimport { findUp } from './fs.mts'\nimport { getDefaultApiToken, getDefaultProxyUrl } from './sdk.mts'\nimport { isYarnBerry } from './yarn-version.mts'\n\nimport type { ShadowBinOptions, ShadowBinResult } from '../shadow/npm-base.mts'\nimport type { CResult } from '../types.mts'\nimport type { SpawnExtra } from '@socketsecurity/registry/lib/spawn'\n\nconst require = createRequire(import.meta.url)\n\nconst { PACKAGE_LOCK_JSON, PNPM_LOCK_YAML, YARN_LOCK } = constants\n\nexport type DlxOptions = ShadowBinOptions & {\n force?: boolean | undefined\n agent?: 'npm' | 'pnpm' | 'yarn' | undefined\n silent?: boolean | undefined\n}\n\nexport type DlxPackageSpec = {\n name: string\n version: string\n}\n\n/**\n * Regex to check if a version string contains range operators.\n * Matches any version with range operators: ~, ^, >, <, =, x, X, *, spaces, or ||.\n */\nconst rangeOperatorsRegExp = /[~^><=xX* ]|\\|\\|/\n\n/**\n * Spawns a package using dlx-style execution (npx/pnpm dlx/yarn dlx).\n * Automatically detects the appropriate package manager if not specified.\n * Uses force/update flags to ensure the latest version within the range is fetched.\n */\nexport async function spawnDlx(\n packageSpec: DlxPackageSpec,\n args: string[] | readonly string[],\n options?: DlxOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<ShadowBinResult> {\n // If version is not pinned exactly, default to force and silent for better UX.\n const isNotPinned = rangeOperatorsRegExp.test(packageSpec.version)\n\n const {\n agent,\n force = false,\n silent = isNotPinned,\n ...shadowOptions\n } = options ?? {}\n\n let finalShadowOptions = shadowOptions\n\n let pm = agent\n\n // Auto-detect package manager if not specified.\n if (!pm) {\n const pnpmLockPath = await findUp(PNPM_LOCK_YAML, { onlyFiles: true })\n const yarnLockPath = pnpmLockPath\n ? undefined\n : await findUp(YARN_LOCK, { onlyFiles: true })\n const npmLockPath =\n pnpmLockPath || yarnLockPath\n ? undefined\n : await findUp(PACKAGE_LOCK_JSON, { onlyFiles: true })\n\n if (pnpmLockPath) {\n pm = PNPM\n } else if (yarnLockPath) {\n pm = YARN\n } else if (npmLockPath) {\n pm = NPM\n } else {\n // Default to npm if no lockfile found.\n pm = NPM\n }\n }\n\n const packageString = `${packageSpec.name}@${packageSpec.version}`\n\n // Build command args based on package manager.\n let spawnArgs: string[]\n\n if (pm === PNPM) {\n spawnArgs = []\n // The --silent flag must come before dlx, not after.\n if (silent) {\n spawnArgs.push(FLAG_SILENT)\n }\n spawnArgs.push('dlx')\n if (force) {\n // For pnpm, set dlx-cache-max-age to 0 via env to force fresh download.\n // This ensures we always get the latest version within the range.\n finalShadowOptions = {\n ...finalShadowOptions,\n env: {\n ...getOwn(finalShadowOptions, 'env'),\n // Set dlx cache max age to 0 minutes to bypass cache.\n // The npm_config_ prefix is how pnpm reads config from environment variables.\n // See: https://pnpm.io/npmrc#settings\n npm_config_dlx_cache_max_age: '0',\n },\n }\n }\n spawnArgs.push(packageString, ...args)\n\n const shadowPnpmBin = /*@__PURE__*/ require(constants.shadowPnpmBinPath)\n return await shadowPnpmBin(spawnArgs, finalShadowOptions, spawnExtra)\n } else if (pm === YARN && isYarnBerry()) {\n spawnArgs = ['dlx']\n // Yarn dlx runs in a temporary environment by design and should always fetch fresh.\n if (silent) {\n spawnArgs.push(FLAG_QUIET)\n }\n spawnArgs.push(packageString, ...args)\n\n const shadowYarnBin = /*@__PURE__*/ require(constants.shadowYarnBinPath)\n return await shadowYarnBin(spawnArgs, finalShadowOptions, spawnExtra)\n } else {\n // Use npm exec/npx.\n // For consistency, we'll use npx which is more commonly used for one-off execution.\n spawnArgs = ['--yes']\n if (force) {\n // Use --force to bypass cache and get latest within range.\n spawnArgs.push('--force')\n }\n if (silent) {\n spawnArgs.push(FLAG_SILENT)\n }\n spawnArgs.push(packageString, ...args)\n\n const shadowNpxBin = /*@__PURE__*/ require(constants.shadowNpxBinPath)\n return await shadowNpxBin(spawnArgs, finalShadowOptions, spawnExtra)\n }\n}\n\n/**\n * Helper to spawn coana with dlx.\n * Automatically uses force and silent when version is not pinned exactly.\n * Returns a CResult with stdout extraction for backward compatibility.\n *\n * If SOCKET_CLI_COANA_LOCAL_PATH environment variable is set, uses the local\n * Coana CLI at that path instead of downloading from npm.\n */\nexport async function spawnCoanaDlx(\n args: string[] | readonly string[],\n orgSlug?: string,\n options?: DlxOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<CResult<string>> {\n const {\n env: spawnEnv,\n ipc,\n ...dlxOptions\n } = {\n __proto__: null,\n ...options,\n } as DlxOptions\n\n const mixinsEnv: Record<string, string> = {\n SOCKET_CLI_VERSION: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n }\n const defaultApiToken = getDefaultApiToken()\n if (defaultApiToken) {\n mixinsEnv['SOCKET_CLI_API_TOKEN'] = defaultApiToken\n }\n\n if (orgSlug) {\n mixinsEnv['SOCKET_ORG_SLUG'] = orgSlug\n } else {\n const orgSlugCResult = await getDefaultOrgSlug()\n if (orgSlugCResult.ok) {\n mixinsEnv['SOCKET_ORG_SLUG'] = orgSlugCResult.data\n }\n }\n\n const proxyUrl = getDefaultProxyUrl()\n if (proxyUrl) {\n mixinsEnv['SOCKET_CLI_API_PROXY'] = proxyUrl\n }\n\n try {\n const localCoanaPath = process.env['SOCKET_CLI_COANA_LOCAL_PATH']\n // Use local Coana CLI if path is provided.\n if (localCoanaPath) {\n const isBinary =\n !localCoanaPath.endsWith('.js') && !localCoanaPath.endsWith('.mjs')\n\n const finalEnv = {\n ...process.env,\n ...constants.processEnv,\n ...mixinsEnv,\n ...spawnEnv,\n }\n\n const spawnArgs = isBinary ? args : [localCoanaPath, ...args]\n const spawnResult = await spawn(\n isBinary ? localCoanaPath : 'node',\n spawnArgs,\n {\n cwd: dlxOptions.cwd,\n env: finalEnv,\n stdio: spawnExtra?.['stdio'] || 'inherit',\n },\n )\n\n return { ok: true, data: spawnResult.stdout }\n }\n\n // Use npm/dlx version.\n const result = await spawnDlx(\n {\n name: '@coana-tech/cli',\n version: constants.ENV.INLINED_SOCKET_CLI_COANA_TECH_CLI_VERSION,\n },\n args,\n {\n force: true,\n silent: true,\n ...dlxOptions,\n env: {\n ...process.env,\n ...constants.processEnv,\n ...mixinsEnv,\n ...spawnEnv,\n },\n ipc: {\n [constants.SOCKET_CLI_SHADOW_ACCEPT_RISKS]: true,\n [constants.SOCKET_CLI_SHADOW_API_TOKEN]:\n constants.SOCKET_PUBLIC_API_TOKEN,\n [constants.SOCKET_CLI_SHADOW_SILENT]: true,\n ...ipc,\n },\n },\n spawnExtra,\n )\n const output = await result.spawnPromise\n return { ok: true, data: output.stdout }\n } catch (e) {\n const stderr = (e as any)?.stderr\n const cause = getErrorCause(e)\n const message = stderr || cause\n return {\n ok: false,\n data: e,\n message,\n }\n }\n}\n\n/**\n * Helper to spawn cdxgen with dlx.\n */\nexport async function spawnCdxgenDlx(\n args: string[] | readonly string[],\n options?: DlxOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<ShadowBinResult> {\n return await spawnDlx(\n {\n name: '@cyclonedx/cdxgen',\n version: constants.ENV.INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION,\n },\n args,\n { force: false, silent: true, ...options },\n spawnExtra,\n )\n}\n\n/**\n * Helper to spawn synp with dlx.\n */\nexport async function spawnSynpDlx(\n args: string[] | readonly string[],\n options?: DlxOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<ShadowBinResult> {\n return await spawnDlx(\n {\n name: 'synp',\n version: `${constants.ENV.INLINED_SOCKET_CLI_SYNP_VERSION}`,\n },\n args,\n { force: false, silent: true, ...options },\n spawnExtra,\n )\n}\n","import type {\n EnterpriseOrganizations,\n Organizations,\n} from '../commands/organization/fetch-organization-list.mts'\n\nexport function getEnterpriseOrgs(\n orgs: Organizations,\n): EnterpriseOrganizations {\n return orgs.filter(o =>\n o.plan.includes('enterprise'),\n ) as EnterpriseOrganizations\n}\n\nexport function getOrgSlugs(orgs: Organizations): string[] {\n return orgs.map(o => o.slug)\n}\n\nexport function hasEnterpriseOrgPlan(orgs: Organizations): boolean {\n return orgs.some(o => o.plan.includes('enterprise'))\n}\n","/**\n * Socket JSON utilities for Socket CLI.\n * Manages .socket/socket.json configuration and scan metadata.\n *\n * Key Functions:\n * - loadDotSocketDirectory: Load .socket directory configuration\n * - saveSocketJson: Persist scan configuration to .socket/socket.json\n * - validateSocketJson: Validate socket.json structure\n *\n * File Structure:\n * - Contains scan metadata and configuration\n * - Stores scan IDs and repository information\n * - Tracks CLI version and scan timestamps\n *\n * Directory Management:\n * - Creates .socket directory as needed\n * - Handles nested directory structures\n * - Supports both read and write operations\n */\n\nimport { existsSync, promises as fs, readFileSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { formatErrorWithDetail } from './errors.mts'\nimport { findUp } from './fs.mts'\nimport { SOCKET_JSON, SOCKET_WEBSITE_URL } from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\n\nexport interface SocketJson {\n ' _____ _ _ ': string\n '| __|___ ___| |_ ___| |_ ': string\n \"|__ | . | _| '_| -_| _| \": string\n '|_____|___|___|_,_|___|_|.dev': string\n version: number\n\n defaults?: {\n manifest?: {\n conda?: {\n disabled?: boolean | undefined\n infile?: string | undefined\n outfile?: string | undefined\n stdin?: boolean | undefined\n stdout?: boolean | undefined\n target?: string | undefined\n verbose?: boolean | undefined\n }\n gradle?: {\n disabled?: boolean | undefined\n bin?: string | undefined\n gradleOpts?: string | undefined\n verbose?: boolean | undefined\n }\n sbt?: {\n disabled?: boolean | undefined\n infile?: string | undefined\n stdin?: boolean | undefined\n bin?: string | undefined\n outfile?: string | undefined\n sbtOpts?: string | undefined\n stdout?: boolean | undefined\n verbose?: boolean | undefined\n }\n }\n scan?: {\n create?: {\n autoManifest?: boolean | undefined\n repo?: string | undefined\n report?: boolean | undefined\n branch?: string | undefined\n }\n github?: {\n all?: boolean | undefined\n githubApiUrl?: string | undefined\n orgGithub?: string | undefined\n repos?: string | undefined\n }\n }\n }\n}\n\nexport function readOrDefaultSocketJson(cwd: string): SocketJson {\n const jsonCResult = readSocketJsonSync(cwd, true)\n return jsonCResult.ok\n ? jsonCResult.data\n : // This should be unreachable but it makes TS happy.\n getDefaultSocketJson()\n}\n\nexport async function findSocketJsonUp(\n cwd: string,\n): Promise<string | undefined> {\n return await findUp(SOCKET_JSON, { onlyFiles: true, cwd })\n}\n\nexport async function readOrDefaultSocketJsonUp(\n cwd: string,\n): Promise<SocketJson> {\n const socketJsonPath = await findSocketJsonUp(cwd)\n if (socketJsonPath) {\n const socketJsonDir = path.dirname(socketJsonPath)\n const jsonCResult = readSocketJsonSync(socketJsonDir, true)\n return jsonCResult.ok ? jsonCResult.data : getDefaultSocketJson()\n }\n return getDefaultSocketJson()\n}\n\nexport function getDefaultSocketJson(): SocketJson {\n return {\n ' _____ _ _ ': `Local config file for Socket CLI tool ( ${SOCKET_WEBSITE_URL}/npm/package/${SOCKET_JSON.replace('.json', '')} ), to work with ${SOCKET_WEBSITE_URL}`,\n '| __|___ ___| |_ ___| |_ ':\n ' The config in this file is used to set as defaults for flags or command args when using the CLI',\n \"|__ | . | _| '_| -_| _| \":\n ' in this dir, often a repo root. You can choose commit or .ignore this file, both works.',\n '|_____|___|___|_,_|___|_|.dev': `Warning: This file may be overwritten without warning by \\`${SOCKET_JSON.replace('.json', '')} manifest setup\\` or other commands`,\n version: 1,\n }\n}\n\nexport async function readSocketJson(\n cwd: string,\n defaultOnError = false,\n): Promise<CResult<SocketJson>> {\n const sockJsonPath = path.join(cwd, SOCKET_JSON)\n if (!existsSync(sockJsonPath)) {\n debugFn('notice', `miss: ${SOCKET_JSON} not found at ${cwd}`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n\n let json = null\n try {\n json = await fs.readFile(sockJsonPath, 'utf8')\n } catch (e) {\n if (defaultOnError) {\n logger.warn(`Failed to read ${SOCKET_JSON}, using default`)\n debugFn('warn', `Failed to read ${SOCKET_JSON}`)\n debugDir('warn', e)\n return { ok: true, data: getDefaultSocketJson() }\n }\n const cause = formatErrorWithDetail(\n `An error occurred while trying to read ${SOCKET_JSON}`,\n e,\n )\n debugFn('error', `Failed to read ${SOCKET_JSON}`)\n debugDir('error', e)\n return {\n ok: false,\n message: `Failed to read ${SOCKET_JSON}`,\n cause,\n }\n }\n\n let obj\n try {\n obj = JSON.parse(json)\n } catch (e) {\n debugFn('error', `Failed to parse ${SOCKET_JSON} as JSON`)\n debugDir('inspect', { json })\n debugDir('error', e)\n if (defaultOnError) {\n logger.warn(`Failed to parse ${SOCKET_JSON}, using default`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n return {\n ok: false,\n message: `Failed to parse ${SOCKET_JSON}`,\n cause: `${SOCKET_JSON} does not contain valid JSON, please verify`,\n }\n }\n\n if (!obj) {\n logger.warn('Warning: file contents was empty, using default')\n return { ok: true, data: getDefaultSocketJson() }\n }\n\n // Do we really care to validate? All properties are optional so code will have\n // to check every step of the way regardless. Who cares about validation here...?\n return { ok: true, data: obj }\n}\n\nexport function readSocketJsonSync(\n cwd: string,\n defaultOnError = false,\n): CResult<SocketJson> {\n const sockJsonPath = path.join(cwd, SOCKET_JSON)\n if (!existsSync(sockJsonPath)) {\n debugFn('notice', `miss: ${SOCKET_JSON} not found at ${cwd}`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n let jsonContent = null\n try {\n jsonContent = readFileSync(sockJsonPath, 'utf8')\n } catch (e) {\n if (defaultOnError) {\n logger.warn(`Failed to read ${SOCKET_JSON}, using default`)\n debugFn('warn', `Failed to read ${SOCKET_JSON} sync`)\n debugDir('warn', e)\n return { ok: true, data: getDefaultSocketJson() }\n }\n const cause = formatErrorWithDetail(\n `An error occurred while trying to read ${SOCKET_JSON}`,\n e,\n )\n debugFn('error', `Failed to read ${SOCKET_JSON} sync`)\n debugDir('error', e)\n return {\n ok: false,\n message: `Failed to read ${SOCKET_JSON}`,\n cause,\n }\n }\n\n let jsonObj\n try {\n jsonObj = JSON.parse(jsonContent)\n } catch (e) {\n debugFn('error', `Failed to parse ${SOCKET_JSON} as JSON (sync)`)\n debugDir('inspect', { jsonContent })\n debugDir('error', e)\n if (defaultOnError) {\n logger.warn(`Failed to parse ${SOCKET_JSON}, using default`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n return {\n ok: false,\n message: `Failed to parse ${SOCKET_JSON}`,\n cause: `${SOCKET_JSON} does not contain valid JSON, please verify`,\n }\n }\n\n if (!jsonObj) {\n logger.warn('Warning: file contents was empty, using default')\n return { ok: true, data: getDefaultSocketJson() }\n }\n\n // TODO: Do we need to validate? All properties are optional so code will have\n // to check every step of the way regardless.\n return { ok: true, data: jsonObj }\n}\n\nexport async function writeSocketJson(\n cwd: string,\n sockJson: SocketJson,\n): Promise<CResult<undefined>> {\n let jsonContent = ''\n try {\n jsonContent = JSON.stringify(sockJson, null, 2)\n } catch (e) {\n debugFn('error', `Failed to serialize ${SOCKET_JSON} to JSON`)\n debugDir('inspect', { sockJson })\n debugDir('error', e)\n return {\n ok: false,\n message: 'Failed to serialize to JSON',\n cause: `There was an unexpected problem converting the ${SOCKET_JSON} object to a JSON string. Unable to store it.`,\n }\n }\n\n const filepath = path.join(cwd, SOCKET_JSON)\n await fs.writeFile(filepath, `${jsonContent}\\n`, 'utf8')\n\n return { ok: true, data: undefined }\n}\n","/**\n * GitHub utilities for Socket CLI.\n * Provides GitHub API integration for repository operations and GHSA vulnerability data.\n *\n * Authentication:\n * - getGitHubToken: Retrieve GitHub token from env/git config\n * - getOctokit: Get authenticated Octokit instance\n * - getOctokitGraphql: Get authenticated GraphQL client\n *\n * Caching:\n * - 5-minute TTL for API responses\n * - Automatic cache invalidation\n * - Persistent cache in node_modules/.cache\n *\n * GHSA Operations:\n * - cacheFetch: Cache API responses with TTL\n * - fetchGhsaDetails: Fetch GitHub Security Advisory details\n * - getGhsaUrl: Generate GHSA advisory URL\n * - readCache/writeCache: Persistent cache operations\n *\n * Repository Operations:\n * - GraphQL queries for complex operations\n * - Integration with Octokit REST API\n * - Support for GitHub Actions environment variables\n */\n\nimport { existsSync, promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport {\n GraphqlResponseError,\n graphql as OctokitGraphql,\n} from '@octokit/graphql'\nimport { Octokit } from '@octokit/rest'\n\nimport { debugDir, debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport {\n readJson,\n safeStatsSync,\n writeJson,\n} from '@socketsecurity/registry/lib/fs'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { parseUrl } from '@socketsecurity/registry/lib/url'\n\nimport { formatErrorWithDetail } from './errors.mts'\nimport constants from '../constants.mts'\n\nimport type { components } from '@octokit/openapi-types'\nimport type { JsonContent } from '@socketsecurity/registry/lib/fs'\nimport type { SpawnOptions } from '@socketsecurity/registry/lib/spawn'\n\nexport type Pr = components['schemas']['pull-request']\n\nasync function readCache(\n key: string,\n // 5 minute in milliseconds time to live (TTL).\n ttlMs = 5 * 60 * 1000,\n): Promise<JsonContent | undefined> {\n const cacheJsonPath = path.join(constants.githubCachePath, `${key}.json`)\n const stat = safeStatsSync(cacheJsonPath)\n if (stat) {\n const isExpired = Date.now() - stat.mtimeMs > ttlMs\n if (!isExpired) {\n return await readJson(cacheJsonPath)\n }\n }\n return undefined\n}\n\nexport async function writeCache(\n key: string,\n data: JsonContent,\n): Promise<void> {\n const { githubCachePath } = constants\n const cacheJsonPath = path.join(githubCachePath, `${key}.json`)\n if (!existsSync(githubCachePath)) {\n await fs.mkdir(githubCachePath, { recursive: true })\n }\n await writeJson(cacheJsonPath, data as JsonContent)\n}\n\nexport async function cacheFetch<T>(\n key: string,\n fetcher: () => Promise<T>,\n ttlMs?: number | undefined,\n): Promise<T> {\n // Optionally disable cache.\n if (constants.ENV.DISABLE_GITHUB_CACHE) {\n return await fetcher()\n }\n let data = (await readCache(key, ttlMs)) as T\n if (!data) {\n data = await fetcher()\n await writeCache(key, data as JsonContent)\n }\n return data\n}\n\nexport type GhsaDetails = {\n ghsaId: string\n cveId?: string | undefined\n summary: string\n severity: string\n publishedAt: string\n withdrawnAt?: string | undefined\n references: Array<{\n url: string\n }>\n vulnerabilities: {\n nodes: Array<{\n package: {\n ecosystem: string\n name: string\n }\n vulnerableVersionRange: string\n }>\n }\n}\n\nexport async function fetchGhsaDetails(\n ids: string[],\n): Promise<Map<string, GhsaDetails>> {\n const results = new Map<string, GhsaDetails>()\n if (!ids.length) {\n return results\n }\n\n const octokitGraphql = getOctokitGraphql()\n try {\n const gqlCacheKey = `${ids.join('-')}-graphql-snapshot`\n\n const aliases = ids\n .map(\n (id, index) =>\n `advisory${index}: securityAdvisory(ghsaId: \"${id}\") {\n ghsaId\n summary\n severity\n publishedAt\n withdrawnAt\n vulnerabilities(first: 10) {\n nodes {\n package {\n ecosystem\n name\n }\n vulnerableVersionRange\n }\n }\n }`,\n )\n .join('\\n')\n\n const gqlResp = await cacheFetch(gqlCacheKey, () =>\n octokitGraphql(`\n query {\n ${aliases}\n }\n `),\n )\n\n for (let i = 0, { length } = ids; i < length; i += 1) {\n const id = ids[i]!\n const advisoryKey = `advisory${i}`\n const advisory = (gqlResp as any)?.[advisoryKey]\n if (advisory && advisory.ghsaId) {\n results.set(id, advisory as GhsaDetails)\n } else {\n debugFn('notice', `miss: no advisory found for ${id}`)\n }\n }\n } catch (e) {\n debugFn('error', formatErrorWithDetail('Failed to fetch GHSA details', e))\n debugDir('error', e)\n }\n\n return results\n}\n\nlet _octokit: Octokit | undefined\nexport function getOctokit(): Octokit {\n if (_octokit === undefined) {\n const { SOCKET_CLI_GITHUB_TOKEN } = constants.ENV\n if (!SOCKET_CLI_GITHUB_TOKEN) {\n debugFn('notice', 'miss: SOCKET_CLI_GITHUB_TOKEN env var')\n }\n const octokitOptions = {\n auth: SOCKET_CLI_GITHUB_TOKEN,\n baseUrl: constants.ENV.GITHUB_API_URL,\n }\n debugDir('inspect', { octokitOptions })\n _octokit = new Octokit(octokitOptions)\n }\n return _octokit\n}\n\nlet _octokitGraphql: typeof OctokitGraphql | undefined\nexport function getOctokitGraphql(): typeof OctokitGraphql {\n if (!_octokitGraphql) {\n const { SOCKET_CLI_GITHUB_TOKEN } = constants.ENV\n if (!SOCKET_CLI_GITHUB_TOKEN) {\n debugFn('notice', 'miss: SOCKET_CLI_GITHUB_TOKEN env var')\n }\n _octokitGraphql = OctokitGraphql.defaults({\n headers: {\n authorization: `token ${SOCKET_CLI_GITHUB_TOKEN}`,\n },\n })\n }\n return _octokitGraphql\n}\n\nexport type PrAutoMergeState = {\n enabled: boolean\n details?: string[] | undefined\n}\n\nexport async function enablePrAutoMerge({\n node_id: prId,\n}: Pr): Promise<PrAutoMergeState> {\n const octokitGraphql = getOctokitGraphql()\n try {\n const gqlResp = await octokitGraphql(\n `\n mutation EnableAutoMerge($pullRequestId: ID!) {\n enablePullRequestAutoMerge(input: {\n pullRequestId: $pullRequestId,\n mergeMethod: SQUASH\n }) {\n pullRequest {\n number\n }\n }\n }`,\n { pullRequestId: prId },\n )\n const respPrNumber = (gqlResp as any)?.enablePullRequestAutoMerge\n ?.pullRequest?.number\n if (respPrNumber) {\n return { enabled: true }\n }\n } catch (e) {\n if (\n e instanceof GraphqlResponseError &&\n Array.isArray(e.errors) &&\n e.errors.length\n ) {\n const details = e.errors.map(({ message: m }) => m.trim())\n return { enabled: false, details }\n }\n }\n return { enabled: false }\n}\n\nexport async function prExistForBranch(\n owner: string,\n repo: string,\n branch: string,\n): Promise<boolean> {\n const octokit = getOctokit()\n try {\n const { data: prs } = await octokit.pulls.list({\n owner,\n repo,\n head: `${owner}:${branch}`,\n state: 'all',\n per_page: 1,\n })\n return prs.length > 0\n } catch {}\n return false\n}\n\nexport async function setGitRemoteGithubRepoUrl(\n owner: string,\n repo: string,\n token: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const { GITHUB_SERVER_URL } = constants.ENV\n const urlObj = parseUrl(GITHUB_SERVER_URL)\n const host = urlObj?.host\n if (!host) {\n debugFn('error', 'invalid: GITHUB_SERVER_URL env var')\n debugDir('inspect', { GITHUB_SERVER_URL })\n return false\n }\n const url = `https://x-access-token:${token}@${host}/${owner}/${repo}`\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git remote set-url origin ${url}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['remote', 'set-url', 'origin', url], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `Git command failed: ${quotedCmd}`)\n debugDir('inspect', { cmd: quotedCmd })\n debugDir('error', e)\n }\n return false\n}\n","/**\n * Command-line utilities for Socket CLI.\n * Handles argument parsing, flag processing, and command formatting.\n *\n * Argument Handling:\n * - Handles both long (--flag) and short (-f) formats\n * - Preserves special characters and escaping\n * - Properly quotes arguments containing spaces\n *\n * Command Names:\n * - commandNameFromCamel: Convert camelCase to kebab-case command names\n * - commandNameFromKebab: Convert kebab-case to camelCase\n *\n * Flag Processing:\n * - cmdFlagsToString: Format arguments for display with proper escaping\n * - cmdPrefixMessage: Generate command prefix message\n * - stripConfigFlags: Remove --config flags from argument list\n * - stripDebugFlags: Remove debug-related flags\n * - stripHelpFlags: Remove help flags (-h, --help)\n */\n\nimport { FLAG_CONFIG, FLAG_HELP } from '../constants.mts'\nimport { camelToKebab } from './strings.mts'\n\nconst CONFIG_FLAG_LONG_NAME = FLAG_CONFIG\nconst CONFIG_FLAG_ASSIGNMENT = `${CONFIG_FLAG_LONG_NAME}=`\nconst CONFIG_FLAG_ASSIGNMENT_LENGTH = CONFIG_FLAG_ASSIGNMENT.length\n\nconst configFlags = new Set([FLAG_CONFIG])\nconst helpFlags = new Set([FLAG_HELP, '-h'])\n\n/**\n * Convert flag values to array format for processing.\n */\nexport function cmdFlagValueToArray(value: any): string[] {\n if (typeof value === 'string') {\n return value.trim().split(/, */).filter(Boolean)\n }\n if (Array.isArray(value)) {\n return value.flatMap(cmdFlagValueToArray)\n }\n return []\n}\n\n/**\n * Convert command arguments to a properly formatted string representation.\n */\nexport function cmdFlagsToString(args: string[] | readonly string[]): string {\n const result = []\n for (let i = 0, { length } = args; i < length; i += 1) {\n const arg = args[i]!.trim()\n if (arg.startsWith('--')) {\n const nextArg = i + 1 < length ? args[i + 1]!.trim() : undefined\n // Check if the next item exists and is NOT another flag.\n if (nextArg && !nextArg.startsWith('--') && !nextArg.startsWith('-')) {\n result.push(`${arg}=${nextArg}`)\n i += 1\n } else {\n result.push(arg)\n }\n } else {\n // Include non-flag arguments (commands, package names, etc.).\n result.push(arg)\n }\n }\n return result.join(' ')\n}\n\n/**\n * Add command name prefix to message text.\n */\nexport function cmdPrefixMessage(cmdName: string, text: string): string {\n const cmdPrefix = cmdName ? `${cmdName}: ` : ''\n return `${cmdPrefix}${text}`\n}\n\n/**\n * Filter out Socket flags from argv before passing to subcommands.\n */\nexport function filterFlags(\n argv: readonly string[],\n flagsToFilter: Record<string, any>,\n exceptions?: string[] | undefined,\n): string[] {\n const filtered: string[] = []\n\n // Build set of flags to filter from the provided flag objects.\n const flagsToFilterSet = new Set<string>()\n const flagsWithValueSet = new Set<string>()\n\n for (const [flagName, flag] of Object.entries(flagsToFilter)) {\n const longFlag = `--${camelToKebab(flagName)}`\n // Special case for negated booleans.\n if (flagName === 'spinner' || flagName === 'banner') {\n flagsToFilterSet.add(`--no-${flagName}`)\n } else {\n flagsToFilterSet.add(longFlag)\n }\n if (flag?.shortFlag) {\n flagsToFilterSet.add(`-${flag.shortFlag}`)\n }\n // Track flags that take values.\n if (flag.type !== 'boolean') {\n flagsWithValueSet.add(longFlag)\n if (flag?.shortFlag) {\n flagsWithValueSet.add(`-${flag.shortFlag}`)\n }\n }\n }\n\n for (let i = 0, { length } = argv; i < length; i += 1) {\n const arg = argv[i]!\n // Check if this flag should be kept as an exception.\n if (exceptions?.includes(arg)) {\n filtered.push(arg)\n // Handle flags that take values.\n if (flagsWithValueSet.has(arg)) {\n // Include the next argument (the flag value).\n i += 1\n if (i < length) {\n filtered.push(argv[i]!)\n }\n }\n } else if (flagsToFilterSet.has(arg)) {\n // Skip flags that take values.\n if (flagsWithValueSet.has(arg)) {\n // Skip the next argument (the flag value).\n i += 1\n }\n // Skip boolean flags (no additional argument to skip).\n } else if (\n arg &&\n Array.from(flagsWithValueSet).some(flag => arg.startsWith(`${flag}=`))\n ) {\n // Skip --flag=value format for Socket flags unless it's an exception.\n if (exceptions?.some(exc => arg.startsWith(`${exc}=`))) {\n filtered.push(arg)\n }\n // Otherwise skip it.\n } else {\n filtered.push(arg!)\n }\n }\n return filtered\n}\n\n/**\n * Extract config flag value from command arguments.\n */\nexport function getConfigFlag(\n argv: string[] | readonly string[],\n): string | undefined {\n for (let i = 0, { length } = argv; i < length; i += 1) {\n const arg = argv[i]!.trim()\n // Handle --config=value format.\n if (arg.startsWith(CONFIG_FLAG_ASSIGNMENT)) {\n return arg.slice(CONFIG_FLAG_ASSIGNMENT_LENGTH)\n }\n // Handle --config value format.\n if (arg === CONFIG_FLAG_LONG_NAME && i + 1 < length) {\n return argv[i + 1]\n }\n }\n return undefined\n}\n\n/**\n * Check if command is an add command (adds new dependencies).\n * Supported by: pnpm, yarn.\n * Note: npm uses 'install' with package names instead of 'add'.\n */\nexport function isAddCommand(command: string): boolean {\n return command === 'add'\n}\n\n/**\n * Check if argument is a config flag.\n */\nexport function isConfigFlag(cmdArg: string): boolean {\n return configFlags.has(cmdArg) || cmdArg.startsWith(CONFIG_FLAG_ASSIGNMENT)\n}\n\n/**\n * Check if argument is a help flag.\n */\nexport function isHelpFlag(cmdArg: string): boolean {\n return helpFlags.has(cmdArg)\n}\n\n/**\n * Check if npm command requires lockfile scanning.\n * npm uses: install, i, update\n */\nexport function isNpmLockfileScanCommand(command: string): boolean {\n return command === 'install' || command === 'i' || command === 'update'\n}\n\n/**\n * Check if pnpm command requires lockfile scanning.\n * pnpm uses: install, i, update, up\n */\nexport function isPnpmLockfileScanCommand(command: string): boolean {\n return (\n command === 'install' ||\n command === 'i' ||\n command === 'update' ||\n command === 'up'\n )\n}\n\n/**\n * Check if yarn command requires lockfile scanning.\n * yarn uses: install, up, upgrade, upgrade-interactive\n */\nexport function isYarnLockfileScanCommand(command: string): boolean {\n return (\n command === 'install' ||\n command === 'up' ||\n command === 'upgrade' ||\n command === 'upgrade-interactive'\n )\n}\n","import { getErrorCause } from './errors.mts'\nimport { cacheFetch, getOctokit } from './github.mts'\n\nimport type { CResult } from '../types.mts'\n\n/**\n * Converts CVE IDs to GHSA IDs using GitHub API.\n * CVE to GHSA mappings are permanent, so we cache for 30 days.\n */\nexport async function convertCveToGhsa(\n cveId: string,\n): Promise<CResult<string>> {\n try {\n const cacheKey = `cve-to-ghsa-${cveId}`\n const octokit = getOctokit()\n\n const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000\n\n const response = await cacheFetch(\n cacheKey,\n () =>\n octokit.rest.securityAdvisories.listGlobalAdvisories({\n cve_id: cveId,\n per_page: 1,\n }),\n THIRTY_DAYS_MS,\n )\n\n if (!response.data.length) {\n return {\n ok: false,\n message: `No GHSA found for CVE ${cveId}`,\n }\n }\n\n return {\n ok: true,\n data: response.data[0]!.ghsa_id,\n }\n } catch (e) {\n const errorCause = getErrorCause(e)\n const errorLower = errorCause.toLowerCase()\n // Detect GitHub API rate limit and network errors.\n const isRateLimitOrNetworkError =\n errorLower.includes('rate limit') ||\n errorLower.includes('epipe') ||\n errorLower.includes('econnreset') ||\n errorLower.includes('status: 403') ||\n errorLower.includes('status code 403')\n\n return {\n ok: false,\n message: isRateLimitOrNetworkError\n ? 'GitHub API rate limit exceeded while converting CVE to GHSA. Wait an hour or set SOCKET_CLI_GITHUB_TOKEN environment variable with a personal access token for higher limits.'\n : `Failed to convert CVE to GHSA: ${errorCause}`,\n }\n }\n}\n","import { cacheFetch, getOctokit } from './github.mts'\nimport { getPurlObject } from './purl.mts'\nimport { LATEST } from '../constants.mts'\nimport { getErrorCause } from './errors.mts'\n\nimport type { CResult } from '../types.mts'\n\nconst PURL_TO_GITHUB_ECOSYSTEM_MAPPING = {\n __proto__: null,\n // GitHub Advisory Database supported ecosystems\n cargo: 'rust',\n composer: 'composer',\n gem: 'rubygems',\n go: 'go',\n golang: 'go',\n maven: 'maven',\n npm: 'npm',\n nuget: 'nuget',\n pypi: 'pip',\n swift: 'swift',\n} as unknown as Record<string, string>\n\n/**\n * Converts PURL to GHSA IDs using GitHub API.\n */\nexport async function convertPurlToGhsas(\n purl: string,\n): Promise<CResult<string[]>> {\n try {\n const purlObj = getPurlObject(purl, { throws: false })\n if (!purlObj) {\n return {\n ok: false,\n message: `Invalid PURL format: ${purl}`,\n }\n }\n\n const { name, type: ecosystem, version } = purlObj\n\n // Map PURL ecosystem to GitHub ecosystem.\n const githubEcosystem = PURL_TO_GITHUB_ECOSYSTEM_MAPPING[ecosystem]\n if (!githubEcosystem) {\n return {\n ok: false,\n message: `Unsupported PURL ecosystem: ${ecosystem}`,\n }\n }\n\n // Search for advisories affecting this package.\n const cacheKey = `purl-to-ghsa-${ecosystem}-${name}-${version || LATEST}`\n const octokit = getOctokit()\n const affects = version ? `${name}@${version}` : name\n\n const response = await cacheFetch(cacheKey, () =>\n octokit.rest.securityAdvisories.listGlobalAdvisories({\n ecosystem: githubEcosystem as any,\n affects,\n }),\n )\n\n return {\n ok: true,\n data: response.data.map(a => a.ghsa_id),\n }\n } catch (e) {\n return {\n ok: false,\n message: `Failed to convert PURL to GHSA: ${getErrorCause(e)}`,\n }\n }\n}\n","import semver from 'semver'\n\nimport type { SemVer } from 'semver'\n\nexport const RangeStyles = ['pin', 'preserve']\n\nexport type RangeStyle = 'pin' | 'preserve'\n\nexport type { SemVer }\n\nexport function getMajor(version: unknown): number | undefined {\n try {\n const coerced = semver.coerce(version as string)\n return coerced ? semver.major(coerced) : undefined\n } catch {}\n return undefined\n}\n\nexport function getMinVersion(range: unknown): SemVer | undefined {\n try {\n return semver.minVersion(range as string) ?? undefined\n } catch {}\n return undefined\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\n\nexport const COMPLETION_CMD_PREFIX = 'complete -F _socket_completion'\n\nexport function getCompletionSourcingCommand(): CResult<string> {\n // Note: this is exported to distPath in .config/rollup.dist.config.mjs\n const completionScriptExportPath = path.join(\n constants.distPath,\n 'socket-completion.bash',\n )\n\n if (!fs.existsSync(completionScriptExportPath)) {\n return {\n ok: false,\n message: 'Tab Completion script not found',\n cause: `Expected to find completion script at \\`${completionScriptExportPath}\\` but it was not there`,\n }\n }\n\n return { ok: true, data: `source ${completionScriptExportPath}` }\n}\n\nexport function getBashrcDetails(targetCommandName: string): CResult<{\n completionCommand: string\n sourcingCommand: string\n toAddToBashrc: string\n targetName: string\n targetPath: string\n}> {\n const sourcingCommand = getCompletionSourcingCommand()\n if (!sourcingCommand.ok) {\n return sourcingCommand\n }\n\n const { socketAppDataPath } = constants\n if (!socketAppDataPath) {\n return {\n ok: false,\n message: 'Could not determine config directory',\n cause: 'Failed to get config path',\n }\n }\n\n // _socket_completion is the function defined in our completion bash script\n const completionCommand = `${COMPLETION_CMD_PREFIX} ${targetCommandName}`\n\n // Location of completion script in config after installing\n const completionScriptPath = path.join(\n path.dirname(socketAppDataPath),\n 'completion',\n 'socket-completion.bash',\n )\n\n const bashrcContent = `# Socket CLI completion for \"${targetCommandName}\"\nif [ -f \"${completionScriptPath}\" ]; then\n # Load the tab completion script\n source \"${completionScriptPath}\"\n # Tell bash to use this function for tab completion of this function\n ${completionCommand}\nfi\n`\n\n return {\n ok: true,\n data: {\n sourcingCommand: sourcingCommand.data,\n completionCommand,\n toAddToBashrc: bashrcContent,\n targetName: targetCommandName,\n targetPath: completionScriptPath,\n },\n }\n}\n","import npmPackageArg from 'npm-package-arg'\n\nexport type {\n AliasResult,\n FileResult,\n HostedGit,\n HostedGitResult,\n RegistryResult,\n Result,\n URLResult,\n} from 'npm-package-arg'\n\n/**\n * Safe wrapper for npm-package-arg that doesn't throw.\n * Returns undefined if parsing fails.\n */\nexport function safeNpa(\n ...args: Parameters<typeof npmPackageArg>\n): ReturnType<typeof npmPackageArg> | undefined {\n try {\n return Reflect.apply(npmPackageArg, undefined, args)\n } catch {}\n return undefined\n}\n","import { existsSync } from 'node:fs'\nimport Module from 'node:module'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, { NODE_MODULES, NPM } from '../constants.mts'\nimport { findBinPathDetailsSync, findNpmDirPathSync } from './path-resolve.mts'\n\nfunction exitWithBinPathError(binName: string): never {\n logger.fail(\n `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`,\n )\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n}\n\nlet _npmBinPath: string | undefined\nexport function getNpmBinPath(): string {\n if (_npmBinPath === undefined) {\n _npmBinPath = getNpmBinPathDetails().path\n if (!_npmBinPath) {\n exitWithBinPathError(NPM)\n }\n }\n return _npmBinPath\n}\n\nlet _npmBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nfunction getNpmBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {\n if (_npmBinPathDetails === undefined) {\n _npmBinPathDetails = findBinPathDetailsSync(NPM)\n }\n return _npmBinPathDetails\n}\n\nlet _npmDirPath: string | undefined\nexport function getNpmDirPath() {\n if (_npmDirPath === undefined) {\n const npmBinPath = getNpmBinPath()\n _npmDirPath = npmBinPath ? findNpmDirPathSync(npmBinPath) : undefined\n if (!_npmDirPath) {\n _npmDirPath = constants.ENV.SOCKET_CLI_NPM_PATH || undefined\n }\n if (!_npmDirPath) {\n let message = 'Unable to find npm CLI install directory.'\n if (npmBinPath) {\n message += `\\nSearched parent directories of ${path.dirname(npmBinPath)}.`\n }\n message +=\n '\\n\\nThis is may be a bug with socket-npm related to changes to the npm CLI.'\n message += `\\nPlease report to ${constants.SOCKET_CLI_ISSUES_URL}.`\n logger.fail(message)\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n }\n }\n return _npmDirPath\n}\n\nlet _npmRequire: NodeJS.Require | undefined\nexport function getNpmRequire(): NodeJS.Require {\n if (_npmRequire === undefined) {\n const npmDirPath = getNpmDirPath()\n const npmNmPath = path.join(npmDirPath, `${NODE_MODULES}/npm`)\n _npmRequire = Module.createRequire(\n path.join(\n existsSync(npmNmPath) ? npmNmPath : npmDirPath,\n '<dummy-basename>',\n ),\n )\n }\n return _npmRequire\n}\n\nlet _npxBinPath: string | undefined\nexport function getNpxBinPath(): string {\n if (_npxBinPath === undefined) {\n _npxBinPath = getNpxBinPathDetails().path\n if (!_npxBinPath) {\n exitWithBinPathError('npx')\n }\n }\n return _npxBinPath\n}\n\nlet _npxBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nfunction getNpxBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {\n if (_npxBinPathDetails === undefined) {\n _npxBinPathDetails = findBinPathDetailsSync('npx')\n }\n return _npxBinPathDetails\n}\n\nexport function isNpmBinPathShadowed() {\n return getNpmBinPathDetails().shadowed\n}\n\nexport function isNpxBinPathShadowed() {\n return getNpxBinPathDetails().shadowed\n}\n","import {\n isNpmAuditFlag,\n isNpmFundFlag,\n isNpmLoglevelFlag,\n isNpmProgressFlag,\n resolveBinPathSync,\n} from '@socketsecurity/registry/lib/agent'\nimport { isDebug } from '@socketsecurity/registry/lib/debug'\nimport { getOwn, isObject } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants, { FLAG_LOGLEVEL, NPM } from '../../constants.mts'\nimport { getNpmBinPath } from '../../utils/npm-paths.mts'\n\nimport type { SpawnResult } from '@socketsecurity/registry/lib/spawn'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\nexport type ShadowNpmInstallOptions = SpawnOption & {\n agentExecPath?: string | undefined\n args?: string[] | readonly string[] | undefined\n ipc?: object | undefined\n spinner?: Spinner | undefined\n}\n\nexport function shadowNpmInstall(\n options?: ShadowNpmInstallOptions | undefined,\n): SpawnResult<string, Record<any, any> | undefined> {\n const {\n agentExecPath = getNpmBinPath(),\n args = [],\n ipc,\n spinner,\n ...spawnOpts\n } = { __proto__: null, ...options } as ShadowNpmInstallOptions\n const useDebug = isDebug('stdio')\n const terminatorPos = args.indexOf('--')\n const rawBinArgs = terminatorPos === -1 ? args : args.slice(0, terminatorPos)\n const binArgs = rawBinArgs.filter(\n a => !isNpmAuditFlag(a) && !isNpmFundFlag(a) && !isNpmProgressFlag(a),\n )\n const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos)\n const progressArg = rawBinArgs.findLast(isNpmProgressFlag) !== '--no-progress'\n const isSilent = !useDebug && !binArgs.some(isNpmLoglevelFlag)\n const logLevelArgs = isSilent ? [FLAG_LOGLEVEL, 'silent'] : []\n const useIpc = isObject(ipc)\n\n // Include 'ipc' in the spawnOpts.stdio when an options.ipc object is provided.\n // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166\n // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.\n let stdio = getOwn(spawnOpts, 'stdio')\n if (typeof stdio === 'string') {\n stdio = useIpc ? [stdio, stdio, stdio, 'ipc'] : [stdio, stdio, stdio]\n } else if (Array.isArray(stdio)) {\n if (useIpc && !stdio.includes('ipc')) {\n stdio = stdio.concat('ipc')\n }\n } else {\n stdio = useIpc ? ['pipe', 'pipe', 'pipe', 'ipc'] : 'pipe'\n }\n\n const spawnPromise = spawn(\n constants.execPath,\n [\n ...constants.nodeNoWarningsFlags,\n ...constants.nodeDebugFlags,\n ...constants.nodeHardenFlags,\n ...constants.nodeMemoryFlags,\n ...(constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD\n ? ['--require', constants.instrumentWithSentryPath]\n : []),\n '--require',\n constants.shadowNpmInjectPath,\n resolveBinPathSync(agentExecPath),\n 'install',\n // Avoid code paths for 'audit' and 'fund'.\n '--no-audit',\n '--no-fund',\n // Add '--no-progress' to fix input being swallowed by the npm spinner.\n '--no-progress',\n // Add 'FLAG_LOGLEVEL silent' if a loglevel flag is not provided and the\n // SOCKET_CLI_DEBUG environment variable is not truthy.\n ...logLevelArgs,\n ...binArgs,\n ...otherArgs,\n ],\n {\n ...spawnOpts,\n env: {\n ...process.env,\n ...constants.processEnv,\n ...getOwn(spawnOpts, 'env'),\n },\n spinner,\n stdio,\n },\n )\n\n if (useIpc) {\n spawnPromise.process.send({\n [constants.SOCKET_IPC_HANDSHAKE]: {\n [constants.SOCKET_CLI_SHADOW_BIN]: NPM,\n [constants.SOCKET_CLI_SHADOW_PROGRESS]: progressArg,\n ...ipc,\n },\n })\n }\n\n return spawnPromise\n}\n","/**\n * Package manager agent utilities for Socket CLI.\n * Manages package installation via different package managers.\n *\n * Key Functions:\n * - runAgentInstall: Execute package installation with detected agent\n *\n * Supported Agents:\n * - npm: Node Package Manager\n * - pnpm: Fast, disk space efficient package manager\n * - yarn: Alternative package manager\n *\n * Features:\n * - Automatic agent detection\n * - Shadow installation for security scanning\n * - Spinner support for progress indication\n */\n\nimport { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport constants, { NPM, PNPM } from '../constants.mts'\nimport { cmdFlagsToString } from './cmd.mts'\nimport { shadowNpmInstall } from '../shadow/npm/install.mts'\n\nimport type { EnvDetails } from './package-environment.mts'\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\nexport type AgentInstallOptions = SpawnOption & {\n args?: string[] | readonly string[] | undefined\n spinner?: Spinner | undefined\n}\n\nexport type AgentSpawnResult = ReturnType<typeof spawn>\n\nexport function runAgentInstall(\n pkgEnvDetails: EnvDetails,\n options?: AgentInstallOptions | undefined,\n): AgentSpawnResult {\n const { agent, agentExecPath, pkgPath } = pkgEnvDetails\n const isNpm = agent === NPM\n const isPnpm = agent === PNPM\n // All package managers support the \"install\" command.\n if (isNpm) {\n return shadowNpmInstall({\n agentExecPath,\n cwd: pkgPath,\n ...options,\n })\n }\n const {\n args = [],\n spinner,\n ...spawnOpts\n } = { __proto__: null, ...options } as AgentInstallOptions\n const skipNodeHardenFlags = isPnpm && pkgEnvDetails.agentVersion.major < 11\n // In CI mode, pnpm uses --frozen-lockfile by default, which prevents lockfile updates.\n // We need to explicitly disable it when updating the lockfile with overrides.\n // Also add --config.confirmModulesPurge=false to avoid interactive prompts.\n const installArgs = isPnpm\n ? [\n 'install',\n '--config.confirmModulesPurge=false',\n '--no-frozen-lockfile',\n ...args,\n ]\n : ['install', ...args]\n\n return spawn(agentExecPath, installArgs, {\n cwd: pkgPath,\n // On Windows, package managers are often .cmd files that require shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\n shell: constants.WIN32,\n spinner,\n stdio: 'inherit',\n ...spawnOpts,\n env: {\n ...process.env,\n ...constants.processEnv,\n // Set CI for pnpm to ensure non-interactive mode and consistent behavior.\n ...(isPnpm ? { CI: '1' } : {}),\n NODE_OPTIONS: cmdFlagsToString([\n ...(skipNodeHardenFlags ? [] : constants.nodeHardenFlags),\n ...constants.nodeNoWarningsFlags,\n ]),\n ...getOwn(spawnOpts, 'env'),\n },\n })\n}\n","/**\n * Package environment detection utilities for Socket CLI.\n * Analyzes project environment and package manager configuration.\n *\n * Key Functions:\n * - getPackageEnvironment: Detect package manager and project details\n * - makeConcurrentExecLimit: Calculate concurrent execution limits\n *\n * Environment Detection:\n * - Detects npm, pnpm, yarn, bun package managers\n * - Analyzes lockfiles for version information\n * - Determines Node.js and engine requirements\n * - Identifies workspace configurations\n *\n * Features:\n * - Browser target detection via browserslist\n * - Engine compatibility checking\n * - Package manager version detection\n * - Workspace and monorepo support\n *\n * Usage:\n * - Auto-detecting appropriate package manager\n * - Validating environment compatibility\n * - Configuring concurrent execution limits\n */\n\nimport { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport browserslist from 'browserslist'\nimport semver from 'semver'\n\nimport { parse as parseBunLockb } from '@socketregistry/hyrious__bun.lockb/index.cjs'\nimport { whichBin } from '@socketsecurity/registry/lib/bin'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { readFileBinary, readFileUtf8 } from '@socketsecurity/registry/lib/fs'\nimport { Logger } from '@socketsecurity/registry/lib/logger'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { cmdPrefixMessage } from './cmd.mts'\nimport { findUp } from './fs.mts'\nimport constants, {\n FLAG_VERSION,\n PACKAGE_LOCK_JSON,\n PNPM_LOCK_YAML,\n YARN_LOCK,\n} from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\nimport type { SemVer } from 'semver'\n\nconst {\n BUN,\n BUN_LOCK,\n BUN_LOCKB,\n DOT_PACKAGE_LOCK_JSON,\n EXT_LOCK,\n EXT_LOCKB,\n NODE_MODULES,\n NPM,\n NPM_BUGGY_OVERRIDES_PATCHED_VERSION,\n NPM_SHRINKWRAP_JSON,\n PACKAGE_JSON,\n PNPM,\n VLT,\n VLT_LOCK_JSON,\n YARN,\n YARN_BERRY,\n YARN_CLASSIC,\n} = constants\n\nexport const AGENTS = [BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT] as const\n\nconst binByAgent = new Map<Agent, string>([\n [BUN, BUN],\n [NPM, NPM],\n [PNPM, PNPM],\n [YARN_BERRY, YARN],\n [YARN_CLASSIC, YARN],\n [VLT, VLT],\n])\n\nexport type Agent = (typeof AGENTS)[number]\n\nexport type EnvBase = {\n agent: Agent\n agentExecPath: string\n agentSupported: boolean\n features: {\n // Fixed by https://github.com/npm/cli/pull/8089.\n // Landed in npm v11.2.0.\n npmBuggyOverrides: boolean\n }\n nodeSupported: boolean\n nodeVersion: SemVer\n npmExecPath: string\n pkgRequirements: {\n agent: string\n node: string\n }\n pkgSupports: {\n agent: boolean\n node: boolean\n }\n}\n\nexport type EnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer\n editablePkgJson: EditablePackageJson\n lockName: string\n lockPath: string\n lockSrc: string\n pkgPath: string\n }\n >\n>\n\nexport type DetectAndValidateOptions = {\n cmdName?: string | undefined\n logger?: Logger | undefined\n prod?: boolean | undefined\n}\n\nexport type DetectOptions = {\n cwd?: string | undefined\n onUnknown?: (pkgManager: string | undefined) => void\n}\n\nexport type PartialEnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer | undefined\n editablePkgJson: EditablePackageJson | undefined\n lockName: string | undefined\n lockPath: string | undefined\n lockSrc: string | undefined\n pkgPath: string | undefined\n }\n >\n>\n\nexport type ReadLockFile =\n | ((lockPath: string) => Promise<string | undefined>)\n | ((lockPath: string, agentExecPath: string) => Promise<string | undefined>)\n | ((\n lockPath: string,\n agentExecPath: string,\n cwd: string,\n ) => Promise<string | undefined>)\n\nconst readLockFileByAgent: Map<Agent, ReadLockFile> = (() => {\n function wrapReader<T extends (...args: any[]) => Promise<any>>(\n reader: T,\n ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>> | undefined> {\n return async (...args: any[]): Promise<any> => {\n try {\n return await reader(...args)\n } catch {}\n return undefined\n }\n }\n\n const binaryReader = wrapReader(readFileBinary)\n\n const defaultReader = wrapReader(\n async (lockPath: string) => await readFileUtf8(lockPath),\n )\n\n return new Map([\n [\n BUN,\n wrapReader(\n async (\n lockPath: string,\n agentExecPath: string,\n cwd = process.cwd(),\n ) => {\n const ext = path.extname(lockPath)\n if (ext === EXT_LOCK) {\n return await defaultReader(lockPath)\n }\n if (ext === EXT_LOCKB) {\n const lockBuffer = await binaryReader(lockPath)\n if (lockBuffer) {\n try {\n return parseBunLockb(lockBuffer)\n } catch {}\n }\n // To print a Yarn lockfile to your console without writing it to disk\n // use `bun bun.lockb`.\n // https://bun.sh/guides/install/yarnlock\n return (\n await spawn(agentExecPath, [lockPath], {\n cwd,\n // On Windows, bun is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\n shell: constants.WIN32,\n })\n ).stdout\n }\n return undefined\n },\n ),\n ],\n [NPM, defaultReader],\n [PNPM, defaultReader],\n [VLT, defaultReader],\n [YARN_BERRY, defaultReader],\n [YARN_CLASSIC, defaultReader],\n ])\n})()\n\n// The order of LOCKS properties IS significant as it affects iteration order.\nconst LOCKS: Record<string, Agent> = {\n [BUN_LOCK]: BUN,\n [BUN_LOCKB]: BUN,\n // If both package-lock.json and npm-shrinkwrap.json are present in the root\n // of a project, npm-shrinkwrap.json will take precedence and package-lock.json\n // will be ignored.\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson\n [NPM_SHRINKWRAP_JSON]: NPM,\n [PACKAGE_LOCK_JSON]: NPM,\n [PNPM_LOCK_YAML]: PNPM,\n [YARN_LOCK]: YARN_CLASSIC,\n [VLT_LOCK_JSON]: VLT,\n // Lastly, look for a hidden lockfile which is present if .npmrc has package-lock=false:\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles\n //\n // Unlike the other LOCKS keys this key contains a directory AND filename so\n // it has to be handled differently.\n [`${NODE_MODULES}/${DOT_PACKAGE_LOCK_JSON}`]: NPM,\n}\n\nasync function getAgentExecPath(agent: Agent): Promise<string> {\n const binName = binByAgent.get(agent)!\n if (binName === NPM) {\n // Try to use constants.npmExecPath first, but verify it exists.\n const npmPath = constants.npmExecPath\n if (existsSync(npmPath)) {\n return npmPath\n }\n // If npmExecPath doesn't exist, try common locations.\n // Check npm in the same directory as node.\n const nodeDir = path.dirname(process.execPath)\n const npmInNodeDir = path.join(nodeDir, NPM)\n if (existsSync(npmInNodeDir)) {\n return npmInNodeDir\n }\n // Fall back to whichBin.\n return (await whichBin(binName, { nothrow: true })) ?? binName\n }\n if (binName === PNPM) {\n // Try to use constants.pnpmExecPath first, but verify it exists.\n const pnpmPath = constants.pnpmExecPath\n if (existsSync(pnpmPath)) {\n return pnpmPath\n }\n // Fall back to whichBin.\n return (await whichBin(binName, { nothrow: true })) ?? binName\n }\n return (await whichBin(binName, { nothrow: true })) ?? binName\n}\n\nasync function getAgentVersion(\n agent: Agent,\n agentExecPath: string,\n cwd: string,\n): Promise<SemVer | undefined> {\n let result\n const quotedCmd = `\\`${agent} ${FLAG_VERSION}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n result =\n // Coerce version output into a valid semver version by passing it through\n // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),\n // and tildes (~).\n semver.coerce(\n // All package managers support the \"--version\" flag.\n (\n await spawn(agentExecPath, [FLAG_VERSION], {\n cwd,\n // On Windows, package managers are often .cmd files that require shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\n shell: constants.WIN32,\n })\n ).stdout,\n ) ?? undefined\n } catch (e) {\n debugFn('error', `Package manager command failed: ${quotedCmd}`)\n debugDir('inspect', { cmd: quotedCmd })\n debugDir('error', e)\n }\n return result\n}\n\nexport async function detectPackageEnvironment({\n cwd = process.cwd(),\n onUnknown,\n}: DetectOptions = {}): Promise<EnvDetails | PartialEnvDetails> {\n let lockPath = await findUp(Object.keys(LOCKS), { cwd })\n let lockName = lockPath ? path.basename(lockPath) : undefined\n const isHiddenLockFile = lockName === DOT_PACKAGE_LOCK_JSON\n const pkgJsonPath = lockPath\n ? path.resolve(\n lockPath,\n `${isHiddenLockFile ? '../' : ''}../${PACKAGE_JSON}`,\n )\n : await findUp(PACKAGE_JSON, { cwd })\n const pkgPath =\n pkgJsonPath && existsSync(pkgJsonPath)\n ? path.dirname(pkgJsonPath)\n : undefined\n const editablePkgJson = pkgPath\n ? await readPackageJson(pkgPath, { editable: true })\n : undefined\n // Read Corepack `packageManager` field in package.json:\n // https://nodejs.org/api/packages.html#packagemanager\n const pkgManager = isNonEmptyString(editablePkgJson?.content?.packageManager)\n ? editablePkgJson.content.packageManager\n : undefined\n\n let agent: Agent | undefined\n if (pkgManager) {\n // A valid \"packageManager\" field value is \"<package manager name>@<version>\".\n // https://nodejs.org/api/packages.html#packagemanager\n const atSignIndex = pkgManager.lastIndexOf('@')\n if (atSignIndex !== -1) {\n const name = pkgManager.slice(0, atSignIndex) as Agent\n const version = pkgManager.slice(atSignIndex + 1)\n if (version && AGENTS.includes(name)) {\n agent = name\n }\n }\n }\n if (\n agent === undefined &&\n !isHiddenLockFile &&\n typeof pkgJsonPath === 'string' &&\n typeof lockName === 'string'\n ) {\n agent = LOCKS[lockName] as Agent\n }\n if (agent === undefined) {\n agent = NPM\n onUnknown?.(pkgManager)\n }\n const agentExecPath = await getAgentExecPath(agent)\n const agentVersion = await getAgentVersion(agent, agentExecPath, cwd)\n if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {\n agent = YARN_BERRY\n }\n const { maintainedNodeVersions } = constants\n const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent)!\n const minSupportedNodeMajor = semver.major(maintainedNodeVersions.last)\n const minSupportedNodeVersion = `${minSupportedNodeMajor}.0.0`\n const minSupportedNodeRange = `>=${minSupportedNodeMajor}`\n const nodeVersion = semver.coerce(process.version)!\n let lockSrc: string | undefined\n let pkgAgentRange: string | undefined\n let pkgNodeRange: string | undefined\n let pkgMinAgentVersion = minSupportedAgentVersion\n let pkgMinNodeVersion = minSupportedNodeVersion\n if (editablePkgJson?.content) {\n const { engines } = editablePkgJson.content\n const engineAgentRange = engines?.[agent]\n const engineNodeRange = engines?.['node']\n if (isNonEmptyString(engineAgentRange)) {\n pkgAgentRange = engineAgentRange\n // Roughly check agent range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n const coerced = semver.coerce(pkgAgentRange)\n if (coerced && semver.lt(coerced, pkgMinAgentVersion)) {\n pkgMinAgentVersion = coerced.version\n }\n }\n if (isNonEmptyString(engineNodeRange)) {\n pkgNodeRange = engineNodeRange\n // Roughly check Node range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n const coerced = semver.coerce(pkgNodeRange)\n if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {\n pkgMinNodeVersion = coerced.version\n }\n }\n const browserslistQuery = editablePkgJson.content['browserslist'] as\n | string[]\n | undefined\n if (Array.isArray(browserslistQuery)) {\n // List Node targets in ascending version order.\n const browserslistNodeTargets = browserslist(browserslistQuery)\n .filter(v => /^node /i.test(v))\n .map(v => v.slice(5 /*'node '.length*/))\n .sort(naturalCompare)\n if (browserslistNodeTargets.length) {\n // browserslistNodeTargets[0] is the lowest Node target version.\n const coerced = semver.coerce(browserslistNodeTargets[0])\n if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {\n pkgMinNodeVersion = coerced.version\n }\n }\n }\n lockSrc =\n typeof lockPath === 'string'\n ? await readLockFileByAgent.get(agent)!(lockPath, agentExecPath, cwd)\n : undefined\n } else {\n lockName = undefined\n lockPath = undefined\n }\n\n // Does the system agent version meet our minimum supported agent version?\n const agentSupported =\n !!agentVersion &&\n semver.satisfies(agentVersion, `>=${minSupportedAgentVersion}`)\n // Does the system Node version meet our minimum supported Node version?\n const nodeSupported = semver.satisfies(nodeVersion, minSupportedNodeRange)\n\n const npmExecPath =\n agent === NPM ? agentExecPath : await getAgentExecPath(NPM)\n const npmBuggyOverrides =\n agent === NPM &&\n !!agentVersion &&\n semver.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION)\n\n const pkgMinAgentRange = `>=${pkgMinAgentVersion}`\n const pkgMinNodeRange = `>=${semver.major(pkgMinNodeVersion)}`\n\n return {\n agent,\n agentExecPath,\n agentSupported,\n agentVersion,\n editablePkgJson,\n features: { npmBuggyOverrides },\n lockName,\n lockPath,\n lockSrc,\n nodeSupported,\n nodeVersion,\n npmExecPath,\n pkgPath,\n pkgRequirements: {\n agent: pkgAgentRange ?? pkgMinAgentRange,\n node: pkgNodeRange ?? pkgMinNodeRange,\n },\n pkgSupports: {\n // Does our minimum supported agent version meet the package's requirements?\n agent: semver.satisfies(minSupportedAgentVersion, pkgMinAgentRange),\n // Does our supported Node versions meet the package's requirements?\n node: maintainedNodeVersions.some(v =>\n semver.satisfies(v, pkgMinNodeRange),\n ),\n },\n }\n}\n\nexport async function detectAndValidatePackageEnvironment(\n cwd: string,\n options?: DetectAndValidateOptions | undefined,\n): Promise<CResult<EnvDetails>> {\n const {\n cmdName = '',\n logger,\n prod,\n } = {\n __proto__: null,\n ...options,\n } as DetectAndValidateOptions\n const details = await detectPackageEnvironment({\n cwd,\n onUnknown(pkgManager: string | undefined) {\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to ${NPM}`,\n ),\n )\n },\n })\n const { agent, nodeVersion, pkgRequirements } = details\n const agentVersion = details.agentVersion ?? 'unknown'\n if (!details.agentSupported) {\n const minVersion = constants.minimumVersionByAgent.get(agent)!\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`,\n ),\n }\n }\n if (!details.nodeSupported) {\n const minVersion = constants.maintainedNodeVersions.last\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`,\n ),\n }\n }\n if (!details.pkgSupports.agent) {\n return {\n ok: false,\n message: 'Engine mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Package engine \"${agent}\" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`,\n ),\n }\n }\n if (!details.pkgSupports.node) {\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Package engine \"node\" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`,\n ),\n }\n }\n const lockName = details.lockName ?? 'lockfile'\n if (details.lockName === undefined || details.lockSrc === undefined) {\n return {\n ok: false,\n message: 'Missing lockfile',\n cause: cmdPrefixMessage(cmdName, `No ${lockName} found`),\n }\n }\n if (details.lockSrc.trim() === '') {\n return {\n ok: false,\n message: 'Empty lockfile',\n cause: cmdPrefixMessage(cmdName, `${lockName} is empty`),\n }\n }\n if (details.pkgPath === undefined) {\n return {\n ok: false,\n message: 'Missing package.json',\n cause: cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`),\n }\n }\n if (prod && (agent === BUN || agent === YARN_BERRY)) {\n return {\n ok: false,\n message: 'Bad input',\n cause: cmdPrefixMessage(\n cmdName,\n `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`,\n ),\n }\n }\n if (\n details.lockPath &&\n path.relative(cwd, details.lockPath).startsWith('.')\n ) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Package ${lockName} found at ${constants.ENV.VITEST ? constants.REDACTED : details.lockPath}`,\n ),\n )\n }\n return { ok: true, data: details as EnvDetails }\n}\n","/**\n * Ecosystem type utilities for Socket CLI.\n * Manages package ecosystem identifiers and mappings.\n *\n * Constants:\n * - ALL_ECOSYSTEMS: Complete list of supported ecosystems\n * - ECOSYSTEM_MAP: Map ecosystem strings to PURL types\n *\n * Type Definitions:\n * - PURL_Type: Package URL type from Socket SDK\n *\n * Supported Ecosystems:\n * - apk, bitbucket, cargo, chrome, cocoapods, composer\n * - conan, conda, cran, deb, docker, gem, generic\n * - github, gitlab, go, hackage, hex, huggingface\n * - maven, mlflow, npm, nuget, oci, pub, pypi, rpm, swift\n *\n * Usage:\n * - Validates ecosystem types\n * - Maps between different ecosystem representations\n * - Ensures type safety for ecosystem operations\n */\n\nimport { NPM } from '../constants.mts'\n\nimport type { EcosystemString } from '@socketsecurity/registry'\nimport type { components } from '@socketsecurity/sdk/types/api'\n\nexport type PURL_Type = components['schemas']['SocketPURL_Type']\n\ntype ExpectNever<T extends never> = T\n\ntype MissingInEcosystemString = Exclude<PURL_Type, EcosystemString>\ntype ExtraInEcosystemString = Exclude<EcosystemString, PURL_Type>\n\nexport type _Check_EcosystemString_has_all_purl_types =\n ExpectNever<MissingInEcosystemString>\nexport type _Check_EcosystemString_has_no_extras =\n ExpectNever<ExtraInEcosystemString>\n\nexport const ALL_ECOSYSTEMS = [\n 'apk',\n 'bitbucket',\n 'cargo',\n 'chrome',\n 'cocoapods',\n 'composer',\n 'conan',\n 'conda',\n 'cran',\n 'deb',\n 'docker',\n 'gem',\n 'generic',\n 'github',\n 'golang',\n 'hackage',\n 'hex',\n 'huggingface',\n 'maven',\n 'mlflow',\n NPM,\n 'nuget',\n 'oci',\n 'pub',\n 'pypi',\n 'qpkg',\n 'rpm',\n 'swift',\n 'swid',\n 'unknown',\n] as const satisfies readonly PURL_Type[]\n\ntype AllEcosystemsUnion = (typeof ALL_ECOSYSTEMS)[number]\ntype MissingInAllEcosystems = Exclude<PURL_Type, AllEcosystemsUnion>\ntype ExtraInAllEcosystems = Exclude<AllEcosystemsUnion, PURL_Type>\n\nexport type _Check_ALL_ECOSYSTEMS_has_all_purl_types =\n ExpectNever<MissingInAllEcosystems>\nexport type _Check_ALL_ECOSYSTEMS_has_no_extras =\n ExpectNever<ExtraInAllEcosystems>\n\nexport const ALL_SUPPORTED_ECOSYSTEMS = new Set<string>(ALL_ECOSYSTEMS)\n\nexport function getEcosystemChoicesForMeow(): string[] {\n return [...ALL_ECOSYSTEMS]\n}\n\nexport function isValidEcosystem(value: string): value is PURL_Type {\n return ALL_SUPPORTED_ECOSYSTEMS.has(value)\n}\n\nexport function parseEcosystems(\n value: string | string[] | undefined,\n): PURL_Type[] {\n if (!value) {\n return []\n }\n const values =\n typeof value === 'string'\n ? value.split(',').map(v => v.trim().toLowerCase())\n : value.map(v => String(v).toLowerCase())\n\n return values.filter(isValidEcosystem)\n}\n","/**\n * Temporary package executor detection utilities for Socket CLI.\n * Identifies and handles temporary execution contexts.\n *\n * Key Functions:\n * - isRunningInTemporaryExecutor: Detects if running in npx/dlx/exec context\n * - shouldSkipShadow: Determines if shadow installation should be skipped\n *\n * Temporary Execution Contexts:\n * - npm exec/npx: Runs packages in temporary npm cache\n * - pnpm dlx: Executes packages in temporary pnpm store\n * - yarn dlx: Runs packages in temporary yarn environment\n *\n * Detection Methods:\n * - Environment variable analysis (npm_config_user_agent)\n * - Path pattern matching for temporary directories\n * - Cache directory identification\n *\n * Usage:\n * - Prevents shadow installation in temporary contexts\n * - Avoids PATH pollution in ephemeral environments\n * - Ensures package manager commands work correctly\n */\n\nimport path from 'node:path'\n\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\n\nimport constants from '../constants.mts'\n\n/**\n * Detects if the current process is running in a temporary package execution context\n * such as npm exec, npx, pnpm dlx, or yarn dlx.\n *\n * When package managers run commands via exec/npx/dlx, they execute in temporary directories\n * that are cleaned up after execution. Creating persistent shadows or modifying PATH\n * in these contexts can break subsequent package manager commands.\n *\n * @returns true if running in an exec/npx/dlx context, false otherwise\n */\nexport function isRunningInTemporaryExecutor(): boolean {\n // Check environment variable for exec/npx/dlx indicators.\n const userAgent = constants.ENV.npm_config_user_agent\n if (\n userAgent?.includes('exec') ||\n userAgent?.includes('npx') ||\n userAgent?.includes('dlx')\n ) {\n return true\n }\n\n // Normalize the __dirname path for consistent checking across platforms.\n const normalizedDirname = normalizePath(__dirname)\n\n // Check if running from npm's npx cache.\n const npmCache = constants.ENV.npm_config_cache\n if (npmCache && normalizedDirname.includes(normalizePath(npmCache))) {\n return true\n }\n\n // Check common temporary execution path patterns.\n const tempPatterns = [\n '_npx', // npm's npx cache directory\n '.pnpm-store', // pnpm dlx temporary store\n 'dlx-', // Common dlx directory prefix\n '.yarn/$$', // Yarn Berry PnP virtual packages\n path.sep === '\\\\'\n ? 'AppData\\\\Local\\\\Temp\\\\xfs-'\n : 'AppData/Local/Temp/xfs-', // Yarn on Windows\n ]\n\n return tempPatterns.some(pattern => normalizedDirname.includes(pattern))\n}\n\nexport type ShadowInstallationOptions = {\n cwd?: string | undefined\n win32?: boolean | undefined\n}\n\n/**\n * Determines if shadow binaries should be installed.\n * Shadows should NOT be installed when:\n * - Running in a temporary execution context (exec/npx/dlx)\n * - On Windows with an existing binary path (required for Windows to function)\n *\n * @param binPath - Path to the binary being shadowed\n * @param options - Configuration options\n * @param options.cwd - Current working directory path to check\n * @param options.win32 - Whether running on Windows\n * @returns true if shadow installation should be skipped\n */\nexport function shouldSkipShadow(\n binPath: string,\n options: ShadowInstallationOptions,\n): boolean {\n const { cwd = process.cwd(), win32 = false } = {\n __proto__: null,\n ...options,\n } as ShadowInstallationOptions\n\n // Windows compatibility: Skip shadow installation if binary is already found.\n //\n // This check is required because Windows handles executables differently than Unix:\n // 1. File locking - Windows locks running executables, so cmd-shim creation would\n // fail with EBUSY/EACCES errors when trying to create wrapper files.\n // 2. PATH conflicts - Attempting to shadow an already-resolved binary can create\n // circular references or ambiguous command resolution.\n // 3. Registry integration - Windows package managers often use system-level\n // integrations beyond just PATH that our shadowing would interfere with.\n //\n // Without this check, users would see \"Access Denied\" or file locking errors\n // that are difficult to debug. This is not a performance optimization - the\n // shadow installation will fail without it.\n if (win32 && binPath) {\n return true\n }\n\n // Check environment variable for exec/npx/dlx indicators.\n const userAgent = constants.ENV.npm_config_user_agent\n if (\n userAgent?.includes('exec') ||\n userAgent?.includes('npx') ||\n userAgent?.includes('dlx')\n ) {\n return true\n }\n\n // Normalize the cwd path for consistent checking across platforms.\n const normalizedCwd = normalizePath(cwd)\n\n // Check if running from npm's npx cache.\n const npmCache = constants.ENV.npm_config_cache\n if (npmCache && normalizedCwd.includes(normalizePath(npmCache))) {\n return true\n }\n\n // Check common temporary execution path patterns.\n const tempPatterns = [\n '_npx', // npm's npx cache directory\n '.pnpm-store', // pnpm dlx temporary store\n 'dlx-', // Common dlx directory prefix\n '.yarn/$$', // Yarn Berry PnP virtual packages\n path.sep === '\\\\'\n ? 'AppData\\\\Local\\\\Temp\\\\xfs-'\n : 'AppData/Local/Temp/xfs-', // Yarn on Windows\n ]\n\n return tempPatterns.some(pattern => normalizedCwd.includes(pattern))\n}\n","/**\n * PNPM path resolution utilities for Socket CLI.\n * Locates and caches PNPM binary paths.\n *\n * Key Functions:\n * - getPnpmBinPath: Get cached PNPM binary path\n * - getPnpmBinPathDetails: Get detailed PNPM path information\n *\n * Error Handling:\n * - Exits with code 127 if PNPM not found\n * - Provides clear error messages for missing binaries\n *\n * Caching:\n * - Caches binary path lookups for performance\n * - Prevents repeated PATH searches\n */\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { findBinPathDetailsSync } from './path-resolve.mts'\n\nfunction exitWithBinPathError(binName: string): never {\n logger.fail(\n `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`,\n )\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n}\n\nlet _pnpmBinPath: string | undefined\nexport function getPnpmBinPath(): string {\n if (_pnpmBinPath === undefined) {\n _pnpmBinPath = getPnpmBinPathDetails().path\n if (!_pnpmBinPath) {\n exitWithBinPathError('pnpm')\n }\n }\n return _pnpmBinPath\n}\n\nlet _pnpmBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nexport function getPnpmBinPathDetails(): ReturnType<\n typeof findBinPathDetailsSync\n> {\n if (_pnpmBinPathDetails === undefined) {\n _pnpmBinPathDetails = findBinPathDetailsSync('pnpm')\n }\n return _pnpmBinPathDetails\n}\n\nexport function isPnpmBinPathShadowed(): boolean {\n return getPnpmBinPathDetails().shadowed\n}\n","/**\n * Shadow binary link installation utilities for Socket CLI.\n * Manages installation of shadow binaries for package managers.\n *\n * Key Functions:\n * - installNpmLinks: Install shadow links for npm binary\n * - installNpxLinks: Install shadow links for npx binary\n * - installPnpmLinks: Install shadow links for pnpm binary\n * - installYarnLinks: Install shadow links for yarn binary\n *\n * Shadow Installation:\n * - Creates symlinks/cmd-shims to intercept package manager commands\n * - Modifies PATH to prioritize shadow binaries\n * - Skips installation in temporary execution contexts\n *\n * Security Integration:\n * - Enables security scanning before package operations\n * - Transparent interception of package manager commands\n * - Preserves original binary functionality\n */\n\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport cmdShim from 'cmd-shim'\n\nimport constants from '../constants.mts'\nimport { shouldSkipShadow } from './dlx-detection.mts'\nimport {\n getNpmBinPath,\n getNpxBinPath,\n isNpmBinPathShadowed,\n isNpxBinPathShadowed,\n} from './npm-paths.mts'\nimport { getPnpmBinPath, isPnpmBinPathShadowed } from './pnpm-paths.mts'\nimport { getYarnBinPath, isYarnBinPathShadowed } from './yarn-paths.mts'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\nexport async function installNpmLinks(shadowBinPath: string): Promise<string> {\n // Find npm being shadowed by this process.\n const binPath = getNpmBinPath()\n const { WIN32 } = constants\n\n // Skip shadow installation when in temporary execution context or when required for Windows.\n if (shouldSkipShadow(binPath, { cwd: __dirname, win32: WIN32 })) {\n return binPath\n }\n const shadowed = isNpmBinPathShadowed()\n // Move our bin directory to front of PATH so its found first.\n if (!shadowed) {\n if (WIN32) {\n await cmdShim(\n path.join(constants.distPath, 'npm-cli.js'),\n path.join(shadowBinPath, 'npm'),\n )\n }\n const { env } = process\n env['PATH'] = `${shadowBinPath}${path.delimiter}${env['PATH']}`\n }\n return binPath\n}\n\nexport async function installNpxLinks(shadowBinPath: string): Promise<string> {\n // Find npx being shadowed by this process.\n const binPath = getNpxBinPath()\n const { WIN32 } = constants\n\n // Skip shadow installation when in temporary execution context or when required for Windows.\n if (shouldSkipShadow(binPath, { cwd: __dirname, win32: WIN32 })) {\n return binPath\n }\n const shadowed = isNpxBinPathShadowed()\n // Move our bin directory to front of PATH so its found first.\n if (!shadowed) {\n if (WIN32) {\n await cmdShim(\n path.join(constants.distPath, 'npx-cli.js'),\n path.join(shadowBinPath, 'npx'),\n )\n }\n const { env } = process\n env['PATH'] = `${shadowBinPath}${path.delimiter}${env['PATH']}`\n }\n return binPath\n}\n\nexport async function installPnpmLinks(shadowBinPath: string): Promise<string> {\n // Find pnpm being shadowed by this process.\n const binPath = getPnpmBinPath()\n const { WIN32 } = constants\n\n // Skip shadow installation when in temporary execution context or when required for Windows.\n if (shouldSkipShadow(binPath, { cwd: __dirname, win32: WIN32 })) {\n return binPath\n }\n\n const shadowed = isPnpmBinPathShadowed()\n\n // Move our bin directory to front of PATH so its found first.\n if (!shadowed) {\n if (WIN32) {\n await cmdShim(\n path.join(constants.distPath, 'pnpm-cli.js'),\n path.join(shadowBinPath, 'pnpm'),\n )\n }\n const { env } = process\n env['PATH'] = `${shadowBinPath}${path.delimiter}${env['PATH']}`\n }\n\n return binPath\n}\n\nexport async function installYarnLinks(shadowBinPath: string): Promise<string> {\n const binPath = getYarnBinPath()\n const { WIN32 } = constants\n\n // Skip shadow installation when in temporary execution context or when required for Windows.\n if (shouldSkipShadow(binPath, { cwd: __dirname, win32: WIN32 })) {\n return binPath\n }\n\n const shadowed = isYarnBinPathShadowed()\n\n if (!shadowed) {\n if (WIN32) {\n await cmdShim(\n path.join(constants.distPath, 'yarn-cli.js'),\n path.join(shadowBinPath, 'yarn'),\n )\n }\n const { env } = process\n env['PATH'] = `${shadowBinPath}${path.delimiter}${env['PATH']}`\n }\n\n return binPath\n}\n","/**\n * Filter configuration utilities for Socket CLI.\n * Manages filter configuration normalization for security scanning.\n *\n * Key Functions:\n * - toFilterConfig: Normalize filter configuration objects\n *\n * Usage:\n * - Normalizes user-provided filter objects\n * - Ensures proper structure for filter configuration\n * - Validates boolean and array values\n */\n\nimport { isObject } from '@socketsecurity/registry/lib/objects'\n\nexport type FilterConfig = {\n [key: string]: boolean | string[]\n}\n\nexport function toFilterConfig(obj: any): FilterConfig {\n const normalized = { __proto__: null } as unknown as FilterConfig\n const keys = isObject(obj) ? Object.keys(obj) : []\n for (const key of keys) {\n const value = obj[key]\n if (typeof value === 'boolean' || Array.isArray(value)) {\n normalized[key] = value\n }\n }\n return normalized\n}\n","import semver from 'semver'\n\nimport { NPM } from '../constants.mts'\nimport { stripPnpmPeerSuffix } from './pnpm.mts'\n\nimport type { PackageURL } from '@socketregistry/packageurl-js'\n\nexport function idToNpmPurl(id: string): string {\n return `pkg:${NPM}/${id}`\n}\n\nexport function idToPurl(id: string, type: string): string {\n return `pkg:${type}/${id}`\n}\n\nexport function resolvePackageVersion(purlObj: PackageURL): string {\n const { version } = purlObj\n if (!version) {\n return ''\n }\n const { type } = purlObj\n return (\n semver.coerce(type === NPM ? stripPnpmPeerSuffix(version) : version)\n ?.version ?? ''\n )\n}\n","import { existsSync } from 'node:fs'\n\nimport yaml from 'js-yaml'\nimport semver from 'semver'\n\nimport { readFileUtf8 } from '@socketsecurity/registry/lib/fs'\nimport { isObjectObject } from '@socketsecurity/registry/lib/objects'\nimport { stripBom } from '@socketsecurity/registry/lib/strings'\n\nimport { idToNpmPurl } from './spec.mts'\n\nimport type { LockfileObject, PackageSnapshot } from '@pnpm/lockfile.fs'\nimport type { SemVer } from 'semver'\n\nexport function extractOverridesFromPnpmLockSrc(lockfileContent: any): string {\n let match\n if (typeof lockfileContent === 'string') {\n match = /^overrides:(?:\\r?\\n {2}.+)+(?:\\r?\\n)*/m.exec(lockfileContent)?.[0]\n }\n return match ?? ''\n}\n\nexport async function extractPurlsFromPnpmLockfile(\n lockfile: LockfileObject,\n): Promise<string[]> {\n const packages = lockfile?.packages ?? {}\n const seen = new Set<string>()\n const visit = (pkgPath: string) => {\n if (seen.has(pkgPath)) {\n return\n }\n const pkg = (packages as any)[pkgPath] as PackageSnapshot\n if (!pkg) {\n return\n }\n seen.add(pkgPath)\n const deps: { [name: string]: string } = {\n __proto__: null,\n ...pkg.dependencies,\n ...pkg.optionalDependencies,\n ...(pkg as any).devDependencies,\n }\n for (const depName in deps) {\n const ref = deps[depName]!\n const subKey = isPnpmDepPath(ref) ? ref : `/${depName}@${ref}`\n visit(subKey)\n }\n }\n for (const pkgPath of Object.keys(packages)) {\n visit(pkgPath)\n }\n return Array.from(seen).map(p =>\n idToNpmPurl(stripPnpmPeerSuffix(stripLeadingPnpmDepPathSlash(p))),\n )\n}\n\nexport function isPnpmDepPath(maybeDepPath: string): boolean {\n return maybeDepPath.length > 0 && maybeDepPath.charCodeAt(0) === 47 /*'/'*/\n}\n\nexport function parsePnpmLockfile(\n lockfileContent: unknown,\n): LockfileObject | null {\n let result\n if (typeof lockfileContent === 'string') {\n try {\n result = yaml.load(stripBom(lockfileContent))\n } catch {}\n }\n return isObjectObject(result) ? (result as LockfileObject) : null\n}\n\nexport function parsePnpmLockfileVersion(version: unknown): SemVer | undefined {\n try {\n return semver.coerce(version as string) ?? undefined\n } catch {}\n return undefined\n}\n\nexport async function readPnpmLockfile(\n lockfilePath: string,\n): Promise<string | undefined> {\n return existsSync(lockfilePath) ? await readFileUtf8(lockfilePath) : undefined\n}\n\nexport function stripLeadingPnpmDepPathSlash(depPath: string): string {\n return isPnpmDepPath(depPath) ? depPath.slice(1) : depPath\n}\n\nexport function stripPnpmPeerSuffix(depPath: string): string {\n const parenIndex = depPath.indexOf('(')\n const index = parenIndex === -1 ? depPath.indexOf('_') : parenIndex\n return index === -1 ? depPath : depPath.slice(0, index)\n}\n","import constants from '../../constants.mts'\n\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n SocketArtifact,\n SocketArtifactAlert,\n} from '@socketsecurity/sdk'\n\nexport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n SocketArtifact,\n SocketArtifactAlert,\n}\n\nexport type CVE_ALERT_TYPE = 'cve' | 'mediumCVE' | 'mildCVE' | 'criticalCVE'\n\nexport type ArtifactAlertCve = Remap<\n Omit<CompactSocketArtifactAlert, 'type'> & {\n type: CVE_ALERT_TYPE\n }\n>\n\nexport type ArtifactAlertCveFixable = Remap<\n Omit<CompactSocketArtifactAlert, 'props' | 'type'> & {\n type: CVE_ALERT_TYPE\n props: CveProps\n }\n>\n\nexport type ArtifactAlertUpgrade = Remap<\n Omit<CompactSocketArtifactAlert, 'type'> & {\n type: 'socketUpgradeAvailable'\n }\n>\n\nexport type CveProps = {\n firstPatchedVersionIdentifier?: string | undefined\n vulnerableVersionRange: string\n [key: string]: any\n}\n\nexport function isArtifactAlertCve(\n alert: CompactSocketArtifactAlert,\n): alert is ArtifactAlertCve {\n const { type } = alert\n return (\n type === constants.ALERT_TYPE_CVE ||\n type === constants.ALERT_TYPE_MEDIUM_CVE ||\n type === constants.ALERT_TYPE_MILD_CVE ||\n type === constants.ALERT_TYPE_CRITICAL_CVE\n )\n}\n","export function createEnum<const T extends Record<string, any>>(\n obj: T,\n): Readonly<T> {\n return Object.freeze({ __proto__: null, ...obj }) as any\n}\n\nexport function pick<T extends Record<string, any>, K extends keyof T>(\n input: T,\n keys: K[] | readonly K[],\n): Pick<T, K> {\n const result: Partial<Pick<T, K>> = {}\n for (const key of keys) {\n result[key] = input[key]\n }\n return result as Pick<T, K>\n}\n","import { createEnum } from '../objects.mts'\n\nexport const ALERT_FIX_TYPE = createEnum({\n cve: 'cve',\n remove: 'remove',\n upgrade: 'upgrade',\n})\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\n\nimport { createEnum, pick } from '../objects.mts'\n\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport const ALERT_SEVERITY = createEnum({\n critical: 'critical',\n high: 'high',\n middle: 'middle',\n low: 'low',\n})\n\nexport type SocketSdkAlertList =\n SocketSdkSuccessResult<'getIssuesByNPMPackage'>['data']\n\nexport type SocketSdkAlert = SocketSdkAlertList[number]['value'] extends\n | infer U\n | undefined\n ? U\n : never\n\n// Ordered from most severe to least.\nexport const ALERT_SEVERITIES_SORTED: ReadonlyArray<\n SocketSdkAlert['severity']\n> = Object.freeze(['critical', 'high', 'middle', 'low'])\n\nfunction getDesiredSeverities(\n lowestToInclude: SocketSdkAlert['severity'] | undefined,\n): Array<SocketSdkAlert['severity']> {\n const result: Array<SocketSdkAlert['severity']> = []\n for (const severity of ALERT_SEVERITIES_SORTED) {\n result.push(severity)\n if (severity === lowestToInclude) {\n break\n }\n }\n return result\n}\n\nexport function formatSeverityCount(\n severityCount: Record<SocketSdkAlert['severity'], number>,\n): string {\n const summary: string[] = []\n for (const severity of ALERT_SEVERITIES_SORTED) {\n if (severityCount[severity]) {\n summary.push(`${severityCount[severity]} ${severity}`)\n }\n }\n return joinAnd(summary)\n}\n\nexport function getSeverityCount(\n issues: SocketSdkAlertList,\n lowestToInclude: SocketSdkAlert['severity'] | undefined,\n): Record<SocketSdkAlert['severity'], number> {\n const severityCount = pick(\n { low: 0, middle: 0, high: 0, critical: 0 },\n getDesiredSeverities(lowestToInclude),\n ) as Record<SocketSdkAlert['severity'], number>\n\n for (const issue of issues) {\n const { value } = issue\n if (!value) {\n continue\n }\n const { severity } = value\n if (severityCount[severity] !== undefined) {\n severityCount[severity] += 1\n }\n }\n return severityCount\n}\n","/**\n * Color and markdown formatting utilities for Socket CLI.\n * Provides dual-mode formatting for terminal colors or markdown output.\n *\n * Key Class:\n * - ColorOrMarkdown: Dual-mode formatter for terminal/markdown output\n *\n * Formatting Methods:\n * - bold: Bold text formatting\n * - codeBlock: Code block formatting\n * - codeInline: Inline code formatting\n * - header: Section headers\n * - hyperlink: Clickable links\n * - indent: Text indentation\n * - italic: Italic text formatting\n * - list: Bullet list formatting\n * - table: Table formatting\n *\n * Usage:\n * - Switches between terminal colors and markdown based on output format\n * - Supports both interactive terminal and report generation\n * - Handles hyperlink fallbacks for terminals without link support\n */\n\nimport terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport indentString from '@socketregistry/indent-string/index.cjs'\n\nexport class ColorOrMarkdown {\n public useMarkdown: boolean\n\n constructor(useMarkdown: boolean) {\n this.useMarkdown = !!useMarkdown\n }\n\n bold(text: string): string {\n return this.useMarkdown ? `**${text}**` : colors.bold(`${text}`)\n }\n\n header(text: string, level = 1): string {\n return this.useMarkdown\n ? `\\n${''.padStart(level, '#')} ${text}\\n`\n : colors.underline(`\\n${level === 1 ? colors.bold(text) : text}\\n`)\n }\n\n hyperlink(\n text: string,\n url: string | undefined,\n {\n fallback = true,\n fallbackToUrl,\n }: {\n fallback?: boolean | undefined\n fallbackToUrl?: boolean | undefined\n } = {},\n ) {\n if (url) {\n return this.useMarkdown\n ? `[${text}](${url})`\n : terminalLink(text, url, {\n fallback: fallbackToUrl ? (_text, url) => url : fallback,\n })\n }\n return text\n }\n\n indent(\n ...args: Parameters<typeof indentString>\n ): ReturnType<typeof indentString> {\n return indentString(...args)\n }\n\n italic(text: string): string {\n return this.useMarkdown ? `_${text}_` : colors.italic(`${text}`)\n }\n\n json(value: any): string {\n return this.useMarkdown\n ? '```json\\n' + JSON.stringify(value) + '\\n```'\n : JSON.stringify(value)\n }\n\n list(items: string[]): string {\n const indentedContent = items.map(item => this.indent(item).trimStart())\n return this.useMarkdown\n ? `* ${indentedContent.join('\\n* ')}\\n`\n : `${indentedContent.join('\\n')}\\n`\n }\n}\n","import { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nconst require = createRequire(import.meta.url)\n\nlet _translations:\n | Readonly<typeof import('../../translations.json')>\n | undefined\n\nexport function getTranslations() {\n if (_translations === undefined) {\n _translations = /*@__PURE__*/ require(\n path.join(constants.rootPath, 'translations.json'),\n )\n }\n return _translations!\n}\n","/**\n * Socket package alert utilities for Socket CLI.\n * Handles security alerts, vulnerabilities, and package risk assessment.\n *\n * Key Functions:\n * - addArtifactToAlertsMap: Add security alert to package map\n * - logAlertsMap: Display alerts in formatted output\n * - shouldSkipPackageAlert: Filter alerts based on criteria\n *\n * Alert Types:\n * - CVE: Common Vulnerabilities and Exposures\n * - GHSA: GitHub Security Advisories\n * - Package quality issues\n * - Supply chain risks\n *\n * Features:\n * - Alert severity classification (critical/high/medium/low)\n * - Fix type detection (major/minor/patch/none)\n * - Alert filtering and suppression\n * - Colorized terminal output\n */\n\nimport semver from 'semver'\nimport colors from 'yoctocolors-cjs'\n\nimport { getManifestData } from '@socketsecurity/registry'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { getOwn, hasOwn } from '@socketsecurity/registry/lib/objects'\nimport { resolvePackageName } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\n\nimport { isArtifactAlertCve } from './alert/artifact.mts'\nimport { ALERT_FIX_TYPE } from './alert/fix.mts'\nimport { ALERT_SEVERITY } from './alert/severity.mts'\nimport { ColorOrMarkdown } from './color-or-markdown.mts'\nimport { toFilterConfig } from './filter-config.mts'\nimport { createEnum } from './objects.mts'\nimport { createPurlObject, getPurlObject } from './purl.mts'\nimport { getMajor } from './semver.mts'\nimport { getSocketDevPackageOverviewUrl } from './socket-url.mts'\nimport { getTranslations } from './translations.mts'\n\nimport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n CveProps,\n} from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport const ALERT_SEVERITY_COLOR = createEnum({\n critical: 'magenta',\n high: 'red',\n middle: 'yellow',\n low: 'white',\n})\n\nexport const ALERT_SEVERITY_ORDER = createEnum({\n critical: 0,\n high: 1,\n middle: 2,\n low: 3,\n none: 4,\n})\n\nexport type SocketPackageAlert = {\n name: string\n version: string\n key: string\n type: string\n blocked: boolean\n critical: boolean\n ecosystem: PURL_Type\n fixable: boolean\n raw: CompactSocketArtifactAlert\n upgradable: boolean\n}\n\nexport type AlertsByPurl = Map<string, SocketPackageAlert[]>\n\nconst MIN_ABOVE_THE_FOLD_COUNT = 3\n\nconst MIN_ABOVE_THE_FOLD_ALERT_COUNT = 1\n\nconst format = new ColorOrMarkdown(false)\n\nexport type RiskCounts = {\n critical: number\n high: number\n middle: number\n low: number\n}\n\nfunction getHiddenRiskCounts(hiddenAlerts: SocketPackageAlert[]): RiskCounts {\n const riskCounts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0,\n }\n for (const alert of hiddenAlerts) {\n switch (getAlertSeverityOrder(alert)) {\n case ALERT_SEVERITY_ORDER.critical:\n riskCounts.critical += 1\n break\n case ALERT_SEVERITY_ORDER.high:\n riskCounts.high += 1\n break\n case ALERT_SEVERITY_ORDER.middle:\n riskCounts.middle += 1\n break\n case ALERT_SEVERITY_ORDER.low:\n riskCounts.low += 1\n break\n }\n }\n return riskCounts\n}\n\nfunction getHiddenRisksDescription(riskCounts: RiskCounts): string {\n const descriptions: string[] = []\n if (riskCounts.critical) {\n descriptions.push(`${riskCounts.critical} ${getSeverityLabel('critical')}`)\n }\n if (riskCounts.high) {\n descriptions.push(`${riskCounts.high} ${getSeverityLabel('high')}`)\n }\n if (riskCounts.middle) {\n descriptions.push(`${riskCounts.middle} ${getSeverityLabel('middle')}`)\n }\n if (riskCounts.low) {\n descriptions.push(`${riskCounts.low} ${getSeverityLabel('low')}`)\n }\n return `(${descriptions.join('; ')})`\n}\n\nexport type AlertFilter = {\n actions?: ALERT_ACTION[] | undefined\n blocked?: boolean | undefined\n critical?: boolean | undefined\n cve?: boolean | undefined\n existing?: boolean | undefined\n fixable?: boolean | undefined\n upgradable?: boolean | undefined\n}\n\nexport type AddArtifactToAlertsMapOptions = {\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n overrides?: { [key: string]: string } | undefined\n socketYml?: SocketYml | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function addArtifactToAlertsMap<T extends AlertsByPurl>(\n artifact: CompactSocketArtifact,\n alertsByPurl: T,\n options?: AddArtifactToAlertsMapOptions | undefined,\n): Promise<T> {\n // Make TypeScript happy.\n if (!artifact.name || !artifact.version || !artifact.alerts?.length) {\n return alertsByPurl\n }\n\n const { type: ecosystem, version } = artifact\n\n const {\n consolidate = false,\n overrides,\n socketYml,\n } = {\n __proto__: null,\n ...options,\n } as AddArtifactToAlertsMapOptions\n\n const name = resolvePackageName(\n artifact as {\n name: string\n namespace?: string | undefined\n },\n )\n\n const filterConfig = toFilterConfig({\n blocked: true,\n critical: true,\n cve: true,\n ...getOwn(options, 'filter'),\n }) as AlertFilter\n\n const enabledState = {\n __proto__: null,\n ...socketYml?.issueRules,\n } as Partial<Record<ALERT_TYPE, boolean>>\n\n let sockPkgAlerts: SocketPackageAlert[] = []\n for (const alert of artifact.alerts) {\n const action = alert.action ?? ''\n const enabledFlag = enabledState[alert.type]\n if (\n (action === 'ignore' && enabledFlag !== true) ||\n enabledFlag === false\n ) {\n continue\n }\n const blocked = action === 'error'\n const critical = alert.severity === ALERT_SEVERITY.critical\n const cve = isArtifactAlertCve(alert)\n const fixType = alert.fix?.type ?? ''\n const fixableCve = fixType === ALERT_FIX_TYPE.cve\n const fixableUpgrade = fixType === ALERT_FIX_TYPE.upgrade\n const fixable = fixableCve || fixableUpgrade\n const upgradable = fixableUpgrade && !hasOwn(overrides, name)\n if (\n (filterConfig.blocked && blocked) ||\n (filterConfig.critical && critical) ||\n (filterConfig.cve && cve) ||\n (filterConfig.fixable && fixable) ||\n (filterConfig.upgradable && upgradable)\n ) {\n sockPkgAlerts.push({\n name,\n version,\n key: alert.key,\n type: alert.type,\n blocked,\n critical,\n ecosystem,\n fixable,\n raw: alert,\n upgradable,\n })\n }\n }\n if (!sockPkgAlerts.length) {\n return alertsByPurl\n }\n const purl = `pkg:${ecosystem}/${name}@${version}`\n const major = getMajor(version)!\n if (consolidate) {\n type HighestVersionByMajor = Map<\n number,\n { alert: SocketPackageAlert; version: string }\n >\n const highestForCve: HighestVersionByMajor = new Map()\n const highestForUpgrade: HighestVersionByMajor = new Map()\n const unfixableAlerts: SocketPackageAlert[] = []\n for (const sockPkgAlert of sockPkgAlerts) {\n const alert = sockPkgAlert.raw\n const fixType = alert.fix?.type ?? ''\n if (fixType === ALERT_FIX_TYPE.cve) {\n // An alert with alert.fix.type of 'cve' should have a\n // alert.props.firstPatchedVersionIdentifier property value.\n // We're just being cautious.\n const firstPatchedVersionIdentifier = (alert.props as CveProps)\n ?.firstPatchedVersionIdentifier\n const patchedMajor = firstPatchedVersionIdentifier\n ? getMajor(firstPatchedVersionIdentifier)\n : null\n if (typeof patchedMajor === 'number') {\n // Consolidate to the highest \"first patched version\" by each major\n // version number.\n const highest = highestForCve.get(patchedMajor)?.version ?? '0.0.0'\n if (semver.gt(firstPatchedVersionIdentifier!, highest)) {\n highestForCve.set(patchedMajor, {\n alert: sockPkgAlert,\n version: firstPatchedVersionIdentifier!,\n })\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n } else if (fixType === ALERT_FIX_TYPE.upgrade) {\n // For Socket Optimize upgrades we assume the highest version available\n // is compatible. This may change in the future.\n const highest = highestForUpgrade.get(major)?.version ?? '0.0.0'\n if (semver.gt(version, highest)) {\n highestForUpgrade.set(major, { alert: sockPkgAlert, version })\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n }\n sockPkgAlerts = [\n // Sort CVE alerts by severity: critical, high, middle, then low.\n ...Array.from(highestForCve.values())\n .map(d => d.alert)\n .sort(alertSeverityComparator),\n ...Array.from(highestForUpgrade.values()).map(d => d.alert),\n ...unfixableAlerts,\n ]\n } else {\n sockPkgAlerts.sort((a, b) => naturalCompare(a.type, b.type))\n }\n if (sockPkgAlerts.length) {\n alertsByPurl.set(purl, sockPkgAlerts)\n }\n return alertsByPurl\n}\n\nexport function alertsHaveBlocked(alerts: SocketPackageAlert[]): boolean {\n return alerts.find(a => a.blocked) !== undefined\n}\n\nexport function alertsHaveSeverity(\n alerts: SocketPackageAlert[],\n severity: `${keyof typeof ALERT_SEVERITY}`,\n): boolean {\n return alerts.find(a => a.raw.severity === severity) !== undefined\n}\n\nexport function alertSeverityComparator(\n a: SocketPackageAlert,\n b: SocketPackageAlert,\n): number {\n // Put the most severe first.\n return getAlertSeverityOrder(a) - getAlertSeverityOrder(b)\n}\n\nexport function getAlertSeverityOrder(alert: SocketPackageAlert): number {\n // The more severe, the lower the sort number.\n const { severity } = alert.raw\n return severity === ALERT_SEVERITY.critical\n ? 0\n : severity === ALERT_SEVERITY.high\n ? 1\n : severity === ALERT_SEVERITY.middle\n ? 2\n : severity === ALERT_SEVERITY.low\n ? 3\n : 4\n}\n\nexport function getAlertsSeverityOrder(alerts: SocketPackageAlert[]): number {\n return alertsHaveBlocked(alerts) ||\n alertsHaveSeverity(alerts, ALERT_SEVERITY.critical)\n ? 0\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.high)\n ? 1\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.middle)\n ? 2\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.low)\n ? 3\n : 4\n}\n\nexport type CveFilter = {\n upgradable?: boolean | undefined\n}\n\nexport type CveInfoByAlertKey = Map<\n string,\n {\n firstPatchedVersionIdentifier: string\n vulnerableVersionRange: string\n }\n>\n\nexport type CveInfoByPartialPurl = Map<string, CveInfoByAlertKey>\n\nexport type GetCveInfoByPackageOptions = {\n filter?: CveFilter | undefined\n}\n\nexport function getCveInfoFromAlertsMap(\n alertsMap: AlertsByPurl,\n options?: GetCveInfoByPackageOptions | undefined,\n): CveInfoByPartialPurl | null {\n const filterConfig = toFilterConfig(getOwn(options, 'filter')) as CveFilter\n\n let infoByPartialPurl: CveInfoByPartialPurl | null = null\n alertsMapLoop: for (const { 0: purl, 1: sockPkgAlerts } of alertsMap) {\n const purlObj = getPurlObject(purl, { throws: false })\n if (!purlObj) {\n debugFn('error', 'invalid PURL')\n debugDir('inspect', { purl })\n continue alertsMapLoop\n }\n const partialPurl = createPurlObject({\n type: purlObj.type,\n namespace: purlObj.namespace,\n name: purlObj.name,\n }).toString()\n const name = resolvePackageName(purlObj)\n sockPkgAlertsLoop: for (const sockPkgAlert of sockPkgAlerts) {\n const alert = sockPkgAlert.raw\n if (\n alert.fix?.type !== ALERT_FIX_TYPE.cve ||\n (filterConfig.upgradable === false &&\n getManifestData(sockPkgAlert.ecosystem as any, name))\n ) {\n continue sockPkgAlertsLoop\n }\n if (!infoByPartialPurl) {\n infoByPartialPurl = new Map()\n }\n let infos = infoByPartialPurl.get(partialPurl)\n if (!infos) {\n infos = new Map()\n infoByPartialPurl.set(partialPurl, infos)\n }\n const { key } = alert\n if (!infos.has(key)) {\n // An alert with alert.fix.type of 'cve' should have a\n // alert.props.firstPatchedVersionIdentifier property value.\n // We're just being cautious.\n const firstPatchedVersionIdentifier = (alert.props as CveProps)\n ?.firstPatchedVersionIdentifier\n const vulnerableVersionRange = (alert.props as CveProps)\n ?.vulnerableVersionRange\n let error: unknown\n if (firstPatchedVersionIdentifier && vulnerableVersionRange) {\n try {\n infos.set(key, {\n firstPatchedVersionIdentifier,\n vulnerableVersionRange: new semver.Range(\n // Replace ', ' in a range like '>= 1.0.0, < 1.8.2' with ' ' so that\n // semver.Range will parse it without erroring.\n vulnerableVersionRange\n .replace(/, +/g, ' ')\n .replace(/; +/g, ' || '),\n ).format(),\n })\n continue sockPkgAlertsLoop\n } catch (e) {\n error = e\n }\n }\n debugFn('error', 'fail: invalid SocketPackageAlert')\n debugDir('inspect', { alert })\n debugDir('error', error)\n }\n }\n }\n return infoByPartialPurl\n}\n\nexport function getSeverityLabel(\n severity: `${keyof typeof ALERT_SEVERITY}`,\n): string {\n return severity === 'middle' ? 'moderate' : severity\n}\n\nexport type LogAlertsMapOptions = {\n hideAt?: `${keyof typeof ALERT_SEVERITY}` | 'none' | undefined\n output?: NodeJS.WriteStream | undefined\n}\n\nexport function logAlertsMap(\n alertsMap: AlertsByPurl,\n options: LogAlertsMapOptions,\n) {\n const { hideAt = 'middle', output = process.stderr } = {\n __proto__: null,\n ...options,\n } as LogAlertsMapOptions\n\n const translations = getTranslations()\n const sortedEntries = Array.from(alertsMap.entries()).sort(\n (a, b) => getAlertsSeverityOrder(a[1]) - getAlertsSeverityOrder(b[1]),\n )\n\n const aboveTheFoldPurls = new Set<string>()\n const viewableAlertsByPurl = new Map<string, SocketPackageAlert[]>()\n const hiddenAlertsByPurl = new Map<string, SocketPackageAlert[]>()\n\n for (let i = 0, { length } = sortedEntries; i < length; i += 1) {\n const { 0: purl, 1: alerts } = sortedEntries[i]!\n const hiddenAlerts: typeof alerts = []\n const viewableAlerts = alerts.filter(a => {\n const keep =\n a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER[hideAt]\n if (!keep) {\n hiddenAlerts.push(a)\n }\n return keep\n })\n if (hiddenAlerts.length) {\n hiddenAlertsByPurl.set(purl, hiddenAlerts.sort(alertSeverityComparator))\n }\n if (!viewableAlerts.length) {\n continue\n }\n viewableAlerts.sort(alertSeverityComparator)\n viewableAlertsByPurl.set(purl, viewableAlerts)\n if (\n viewableAlerts.find(\n (a: SocketPackageAlert) =>\n a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER.middle,\n )\n ) {\n aboveTheFoldPurls.add(purl)\n }\n }\n\n // If MIN_ABOVE_THE_FOLD_COUNT is NOT met add more from viewable pkg ids.\n for (const { 0: purl } of viewableAlertsByPurl.entries()) {\n if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {\n break\n }\n aboveTheFoldPurls.add(purl)\n }\n // If MIN_ABOVE_THE_FOLD_COUNT is STILL NOT met add more from hidden pkg ids.\n for (const { 0: purl, 1: hiddenAlerts } of hiddenAlertsByPurl.entries()) {\n if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {\n break\n }\n aboveTheFoldPurls.add(purl)\n const viewableAlerts = viewableAlertsByPurl.get(purl) ?? []\n if (viewableAlerts.length < MIN_ABOVE_THE_FOLD_ALERT_COUNT) {\n const neededCount = MIN_ABOVE_THE_FOLD_ALERT_COUNT - viewableAlerts.length\n let removedHiddenAlerts: SocketPackageAlert[] | undefined\n if (hiddenAlerts.length - neededCount > 0) {\n removedHiddenAlerts = hiddenAlerts.splice(\n 0,\n MIN_ABOVE_THE_FOLD_ALERT_COUNT,\n )\n } else {\n removedHiddenAlerts = hiddenAlerts\n hiddenAlertsByPurl.delete(purl)\n }\n viewableAlertsByPurl.set(purl, [\n ...viewableAlerts,\n ...removedHiddenAlerts,\n ])\n }\n }\n\n const mentionedPurlsWithHiddenAlerts = new Set<string>()\n for (\n let i = 0,\n prevAboveTheFold = true,\n entries = Array.from(viewableAlertsByPurl.entries()),\n { length } = entries;\n i < length;\n i += 1\n ) {\n const { 0: purl, 1: alerts } = entries[i]!\n const lines = new Set<string>()\n for (const alert of alerts) {\n const { type } = alert\n const severity = alert.raw.severity ?? ''\n const attributes = [\n ...(severity\n ? [colors[ALERT_SEVERITY_COLOR[severity]](getSeverityLabel(severity))]\n : []),\n ...(alert.blocked ? [colors.bold(colors.red('blocked'))] : []),\n ...(alert.fixable ? ['fixable'] : []),\n ]\n const maybeAttributes = attributes.length\n ? ` ${colors.italic(`(${attributes.join('; ')})`)}`\n : ''\n // Based data from { pageProps: { alertTypes } } of:\n // https://socket.dev/_next/data/9a6db8224b68b6da0eb9f7dbb17aff7e51568ac2/en-US.json\n const info = (translations.alerts as any)[type]\n const title = info?.title ?? type\n const maybeDesc = info?.description ? ` - ${info.description}` : ''\n const content = `${title}${maybeAttributes}${maybeDesc}`\n // TODO: An added emoji seems to mis-align terminals sometimes.\n lines.add(` ${content}`)\n }\n const purlObj = getPurlObject(purl)\n const pkgName = resolvePackageName(purlObj)\n const hyperlink = format.hyperlink(\n `${pkgName}@${purlObj.version}`,\n getSocketDevPackageOverviewUrl(purlObj.type, pkgName, purlObj.version),\n )\n const isAboveTheFold = aboveTheFoldPurls.has(purl)\n if (isAboveTheFold) {\n aboveTheFoldPurls.add(purl)\n output.write(`${i ? '\\n' : ''}${hyperlink}:\\n`)\n } else {\n output.write(`${prevAboveTheFold ? '\\n' : ''}${hyperlink}:\\n`)\n }\n for (const line of lines) {\n output.write(`${line}\\n`)\n }\n const hiddenAlerts = hiddenAlertsByPurl.get(purl) ?? []\n const { length: hiddenAlertsCount } = hiddenAlerts\n if (hiddenAlertsCount) {\n mentionedPurlsWithHiddenAlerts.add(purl)\n if (hiddenAlertsCount === 1) {\n output.write(\n ` ${colors.dim(`+1 Hidden ${getSeverityLabel(hiddenAlerts[0]!.raw.severity ?? 'low')} risk alert`)}\\n`,\n )\n } else {\n output.write(\n ` ${colors.dim(`+${hiddenAlertsCount} Hidden alerts ${colors.italic(getHiddenRisksDescription(getHiddenRiskCounts(hiddenAlerts)))}`)}\\n`,\n )\n }\n }\n prevAboveTheFold = isAboveTheFold\n }\n\n const additionalHiddenCount =\n hiddenAlertsByPurl.size - mentionedPurlsWithHiddenAlerts.size\n if (additionalHiddenCount) {\n const totalRiskCounts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0,\n }\n for (const { 0: purl, 1: alerts } of hiddenAlertsByPurl.entries()) {\n if (mentionedPurlsWithHiddenAlerts.has(purl)) {\n continue\n }\n const riskCounts = getHiddenRiskCounts(alerts)\n totalRiskCounts.critical += riskCounts.critical\n totalRiskCounts.high += riskCounts.high\n totalRiskCounts.middle += riskCounts.middle\n totalRiskCounts.low += riskCounts.low\n }\n output.write(\n `${aboveTheFoldPurls.size ? '\\n' : ''}${colors.dim(`${aboveTheFoldPurls.size ? '+' : ''}${additionalHiddenCount} Packages with hidden alerts ${colors.italic(getHiddenRisksDescription(totalRiskCounts))}`)}\\n`,\n )\n }\n output.write('\\n')\n}\n","/**\n * Alerts map utilities for Socket CLI.\n * Manages security alerts and vulnerability mappings for packages.\n *\n * Key Functions:\n * - getAlertsMapFromPnpmLockfile: Extract alerts from pnpm lockfile\n * - getAlertsMapFromPurls: Get alerts for specific package URLs\n * - processAlertsApiResponse: Process API response into alerts map\n *\n * Alert Processing:\n * - Filters alerts based on socket.yml configuration\n * - Maps package URLs to security vulnerabilities\n * - Supports batch processing for performance\n *\n * Integration:\n * - Works with pnpm lockfiles for dependency scanning\n * - Uses Socket API for vulnerability data\n * - Respects filter configurations from socket.yml\n */\n\nimport { arrayUnique } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { findSocketYmlSync } from './config.mts'\nimport { toFilterConfig } from './filter-config.mts'\nimport { extractPurlsFromPnpmLockfile } from './pnpm.mts'\nimport { setupSdk } from './sdk.mts'\nimport { addArtifactToAlertsMap } from './socket-package-alert.mts'\n\nimport type { CompactSocketArtifact } from './alert/artifact.mts'\nimport type { AlertFilter, AlertsByPurl } from './socket-package-alert.mts'\nimport type { LockfileObject } from '@pnpm/lockfile.fs'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport type GetAlertsMapFromPnpmLockfileOptions = {\n apiToken?: string | undefined\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n overrides?: { [key: string]: string } | undefined\n nothrow?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPnpmLockfile(\n lockfile: LockfileObject,\n options?: GetAlertsMapFromPnpmLockfileOptions | undefined,\n): Promise<AlertsByPurl> {\n const purls = await extractPurlsFromPnpmLockfile(lockfile)\n return await getAlertsMapFromPurls(purls, {\n overrides: lockfile.overrides,\n ...options,\n })\n}\n\nexport type GetAlertsMapFromPurlsOptions = {\n apiToken?: string | undefined\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n onlyFixable?: boolean | undefined\n overrides?: { [key: string]: string } | undefined\n nothrow?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPurls(\n purls: string[] | readonly string[],\n options?: GetAlertsMapFromPurlsOptions | undefined,\n): Promise<AlertsByPurl> {\n const uniqPurls = arrayUnique(purls)\n debugDir('silly', { purls: uniqPurls })\n\n let { length: remaining } = uniqPurls\n const alertsByPurl: AlertsByPurl = new Map()\n\n if (!remaining) {\n return alertsByPurl\n }\n\n const opts = {\n __proto__: null,\n consolidate: false,\n nothrow: false,\n ...options,\n filter: toFilterConfig(getOwn(options, 'filter')),\n } as GetAlertsMapFromPurlsOptions & { filter: AlertFilter }\n\n if (opts.onlyFixable) {\n opts.filter.fixable = true\n }\n\n const { apiToken, spinner } = opts\n\n const getText = () => `Looking up data for ${remaining} packages`\n\n spinner?.start(getText())\n\n const sockSdkCResult = await setupSdk({ apiToken })\n if (!sockSdkCResult.ok) {\n spinner?.stop()\n throw new Error('Auth error: Run `socket login` first.')\n }\n const sockSdk = sockSdkCResult.data\n const socketYmlResult = findSocketYmlSync()\n const socketYml =\n socketYmlResult.ok && socketYmlResult.data\n ? socketYmlResult.data.parsed\n : undefined\n\n const alertsMapOptions = {\n consolidate: opts.consolidate,\n filter: opts.filter,\n overrides: opts.overrides,\n socketYml,\n spinner,\n }\n\n try {\n for await (const batchResult of sockSdk.batchPackageStream(\n {\n components: uniqPurls.map(purl => ({ purl })),\n },\n {\n queryParams: {\n alerts: 'true',\n compact: 'true',\n ...(opts.onlyFixable ? { fixable: 'true ' } : {}),\n ...(Array.isArray(opts.filter.actions)\n ? { actions: opts.filter.actions.join(',') }\n : {}),\n },\n },\n )) {\n if (batchResult.success) {\n const artifact = batchResult.data as CompactSocketArtifact\n await addArtifactToAlertsMap(artifact, alertsByPurl, alertsMapOptions)\n } else if (!opts.nothrow) {\n spinner?.stop()\n if (isNonEmptyString(batchResult.error)) {\n throw new Error(batchResult.error)\n }\n const statusCode = batchResult.status ?? 'unknown'\n throw new Error(\n `Socket API server error (${statusCode}): No status message`,\n )\n } else {\n spinner?.stop()\n logger.fail(\n `Received a ${batchResult.status} response from Socket API which we consider a permanent failure:`,\n batchResult.error,\n batchResult.cause ? `( ${batchResult.cause} )` : '',\n )\n debugDir('inspect', { batchResult })\n break\n }\n remaining -= 1\n if (remaining > 0) {\n spinner?.start(getText())\n }\n }\n } catch (e) {\n spinner?.stop()\n throw e\n }\n\n spinner?.stop()\n\n return alertsByPurl\n}\n","/**\n * npm package specification utilities for Socket CLI.\n * Parses and handles various npm package specification formats.\n *\n * Supported Formats:\n * - Regular packages: lodash, lodash@4.17.21\n * - Scoped packages: @types/node, @types/node@20.0.0\n * - Version ranges: lodash@^4.0.0, lodash@~4.17.0\n * - Git URLs: git+https://github.com/user/repo.git\n * - File paths: file:../local-package\n * - Aliases: my-alias@npm:real-package@1.0.0\n *\n * Key Functions:\n * - safeNpa: Safe wrapper for npm-package-arg\n * - safeNpmSpecToPurl: Convert npm spec to PURL\n * - safeParseNpmSpec: Parse npm spec to name/version\n *\n * Error Handling:\n * - Returns undefined for invalid specs\n * - Fallback parsing for edge cases\n * - Safe against malformed input\n */\n\nimport npmPackageArg from 'npm-package-arg'\n\nimport { NPM } from '../constants.mts'\nimport { createPurlObject } from './purl.mts'\n\n// @ts-expect-error - Result is re-exported below.\nimport type { Result } from 'npm-package-arg'\n\nexport type {\n AliasResult,\n FileResult,\n HostedGit,\n HostedGitResult,\n RegistryResult,\n Result,\n URLResult,\n} from 'npm-package-arg'\n\nexport type ParsedPackageSpec = {\n name: string\n version: string | undefined\n}\n\n/**\n * Safe wrapper for npm-package-arg that doesn't throw.\n * Returns undefined if parsing fails.\n */\nexport function safeNpa(\n ...args: Parameters<typeof npmPackageArg>\n): ReturnType<typeof npmPackageArg> | undefined {\n try {\n return Reflect.apply(npmPackageArg, undefined, args)\n } catch {}\n return undefined\n}\n\n/**\n * Parse npm package specification into name and version.\n * Uses npm-package-arg for proper handling of various spec formats:\n * - Regular packages: lodash, lodash@4.17.21\n * - Scoped packages: @types/node, @types/node@20.0.0\n * - Version ranges: lodash@^4.0.0\n * - Git URLs, file paths, etc.\n *\n * Returns undefined if parsing fails.\n */\nexport function safeParseNpmSpec(\n pkgSpec: string,\n): ParsedPackageSpec | undefined {\n // Use npm-package-arg for proper spec parsing.\n const parsed = safeNpa(pkgSpec)\n\n if (!parsed) {\n // Fallback to simple parsing if npm-package-arg fails.\n // Handle scoped packages first to avoid confusion with version delimiter.\n if (pkgSpec.startsWith('@')) {\n const scopedMatch = pkgSpec.match(/^(@[^/@]+\\/[^/@]+)(?:@(.+))?$/)\n if (scopedMatch) {\n return {\n name: scopedMatch[1]!,\n version: scopedMatch[2],\n }\n }\n }\n\n // Handle regular packages.\n const atIndex = pkgSpec.indexOf('@')\n if (atIndex === -1) {\n return { name: pkgSpec, version: undefined }\n }\n\n return {\n name: pkgSpec.slice(0, atIndex),\n version: pkgSpec.slice(atIndex + 1),\n }\n }\n\n // Extract name and version from parsed spec.\n const name = parsed.name || pkgSpec\n let version: string | undefined\n\n // Handle different spec types from npm-package-arg.\n if (\n parsed.type === 'tag' ||\n parsed.type === 'version' ||\n parsed.type === 'range'\n ) {\n // For npm registry packages:\n // - type 'tag': latest, beta, etc.\n // - type 'version': exact version like 1.0.0\n // - type 'range': version range like ^1.0.0, ~1.0.0, or * for bare names\n // Don't include '*' as a version - it means \"any version\".\n if (parsed.fetchSpec && parsed.fetchSpec !== '*') {\n version = parsed.fetchSpec\n } else if (\n parsed.rawSpec &&\n parsed.rawSpec !== '*' &&\n parsed.rawSpec !== parsed.name\n ) {\n version = parsed.rawSpec\n }\n } else if (\n parsed.type === 'git' ||\n parsed.type === 'remote' ||\n parsed.type === 'file'\n ) {\n // For non-registry specs, use rawSpec if different from name.\n if (parsed.rawSpec && parsed.rawSpec !== parsed.name) {\n version = parsed.rawSpec\n }\n }\n\n return { name, version }\n}\n\n/**\n * Convert npm package spec to PURL string.\n * Handles various npm spec formats and converts them to standardized PURLs.\n * Returns undefined if conversion fails.\n */\nexport function safeNpmSpecToPurl(pkgSpec: string): string | undefined {\n const parsed = safeParseNpmSpec(pkgSpec)\n if (!parsed) {\n return undefined\n }\n\n const { name, version } = parsed\n\n // Create PURL object to ensure proper formatting.\n const purlObj = createPurlObject({\n type: NPM,\n name,\n version,\n throws: false,\n })\n\n return (\n purlObj?.toString() ?? `pkg:${NPM}/${name}${version ? `@${version}` : ''}`\n )\n}\n\n/**\n * Convert npm package spec to PURL string.\n * Handles various npm spec formats and converts them to standardized PURLs.\n * Throws if conversion fails.\n */\nexport function npmSpecToPurl(pkgSpec: string): string {\n const purl = safeNpmSpecToPurl(pkgSpec)\n if (!purl) {\n throw new Error(`Failed to convert ${NPM} spec to PURL: ${pkgSpec}`)\n }\n return purl\n}\n"],"names":["logger","debugFn","phase","details","git_op","getSentry","constructor","socketAppDataPath","debugConfig","updateConfigValue","mkdirSync","recursive","ok","data","yml","path","parsed","debugDir","prevDir","message","cause","_configFromFlag","_cachedConfig","localConfig","wasDeleted","_pendingSave","writeFileSync","_requirements","_defaultToken","__proto__","apiProxy","proxy","baseUrl","timeout","name","version","homepage","hooks","debugApiRequest","spinner","socketSdkErrorResult","sdkResult","method","headers","Authorization","result","status","code","body","mw2","lines","cols","length","cws","url","msg","indent","keyPrefix","padName","bestScore","bestMatch","REDACTED","matrix","subcommands","type","hidden","description","commandOrAliasName","argv","allowUnknownFlags","autoHelp","autoVersion","booleanDefault","compactHeader","config","org","configOverrideResult","emitBanner","parentName","Object","commands","noBanner","noSpinner","help","collectUnknownFlags","importMeta","cli","console","process","meow","numeric","style","sdkOpts","organizations","value","choices","orgSlug","GITHUB_REF_TYPE","cwd","info","remoteUrl","error","stdio","debugGit","branch","email","user","filepaths","commitMsg","owner","repo","parsedGitRemoteUrlCache","opts","throws","namespace","keys","onlyDirectories","onlyFiles","root","dir","NODE_MODULES","workspacePatterns","ignores","absolute","ignore","concurrency","hasNegatedPattern","dot","filtered","all","nothrow","shadowBinPath","shadowIndex","theBinPath","WIN32","thePath","socketConfig","_yarnBinPath","_yarnBinPathDetails","encoding","_isYarnBerry","YARN_LOCK","force","silent","pm","spawnArgs","finalShadowOptions","env","npm_config_dlx_cache_max_age","SOCKET_CLI_VERSION","mixinsEnv","ipc","getDefaultSocketJson","jsonContent","jsonObj","sockJson","ttlMs","githubCachePath","results","SOCKET_CLI_GITHUB_TOKEN","auth","octokitOptions","_octokit","_octokitGraphql","node_id","pullRequestId","enabled","GITHUB_SERVER_URL","cmd","i","flagsToFilterSet","flagsWithValueSet","cve_id","per_page","cargo","composer","gem","go","golang","maven","npm","nuget","pypi","swift","ecosystem","affects","toAddToBashrc","targetName","targetPath","_npmBinPath","_npmBinPathDetails","_npmDirPath","_npxBinPath","_npxBinPathDetails","args","spawnPromise","pkgPath","CI","NODE_OPTIONS","YARN_CLASSIC","semver","onUnknown","editable","agent","maintainedNodeVersions","engines","pkgAgentRange","pkgNodeRange","lockName","lockPath","features","npmBuggyOverrides","pkgRequirements","pkgSupports","node","cmdName","prod","win32","_pnpmBinPath","_pnpmBinPathDetails","normalized","seen","cve","remove","upgrade","critical","high","middle","low","header","hyperlink","fallback","fallbackToUrl","_translations","none","descriptions","consolidate","socketYml","blocked","raw","upgradable","highestForCve","alert","unfixableAlerts","highestForUpgrade","sockPkgAlerts","alertsByPurl","severity","hideAt","hiddenAlerts","viewableAlerts","viewableAlertsByPurl","aboveTheFoldPurls","removedHiddenAlerts","hiddenAlertsByPurl","output","mentionedPurlsWithHiddenAlerts","prevAboveTheFold","totalRiskCounts","purls","apiToken","components","purl","queryParams","alerts","compact","fixable","batchResult","remaining"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAOA;AACA;AACA;AACA;AACO;AAKL;;AAEEA;AAGF;AACF;;AAEA;AACA;AACA;AACA;AACO;AAQL;AACE;AACF;AAEA;AACEA;AAGA;AACEA;AAGF;AACF;;;AAKE;AACEA;AAGF;AACF;AACF;;AAEA;AACA;AACA;AACA;AACO;AAKL;;;;;AAKE;AACF;;AAEA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAKL;AACE;AACE;AACEC;AACF;AACA;AACF;AACE;AACEA;AACF;AACA;AACF;AACEA;AAIA;AACF;;AAEIC;AACAC;AACF;AACA;AACJ;AACF;;AAEA;AACA;AACA;AACO;AAKL;;;;AAIE;;AAEAF;AACF;AACEA;AACF;AACF;;AAEA;AACA;AACA;AACA;AACO;;;AAODG;;AAEF;;AAKA;AACAH;AACF;AACEA;AACF;AACF;;ACjLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAQA;;AAEE;AAA+DI;AAAU;AAC3E;AAIO;AAEA;AAGLC;;;AAGA;AACF;AAEO;AAIL;AACA;;AAEA;AACF;AAEO;AAIL;;AAEE;AACF;AACAL;AACA;AACF;;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AAEP;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;;AAEF;;ACzIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA0CA;AAIA;AAsBA;AAGA;AAEA;;AAEI;;;AAEQM;AAAkB;AAC1B;AACE;AACA;;;AAMIC;;AAEAR;AACAQ;AACF;AACA;AACA;AACA;AACE;;AAEAC;AACF;AACF;AACEC;AAA6CC;AAAgB;AAC/D;AACF;AACF;AACA;AACF;AAEA;AAGE;AACA;AACA;AACA;AAMA;;AAEIC;;AAEAC;;AAEJ;;AACSD;AAAUC;;AACrB;AAOO;;;;AAMH;;;AAGEC;AACF;AACA;;;AAGMF;AACAC;AACEE;AACAC;AACF;;;AAGFf;AACAgB;;AAEEL;;;;AAIJ;AACF;AACAM;;AAEF;;AACSN;AAAUC;;AACrB;AAEO;AAGL;AACA;AACA;AACE;AACF;;AACSD;AAAUC;;AACrB;;AAEA;AACA;AACO;AAGL;AACA;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACA;AACE;;AAEF;AAEO;;AAEP;AAEO;;AAEP;AAEO;AACL;AACF;AAEO;AACL;AACF;AAEO;AACL;AACF;AAEA;AACA;AACA;AAEO;AACLZ;AAEA;;;AAGE;AACE;AACA;;AAEEW;AACAO;AACAC;;AAGJ;AACF;AACE;;AAEAC;;AAGET;AACAO;AACAC;;AAGJ;;AAEA;AACAE;AACAD;;AAEA;AACA;AACE;AACErB;AAGF;AACAsB;;AAEF;;AAESV;AAAUC;;AACrB;AAEO;AACLZ;AACA;AACAqB;AACE;AACA;;;;AAEFD;AACF;AAEA;AACO;AAIL;AACA;AACA;AACE;AACF;AACA;AACA;AACA;;AAEE;AACEE;AACF;;AAEEC;AACF;AACF;;AAEIxB;AAGF;AACAuB;AACF;AACA;;AAEIX;;AAEAC;;AAEJ;;AAGEY;;AAEEA;;AACQlB;AAAkB;AAC1B;AACEmB;AAIF;AACF;AACF;;AAGEd;;AAEAC;;AAEJ;;ACrWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAOA;AAEA;AAIO;;AAEHc;AAGF;AACA;AACF;;AAEA;AACA;AACA;AACO;AACL;AACF;;ACvCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAsBA;AACA;AACA;;AAEA;AACO;;AAIL;AACF;;AAEA;AACO;;AAIL;AACF;;AAEA;AACA;AACO;AACL;AACEC;AACA;AACF;AAEA;;AAMA;AACF;AAEO;AACL;AAKF;AAEO;AACL;AACA;AAMF;AAEO;AACL;AACF;AAQO;AAGL;AAAeC;;;;;AACuB;;;AAIlCV;AAEF;AACAS;AACF;;;AAIIhB;AACAO;AACAC;;AAEJ;;AAEMU;AAAS;AACf;;AAEA;;;AAE4C;;AAE5C;AACA;;AAKA;AACE;;AAAyCC;;;AACzC;AAAmBC;;AACnBC;;AAEEC;AACAC;AACAC;AACF;AACA;AACA;AAEMC;;AAEIC;;;;AAWF;AACF;;;AAKR;;AAIA;;;AAKE1B;AACAC;;AAEJ;;AC7LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA+BA;AAOA;AACA;AACA;AACA;;AAII;AACF;AAEA;AACA;AACA;AACF;;AAEA;AACA;AACA;AACA;AACE;AACA;AACE;AACF;AAEAb;AACA;AACEA;AACF;AACAA;AACF;;AAEA;AACO;;AAIL;AACE;AACF;AACA;AACA;AACF;;AAEA;AACA;AACA;AACO;;AAEH;AACF;AACA;AACE;AACF;;AAEE;AACF;;AAEE;AACF;;AAEF;AAYA;AACA;AACA;AACO;;;;AAI6BuC;AAAQ;AACxCV;;;AAIF;AACEU;AACF;;AAEA;AAEA;;;;AAIE;AACE;;AAEEvC;AACF;AACEA;AACF;AACF;;;AAGA;AACEY;AACAO;;;AAGF;AACEnB;AACF;;AACsBwC;AAAqB;AAC3C;AACF;;AAEA;AACA;AACE;;;AAEsBC;AAAU;;AAGhC;AACA;AACA;AACA;AAEA;AACE7B;AACAO;;AAEAN;;AAEA;;;AAGF;AACA;;AAEA;AAEA;AACF;AACA;AACED;;;AAGF;AACF;AAEO;AAIL;;;;AAIEX;AACAgB;AAEA;;AAEA;;;AAIEL;;AAEA;AAAcQ;;;AAElB;;AAEA;AACA;AACEnB;;AACsBwC;AAAU;;AAGhC;AAGA;AACA;AACA;;AAIE7B;AACAO;;AAEAN;;AAEA;;AAEJ;;;AAGID;;;AAGJ;AACF;AAEA;AACE;;AAEE;AACF;AAEA;AACA;AACE8B;AACAC;AACEC;AACF;AACF;AACA;AACF;;AAEA;AACA;AACA;AACO;AAKL;;;AAGIhC;AACAO;AACAC;;AAGJ;;AAEQmB;AAAQ;AAEhB;AACEA;;AAEF;AAEA;AACA;;AAEEM;;;AAUA;AACEN;AAGF;;;AAGA;AACEA;;AAIF;AAEAtC;AACAgB;AAEA;;AAEA;;;AAIEL;;AAEA;AAAcQ;;;AAElB;AAEA;;AACU0B;AAAO;;AAMblC;AACAO;;AAEAN;AACEkC;AACF;;AAEJ;;AAGE;;AAEEnC;AACAC;;;AAGFZ;AACAgB;;AAGEL;AACAO;AACAC;;AAEJ;AACF;;AAEA;AACA;AACA;AACO;;AAML;AACE;AACF;;;AAIIR;AACAC;;;;AAIAD;AACAO;AACAC;;AAEJ;AACF;AASA;AACA;AACA;AACO;AAIL;;;AAGIR;AACAO;AACAC;;AAGJ;AAEA;;;AAGIR;AACAO;AACAC;;AAGJ;;;;;AAEwCsB;AAAO;AAC7Cb;;;;AAGMU;AAAQ;AAEhB;AACEA;AACF;AAEA;;AAEE;;AAEEI;;AAEE;;AAEF;AAAaK;;;;AAOf;AACET;AAGF;;AAEA;AACEA;AAGF;AAEAtC;AACAgB;AAEA;;AAEA;;;AAIEL;;AAEA;AAAcQ;;;AAElB;AAEA;;AACU0B;AAAO;AACf;AACA;;AAEA;;AAEElC;AACAO;;AAEAN;AACEkC;AACF;;AAEJ;;AAGE;;AAEEnC;AACAC;;;AAGFZ;AACAgB;;AAEEL;AACAO;AACAC;;AAEJ;AACF;;ACzgBO;;AAOL;AACA;AACF;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEO;AAKL;AACA;AACA;AACA;AACA;AACA;AACA;AAAa;AAAQ;AAAS;;AAE5B6B;AACF;;;AAIAC;AACA;AAAa;AAAQ;AAAS;;AAI9B;AACAA;AAEA;AACF;AAEO;AAEL;AACA;AACAC;AAGA;;AAGA;AACE;AAAkBC;;AAChB;AACA;AACAC;AAKF;AACF;;AAGA;;AAEA;;AAGA;AAAkBD;;;AAElB;;AAGA;AACEJ;AACA;AAAkBI;;AAChB;AACA;;AAEF;AACAJ;AACF;AAEA;AACF;AAEO;AAEL;AACA;AACAG;AAEA;;;;;AAMA;;AAGA;;AAEA;;AAGA;AAAkBC;;;AAElB;;;AAIEJ;;;AAGAA;AACF;AAEA;AACF;;ACxHA;AACA;AACO;AACL;;;AAGuBnC;AAAK;;AAE1B;AACA;AACA;AACED;AACAO;AACAC;AAEF;AACF;;AAGE;;;;AAOApB;AACAC;AACAgB;;AAEA;AACA;AACEL;AACAO;AACAC;AACF;AACF;AACF;;ACrCA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;;AAIF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;;AAEP;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;AAIF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AAML;AACA;AACE;AACA;AACEkC;AACF;AACEA;AACF;AACF;AACEA;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACF;;ACnGO;;AAWH;AACF;AAEA;AACA;AACE;AACA;AACE;AACF;;;AAEQF;AAAkB;;AAExB;AACF;AACA;AACA;AACA;AACA;;AAEA;AACE;;AAEF;AACAG;;AAEEA;AACF;AACF;;AAEA;AACA;AACA;;;AAIEvD;AAEIY;AACAO;;AAEF;AAEJ;AACEnB;AACF;AAEA;AACF;;ACpEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMO;AACL;AACE;AACF;AACA;AACE;AACF;AACA;AACF;;AC9BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEO;AACL;AACF;;ACfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA2BO;;AAIGwD;AAAW;AACjB3B;;;AAGF;AACA;AACA;;AAEA;AACE;AACA;AACA;;;AAGEqB;AACF;;;;AAIA;AACAL;AACF;AACA;AACF;AAEO;;AAIGY;AAAiB;AACvB5B;;;AAGF;;AAGE;AACE;AAAY4B;AAAU;AAE5B;AAEO;;AAKHD;AACAC;AACAC;AACF;AACE7B;;;;AAIF;AACA;AACE;AACA;;AAEE;AACF;;;AAIAgB;;AAGA;AACEA;AACF;AACAA;AACF;AACA;AACF;;AC3HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAQO;;AAKP;;AC4CA;AACA;AACA;;AA2BA;AAEA;;AAEA;AACA;AACA;AACA;AACE;AACA;;AAGF;;AAEA;AACA;AACA;AACA;;;AAOE;AACA;AACE;AAIA;AACA;;AAEEc;AACAC;AACF;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACE;AACE;AACF;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AAKE;;AACQC;AAAS;AACjB;;AAEA;AACA;AACA;AACA;;AAOA;AACA;;;AAGA;AACA;;AAEA;AACA;AAQA;;AAEA;AACA;;AAQA;AACA;;AAME;;AAIF;;AAEA;AACA;AACA;AACF;AACA;AACA;AACA;AACA;AACE;;AAEF;;AAEA;AACA;AACA;AACA;AACE;AAA4BT;AAAqB;AAGjD;AACEU;AACF;AACA;AACEA;AACF;AACA;AACE;AACE;;AAEE;;AAEA;;AAEA;AACAA;AAEJ;AACF;;AAEF;;AAEA;AACA;AACA;AACA;AACE;AAGF;;AAEA;AACA;AACA;AACO;AAKL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEF;;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;;;;AAI2BC;AAAY;AAC1ClC;;;;;;;AAOF;AAAMA;;;AACN;AACE;AACAM;AACE6B;AACAC;AACAC;;AAEF;;AAGF;;AAEA;AACEC;AACF;;AAEA;AACA;;AAIA;;AAEE;AACEnE;;;;;AAE0D+D;;AAG5D;AACA;AACA;;AAEE/D;;;AAIIoE;;AAOAL;;AAIN;AACF;AAEA;AACE;;;AAIEE;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKJ;;;;AAIA;;AAEA;AACA;;;;AAIE;;AAEA;AACAI;AACA;AACAC;AACAC;AACA;AACAC;AACF;;AAGEC;AACAC;AACAC;AACApC;;AAQF;;;AAIA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;;AAGEqC;AACF;AAEA;AACE;AACA;;AAEF;AACE;AACA;AACE;AACA;;AAEF;AACF;AAEA;AACE;AACEC;AACA;AACA7E;AACF;AACAA;;AAEA;AACF;;AAEA;AACA;AACE;AACA;;AAIA;;AAEA;AACA;AACE;AACA;;AAEE8E;AACF;AACF;;AAEA;AACA;;AAEE;;;AAKE;AACF;AACF;AACF;;AAGA;AACE5B;AAIF;AACAA;AACA;AACE;;AAUE;AACA;AASA;AACA;AACA;AAIA;;AAIA;AAAA;AAEF6B;AAII;AACEC;AACF;AACEhF;AACF;AACF;;;AAIE;AACA;;AAOJ;;AAqCF;AACEkD;AACAA;AAGM;AAGS;AAAc;AAIvB;AAEsC;AAAS;;AACjCe;AAAO;;;AAGf;AACF;AAGN;AACET;AAAqBE;;AAG7B;AAEAR;AACA;AACEA;AAIF;AACEA;AACF;AACAA;AAGM;AACA;AACA+B;;AAEEhB;;AAEF;AACAiB;;AAEEjB;AACF;AACF;AACET;AAAqBE;;AAG3B;AACE;AACA;AAEA;AACE;;AA+BF;AACE;AACAR;AAKF;AACF;;AAEA;AACA;AACA;;;;AAIE;;AAEA;AACAmB;AACA;AACA;AACAC;AACAC;AACA;AACAC;AACAW;AACF;;;AAEgBA;;;AAKhB;AACA;AACEN;AACA;AACF;AACA;;;AAGA;AACE;AACA;;AAEF;AACF;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;;AAMHH;;AAEAI;AACF;AAAMjD;;;;AACEwC;AAAyB;AAC/BxC;;;;;AAMF;;;AAGE;AACAyC;AACAC;AACA;AACAC;AACAY;;;;AAIAC;AACF;;AAGEZ;AACAU;AACAR;AACApC;AACAJ;;AASF;;;AAIA;AACA;AACA;;AAEA;AAEA;AACE0C;AACA;AACA;AACA7E;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACEsF;AACF;;AAEA;;AAEE;AACAC;AACA;AACAC;AACA;AACA;AACF;;AAEA;AACA;AACA;;AAEAC;;AAEE;AACA;AACApB;AACA;AACAC;AACAC;;;;;AAKF;AACA;;AAGA;AACF;;ACp4BO;AACL;AACA;;AAGEmB;AACAC;AACF;AAEA;AACA;AACE;AACA;;;AAGA;AACF;;AAEE;AACF;AACE;AACF;AACF;;ACMO;;AAIHzB;;AAEA0B;AACF;AACE/D;;;;;AAMA;AACA;AACE;AACF;;AAEF;;AAGEqC;AACF;AACA;AACE;AACF;;AAGE;AACArD;;AAEA;;AAEJ;;ACzDO;AACL;AACA;AACEb;AAGA;AACF;;AAEA;AACA;;AACQ6F;;AACR;AACE1E;;;;;AAOM2E;;;AAGJ;AAEE5D;AACA4D;AACA5B;;AAIN;AAEA;AACE;AACF;AACA;AACF;;ACrCO;AACL;;AAEE;AACA;AACF;AAEA;;AAEE6B;AAEI7D;AACA4D;AACA5B;AACF;AAEEhC;AACA4D;AACA5B;AACF;AAEEhC;AACA4D;AACA5B;;AAIN;;AAEE;;AAEElE;AACF;;AAKA;AACF;AACE;;AAEEA;AACF;;AAIA;AACF;AACF;;AC1CO;AAKL;;;;AAIIA;AAGAA;AACAA;AAGAA;AAGAA;AAGAA;AAGAA;AACAA;AAGAA;;AAIAA;AACAA;AAGA;AACF;AAEAA;AAGAA;AACAA;AACAA;AACA;AACEA;AACF;AACEgG;AACA;;AAEA;AACF;AACF;AAEA;AACF;;AC5DA;AACO;AACL;AACA;AACE/F;;AAKSW;AAAUC;;AACrB;AAEA;AACA;AACEZ;;AAKSW;AAAUC;;AACrB;AAEA;AACA;AACE;AACF;;AAEQgF;;AACR;AACA;;AAEIjF;AACAO;AACAN;;AAEJ;AAEA;;;AAGID;AACAO;AACAN;;AAEJ;AAEAZ;;AAGEW;AACAO;AACAN;;AAEJ;;AC3DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEI;AACF;;AAEA;AACA;AACE;AAAA;AAEA;AAAA;AAEA;AAAA;AAEA;AAAA;AAEA;AAAA;AAGF;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACA;AACF;;ACxCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAaA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGK;;;;AACqCoF;;AAC1C;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;;AAEE;AACqDC;;AAGrD;AACA;AACE;AACF;;AAEF;AACA;AACA;AACF;AAOO;AAGL;;AAEE;AACwDA;;AAExDC;;AAEElG;;AACmBmG;AAAU;AAC/B;;AAEA;;AACsBjF;AAAsCkF;AAAS;AACvE;AACA;AACF;AAEO;AACL;AACA;AAGF;AASO;AAGL;AAAyCH;;AACzC;AACA;;AAEE;;;AAOA;;AACsB/E;AAAmCkF;AAAS;AACpE;AACA;AACA;;AAEE;;;AAOA;;AACsBlF;AAA2CkF;AAAS;AAC5E;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAGL;AACA;AACE;AACA;AACE;AACF;AACF;AACA;AACA;AACE;AACA;AACE;AACF;AACF;;AAEF;AAQO;AACL;;AAEEC;;;;AAIAC;AACA;;AAEAA;AAAgCF;AAAS;AAC3C;AACA;AACF;AAEO;AAIL;;AAEEC;;;;AAIAC;AACA;;AAEAA;AAAwCF;AAAS;AACnD;AACA;AACF;AAEO;AAIL;AACE;AACF;AACA;;AAEEC;;;;AAIAC;AACA;;AAEAA;AAAsCF;AAAS;AACjD;AACA;AACF;AAEO;AAIL;;AAEEC;;;AAGA;AAKAC;AACA;;;AAGEtG;AAIAgB;;AACsBuF;AAAO;AAC/B;AACED;AAAoCF;AAAS;AAC/C;AACF;AACA;AACF;AAEO;AAKL;AACEpG;AACA;AACF;;AAEEiG;AACAO;AACAC;AACF;AAAM7E;;;AAEN;AAEA;;AAEEyE;;;AAGA;AACAC;;AAAgD;;AAEhDA;AAAyBF;AAAS;;AACZM;AAAU;AAChC;AACF;;AAGE;AACAJ;AACA;;AAEAA;AAA4BF;AAAS;;AACfO;AAAU;AAClC;AACA;AACF;AAEO;AAIL;;AAEEN;;;AAGA;AACA;AACA;;AAEA;;;AAGED;AACF;AACF;AACA;AACF;AAEO;AAIL;;AAEEC;;;AAGA;AACA;AAKA;;AAEA;;;AAGED;AACF;AACF;AACA;AACF;AAEO;AAKL;AAAyCH;;AACzC;;AAK4B;AAAS;AAAS;AAC1C;;AAEE;AACA;;;AAOA;;;AAGEG;AACF;AACF;;AAEE;;AAEEC;;;AAGA;;AAEArG;AACAgB;;AACsB6E;AAAM;AAC9B;AACF;AACF;AAEJ;AAEO;AAIL;;AAEEQ;;;AAGA;AACA;AAKA;AACF;AACE;AAAA;AAEF;AACF;AAEO;AAIL;AAAyCJ;;;AAEvC;AAKA;;AAEA;;;AAGEG;AACF;AACF;AACA;AACF;AAEO;AAIL;AACA;AACA;;AAEF;AAEO;AAIL;;AAEEC;;;AAGA;AACAC;AACA;;AAEAA;AAA4CF;AAAS;AACvD;AACA;AACF;AAEO;AAGL;AAAyCH;;;AAEvC;AAKA;AACA;;AAEEtF;;;;AAIFX;AACAgB;;AAEEL;AACAO;AACAC;;AAEJ;AACF;AAEA;AAEO;AACL;AACA;;;;AAEA;AACA;AACA;AACA;AACA;AACEyB;AAAWgE;;;AACb;AACE;;AAEE;AACA;AACA;AACA;;AAEA;AACA;;AAEEhE;;AAAkBiE;;AACpB;;AAEJ;AACAC;AACA;;AAA4B;AAC9B;;AChhBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA0DO;AAKL;AACA;AACEC;AAASnF;;;;;AAGX;AACEmF;AAASnF;;;;AAEX;AACEmF;AAASnF;;;AACT;;AAEA;AACF;;;;;;AACgDM;AAAQ;;;AAGtD;;AASA;AACE;AACF;AACF;AACA;AACF;AAwCO;;AAIG8E;AAAO;AAAMpF;;;;;AAGnB;;AAIA;AACE;AACF;AACA;AACF;AACF;AAEO;;AAEP;;ACjLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASO;AAGL;;;AACcqF;AAAU;AACxB;AAGF;AAMO;AAGL;AACA;;AAEF;AAEO;;;AASP;;ACtDA;AACA;AACA;AACA;AACO;AAGL;AAMF;;ACfO;AAIL;AAAa;AAAQ;AAAS;;;AAG5B;;AACUC;AAAsBrB;;AAChC;AACF;AACF;;ACbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIO;AAGL;AAA6CmB;AAAc;AAC3D;;AAMF;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAcO;AAIL;AAAepF;;;;AACPqE;;AAAoD;;AACtDkB;AAAyBC;AAAiB;AAChD;AACEA;AACF;AACA;AACED;AACF;AACA;;AACQE;AAAK;;AAEb;AACE;;AAEI;AACF;;;AAGE;;;AAGE;AACF;;AAEE;AACF;;AAEJ;AACAC;AACF;AACA;AACF;;AC/CA;AAIA;AACE;AACA;AACA;AAAQ;AACR;AAAQ;AACR;AAAe;AACf;AAAe;AACf;AAAS;AACT;AAAoB;AACpB;AAAY;AACZC;AAAc;AACd;AACA;AACA;AAGF;AAEA;AAIE;;;AAGE;AACA;;AAEIC;;AAEJ;AACF;AACEA;AAAkDR;;AAGpD;AACA;AAKF;AAEA;;;AAOE;AAAkB7D;;;AAEhB;;AAQA;AACF;AACA;AACF;AAEA;AAKE;AACF;;AAEA;AACA;AACA;AACA;AACA;AACE;AACA;AACA;AACA;AACA;AAME;AACF;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AAQF;AAEA;;AACUA;AAAO;;AAEb;AACF;AACA;;;AAGA;AACA;AACA;;AAMA;AACA;;AAEF;AAEO;AAIL;AACA;AACF;AAEO;;;AAKH;AACA;;AAEA;AACF;AACA;AACF;AAMO;;AAKH8C;;;AAGF;AAAMrE;;;AAEN;AAEA;AACA;AACE;AAKA;AACE6F;AACF;AACF;;AAGEC;;AAEAC;AACF;;AASIC;AAAe;AAEjB;AACEH;AACF;AACF;;AAGA;;AAEII;AACA;AACF;AACF;AAEA;AACEjG;AACA8F;;AAEAI;;;;;;AAOF;;AAEA;AACA;;AAEA;;AAKA;AACE;AACA;AACA;AACA;AACEC;AACF;AACF;AACA;AACF;AAEO;;;AAOCL;;AAEAC;;AAGR;AAEO;AAIL;AACA;AACF;AAEO;AAIL;AACA;AACE;AACA;AACE;AACF;;AAIA;AACA;;AAEA;AACA;AACF;AACF;;ACjSO;AAKL;AAEIK;AACAC;;AAEJ;AACA;;;AAMQC;AAAc;;AAEtB;AACA;AAAkB/E;;AAChB;AACA;;AAEEgF;AACF;AACEC;AACA;AACF;AACF;;AACSnG;AAAenB;;;AAC1B;AAEO;;AACGuH;AAAM;;AAEd;AACE;AACA;AACA;AACA;AACA;AACA;AACE;AACA;AACA;;AAGAC;AACF;AACA;AACA;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;;AAEF;AACCxH;AACC;AACCuH;;AAGL;AACA;;AAEE;AACF;AACAC;AACF;AACF;AAOO;;AAKG7D;AAAsBwB;AAAoB;AAChDrE;;;AAIF;;AAII2G;AACF;AAGF;AACF;;ACxHA;AACExI;AAGA;AACA;AACA;AACAwF;AACA;AACA;AACF;AAEA;AACO;;AAEHiD;;;AAGA;AACF;AACA;AACF;AAEA;AACO;;AAIHC;AACF;AACA;AACF;AAEO;AACL;AACF;;ACnCA;AACO;;;AAGD;;AAEEC;AACA;AACA;AACA;;AAEF;;AAGE;AACA;AACA;;AAEF;AACEC;AACF;AACF;AACEA;AACF;AACF;AACA;AACF;;AC/BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAwBA;AAEA;;;AAA2CC;AAAU;AAarD;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACO;AAML;;;;AAKEC;AACAC;;AAEF;;;;AAMA;;AAEE;AAAoD1B;AAAgB;;AAGtCA;AAAgB;AAC9C;AAGwCA;AAAgB;AAExD;AACE2B;;AAEAA;;AAEAA;AACF;AACE;AACAA;AACF;AACF;;;AAIA;AACA;;AAGEC;AACA;AACA;AACEA;AACF;AACAA;AACA;AACE;AACA;AACAC;AACE;AACAC;AACE;AACA;AACA;AACA;AACAC;AACF;;AAEJ;AACAH;;;;;AAMA;AACA;AACEA;AACF;AACAA;;;AAIF;AACE;AACA;;AAEA;AACE;AACAA;AACF;AACA;AACEA;AACF;AACAA;;;AAIF;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AAOHE;;;AAGF;AACEtH;;;AAIF;AACEwH;;AAEF;AACA;AACEC;AACF;AAEA;AACEA;AACF;AACE;;AAEEA;AACF;AACF;AAEA;AACA;AACEA;AACF;;AAGE;AACA;AACA;AACE;AAGA;;;AAGE;;;;AAKF;;AAKIH;AACA7C;AACF;;AAGO1F;;;AACX;;AAEA;AACA;AAEIsB;AACAC;;AAIA2G;AACAC;AACA;AACAI;;;AAGE;;;AAGFI;AACE;AACA;AAEA;;AAEF;;AAIJ;;AACS3I;;;;AAET;AACA;AACA;;AAEEA;AACAC;AACAM;;AAEJ;AACF;;AAEA;AACA;AACA;AACO;;AAODe;AACAC;;AAGA2G;AAAcC;;;AAGpB;;AAEA;AACA;AACA;AACO;;AAOD7G;AACAC;;AAGA2G;AAAcC;;;AAGpB;;AC1TO;AAGL;AAGF;AAEO;;AAEP;AAEO;AACL;AACF;;ACnBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAkEO;AACL;AACA;AAEI;AACAS;AACN;AAEO;AAGL;AAAmCnC;AAAiBnB;AAAI;AAC1D;AAEO;AAGL;AACA;AACE;AACA;;AAEF;;AAEF;AAEO;;AAEH;AACA;AAEA;;AAGA/D;;AAEJ;AA+DO;;AAKL;;;AAEWvB;;;AACX;;;AAGE6I;;AAEA;AACEzJ;AACAC;AACAgB;;AACSL;;;AACX;;AAKAX;AACAgB;;AAEEL;;AAEAQ;;AAEJ;AAEA;;AAEEsI;;AAEAzJ;;AACsBwJ;AAAY;AAClCxI;AACA;AACEjB;;AACSY;;;AACX;;AAEEA;;;;AAIJ;;AAGEZ;;AACSY;;;AACX;;AAEA;AACA;;AACSA;AAAUC;;AACrB;AAEO;;;;;AAQHZ;;AACsB0J;AAAS;AAC/B1I;;AAEEL;AACAO;;;AAGJ;;;;AAKSP;AAAUC;;AACrB;;ACzQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA6BA;AAEE;AACA+I;AAEA;AACA;AACA;AACE;;AAEE;AACF;AACF;AACA;AACF;AAEO;;AAIGC;AAAgB;;AAExB;AACE;AAAkClJ;AAAgB;AACpD;AACA;AACF;AAEO;AAKL;AACA;;AAEA;;;AAGEE;AACA;AACF;AACA;AACF;AAuBO;AAGL;AACA;AACE;AACF;AAEA;;;AAIE;AAIJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA;AACA;AACA;AACA;AAGI;AAAkBuC;;AAChB;AACA;AACA;AACA;AACE0G;AACF;AACE7J;AACF;AACF;;;AAGAgB;AACF;AAEA;AACF;AAEA;AACO;;;AAEK8I;;;AAEN9J;AACF;AACA;AACE+J;AACAhI;;;AAEoBiI;AAAe;AACrCC;AACF;AACA;AACF;AAEA;AACO;;;AAEKH;;;AAEN9J;AACF;AACAkK;AACExH;;AAEA;AACF;AACF;AACA;AACF;AAOO;AACLyH;AACE;AACF;;AAEE;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACQC;AAAoB;;AAIxB;;AACWC;;AACX;;AAEA;;AAKkCnJ;AAAW;;AAClCmJ;AAAgBnK;;AAC3B;AACF;;AACSmK;;AACX;AAqBO;;AAMGC;;AACR;AACA;;AAEEtK;;AACsBsK;AAAkB;AACxC;AACF;;AAEA;;AAEEjE;;AAEF;AACArG;;AAEE;AACA;;AAEAA;;AACsBuK;AAAe;AACrCvJ;AACF;AACA;AACF;;AC/SA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAUA;;AAEA;AACA;AACA;AACO;AACL;AACE;AACF;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACA;AACO;;AAEL;AAAkBmC;;;AAEhB;AACE;AACA;AACA;;AAEEqH;AACF;AACE5H;AACF;AACF;AACE;AACAA;AACF;AACF;AACA;AACF;;AAEA;AACA;AACA;AACO;;AAEL;AACF;;AAEA;AACA;AACA;AACO;;;AAOL;AACA;AACA;AAEA;AACE;AACA;AACA;AACE6H;AACF;AACEA;AACF;;;AAGA;AACA;AACA;AACEC;;;AAGA;AACF;AACF;AAEA;AAAkBvH;;AAChB;AACA;AACA;AACE4E;AACA;AACA;AACE;AACAyC;;AAEEzC;AACF;AACF;;AAEA;AACA;AACE;AACAyC;AACF;AACA;;AAKA;AACA;AACEzC;AACF;AACA;AACF;AACEA;AACF;AACF;AACA;AACF;;AAsBA;AACA;AACA;AACA;AACA;AACO;;AAEP;;AASA;AACA;AACA;AACO;AACL;AACF;;AAUA;AACA;AACA;AACA;AACO;AACL;AAMF;;AC3MA;AACA;AACA;AACA;AACO;;AAIH;AACA;;AAIA;AAIM4C;AACAC;;AAKN;;AAEIjK;;;AAGJ;;AAGEA;AACAC;;;AAGF;AACA;AACA;AACA;;AAQED;AACAO;;AAIJ;AACF;;AClDA;AACEU;AACA;AACAiJ;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACF;;AAEA;AACA;AACA;AACO;;AAIH;AAAsCtE;AAAc;;;AAGhDrG;;;AAGJ;;;AAEcoD;AAAiB7B;AAAQ;;AAEvC;AACA;;;AAGIvB;;;AAGJ;;AAEA;;AAEA;;AAGA;AAEI4K;AACAC;AACF;;AAIA7K;;;;;AAKAA;AACAO;;AAEJ;AACF;;;AC5DO;;AAEH;;;AAGF;AACF;;ACTO;AAEA;AACL;;AAMA;;AAEIP;AACAO;;;AAGJ;;AAESP;;;AACX;AAEO;AAOL;AACA;AACE;AACF;;AAEQL;AAAkB;;;AAGtBK;AACAO;AACAC;;AAEJ;;AAEA;AACA;;AAEA;AACA;;AAOF;AACA;AACA;AACA;AACA;AACA;AACA;;AAGIR;AACAC;;;AAGE6K;AACAC;AACAC;AACF;;AAEJ;;ACjEA;AACA;AACA;AACA;AACO;;;;AAML;AACF;;ACdA;AACE5L;AAGA;AACA;AACA;AACAwF;AACA;AACA;AACF;AAEA;AACO;;AAEHqG;;;AAGA;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACF;AACA;AACF;AAEA;AACO;;AAEH;;;AAGEC;AACF;;;AAGE;;AAEA;AACA5K;AAEAA;AACAnB;AACA;AACA;AACA;AACAwF;AACA;AACA;AACF;AACF;AACA;AACF;AAEA;AACO;;AAEH;;;AAQF;AACA;AACF;AAEA;AACO;;AAEHwG;;;AAGA;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACF;AACA;AACF;AAEO;AACL;AACF;AAEO;AACL;AACF;;AClFO;;;AAKHC;;;;AAIF;AAAMrK;;;AACN;AACA;AACA;;AAIA;;;;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACEyE;;;AAGEA;AACF;AACF;AACEA;AACF;;AAgBI;AACA;AAEA;;AAEA;AACA;;AAMA;AACA6C;;;AAGE;;;AAGF7C;AACF;AAGF;AACE6F;;AAEI;AACA;;AAEF;AACF;AACF;AAEA;AACF;;AC9GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAqBO;;;;AAIyBC;AAAQ;AACtC;AACA;AACA;AACA;AACE;;AAEElG;;AAEF;AACF;;AAEEgG;;;AAGF;AAAMrK;;;;AAEN;AACA;AACA;;AAUA;AACEqE;AACA;AACA;AACA;;;AAGAI;AACA;AACA6C;;;AAGE;AACA;AAAekD;;AACfC;AAIA;AACF;AACF;AACF;;AC3FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAgCA;;;;;;;;;;;;;;;;;AAiBEC;AACF;AAEO;AAEP;AA+EA;;;;AAMQ;;AAEF;;AAEJ;AAEA;AAEA;;AAaQ;;AAEE;AACF;;AAEE;AACA;;;;AAIA;AACA;AACA;AACA;;;AAII;AACA;AACA;;;AAIN;AACA;AACF;AASR;;AAEA;AACA;;;AAGE;AACA;AACA;AACA;;;;;;AAMA;AACA;AACA;AACA;AACA;AACA;AACF;AAEA;AACE;;AAEE;AACA;AACA;AACE;AACF;AACA;AACA;;;AAGA;AACE;AACF;AACA;AACA;AAAkCrE;;AACpC;;AAEE;AACA;AACA;AACE;AACF;AACA;AACA;AAAkCA;;AACpC;AACA;AAAkCA;;AACpC;AAEA;AAKE;AACA;AACAjI;;;AAGI;AACA;AACA;AACAuM;AACE;;;AAII;AACA;AACA;;AAEF;;AAINvM;;AACsBuK;AAAe;AACrCvJ;AACF;AACA;AACF;AAEO;AACLiF;AACAuG;AACa;;AACqCvG;AAAI;;AAEtD;;AAMiCA;AAAI;AACrC;;AAKqCwG;;AAErC;AACA;AACA;AAIA;AACA;AACE;AACA;AACA;AACA;;;;AAIIC;AACF;AACF;AACF;AACA;AAMEA;AACF;;AAEEA;;AAEF;AACA;;AAEA;AACEA;AACF;;AACQC;AAAuB;;;AAG/B;AACA;;AAEA;AACA;AACA;;;;;AAIUC;;AACR;AACA;AACA;AACEC;AACA;AACA;AACA;;;AAGA;AACF;AACA;AACEC;AACA;AACA;AACA;;;AAGA;AACF;AACA;AAGA;AACE;AACA;;AAKE;;;;AAIA;AACF;AACF;;AAKF;AACEC;AACAC;AACF;;AAEA;AACA;AAGA;;AAGA;AAEA;AAKA;;;;;;;;AASEC;AAAYC;;;;;;;;;AAQZC;;;;AAIAC;AACE;;AAEA;AACAC;AAGF;;AAEJ;AAEO;;AAKHC;;AAEAC;AACF;AACE3L;;;AAGF;;;AAGI7B;AAMF;AACF;;;;AAC4BoN;AAAgB;AAC5C;AACA;;;AAGIxM;AACAO;;;AAMJ;AACA;AACE;;AAEEP;AACAO;;;AAMJ;AACA;;AAEIP;AACAO;AACAC;;AAKJ;AACA;;AAEIR;AACAO;;;AAMJ;AACA;;;AAGIP;AACAO;AACAC;;AAEJ;;;AAGIR;AACAO;AACAC;;AAEJ;AACA;;AAEIR;AACAO;AACAC;;AAEJ;;;AAGIR;AACAO;AACAC;;AAKJ;AACA;AAIE;;AAOF;;AACSR;AAAUC;;AACrB;;ACjkBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAmBO;AA0CiC;AAEjC;;AAEP;;ACtFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAyDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AAIGqF;AAAqBuH;AAAc;AACzC5L;;;;AAIF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEE;AACF;;AAEA;AACA;;AAME;AACF;;AAEA;AACA;;AAEA;AACA;;AAEE;AACF;;AAEA;;AAEU;;AACO;;AACP;;AACI;;;AAMd;AACF;;ACpJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA;AACE7B;AAGA;AACA;AACA;AACAwF;AACA;AACA;AACF;AAEA;AACO;;AAEHkI;;;AAGA;AACF;AACA;AACF;AAEA;AACO;;AAIHC;AACF;AACA;AACF;AAEO;AACL;AACF;;ACxDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAkBA;AACA;AAEO;AACL;AACA;;AACQrF;AAAM;;AAEd;;AACgCpC;AAAgBuH;AAAa;AAC3D;AACF;AACA;AACA;;AAEE;;AAKA;;AACQtE;AAAI;AACZA;AACF;AACA;AACF;AAEO;AACL;AACA;;AACQb;AAAM;;AAEd;;AACgCpC;AAAgBuH;AAAa;AAC3D;AACF;AACA;AACA;;AAEE;;AAKA;;AACQtE;AAAI;AACZA;AACF;AACA;AACF;AAEO;AACL;AACA;;AACQb;AAAM;;AAEd;;AACgCpC;AAAgBuH;AAAa;AAC3D;AACF;AAEA;;AAEA;;AAEE;;AAKA;;AACQtE;AAAI;AACZA;AACF;AAEA;AACF;AAEO;AACL;;AACQb;AAAM;;AAEd;;AACgCpC;AAAgBuH;AAAa;AAC3D;AACF;AAEA;;AAGE;;AAKA;;AACQtE;AAAI;AACZA;AACF;AAEA;AACF;;AC1IA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAQO;AACL;AAAqBtH;;AACrB;AACA;AACE;;AAEE+L;AACF;AACF;AACA;AACF;;ACtBO;AACL;AACF;;ACaO;AAGL;AACA;;AAEE;AACE;AACF;AACA;;AAEE;AACF;AACAC;AACA;AACEhM;;;AAGA;;AAEF;AACE;AACA;;AAEF;;;;AAIF;;AAIF;AAEO;AACL;AACF;AAEO;AAGL;AACA;;;;AAIA;AACA;AACF;AASO;;AAIP;AAEO;AACL;AACF;AAEO;AACL;AACA;AACA;AACF;;AC7CO;;AAGGmC;AAAK;;AAOf;;AC1DO;;AAGkBnC;;AAAwB;AACjD;;ACFO;AACLiM;AACAC;AACAC;AACF;;ACAO;AACLC;AACAC;AACAC;AACAC;AACF;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAOO;;AAIH;AACF;;AAGE;AACF;AAEAC;AACE;AAGF;AAEAC;AAIIC;AACAC;;AAMF;AACE;;AAII;AACN;AACA;AACF;;AAKE;AACF;;AAGE;AACF;;;AAMA;;AAGE;;AAIF;AACF;;ACpFA;AAEA;AAIO;;AAEHC;AAGF;AACA;AACF;;AClBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAiCO;AACLR;AACAC;AACAC;AACAC;AACF;AAEO;AACLH;AACAC;AACAC;AACAC;AACAM;AACF;AAiBA;AAEA;AAEA;AASA;AACE;AACET;AACAC;AACAC;AACAC;;AAEF;;;;AAIM;;;AAGA;;;AAGA;;;AAGA;AACJ;AACF;AACA;AACF;AAEA;;;AAGIO;AACF;;AAEEA;AACF;;AAEEA;AACF;;AAEEA;AACF;AACA;AACF;AAoBO;AAKL;AACA;AACE;AACF;;AAEQ3K;AAAiB7B;AAAQ;;AAG/ByM;;AAEAC;AACF;AACEhN;;;AAIF;;AAQEiN;AACAb;AACAH;AACA;AACF;AAEA;AACEjM;AACA;;;AAIF;AACE;AACA;;AAKE;AACF;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;;;;;;;;;AAgBIkN;AACAC;AACF;AACF;AACF;AACA;AACE;AACF;;AAEA;AACA;AAKE;AACA;;AAEA;AACE;;AAEA;AACE;AACA;AACA;AACA;;AAKA;AACE;AACA;;;AAGEC;AACEC;AACA/M;AACF;AACF;AACF;AACEgN;AACF;AACF;AACE;AACA;;;AAGEC;AAA+BF;AAAqB/M;AAAQ;AAC9D;AACF;AACEgN;AACF;AACF;AACAE;AACE;;AAOJ;AACEA;AACF;;AAEEC;AACF;AACA;AACF;AAEO;;AAEP;AAEO;AAIL;AACF;AAEO;AAIL;;AAEF;AAEO;AACL;;AACQC;;AACR;AASF;AAEO;;AAWP;AA6FO;AAGL;AACF;AAOO;;AAIGC;;AAA2C;AACjD3N;;;AAIF;AACA;AAIA;AACA;AACA;AAEA;AAAkBuB;;;AACR;AAAS;AAAU;;AAE3B;AACE;;AAGEqM;AACF;AACA;AACF;;;AAGA;AACA;AACE;AACF;AACAC;AACAC;AACA;AAMEC;AACF;AACF;;AAEA;AACA;AAAa;AAAQ;AACnB;AACE;AACF;AACAA;AACF;AACA;AACA;AAAa;AAAS;AAAgB;AACpC;AACE;AACF;AACAA;;AAEA;AACE;AACA;AACA;;AAKA;AACEC;AACAC;AACF;AACAH;AAIF;AACF;AAEA;;AAKMvM;;;AAII;AAAS;AAAU;AAC3B;AACA;;AACUY;AAAK;;;;AAYb;AACA;AACA;AACA;AACA;;AAEA;AACAd;AACF;AACA;AACA;;AAKA;AACA;AACE0M;AACAG;AACF;AACEA;AACF;AACA;AACEA;AACF;;;AAEQ3M;AAA0B;AAClC;AACE4M;;;AAKA;;AAIA;AACF;AACAC;AACF;;AAIA;AACE;AACEhC;AACAC;AACAC;AACAC;;AAEF;AAAa;AAAS;AAAU;AAC9B;AACE;AACF;AACA;AACA8B;AACAA;AACAA;AACAA;AACF;AACAH;AAGF;AACAA;AACF;;AC5mBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA4BO;AAIL;AACA;;;AAGA;AACF;AAYO;AAIL;;AACoBI;AAAiB;;AAE/B/M;AAAkB;AACxB;;AAGE;AACF;AAEA;AACEvB;AACA+M;AACA1G;AACA;;;;AAKAlB;AACF;;;AAEkBzE;AAAQ;AAE1B;AAEAA;AAEA;AAAwC6N;AAAS;AACjD;;AAEE;AACF;AACA;AACA;AACA;AAKA;;;;;AAKE7N;;;AAIA;AAEI8N;AAAqCC;AAAK;AAC5C;AAEEC;AACEC;AACAC;;AACyBC;;;;;AAI3B;AACF;;AAGE;AACA;AACF;;AAEE;AACE;AACF;AACA;AACA;AAGF;;;;AAOwBC;AAAY;AAClC;AACF;AACAC;;AAEErO;AACF;AACF;;;AAGA;AACF;;AAIA;AACF;;AC1KA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAOA;;AAkBA;AACA;AACA;AACA;AACO;;;;AAML;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AAGL;AACA;;AAGE;AACA;AACA;AACE;AACA;;AAEIL;;;AAGJ;AACF;;AAEA;AACA;AACA;;AACWA;AAAeC;;AAC1B;;;AAIEA;;AAEJ;;AAEA;AACA;AACA;;AAEA;AACA;AAKE;AACA;AACA;AACA;AACA;;;AAGA;;AAMA;AACF;AAKE;;;AAGA;AACF;;;AAEeA;;AACjB;;AAEA;AACA;AACA;AACA;AACA;AACO;AACL;;AAEE;AACF;;;AAEcA;AAAQ;;AAEtB;;AAEE6B;;;AAGAiD;AACF;AAEA;AAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","debugId":"652cf0d7-6cf6-49b1-86ba-204b62a4be68"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../src/utils/debug.mts","../src/utils/errors.mts","../src/utils/config.mts","../src/utils/requirements.mts","../src/utils/sdk.mts","../src/utils/api.mts","../src/utils/fail-msg-with-badge.mts","../src/utils/markdown.mts","../src/utils/serialize-result-json.mts","../src/utils/terminal-link.mts","../src/utils/check-input.mts","../src/utils/get-output-kind.mts","../src/utils/strings.mts","../src/utils/output-formatting.mts","../src/utils/tildify.mts","../src/utils/meow-with-subcommands.mts","../src/utils/ms-at-home.mts","../src/commands/organization/fetch-organization-list.mts","../src/commands/scan/suggest-org-slug.mts","../src/commands/scan/suggest-to-persist-orgslug.mts","../src/utils/determine-org-slug.mts","../src/commands/ci/fetch-default-org-slug.mts","../src/utils/extract-names.mts","../src/utils/git.mts","../src/utils/purl.mts","../src/utils/socket-url.mts","../src/utils/map-to-object.mts","../src/utils/walk-nested-map.mts","../src/utils/coana.mts","../src/utils/fs.mts","../src/utils/glob.mts","../src/utils/path-resolve.mts","../src/utils/yarn-paths.mts","../src/utils/yarn-version.mts","../src/utils/dlx.mts","../src/utils/organization.mts","../src/utils/socket-json.mts","../src/utils/github.mts","../src/utils/cve-to-ghsa.mts","../src/utils/purl-to-ghsa.mts","../src/utils/cmd.mts","../src/utils/semver.mts","../src/utils/completion.mts","../src/utils/npm-package-arg.mts","../src/utils/npm-paths.mts","../src/shadow/npm/install.mts","../src/utils/agent.mts","../src/utils/package-environment.mts","../src/utils/ecosystem.mts","../src/utils/dlx-detection.mts","../src/utils/pnpm-paths.mts","../src/utils/shadow-links.mts","../src/utils/filter-config.mts","../src/utils/spec.mts","../src/utils/pnpm.mts","../src/utils/alert/artifact.mts","../src/utils/objects.mts","../src/utils/alert/fix.mts","../src/utils/alert/severity.mts","../src/utils/color-or-markdown.mts","../src/utils/translations.mts","../src/utils/socket-package-alert.mts","../src/utils/alerts-map.mts","../src/utils/npm-spec.mts"],"sourcesContent":["/**\n * Debug utilities for Socket CLI.\n * Provides structured debugging with categorized levels and helpers.\n *\n * Debug Categories:\n * DEFAULT (shown with SOCKET_CLI_DEBUG=1):\n * - 'error': Critical errors that prevent operation\n * - 'warn': Important warnings that may affect behavior\n * - 'notice': Notable events and state changes\n * - 'silly': Very verbose debugging info\n *\n * OPT-IN ONLY (require explicit DEBUG='category' even with SOCKET_CLI_DEBUG=1):\n * - 'inspect': Detailed object inspection (DEBUG='inspect' or DEBUG='*')\n * - 'stdio': Command execution logs (DEBUG='stdio' or DEBUG='*')\n *\n * These opt-in categories are intentionally excluded from default debug output\n * to reduce noise. Enable them explicitly when needed for deep debugging.\n */\n\nimport { debugDir, debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants from '../constants.mts'\n\n/**\n * Debug an API request start.\n * Logs essential info without exposing sensitive data.\n */\nexport function debugApiRequest(\n method: string,\n endpoint: string,\n timeout?: number | undefined,\n): void {\n if (constants.ENV.SOCKET_CLI_DEBUG) {\n const timeoutStr = timeout !== undefined ? ` (timeout: ${timeout}ms)` : ''\n logger.info(\n `[DEBUG] ${new Date().toISOString()} request started: ${method} ${endpoint}${timeoutStr}`,\n )\n }\n}\n\n/**\n * Debug an API response end.\n * Logs essential info without exposing sensitive data.\n */\nexport function debugApiResponse(\n method: string,\n endpoint: string,\n status?: number | undefined,\n error?: unknown | undefined,\n duration?: number | undefined,\n headers?: Record<string, string> | undefined,\n): void {\n if (!constants.ENV.SOCKET_CLI_DEBUG) {\n return\n }\n\n if (error) {\n logger.fail(\n `[DEBUG] ${new Date().toISOString()} request error: ${method} ${endpoint} - ${error instanceof Error ? error.message : 'Unknown error'}${duration !== undefined ? ` (${duration}ms)` : ''}`,\n )\n if (headers) {\n logger.info(\n `[DEBUG] response headers: ${JSON.stringify(headers, null, 2)}`,\n )\n }\n } else {\n const durationStr = duration !== undefined ? ` (${duration}ms)` : ''\n logger.info(\n `[DEBUG] ${new Date().toISOString()} request ended: ${method} ${endpoint}: HTTP ${status}${durationStr}`,\n )\n if (headers && status && status >= 400) {\n logger.info(\n `[DEBUG] response headers: ${JSON.stringify(headers, null, 2)}`,\n )\n }\n }\n}\n\n/**\n * Debug file operation.\n * Logs file operations with appropriate level.\n */\nexport function debugFileOp(\n operation: 'read' | 'write' | 'delete' | 'create',\n filepath: string,\n error?: unknown | undefined,\n): void {\n if (error) {\n debugDir('warn', {\n operation,\n filepath,\n error: error instanceof Error ? error.message : 'Unknown error',\n })\n } else if (isDebug('silly')) {\n debugFn('silly', `File ${operation}: ${filepath}`)\n }\n}\n\n/**\n * Debug package scanning.\n * Provides insight into security scanning.\n */\nexport function debugScan(\n phase: 'start' | 'progress' | 'complete' | 'error',\n packageCount?: number | undefined,\n details?: unknown | undefined,\n): void {\n switch (phase) {\n case 'start':\n if (packageCount) {\n debugFn('notice', `Scanning ${packageCount} packages`)\n }\n break\n case 'progress':\n if (isDebug('silly') && packageCount) {\n debugFn('silly', `Scan progress: ${packageCount} packages processed`)\n }\n break\n case 'complete':\n debugFn(\n 'notice',\n `Scan complete${packageCount ? `: ${packageCount} packages` : ''}`,\n )\n break\n case 'error':\n debugDir('error', {\n phase: 'scan_error',\n details,\n })\n break\n }\n}\n\n/**\n * Debug configuration loading.\n */\nexport function debugConfig(\n source: string,\n found: boolean,\n error?: unknown | undefined,\n): void {\n if (error) {\n debugDir('warn', {\n source,\n error: error instanceof Error ? error.message : 'Unknown error',\n })\n } else if (found) {\n debugFn('notice', `Config loaded: ${source}`)\n } else if (isDebug('silly')) {\n debugFn('silly', `Config not found: ${source}`)\n }\n}\n\n/**\n * Debug git operations.\n * Only logs important git operations, not every command.\n */\nexport function debugGit(\n operation: string,\n success: boolean,\n details?: Record<string, unknown> | undefined,\n): void {\n if (!success) {\n debugDir('warn', {\n git_op: operation,\n ...details,\n })\n } else if (\n (isDebug('notice') && operation.includes('push')) ||\n operation.includes('commit')\n ) {\n // Only log important operations like push and commit.\n debugFn('notice', `Git ${operation} succeeded`)\n } else if (isDebug('silly')) {\n debugFn('silly', `Git ${operation}`)\n }\n}\n\nexport { debugDir, debugFn, isDebug }\n","/**\n * Error utilities for Socket CLI.\n * Provides consistent error handling, formatting, and message extraction.\n *\n * Key Classes:\n * - AuthError: Authentication failures (401/403 responses)\n * - InputError: User input validation failures\n *\n * Key Functions:\n * - captureException: Send errors to Sentry for monitoring\n * - formatErrorWithDetail: Format errors with detailed context\n * - getErrorCause: Get error cause with fallback to UNKNOWN_ERROR\n * - getErrorMessage: Extract error message from any thrown value\n *\n * Error Handling Strategy:\n * - Always prefer specific error types over generic errors\n * - Use formatErrorWithDetail for user-facing error messages\n * - Log errors to Sentry in production for monitoring\n */\n\nimport { setTimeout as wait } from 'node:timers/promises'\n\nimport { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants, { UNKNOWN_ERROR } from '../constants.mts'\n\nconst {\n kInternalsSymbol,\n [kInternalsSymbol as unknown as 'Symbol(kInternalsSymbol)']: { getSentry },\n} = constants\n\ntype EventHintOrCaptureContext = { [key: string]: any } | Function\n\nexport class AuthError extends Error {}\n\nexport class InputError extends Error {\n public body: string | undefined\n\n constructor(message: string, body?: string | undefined) {\n super(message)\n this.body = body\n }\n}\n\nexport async function captureException(\n exception: unknown,\n hint?: EventHintOrCaptureContext | undefined,\n): Promise<string> {\n const result = captureExceptionSync(exception, hint)\n // \"Sleep\" for a second, just in case, hopefully enough time to initiate fetch.\n await wait(1000)\n return result\n}\n\nexport function captureExceptionSync(\n exception: unknown,\n hint?: EventHintOrCaptureContext | undefined,\n): string {\n const Sentry = getSentry()\n if (!Sentry) {\n return ''\n }\n debugFn('notice', 'send: exception to Sentry')\n return Sentry.captureException(exception, hint) as string\n}\n\nexport function isErrnoException(\n value: unknown,\n): value is NodeJS.ErrnoException {\n if (!(value instanceof Error)) {\n return false\n }\n return (value as NodeJS.ErrnoException).code !== undefined\n}\n\n/**\n * Extracts an error message from an unknown value.\n * Returns the message if it's an Error object, otherwise returns undefined.\n *\n * @param error - The error object to extract message from\n * @returns The error message or undefined\n */\nexport function getErrorMessage(error: unknown): string | undefined {\n return (error as Error)?.message\n}\n\n/**\n * Extracts an error message from an unknown value with a fallback.\n * Returns the message if it's an Error object, otherwise returns the fallback.\n *\n * @param error - The error object to extract message from\n * @param fallback - The fallback message if no error message is found\n * @returns The error message or fallback\n *\n * @example\n * getErrorMessageOr(error, 'Unknown error occurred')\n * // Returns: \"ENOENT: no such file or directory\" or \"Unknown error occurred\"\n */\nexport function getErrorMessageOr(error: unknown, fallback: string): string {\n return getErrorMessage(error) || fallback\n}\n\n/**\n * Extracts an error cause from an unknown value.\n * Returns the error message if available, otherwise UNKNOWN_ERROR.\n * Commonly used for creating CResult error causes.\n *\n * @param error - The error object to extract message from\n * @returns The error message or UNKNOWN_ERROR constant\n *\n * @example\n * return { ok: false, message: 'Operation failed', cause: getErrorCause(e) }\n */\nexport function getErrorCause(error: unknown): string {\n return getErrorMessageOr(error, UNKNOWN_ERROR)\n}\n\n/**\n * Formats an error message with an optional error detail appended.\n * Extracts the message from an unknown error value and appends it\n * to the base message if available.\n *\n * @param baseMessage - The base message to display\n * @param error - The error object to extract message from\n * @returns Formatted message with error detail if available\n *\n * @example\n * formatErrorWithDetail('Failed to delete file', error)\n * // Returns: \"Failed to delete file: ENOENT: no such file or directory\"\n * // Or just: \"Failed to delete file\" if no error message\n */\nexport function formatErrorWithDetail(\n baseMessage: string,\n error: unknown,\n): string {\n const errorMessage = getErrorMessage(error)\n return `${baseMessage}${errorMessage ? `: ${errorMessage}` : ''}`\n}\n","/**\n * Configuration utilities for Socket CLI.\n * Manages CLI configuration including API tokens, org settings, and preferences.\n *\n * Configuration Hierarchy (highest priority first):\n * 1. Environment variables (SOCKET_CLI_*)\n * 2. Command-line --config flag\n * 3. Persisted config file (base64 encoded JSON)\n *\n * Supported Config Keys:\n * - apiBaseUrl: Socket API endpoint URL\n * - apiProxy: Proxy for API requests\n * - apiToken: Authentication token for Socket API\n * - defaultOrg/org: Default organization slug\n * - enforcedOrgs: Organizations with enforced security policies\n *\n * Key Functions:\n * - findSocketYmlSync: Locate socket.yml configuration file\n * - getConfigValue: Retrieve configuration value by key\n * - overrideCachedConfig: Apply temporary config overrides\n * - updateConfigValue: Persist configuration changes\n */\n\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\n\nimport config from '@socketsecurity/config'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { safeReadFileSync } from '@socketsecurity/registry/lib/fs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\n\nimport { debugConfig } from './debug.mts'\nimport constants, {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_DEFAULT_ORG,\n CONFIG_KEY_ENFORCED_ORGS,\n CONFIG_KEY_ORG,\n SOCKET_YAML,\n SOCKET_YML,\n} from '../constants.mts'\nimport { getErrorCause } from './errors.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { SocketYml } from '@socketsecurity/config'\n\nexport interface LocalConfig {\n apiBaseUrl?: string | null | undefined\n // @deprecated ; use apiToken. when loading a config, if this prop exists it\n // is deleted and set to apiToken instead, and then persisted.\n // should only happen once for legacy users.\n apiKey?: string | null | undefined\n apiProxy?: string | null | undefined\n apiToken?: string | null | undefined\n defaultOrg?: string | undefined\n enforcedOrgs?: string[] | readonly string[] | null | undefined\n skipAskToPersistDefaultOrg?: boolean | undefined\n // Convenience alias for defaultOrg.\n org?: string | undefined\n}\n\nconst sensitiveConfigKeyLookup: Set<keyof LocalConfig> = new Set([\n CONFIG_KEY_API_TOKEN,\n])\n\nconst supportedConfig: Map<keyof LocalConfig, string> = new Map([\n [CONFIG_KEY_API_BASE_URL, 'Base URL of the Socket API endpoint'],\n [CONFIG_KEY_API_PROXY, 'A proxy through which to access the Socket API'],\n [\n CONFIG_KEY_API_TOKEN,\n 'The Socket API token required to access most Socket API endpoints',\n ],\n [\n CONFIG_KEY_DEFAULT_ORG,\n 'The default org slug to use; usually the org your Socket API token has access to. When set, all orgSlug arguments are implied to be this value.',\n ],\n [\n CONFIG_KEY_ENFORCED_ORGS,\n 'Orgs in this list have their security policies enforced on this machine',\n ],\n [\n 'skipAskToPersistDefaultOrg',\n 'This flag prevents the Socket CLI from asking you to persist the org slug when you selected one interactively',\n ],\n [CONFIG_KEY_ORG, 'Alias for defaultOrg'],\n])\n\nconst supportedConfigEntries = [...supportedConfig.entries()].sort((a, b) =>\n naturalCompare(a[0], b[0]),\n)\nconst supportedConfigKeys = supportedConfigEntries.map(p => p[0])\n\nfunction getConfigValues(): LocalConfig {\n if (_cachedConfig === undefined) {\n // Order: env var > --config flag > file\n _cachedConfig = {} as LocalConfig\n const { socketAppDataPath } = constants\n if (socketAppDataPath) {\n const raw = safeReadFileSync(socketAppDataPath)\n if (raw) {\n try {\n Object.assign(\n _cachedConfig,\n JSON.parse(Buffer.from(raw, 'base64').toString()),\n )\n debugConfig(socketAppDataPath, true)\n } catch (e) {\n logger.warn(`Failed to parse config at ${socketAppDataPath}`)\n debugConfig(socketAppDataPath, false, e)\n }\n // Normalize apiKey to apiToken and persist it.\n // This is a one time migration per user.\n if (_cachedConfig['apiKey']) {\n const token = _cachedConfig['apiKey']\n delete _cachedConfig['apiKey']\n updateConfigValue(CONFIG_KEY_API_TOKEN, token)\n }\n } else {\n mkdirSync(path.dirname(socketAppDataPath), { recursive: true })\n }\n }\n }\n return _cachedConfig\n}\n\nfunction normalizeConfigKey(\n key: keyof LocalConfig,\n): CResult<keyof LocalConfig> {\n // Note: apiKey was the old name of the token. When we load a config with\n // property apiKey, we'll copy that to apiToken and delete the old property.\n // We added `org` as a convenience alias for `defaultOrg`\n const normalizedKey =\n key === 'apiKey'\n ? CONFIG_KEY_API_TOKEN\n : key === CONFIG_KEY_ORG\n ? CONFIG_KEY_DEFAULT_ORG\n : key\n if (!isSupportedConfigKey(normalizedKey)) {\n return {\n ok: false,\n message: `Invalid config key: ${normalizedKey}`,\n data: undefined,\n }\n }\n return { ok: true, data: normalizedKey }\n}\n\nexport type FoundSocketYml = {\n path: string\n parsed: SocketYml\n}\n\nexport function findSocketYmlSync(\n dir = process.cwd(),\n): CResult<FoundSocketYml | undefined> {\n let prevDir = null\n while (dir !== prevDir) {\n let ymlPath = path.join(dir, SOCKET_YML)\n let yml = safeReadFileSync(ymlPath)\n if (yml === undefined) {\n ymlPath = path.join(dir, SOCKET_YAML)\n yml = safeReadFileSync(ymlPath)\n }\n if (typeof yml === 'string') {\n try {\n return {\n ok: true,\n data: {\n path: ymlPath,\n parsed: config.parseSocketConfig(yml),\n },\n }\n } catch (e) {\n debugFn('error', `Failed to parse config file: ${ymlPath}`)\n debugDir('error', e)\n return {\n ok: false,\n message: `Found file but was unable to parse ${ymlPath}`,\n cause: getErrorCause(e),\n }\n }\n }\n prevDir = dir\n dir = path.join(dir, '..')\n }\n return { ok: true, data: undefined }\n}\n\nexport function getConfigValue<Key extends keyof LocalConfig>(\n key: Key,\n): CResult<LocalConfig[Key]> {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(key)\n if (!keyResult.ok) {\n return keyResult\n }\n return { ok: true, data: localConfig[keyResult.data as Key] }\n}\n\n// This version squashes errors, returning undefined instead.\n// Should be used when we can reasonably predict the call can't fail.\nexport function getConfigValueOrUndef<Key extends keyof LocalConfig>(\n key: Key,\n): LocalConfig[Key] | undefined {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(key)\n if (!keyResult.ok) {\n return undefined\n }\n return localConfig[keyResult.data as Key]\n}\n\n// Ensure export because dist/utils.js is required in src/constants.mts.\n// eslint-disable-next-line n/exports-style\nif (typeof exports === 'object' && exports !== null) {\n // eslint-disable-next-line n/exports-style\n exports.getConfigValueOrUndef = getConfigValueOrUndef\n}\n\nexport function getSupportedConfigEntries() {\n return [...supportedConfigEntries]\n}\n\nexport function getSupportedConfigKeys() {\n return [...supportedConfigKeys]\n}\n\nexport function isConfigFromFlag() {\n return _configFromFlag\n}\n\nexport function isSensitiveConfigKey(key: string): key is keyof LocalConfig {\n return sensitiveConfigKeyLookup.has(key as keyof LocalConfig)\n}\n\nexport function isSupportedConfigKey(key: string): key is keyof LocalConfig {\n return supportedConfig.has(key as keyof LocalConfig)\n}\n\nlet _cachedConfig: LocalConfig | undefined\n// When using --config or SOCKET_CLI_CONFIG, do not persist the config.\nlet _configFromFlag = false\n\nexport function overrideCachedConfig(jsonConfig: unknown): CResult<undefined> {\n debugFn('notice', 'override: full config (not stored)')\n\n let config\n try {\n config = JSON.parse(String(jsonConfig))\n if (!config || typeof config !== 'object') {\n // `null` is valid json, so are primitive values.\n // They're not valid config objects :)\n return {\n ok: false,\n message: 'Could not parse Config as JSON',\n cause:\n \"Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again.\",\n }\n }\n } catch {\n // Force set an empty config to prevent accidentally using system settings.\n _cachedConfig = {} as LocalConfig\n _configFromFlag = true\n\n return {\n ok: false,\n message: 'Could not parse Config as JSON',\n cause:\n \"Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again.\",\n }\n }\n\n // @ts-ignore Override an illegal object.\n _cachedConfig = config as LocalConfig\n _configFromFlag = true\n\n // Normalize apiKey to apiToken.\n if (_cachedConfig['apiKey']) {\n if (_cachedConfig['apiToken']) {\n logger.warn(\n 'Note: The config override had both apiToken and apiKey. Using the apiToken value. Remove the apiKey to get rid of this message.',\n )\n }\n _cachedConfig['apiToken'] = _cachedConfig['apiKey']\n delete _cachedConfig['apiKey']\n }\n\n return { ok: true, data: undefined }\n}\n\nexport function overrideConfigApiToken(apiToken: unknown) {\n debugFn('notice', 'override: Socket API token (not stored)')\n // Set token to the local cached config and mark it read-only so it doesn't persist.\n _cachedConfig = {\n ...config,\n ...(apiToken === undefined ? {} : { apiToken: String(apiToken) }),\n } as LocalConfig\n _configFromFlag = true\n}\n\nlet _pendingSave = false\nexport function updateConfigValue<Key extends keyof LocalConfig>(\n configKey: keyof LocalConfig,\n value: LocalConfig[Key],\n): CResult<undefined | string> {\n const localConfig = getConfigValues()\n const keyResult = normalizeConfigKey(configKey)\n if (!keyResult.ok) {\n return keyResult\n }\n const key: Key = keyResult.data as Key\n // Implicitly deleting when serializing.\n let wasDeleted = value === undefined\n if (key === 'skipAskToPersistDefaultOrg') {\n if (value === 'true' || value === 'false') {\n localConfig['skipAskToPersistDefaultOrg'] = value === 'true'\n } else {\n delete localConfig['skipAskToPersistDefaultOrg']\n wasDeleted = true\n }\n } else {\n if (value === 'undefined' || value === 'true' || value === 'false') {\n logger.warn(\n `Note: The value is set to \"${value}\", as a string (!). Use \\`socket config unset\\` to reset a key.`,\n )\n }\n localConfig[key] = value\n }\n if (_configFromFlag) {\n return {\n ok: true,\n message: `Config key '${key}' was ${wasDeleted ? 'deleted' : `updated`}`,\n data: 'Change applied but not persisted; current config is overridden through env var or flag',\n }\n }\n\n if (!_pendingSave) {\n _pendingSave = true\n process.nextTick(() => {\n _pendingSave = false\n const { socketAppDataPath } = constants\n if (socketAppDataPath) {\n writeFileSync(\n socketAppDataPath,\n Buffer.from(JSON.stringify(localConfig)).toString('base64'),\n )\n }\n })\n }\n\n return {\n ok: true,\n message: `Config key '${key}' was ${wasDeleted ? 'deleted' : `updated`}`,\n data: undefined,\n }\n}\n","/**\n * Requirements configuration utilities for Socket CLI.\n * Manages API permissions and quota requirements for commands.\n *\n * Key Functions:\n * - getRequirements: Load requirements configuration\n * - getRequirementsKey: Convert command path to requirements key\n *\n * Configuration:\n * - Loads from requirements.json\n * - Maps command paths to permission requirements\n * - Used for permission validation and help text\n */\n\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nconst require = createRequire(import.meta.url)\n\nlet _requirements:\n | Readonly<typeof import('../../requirements.json')>\n | undefined\n\nexport function getRequirements() {\n if (_requirements === undefined) {\n _requirements = /*@__PURE__*/ require(\n path.join(constants.rootPath, 'requirements.json'),\n )\n }\n return _requirements!\n}\n\n/**\n * Convert command path to requirements key.\n */\nexport function getRequirementsKey(cmdPath: string): string {\n return cmdPath.replace(/^socket[: ]/, '').replace(/ +/g, ':')\n}\n","/**\n * Socket SDK utilities for Socket CLI.\n * Manages SDK initialization and configuration for API communication.\n *\n * Authentication:\n * - Interactive password prompt for missing tokens\n * - Supports environment variable (SOCKET_CLI_API_TOKEN)\n * - Validates token format and presence\n *\n * Proxy Support:\n * - Automatic proxy agent selection\n * - HTTP/HTTPS proxy configuration\n * - Respects SOCKET_CLI_API_PROXY environment variable\n *\n * SDK Setup:\n * - createSocketSdk: Create configured SDK instance\n * - getDefaultApiToken: Retrieve API token from config/env\n * - getDefaultProxyUrl: Retrieve proxy URL from config/env\n * - getPublicApiToken: Get public API token constant\n * - setupSdk: Initialize Socket SDK with authentication\n *\n * User Agent:\n * - Automatic user agent generation from package.json\n * - Includes CLI version and platform information\n */\n\nimport { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'\n\nimport isInteractive from '@socketregistry/is-interactive/index.cjs'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { password } from '@socketsecurity/registry/lib/prompts'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\nimport { isUrl } from '@socketsecurity/registry/lib/url'\nimport { SocketSdk, createUserAgentFromPkgJson } from '@socketsecurity/sdk'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport { debugApiRequest, debugApiResponse } from './debug.mts'\nimport constants, {\n CONFIG_KEY_API_BASE_URL,\n CONFIG_KEY_API_PROXY,\n CONFIG_KEY_API_TOKEN,\n} from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { RequestInfo, ResponseInfo } from '@socketsecurity/sdk'\n\nconst TOKEN_PREFIX = 'sktsec_'\nconst TOKEN_PREFIX_LENGTH = TOKEN_PREFIX.length\nconst TOKEN_VISIBLE_LENGTH = 5\n\n// The Socket API server that should be used for operations.\nexport function getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n constants.ENV.SOCKET_CLI_API_BASE_URL ||\n getConfigValueOrUndef(CONFIG_KEY_API_BASE_URL)\n return isUrl(baseUrl) ? baseUrl : undefined\n}\n\n// The Socket API server that should be used for operations.\nexport function getDefaultProxyUrl(): string | undefined {\n const apiProxy =\n constants.ENV.SOCKET_CLI_API_PROXY ||\n getConfigValueOrUndef(CONFIG_KEY_API_PROXY)\n return isUrl(apiProxy) ? apiProxy : undefined\n}\n\n// This Socket API token should be stored globally for the duration of the CLI execution.\nlet _defaultToken: string | undefined\nexport function getDefaultApiToken(): string | undefined {\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n _defaultToken = undefined\n return _defaultToken\n }\n\n const key =\n constants.ENV.SOCKET_CLI_API_TOKEN ||\n getConfigValueOrUndef(CONFIG_KEY_API_TOKEN) ||\n _defaultToken\n\n _defaultToken = isNonEmptyString(key) ? key : undefined\n return _defaultToken\n}\n\nexport function getPublicApiToken(): string {\n return (\n getDefaultApiToken() ||\n constants.ENV.SOCKET_CLI_API_TOKEN ||\n constants.SOCKET_PUBLIC_API_TOKEN\n )\n}\n\nexport function getVisibleTokenPrefix(): string {\n const apiToken = getDefaultApiToken()\n return apiToken\n ? apiToken.slice(\n TOKEN_PREFIX_LENGTH,\n TOKEN_PREFIX_LENGTH + TOKEN_VISIBLE_LENGTH,\n )\n : ''\n}\n\nexport function hasDefaultApiToken(): boolean {\n return !!getDefaultApiToken()\n}\n\nexport type SetupSdkOptions = {\n apiBaseUrl?: string | undefined\n apiProxy?: string | undefined\n apiToken?: string | undefined\n}\n\nexport async function setupSdk(\n options?: SetupSdkOptions | undefined,\n): Promise<CResult<SocketSdk>> {\n const opts = { __proto__: null, ...options } as SetupSdkOptions\n let { apiToken = getDefaultApiToken() } = opts\n\n if (typeof apiToken !== 'string' && isInteractive()) {\n apiToken = await password({\n message:\n 'Enter your Socket.dev API token (not saved, use socket login to persist)',\n })\n _defaultToken = apiToken\n }\n\n if (!apiToken) {\n return {\n ok: false,\n message: 'Auth Error',\n cause: 'You need to provide an API token. Run `socket login` first.',\n }\n }\n\n let { apiProxy } = opts\n if (!isUrl(apiProxy)) {\n apiProxy = getDefaultProxyUrl()\n }\n\n const { apiBaseUrl = getDefaultApiBaseUrl() } = opts\n\n // Usage of HttpProxyAgent vs. HttpsProxyAgent based on the chart at:\n // https://github.com/delvedor/hpagent?tab=readme-ov-file#usage\n const ProxyAgent = apiBaseUrl?.startsWith('http:')\n ? HttpProxyAgent\n : HttpsProxyAgent\n\n const sdkOptions = {\n ...(apiProxy ? { agent: new ProxyAgent({ proxy: apiProxy }) } : {}),\n ...(apiBaseUrl ? { baseUrl: apiBaseUrl } : {}),\n timeout: constants.ENV.SOCKET_CLI_API_TIMEOUT,\n userAgent: createUserAgentFromPkgJson({\n name: constants.ENV.INLINED_SOCKET_CLI_NAME,\n version: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n homepage: constants.ENV.INLINED_SOCKET_CLI_HOMEPAGE,\n }),\n // Add HTTP request hooks for debugging if SOCKET_CLI_DEBUG is enabled.\n ...(constants.ENV.SOCKET_CLI_DEBUG\n ? {\n hooks: {\n onRequest: (info: RequestInfo) => {\n debugApiRequest(info.method, info.url, info.timeout)\n },\n onResponse: (info: ResponseInfo) => {\n debugApiResponse(\n info.method,\n info.url,\n info.status,\n info.error,\n info.duration,\n info.headers,\n )\n },\n },\n }\n : {}),\n }\n\n if (constants.ENV.SOCKET_CLI_DEBUG) {\n logger.info(\n `[DEBUG] ${new Date().toISOString()} SDK options: ${JSON.stringify(sdkOptions)}`,\n )\n }\n\n const sdk = new SocketSdk(apiToken, sdkOptions)\n\n return {\n ok: true,\n data: sdk,\n }\n}\n","/**\n * API utilities for Socket CLI.\n * Provides consistent API communication with error handling and permissions management.\n *\n * Key Functions:\n * - getDefaultApiBaseUrl: Get configured API endpoint\n * - getErrorMessageForHttpStatusCode: User-friendly HTTP error messages\n * - handleApiCall: Execute Socket SDK API calls with error handling\n * - handleApiCallNoSpinner: Execute API calls without UI spinner\n * - queryApi: Execute raw API queries with text response\n *\n * Error Handling:\n * - Automatic permission requirement logging for 403 errors\n * - Detailed error messages for common HTTP status codes\n * - Integration with debug helpers for API response logging\n *\n * Configuration:\n * - Respects SOCKET_CLI_API_BASE_URL environment variable\n * - Falls back to configured apiBaseUrl or default API_V0_URL\n */\n\nimport { messageWithCauses } from 'pony-cause'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { getConfigValueOrUndef } from './config.mts'\nimport { debugApiRequest, debugApiResponse } from './debug.mts'\nimport constants, {\n CONFIG_KEY_API_BASE_URL,\n EMPTY_VALUE,\n HTTP_STATUS_BAD_REQUEST,\n HTTP_STATUS_FORBIDDEN,\n HTTP_STATUS_INTERNAL_SERVER_ERROR,\n HTTP_STATUS_NOT_FOUND,\n HTTP_STATUS_UNAUTHORIZED,\n} from '../constants.mts'\nimport { getRequirements, getRequirementsKey } from './requirements.mts'\nimport { getDefaultApiToken } from './sdk.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\nimport type {\n SocketSdkErrorResult,\n SocketSdkOperations,\n SocketSdkResult,\n SocketSdkSuccessResult,\n} from '@socketsecurity/sdk'\n\nconst NO_ERROR_MESSAGE = 'No error message returned'\n\nexport type CommandRequirements = {\n permissions?: string[] | undefined\n quota?: number | undefined\n}\n\n/**\n * Get command requirements from requirements.json based on command path.\n */\nfunction getCommandRequirements(\n cmdPath?: string | undefined,\n): CommandRequirements | undefined {\n if (!cmdPath) {\n return undefined\n }\n\n const requirements = getRequirements()\n const key = getRequirementsKey(cmdPath)\n return (requirements.api as any)[key] || undefined\n}\n\n/**\n * Log required permissions for a command when encountering 403 errors.\n */\nfunction logPermissionsFor403(cmdPath?: string | undefined): void {\n const requirements = getCommandRequirements(cmdPath)\n if (!requirements?.permissions?.length) {\n return\n }\n\n logger.error('This command requires the following API permissions:')\n for (const permission of requirements.permissions) {\n logger.error(` - ${permission}`)\n }\n logger.error('Please ensure your API token has the required permissions.')\n}\n\n// The Socket API server that should be used for operations.\nexport function getDefaultApiBaseUrl(): string | undefined {\n const baseUrl =\n constants.ENV.SOCKET_CLI_API_BASE_URL ||\n getConfigValueOrUndef(CONFIG_KEY_API_BASE_URL)\n if (isNonEmptyString(baseUrl)) {\n return baseUrl\n }\n const API_V0_URL = constants.API_V0_URL\n return API_V0_URL\n}\n\n/**\n * Get user-friendly error message for HTTP status codes.\n */\nexport async function getErrorMessageForHttpStatusCode(code: number) {\n if (code === HTTP_STATUS_BAD_REQUEST) {\n return 'One of the options passed might be incorrect'\n }\n if (code === HTTP_STATUS_FORBIDDEN || code === HTTP_STATUS_UNAUTHORIZED) {\n return 'Your Socket API token may not have the required permissions for this command or you might be trying to access (data from) an organization that is not linked to the API token you are logged in with'\n }\n if (code === HTTP_STATUS_NOT_FOUND) {\n return 'The requested Socket API endpoint was not found (404) or there was no result for the requested parameters. If unexpected, this could be a temporary problem caused by an incident or a bug in the CLI. If the problem persists please let us know.'\n }\n if (code === HTTP_STATUS_INTERNAL_SERVER_ERROR) {\n return 'There was an unknown server side problem with your request. This ought to be temporary. Please let us know if this problem persists.'\n }\n return `Server responded with status code ${code}`\n}\n\nexport type HandleApiCallOptions = {\n description?: string | undefined\n spinner?: Spinner | undefined\n commandPath?: string | undefined\n}\n\nexport type ApiCallResult<T extends SocketSdkOperations> = CResult<\n SocketSdkSuccessResult<T>['data']\n>\n\n/**\n * Handle Socket SDK API calls with error handling and permission logging.\n */\nexport async function handleApiCall<T extends SocketSdkOperations>(\n value: Promise<SocketSdkResult<T>>,\n options?: HandleApiCallOptions | undefined,\n): Promise<ApiCallResult<T>> {\n const { commandPath, description, spinner } = {\n __proto__: null,\n ...options,\n } as HandleApiCallOptions\n\n if (description) {\n spinner?.start(`Requesting ${description} from API...`)\n } else {\n spinner?.start()\n }\n\n let sdkResult: SocketSdkResult<T>\n try {\n sdkResult = await value\n spinner?.stop()\n if (description) {\n const message = `Received Socket API response (after requesting ${description}).`\n if (sdkResult.success) {\n logger.success(message)\n } else {\n logger.info(message)\n }\n }\n } catch (e) {\n spinner?.stop()\n const socketSdkErrorResult: ApiCallResult<T> = {\n ok: false,\n message: 'Socket API error',\n cause: messageWithCauses(e as Error),\n }\n if (description) {\n logger.fail(`An error was thrown while requesting ${description}`)\n }\n debugDir('inspect', { socketSdkErrorResult })\n return socketSdkErrorResult\n }\n\n // Note: TS can't narrow down the type of result due to generics.\n if (sdkResult.success === false) {\n const endpoint = description || 'Socket API'\n debugApiResponse('API', endpoint, sdkResult.status as number)\n debugDir('inspect', { sdkResult })\n\n const errCResult = sdkResult as SocketSdkErrorResult<T>\n const errStr = errCResult.error ? String(errCResult.error).trim() : ''\n const message = errStr || NO_ERROR_MESSAGE\n const reason = errCResult.cause || NO_ERROR_MESSAGE\n const cause =\n reason && message !== reason ? `${message} (reason: ${reason})` : message\n const socketSdkErrorResult: ApiCallResult<T> = {\n ok: false,\n message: 'Socket API error',\n cause,\n data: {\n code: sdkResult.status,\n },\n }\n\n // Log required permissions for 403 errors when in a command context.\n if (commandPath && sdkResult.status === 403) {\n logPermissionsFor403(commandPath)\n }\n\n return socketSdkErrorResult\n }\n const socketSdkSuccessResult: ApiCallResult<T> = {\n ok: true,\n data: (sdkResult as SocketSdkSuccessResult<T>).data,\n }\n return socketSdkSuccessResult\n}\n\nexport async function handleApiCallNoSpinner<T extends SocketSdkOperations>(\n value: Promise<SocketSdkResult<T>>,\n description: string,\n): Promise<CResult<SocketSdkSuccessResult<T>['data']>> {\n let sdkResult: SocketSdkResult<T>\n try {\n sdkResult = await value\n } catch (e) {\n debugFn('error', `API request failed: ${description}`)\n debugDir('error', e)\n\n const errStr = e ? String(e).trim() : ''\n const message = 'Socket API error'\n const rawCause = errStr || NO_ERROR_MESSAGE\n const cause = message !== rawCause ? rawCause : ''\n\n return {\n ok: false,\n message,\n ...(cause ? { cause } : {}),\n }\n }\n\n // Note: TS can't narrow down the type of result due to generics\n if (sdkResult.success === false) {\n debugFn('error', `fail: ${description} bad response`)\n debugDir('inspect', { sdkResult })\n\n const sdkErrorResult = sdkResult as SocketSdkErrorResult<T>\n const errStr = sdkErrorResult.error\n ? String(sdkErrorResult.error).trim()\n : ''\n const message = errStr || NO_ERROR_MESSAGE\n const reason = sdkErrorResult.cause || NO_ERROR_MESSAGE\n const cause =\n reason && message !== reason ? `${message} (reason: ${reason})` : message\n\n return {\n ok: false,\n message: 'Socket API error',\n cause,\n data: {\n code: sdkResult.status,\n },\n }\n } else {\n const sdkSuccessResult = sdkResult as SocketSdkSuccessResult<T>\n return {\n ok: true,\n data: sdkSuccessResult.data,\n }\n }\n}\n\nasync function queryApi(path: string, apiToken: string) {\n const baseUrl = getDefaultApiBaseUrl()\n if (!baseUrl) {\n throw new Error('Socket API base URL is not configured.')\n }\n\n const url = `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`\n const result = await fetch(url, {\n method: 'GET',\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:`)}`,\n },\n })\n return result\n}\n\n/**\n * Query Socket API endpoint and return text response with error handling.\n */\nexport async function queryApiSafeText(\n path: string,\n description?: string | undefined,\n commandPath?: string | undefined,\n): Promise<CResult<string>> {\n const apiToken = getDefaultApiToken()\n if (!apiToken) {\n return {\n ok: false,\n message: 'Authentication Error',\n cause:\n 'User must be authenticated to run this command. Run `socket login` and enter your Socket API token.',\n }\n }\n\n const { spinner } = constants\n\n if (description) {\n spinner.start(`Requesting ${description} from API...`)\n debugApiRequest('GET', path, constants.ENV.SOCKET_CLI_API_TIMEOUT)\n }\n\n let result\n const startTime = Date.now()\n try {\n result = await queryApi(path, apiToken)\n const duration = Date.now() - startTime\n debugApiResponse(\n 'GET',\n path,\n result.status,\n undefined,\n duration,\n Object.fromEntries(result.headers.entries()),\n )\n if (description) {\n spinner.successAndStop(\n `Received Socket API response (after requesting ${description}).`,\n )\n }\n } catch (e) {\n const duration = Date.now() - startTime\n if (description) {\n spinner.failAndStop(\n `An error was thrown while requesting ${description}.`,\n )\n debugApiResponse('GET', path, undefined, e, duration)\n }\n\n debugFn('error', 'Query API request failed')\n debugDir('error', e)\n\n const errStr = e ? String(e).trim() : ''\n const message = 'API request failed'\n const rawCause = errStr || NO_ERROR_MESSAGE\n const cause = message !== rawCause ? rawCause : ''\n\n return {\n ok: false,\n message,\n ...(cause ? { cause } : {}),\n }\n }\n\n if (!result.ok) {\n const { status } = result\n // Log required permissions for 403 errors when in a command context.\n if (commandPath && status === 403) {\n logPermissionsFor403(commandPath)\n }\n return {\n ok: false,\n message: 'Socket API error',\n cause: `${result.statusText} (reason: ${await getErrorMessageForHttpStatusCode(status)})`,\n data: {\n code: status,\n },\n }\n }\n\n try {\n const data = await result.text()\n return {\n ok: true,\n data,\n }\n } catch (e) {\n debugFn('error', 'Failed to read API response text')\n debugDir('error', e)\n\n return {\n ok: false,\n message: 'API request failed',\n cause: 'Unexpected error reading response text',\n }\n }\n}\n\n/**\n * Query Socket API endpoint and return parsed JSON response.\n */\nexport async function queryApiSafeJson<T>(\n path: string,\n description = '',\n): Promise<CResult<T>> {\n const result = await queryApiSafeText(path, description)\n\n if (!result.ok) {\n return result\n }\n\n try {\n return {\n ok: true,\n data: JSON.parse(result.data) as T,\n }\n } catch (e) {\n return {\n ok: false,\n message: 'Server returned invalid JSON',\n cause: `Please report this. JSON.parse threw an error over the following response: \\`${(result.data?.slice?.(0, 100) || EMPTY_VALUE).trim() + (result.data?.length > 100 ? '...' : '')}\\``,\n }\n }\n}\n\nexport type SendApiRequestOptions = {\n method: 'POST' | 'PUT'\n body?: unknown | undefined\n description?: string | undefined\n commandPath?: string | undefined\n}\n\n/**\n * Send POST/PUT request to Socket API with JSON response handling.\n */\nexport async function sendApiRequest<T>(\n path: string,\n options?: SendApiRequestOptions | undefined,\n): Promise<CResult<T>> {\n const apiToken = getDefaultApiToken()\n if (!apiToken) {\n return {\n ok: false,\n message: 'Authentication Error',\n cause:\n 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your Socket API token.',\n }\n }\n\n const baseUrl = getDefaultApiBaseUrl()\n if (!baseUrl) {\n return {\n ok: false,\n message: 'Configuration Error',\n cause:\n 'Socket API endpoint is not configured. Please check your environment configuration.',\n }\n }\n\n const { body, commandPath, description, method } = {\n __proto__: null,\n ...options,\n } as SendApiRequestOptions\n const { spinner } = constants\n\n if (description) {\n spinner.start(`Requesting ${description} from API...`)\n }\n\n let result\n try {\n const fetchOptions = {\n method,\n headers: {\n Authorization: `Basic ${btoa(`${apiToken}:`)}`,\n 'Content-Type': 'application/json',\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n }\n\n result = await fetch(\n `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`,\n fetchOptions,\n )\n if (description) {\n spinner.successAndStop(\n `Received Socket API response (after requesting ${description}).`,\n )\n }\n } catch (e) {\n if (description) {\n spinner.failAndStop(\n `An error was thrown while requesting ${description}.`,\n )\n }\n\n debugFn('error', `API ${method} request failed`)\n debugDir('error', e)\n\n const errStr = e ? String(e).trim() : ''\n const message = 'API request failed'\n const rawCause = errStr || NO_ERROR_MESSAGE\n const cause = message !== rawCause ? rawCause : ''\n\n return {\n ok: false,\n message,\n ...(cause ? { cause } : {}),\n }\n }\n\n if (!result.ok) {\n const { status } = result\n // Log required permissions for 403 errors when in a command context.\n if (commandPath && status === 403) {\n logPermissionsFor403(commandPath)\n }\n return {\n ok: false,\n message: 'Socket API error',\n cause: `${result.statusText} (reason: ${await getErrorMessageForHttpStatusCode(status)})`,\n data: {\n code: status,\n },\n }\n }\n\n try {\n const data = await result.json()\n return {\n ok: true,\n data: data as T,\n }\n } catch (e) {\n debugFn('error', 'Failed to parse API response JSON')\n debugDir('error', e)\n return {\n ok: false,\n message: 'API request failed',\n cause: 'Unexpected error parsing response JSON',\n }\n }\n}\n","import colors from 'yoctocolors-cjs'\n\nexport function failMsgWithBadge(\n badge: string,\n message: string | undefined,\n): string {\n const prefix = colors.bgRedBright(\n colors.bold(colors.white(` ${badge}${message ? ': ' : ''}`)),\n )\n const postfix = message ? ` ${colors.bold(message)}` : ''\n return `${prefix}${postfix}`\n}\n","/**\n * Markdown utilities for Socket CLI.\n * Generates formatted markdown output for reports and documentation.\n *\n * Key Functions:\n * - mdTableStringNumber: Create markdown table with string keys and number values\n *\n * Table Features:\n * - Auto-sizing columns based on content\n * - Proper alignment for headers and data\n * - Clean markdown-compliant formatting\n *\n * Usage:\n * - Analytics reports\n * - Scan result tables\n * - Statistical summaries\n */\n\nexport function mdTableStringNumber(\n title1: string,\n title2: string,\n obj: Record<string, number | string>,\n): string {\n // | Date | Counts |\n // | ----------- | ------ |\n // | Header | 201464 |\n // | Paragraph | 18 |\n let mw1 = title1.length\n let mw2 = title2.length\n for (const { 0: key, 1: value } of Object.entries(obj)) {\n mw1 = Math.max(mw1, key.length)\n mw2 = Math.max(mw2, String(value ?? '').length)\n }\n\n const lines = []\n lines.push(`| ${title1.padEnd(mw1, ' ')} | ${title2.padEnd(mw2)} |`)\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n for (const { 0: key, 1: value } of Object.entries(obj)) {\n lines.push(\n `| ${key.padEnd(mw1, ' ')} | ${String(value ?? '').padStart(mw2, ' ')} |`,\n )\n }\n lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)\n\n return lines.join('\\n')\n}\n\nexport function mdTable<T extends Array<Record<string, string>>>(\n logs: T,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: Array<string & keyof T[number]>,\n titles: string[] = cols,\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const log of logs) {\n for (let i = 0, { length } = cols; i < length; i += 1) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n cws[i] = Math.max(\n cws[i] ?? 0,\n String(val).length,\n (titles[i] || '').length,\n )\n }\n }\n\n let div = '|'\n for (const cw of cws) {\n div += ' ' + '-'.repeat(cw) + ' |'\n }\n\n let header = '|'\n for (let i = 0, { length } = titles; i < length; i += 1) {\n header += ' ' + String(titles[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n\n let body = ''\n for (const log of logs) {\n body += '|'\n for (let i = 0, { length } = cols; i < length; i += 1) {\n // @ts-ignore\n const val: unknown = log[cols[i] ?? ''] ?? ''\n body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => s.trim()).join('\\n')\n}\n\nexport function mdTableOfPairs(\n arr: Array<[string, string]>,\n // This is saying \"an array of strings and the strings are a valid key of elements of T\"\n // In turn, T is defined above as the audit log event type from our OpenAPI docs.\n cols: string[],\n): string {\n // Max col width required to fit all data in that column\n const cws = cols.map(col => col.length)\n\n for (const [key, val] of arr) {\n cws[0] = Math.max(cws[0] ?? 0, String(key).length)\n cws[1] = Math.max(cws[1] ?? 0, String(val ?? '').length)\n }\n\n let div = '|'\n for (const cw of cws) {\n div += ' ' + '-'.repeat(cw) + ' |'\n }\n\n let header = '|'\n for (let i = 0, { length } = cols; i < length; i += 1) {\n header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |'\n }\n\n let body = ''\n for (const [key, val] of arr) {\n body += '|'\n body += ' ' + String(key).padEnd(cws[0] ?? 0, ' ') + ' |'\n body += ' ' + String(val ?? '').padEnd(cws[1] ?? 0, ' ') + ' |'\n body += '\\n'\n }\n\n return [div, header, div, body.trim(), div].filter(s => s.trim()).join('\\n')\n}\n","import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { isObject } from '@socketsecurity/registry/lib/objects'\n\nimport type { CResult } from '../types.mts'\n\n// Serialize the final result object before printing it\n// All commands that support the --json flag should call this before printing\nexport function serializeResultJson(data: CResult<unknown>): string {\n if (!isObject(data)) {\n process.exitCode = 1\n\n debugFn('inspect', { data })\n\n // We should not allow the JSON value to be \"null\", or a boolean/number/string,\n // even if they are valid \"json\".\n return `${JSON.stringify({\n ok: false,\n message: 'Unable to serialize JSON',\n cause:\n 'There was a problem converting the data set to JSON. The JSON was not an object. Please try again without --json',\n }).trim()}\\n`\n }\n\n try {\n return `${JSON.stringify(data, null, 2).trim()}\\n`\n } catch (e) {\n process.exitCode = 1\n\n const message =\n 'There was a problem converting the data set to JSON. Please try again without --json'\n\n logger.fail(message)\n debugFn('error', 'JSON serialization failed')\n debugDir('error', e)\n\n // This could be caused by circular references, which is an \"us\" problem.\n return `${JSON.stringify({\n ok: false,\n message: 'Unable to serialize JSON',\n cause: message,\n }).trim()}\\n`\n }\n}\n","import path from 'node:path'\n\nimport terminalLink from 'terminal-link'\n\nimport { SOCKET_WEBSITE_URL } from '../constants.mts'\n\n/**\n * Creates a terminal link to a local file.\n * @param filePath The file path to link to\n * @param text Optional display text (defaults to the file path itself)\n * @returns A terminal link to the file\n */\nexport function fileLink(filePath: string, text?: string | undefined): string {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(filePath)\n return terminalLink(text ?? filePath, `file://${absolutePath}`)\n}\n\n/**\n * Creates a terminal link to an email address.\n * @param email The email address\n * @param text Optional display text (defaults to the email address itself)\n * @returns A terminal link to compose an email\n */\nexport function mailtoLink(email: string, text?: string | undefined): string {\n return terminalLink(text ?? email, `mailto:${email}`)\n}\n\n/**\n * Creates a terminal link to the Socket.dev dashboard.\n * @param path The path within the dashboard (e.g., '/org/YOURORG/alerts')\n * @param text Optional display text\n * @returns A terminal link to the Socket.dev dashboard URL\n */\nexport function socketDashboardLink(\n dashPath: string,\n text?: string | undefined,\n): string {\n const url = `https://socket.dev/dashboard${dashPath.startsWith('/') ? dashPath : `/${dashPath}`}`\n return terminalLink(text ?? url, url)\n}\n\n/**\n * Creates a terminal link to the Socket.dev website.\n * @param text Display text for the link (defaults to 'Socket.dev')\n * @param urlPath Optional path to append to the base URL (e.g., '/pricing')\n * @returns A terminal link to Socket.dev\n */\nexport function socketDevLink(\n text?: string | undefined,\n urlPath?: string | undefined,\n): string {\n return terminalLink(\n text ?? 'Socket.dev',\n `${SOCKET_WEBSITE_URL}${urlPath || ''}`,\n )\n}\n\n/**\n * Creates a terminal link to Socket.dev documentation.\n * @param docPath The documentation path (e.g., '/docs/api-keys')\n * @param text Optional display text\n * @returns A terminal link to the Socket.dev documentation\n */\nexport function socketDocsLink(\n docPath: string,\n text?: string | undefined,\n): string {\n const url = `https://docs.socket.dev${docPath.startsWith('/') ? docPath : `/${docPath}`}`\n return terminalLink(text ?? url, url)\n}\n\n/**\n * Creates a terminal link to Socket.dev package page.\n * @param ecosystem The package ecosystem (e.g., 'npm')\n * @param packageName The package name\n * @param version Optional package version or path (e.g., 'files/1.0.0/CHANGELOG.md')\n * @param text Optional display text\n * @returns A terminal link to the Socket.dev package page\n */\nexport function socketPackageLink(\n ecosystem: string,\n packageName: string,\n version?: string | undefined,\n text?: string | undefined,\n): string {\n let url: string\n if (version) {\n // Check if version contains a path like 'files/1.0.0/CHANGELOG.md'.\n if (version.includes('/')) {\n url = `https://socket.dev/${ecosystem}/package/${packageName}/${version}`\n } else {\n url = `https://socket.dev/${ecosystem}/package/${packageName}/overview/${version}`\n }\n } else {\n url = `https://socket.dev/${ecosystem}/package/${packageName}`\n }\n return terminalLink(text ?? url, url)\n}\n\n/**\n * Creates a terminal link to a web URL.\n * @param url The web URL to link to\n * @param text Optional display text (defaults to the URL itself)\n * @returns A terminal link to the URL\n */\nexport function webLink(url: string, text?: string | undefined): string {\n return terminalLink(text ?? url, url)\n}\n","import colors from 'yoctocolors-cjs'\n\nimport { LOG_SYMBOLS, logger } from '@socketsecurity/registry/lib/logger'\nimport { stripAnsi } from '@socketsecurity/registry/lib/strings'\n\nimport { failMsgWithBadge } from './fail-msg-with-badge.mts'\nimport { serializeResultJson } from './serialize-result-json.mts'\n\nimport type { OutputKind } from '../types.mts'\n\nexport function checkCommandInput(\n outputKind: OutputKind,\n ...checks: Array<{\n fail: string\n message: string\n test: boolean\n nook?: boolean | undefined\n pass?: string | undefined\n }>\n): boolean {\n if (checks.every(d => d.test)) {\n return true\n }\n\n const msg = ['Please review the input requirements and try again', '']\n for (const d of checks) {\n // If nook, then ignore when test is ok\n if (d.nook && d.test) {\n continue\n }\n const lines = d.message.split('\\n')\n const { length: lineCount } = lines\n if (!lineCount) {\n continue\n }\n // If the message has newlines then format the first line with the input\n // expectation and the rest indented below it.\n const logSymbol = d.test ? LOG_SYMBOLS.success : LOG_SYMBOLS.fail\n const reason = d.test ? d.pass : d.fail\n let listItem = ` ${logSymbol} ${lines[0]}`\n if (reason) {\n const styledReason = d.test ? colors.green(reason) : colors.red(reason)\n listItem += ` (${styledReason})`\n }\n msg.push(listItem)\n if (lineCount > 1) {\n msg.push(...lines.slice(1).map(str => ` ${str}`))\n }\n }\n\n // Use exit status of 2 to indicate incorrect usage, generally invalid\n // options or missing arguments.\n // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html\n process.exitCode = 2\n\n if (outputKind === 'json') {\n logger.log(\n serializeResultJson({\n ok: false,\n message: 'Input error',\n data: stripAnsi(msg.join('\\n')),\n }),\n )\n } else {\n logger.fail(failMsgWithBadge('Input error', msg.join('\\n')))\n }\n\n return false\n}\n","/**\n * Output format detection utilities for Socket CLI.\n * Determines output format based on command flags.\n *\n * Key Functions:\n * - getOutputKind: Determine output format from flags\n *\n * Supported Formats:\n * - JSON: Machine-readable JSON output\n * - Markdown: Formatted markdown for reports\n * - Text: Plain text for terminal display\n *\n * Usage:\n * - Processes --json and --markdown flags\n * - Returns appropriate output format constant\n * - Defaults to text format for terminal display\n */\n\nimport { OUTPUT_JSON, OUTPUT_MARKDOWN, OUTPUT_TEXT } from '../constants.mts'\n\nimport type { OutputKind } from '../types.mts'\n\nexport function getOutputKind(json: unknown, markdown: unknown): OutputKind {\n if (json) {\n return OUTPUT_JSON\n }\n if (markdown) {\n return OUTPUT_MARKDOWN\n }\n return OUTPUT_TEXT\n}\n","/**\n * String manipulation utilities for Socket CLI.\n * Provides common string transformations and formatting.\n *\n * Key Functions:\n * - camelToKebab: Convert camelCase to kebab-case\n *\n * Usage:\n * - Command name transformations\n * - Flag name conversions\n * - Consistent string formatting\n */\n\nexport function camelToKebab(str: string): string {\n return str === '' ? '' : str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()\n}\n","/**\n * Output formatting utilities for Socket CLI.\n * Provides consistent formatting for help text and command output.\n *\n * Key Functions:\n * - getFlagApiRequirementsOutput: Format API requirements for flags\n * - getHelpListOutput: Format help text lists with descriptions\n * - getFlagsHelpOutput: Generate formatted help for command flags\n *\n * Formatting Features:\n * - Automatic indentation and alignment\n * - Flag description formatting\n * - Requirements and permissions display\n * - Hidden flag filtering\n *\n * Usage:\n * - Used by command help systems\n * - Provides consistent terminal output formatting\n * - Handles kebab-case conversion for flags\n */\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { isObject } from '@socketsecurity/registry/lib/objects'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { indentString } from '@socketsecurity/registry/lib/strings'\nimport { pluralize } from '@socketsecurity/registry/lib/words'\n\nimport { getRequirements, getRequirementsKey } from './requirements.mts'\nimport { camelToKebab } from './strings.mts'\n\nimport type { MeowFlags } from '../flags.mts'\n\ntype ApiRequirementsOptions = {\n indent?: number | undefined\n}\n\ntype HelpListOptions = {\n indent?: number | undefined\n keyPrefix?: string | undefined\n padName?: number | undefined\n}\n\ntype ListDescription =\n | { description: string }\n | { description: string; hidden: boolean }\n\nexport function getFlagApiRequirementsOutput(\n cmdPath: string,\n options?: ApiRequirementsOptions | undefined,\n): string {\n const { indent = 6 } = {\n __proto__: null,\n ...options,\n } as ApiRequirementsOptions\n const key = getRequirementsKey(cmdPath)\n const requirements = getRequirements()\n const data = (requirements.api as any)[key]\n let result = ''\n if (data) {\n const quota: number = data?.quota\n const rawPerms: string[] = data?.permissions\n const padding = ''.padEnd(indent)\n const lines = []\n if (Number.isFinite(quota) && quota > 0) {\n lines.push(`${padding}- Quota: ${quota} ${pluralize('unit', quota)}`)\n }\n if (Array.isArray(rawPerms) && rawPerms.length) {\n const perms = rawPerms.slice().sort(naturalCompare)\n lines.push(`${padding}- Permissions: ${joinAnd(perms)}`)\n }\n result += lines.join('\\n')\n }\n return result.trim() || '(none)'\n}\n\nexport function getFlagListOutput(\n list: MeowFlags,\n options?: HelpListOptions | undefined,\n): string {\n const { keyPrefix = '--' } = {\n __proto__: null,\n ...options,\n } as HelpListOptions\n return getHelpListOutput(\n {\n ...list,\n },\n { ...options, keyPrefix },\n )\n}\n\nexport function getHelpListOutput(\n list: Record<string, ListDescription>,\n options?: HelpListOptions | undefined,\n): string {\n const {\n indent = 6,\n keyPrefix = '',\n padName = 20,\n } = {\n __proto__: null,\n ...options,\n } as HelpListOptions\n let result = ''\n const names = Object.keys(list).sort(naturalCompare)\n for (const name of names) {\n const entry = list[name]\n const entryIsObj = isObject(entry)\n if (entryIsObj && 'hidden' in entry && entry?.hidden) {\n continue\n }\n const printedName = `${keyPrefix}${camelToKebab(name)}`\n const preDescription = `${''.padEnd(indent)}${printedName.padEnd(Math.max(printedName.length + 2, padName))}`\n\n result += preDescription\n\n const description = entryIsObj ? entry.description : String(entry)\n if (description) {\n result += indentString(description, preDescription.length).trimStart()\n }\n result += '\\n'\n }\n return result.trim() || '(none)'\n}\n","/**\n * Path tildification utilities for Socket CLI.\n * Abbreviates home directory paths with tilde notation.\n *\n * Key Functions:\n * - tildify: Replace home directory with ~ in paths\n *\n * Usage:\n * - Shortens absolute paths for display\n * - Converts /Users/name/... to ~/...\n * - Common Unix convention for home directory\n */\n\nimport path from 'node:path'\n\nimport { escapeRegExp } from '@socketsecurity/registry/lib/regexps'\n\nimport constants from '../constants.mts'\n\nexport function tildify(cwd: string) {\n return cwd.replace(\n new RegExp(`^${escapeRegExp(constants.homePath)}(?:${path.sep}|$)`, 'i'),\n '~/',\n )\n}\n","import meow from 'meow'\nimport terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport { joinAnd } from '@socketsecurity/registry/lib/arrays'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport {\n getOwn,\n hasOwn,\n toSortedObject,\n} from '@socketsecurity/registry/lib/objects'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { getCliSpinners } from '@socketsecurity/registry/lib/spinner'\nimport {\n indentString,\n trimNewlines,\n} from '@socketsecurity/registry/lib/strings'\n\nimport {\n getConfigValueOrUndef,\n isConfigFromFlag,\n overrideCachedConfig,\n overrideConfigApiToken,\n} from './config.mts'\nimport { isDebug } from './debug.mts'\nimport { getFlagListOutput, getHelpListOutput } from './output-formatting.mts'\nimport { socketPackageLink } from './terminal-link.mts'\nimport constants, {\n API_V0_URL,\n CONFIG_KEY_API_TOKEN,\n CONFIG_KEY_DEFAULT_ORG,\n FLAG_HELP_FULL,\n FLAG_JSON,\n FLAG_MARKDOWN,\n FLAG_ORG,\n NPM,\n NPX,\n // PNPM,\n // YARN,\n} from '../constants.mts'\nimport { commonFlags } from '../flags.mts'\nimport { getVisibleTokenPrefix } from './sdk.mts'\nimport { tildify } from './tildify.mts'\n\nimport type { MeowFlag, MeowFlags } from '../flags.mts'\nimport type { Options, Result } from 'meow'\n\nexport interface CliAlias {\n description: string\n argv: readonly string[]\n hidden?: boolean | undefined\n}\n\nexport type CliAliases = Record<string, CliAlias>\n\nexport type CliSubcommandRun = (\n argv: string[] | readonly string[],\n importMeta: ImportMeta,\n context: { parentName: string; rawArgv?: readonly string[] },\n) => Promise<void> | void\n\nexport interface CliSubcommand {\n description: string\n hidden?: boolean | undefined\n run: CliSubcommandRun\n}\n\n// Property names are picked such that the name is at the top when the props\n// get ordered by alphabet while flags is near the bottom and the help text\n// at the bottom, because they tend ot occupy the most lines of code.\nexport interface CliCommandConfig {\n commandName: string\n description: string\n hidden: boolean\n flags: MeowFlags\n help: (command: string, config: CliCommandConfig) => string\n}\n\nexport interface CliCommandContext {\n parentName: string\n rawArgv?: string[] | readonly string[]\n}\n\nexport interface MeowConfig {\n name: string\n argv: string[] | readonly string[]\n importMeta: ImportMeta\n subcommands: Record<string, CliSubcommand>\n}\n\nexport interface MeowOptions extends Omit<Options<any>, 'argv' | 'importMeta'> {\n aliases?: CliAliases | undefined\n // When no sub-command is given, default to this sub-command.\n defaultSub?: string | undefined\n}\n\nconst HELP_INDENT = 2\n\nconst HELP_PAD_NAME = 28\n\n/**\n * Format a command description for help output.\n */\nfunction description(command: CliSubcommand | undefined): string {\n const description = command?.description\n const str =\n typeof description === 'string' ? description : String(description)\n return indentString(str, HELP_PAD_NAME).trimStart()\n}\n\n/**\n * Find the best matching command name for a typo.\n */\nfunction findBestCommandMatch(\n input: string,\n subcommands: Record<string, unknown>,\n aliases: Record<string, unknown>,\n): string | null {\n let bestMatch = null\n let bestScore = Infinity\n const allCommands = [...Object.keys(subcommands), ...Object.keys(aliases)]\n for (const command of allCommands) {\n const distance = levenshteinDistance(\n input.toLowerCase(),\n command.toLowerCase(),\n )\n const maxLength = Math.max(input.length, command.length)\n // Only suggest if the similarity is reasonable (more than 50% similar).\n if (distance < maxLength * 0.5 && distance < bestScore) {\n bestScore = distance\n bestMatch = command\n }\n }\n return bestMatch\n}\n\n/**\n * Determine the origin of the API token.\n */\nfunction getTokenOrigin(): string {\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n return ''\n }\n if (constants.ENV.SOCKET_CLI_API_TOKEN) {\n return '(env)'\n }\n const configToken = getConfigValueOrUndef(CONFIG_KEY_API_TOKEN)\n if (configToken) {\n return isConfigFromFlag() ? '(--config flag)' : '(config)'\n }\n return ''\n}\n\n/**\n * Generate the ASCII banner header for Socket CLI commands.\n */\nfunction getAsciiHeader(\n command: string,\n orgFlag: string | undefined,\n compactMode: boolean = false,\n) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n const { REDACTED } = constants\n const redacting = constants.ENV.VITEST\n\n // Version display: show hash in debug mode, otherwise show semantic version.\n const fullVersion = constants.ENV.INLINED_SOCKET_CLI_VERSION\n const versionHash = constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH\n const cliVersion = redacting\n ? REDACTED\n : isDebug()\n ? versionHash\n : `v${fullVersion}`\n\n const nodeVersion = redacting ? REDACTED : process.version\n const showNodeVersion = !redacting && isDebug()\n const defaultOrg = getConfigValueOrUndef(CONFIG_KEY_DEFAULT_ORG)\n const configFromFlagDot = isConfigFromFlag() ? '*' : '.'\n\n // Token display with origin indicator.\n const tokenPrefix = getVisibleTokenPrefix()\n const tokenOrigin = redacting ? '' : getTokenOrigin()\n const noApiToken = constants.ENV.SOCKET_CLI_NO_API_TOKEN\n const shownToken = redacting\n ? REDACTED\n : noApiToken\n ? colors.red('(disabled)')\n : tokenPrefix\n ? `${colors.green(tokenPrefix)}***${tokenOrigin ? ` ${tokenOrigin}` : ''}`\n : colors.yellow('(not set)')\n\n const relCwd = redacting ? REDACTED : normalizePath(tildify(process.cwd()))\n\n // Consolidated org display format.\n const orgPart = redacting\n ? `org: ${REDACTED}`\n : orgFlag\n ? `org: ${colors.cyan(orgFlag)} (${FLAG_ORG} flag)`\n : defaultOrg && defaultOrg !== 'null'\n ? `org: ${colors.cyan(defaultOrg)} (config)`\n : colors.yellow('org: (not set)')\n\n // Compact mode for CI/automation.\n if (compactMode) {\n const compactToken = noApiToken\n ? '(disabled)'\n : tokenPrefix\n ? `${tokenPrefix}***${tokenOrigin ? ` ${tokenOrigin}` : ''}`\n : '(not set)'\n const compactOrg =\n orgFlag ||\n (defaultOrg && defaultOrg !== 'null' ? defaultOrg : '(not set)')\n return `CLI: ${cliVersion} | cmd: ${command} | org: ${compactOrg} | token: ${compactToken}`\n }\n\n // Note: We could draw these with ascii box art instead but I worry about\n // portability and paste-ability. \"simple\" ascii chars just work.\n const body = `\n _____ _ _ /---------------\n | __|___ ___| |_ ___| |_ | CLI: ${cliVersion}\n |__ | ${configFromFlagDot} | _| '_| -_| _| | ${showNodeVersion ? `Node: ${nodeVersion}, ` : ''}token: ${shownToken}, ${orgPart}\n |_____|___|___|_,_|___|_|.dev | Command: \\`${command}\\`, cwd: ${relCwd}\n `.trim()\n // Note: logger will auto-append a newline.\n return ` ${body}`\n}\n\n/**\n * Calculate Levenshtein distance between two strings for fuzzy matching.\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const matrix = Array.from({ length: a.length + 1 }, () =>\n Array(b.length + 1).fill(0),\n )\n for (let i = 0; i <= a.length; i++) {\n matrix[i]![0] = i\n }\n for (let j = 0; j <= b.length; j++) {\n matrix[0]![j] = j\n }\n for (let i = 1; i <= a.length; i++) {\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n matrix[i]![j] = Math.min(\n // Deletion.\n matrix[i - 1]![j]! + 1,\n // Insertion.\n matrix[i]![j - 1]! + 1,\n // Substitution.\n matrix[i - 1]![j - 1]! + cost,\n )\n }\n }\n return matrix[a.length]![b.length]!\n}\n\n/**\n * Determine if the banner should be suppressed based on output flags.\n */\nfunction shouldSuppressBanner(flags: Record<string, unknown>): boolean {\n return Boolean(\n flags['json'] || flags['markdown'] || flags['banner'] === false,\n )\n}\n\n/**\n * Emit the Socket CLI banner to stderr for branding and debugging.\n */\nexport function emitBanner(\n name: string,\n orgFlag: string | undefined,\n compactMode: boolean = false,\n) {\n // Print a banner at the top of each command.\n // This helps with brand recognition and marketing.\n // It also helps with debugging since it contains version and command details.\n // Note: print over stderr to preserve stdout for flags like --json and\n // --markdown. If we don't do this, you can't use --json in particular\n // and pipe the result to other tools. By emitting the banner over stderr\n // you can do something like `socket scan view xyz | jq | process`.\n // The spinner also emits over stderr for example.\n logger.error(getAsciiHeader(name, orgFlag, compactMode))\n}\n\n// For debugging. Whenever you call meowOrExit it will store the command here\n// This module exports a getter that returns the current value.\nlet lastSeenCommand = ''\n\n/**\n * Get the last command that was processed by meowOrExit (for debugging).\n */\nexport function getLastSeenCommand(): string {\n return lastSeenCommand\n}\n\n/**\n * Main function for handling CLI with subcommands using meow.\n * @param config Configuration object with name, argv, importMeta, and subcommands.\n * @param options Optional settings like aliases and defaultSub.\n * @example\n * meowWithSubcommands(\n * { name, argv, importMeta, subcommands },\n * { aliases, defaultSub }\n * )\n */\nexport async function meowWithSubcommands(\n config: MeowConfig,\n options?: MeowOptions | undefined,\n): Promise<void> {\n const { argv, importMeta, name, subcommands } = {\n __proto__: null,\n ...config,\n } as MeowConfig\n const {\n aliases = {},\n defaultSub,\n ...additionalOptions\n } = { __proto__: null, ...options } as MeowOptions\n const flags: MeowFlags = {\n ...commonFlags,\n version: {\n type: 'boolean',\n hidden: true,\n description: 'Print the app version',\n },\n ...getOwn(additionalOptions, 'flags'),\n }\n\n const [commandOrAliasName_, ...rawCommandArgv] = argv\n let commandOrAliasName = commandOrAliasName_\n if (!commandOrAliasName && defaultSub) {\n commandOrAliasName = defaultSub\n }\n\n // No further args or first arg is a flag (shrug).\n const isRootCommand =\n name === 'socket' &&\n (!commandOrAliasName || commandOrAliasName?.startsWith('-'))\n\n // Try to support `socket <purl>` as a shorthand for `socket package score <purl>`.\n if (!isRootCommand) {\n if (commandOrAliasName?.startsWith('pkg:')) {\n logger.info('Invoking `socket package score`.')\n return await meowWithSubcommands(\n { name, argv: ['package', 'deep', ...argv], importMeta, subcommands },\n options,\n )\n }\n // Support `socket npm/lodash` or whatever as a shorthand, too.\n // Accept any ecosystem and let the remote sort it out.\n if (/^[a-z]+\\//.test(commandOrAliasName || '')) {\n logger.info('Invoking `socket package score`.')\n return await meowWithSubcommands(\n {\n name,\n argv: [\n 'package',\n 'deep',\n `pkg:${commandOrAliasName}`,\n ...rawCommandArgv,\n ],\n importMeta,\n subcommands,\n },\n options,\n )\n }\n }\n\n if (isRootCommand) {\n const hiddenDebugFlag = !isDebug()\n\n flags['compactHeader'] = {\n ...flags['compactHeader'],\n hidden: false,\n } as MeowFlag\n\n flags['config'] = {\n ...flags['config'],\n hidden: false,\n } as MeowFlag\n\n flags['dryRun'] = {\n ...flags['dryRun'],\n hidden: false,\n } as MeowFlag\n\n flags['help'] = {\n ...flags['help'],\n hidden: false,\n } as MeowFlag\n\n flags['helpFull'] = {\n ...flags['helpFull'],\n hidden: false,\n } as MeowFlag\n\n flags['maxOldSpaceSize'] = {\n ...flags['maxOldSpaceSize'],\n hidden: hiddenDebugFlag,\n } as MeowFlag\n\n flags['maxSemiSpaceSize'] = {\n ...flags['maxSemiSpaceSize'],\n hidden: hiddenDebugFlag,\n } as MeowFlag\n\n flags['version'] = {\n ...flags['version'],\n hidden: false,\n } as MeowFlag\n\n delete flags['json']\n delete flags['markdown']\n } else {\n delete flags['help']\n delete flags['helpFull']\n delete flags['version']\n }\n\n // This is basically a dry-run parse of cli args and flags. We use this to\n // determine config overrides and expected output mode.\n const cli1 = meow({\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n // Ensure we don't check unknown flags.\n allowUnknownFlags: true,\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n })\n\n const {\n compactHeader: compactHeaderFlag,\n config: configFlag,\n org: orgFlag,\n spinner: spinnerFlag,\n } = cli1.flags as {\n compactHeader: boolean\n config: string\n org: string\n spinner: boolean\n }\n\n const compactMode =\n compactHeaderFlag || (constants.ENV.CI && !constants.ENV.VITEST)\n const noSpinner = spinnerFlag === false || isDebug()\n\n // Use CI spinner style when --no-spinner is passed or debug mode is enabled.\n // This prevents the spinner from interfering with debug output.\n if (noSpinner) {\n constants.spinner.spinner = getCliSpinners('ci')!\n }\n // Hard override the config if instructed to do so.\n // The env var overrides the --flag, which overrides the persisted config\n // Also, when either of these are used, config updates won't persist.\n let configOverrideResult\n if (constants.ENV.SOCKET_CLI_CONFIG) {\n configOverrideResult = overrideCachedConfig(constants.ENV.SOCKET_CLI_CONFIG)\n } else if (configFlag) {\n configOverrideResult = overrideCachedConfig(configFlag)\n }\n\n if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {\n // This overrides the config override and even the explicit token env var.\n // The config will be marked as readOnly to prevent persisting it.\n overrideConfigApiToken(undefined)\n } else {\n const tokenOverride = constants.ENV.SOCKET_CLI_API_TOKEN\n if (tokenOverride) {\n // This will set the token (even if there was a config override) and\n // set it to readOnly, making sure the temp token won't be persisted.\n overrideConfigApiToken(tokenOverride)\n }\n }\n\n if (configOverrideResult?.ok === false) {\n if (!shouldSuppressBanner(cli1.flags)) {\n emitBanner(name, orgFlag, compactMode)\n // Add newline in stderr.\n logger.error('')\n }\n logger.fail(configOverrideResult.message)\n process.exitCode = 2\n return\n }\n\n // If we have got some args, then lets find out if we can find a command.\n if (commandOrAliasName) {\n const alias = aliases[commandOrAliasName]\n // First: Resolve argv data from alias if its an alias that's been given.\n const [commandName, ...commandArgv] = alias\n ? [...alias.argv, ...rawCommandArgv]\n : [commandOrAliasName, ...rawCommandArgv]\n // Second: Find a command definition using that data.\n const commandDefinition = commandName ? subcommands[commandName] : undefined\n // Third: If a valid command has been found, then we run it...\n if (commandDefinition) {\n // Extract the original command arguments from the full argv\n // by skipping the command name\n return await commandDefinition.run(commandArgv, importMeta, {\n parentName: name,\n })\n }\n\n // Suggest similar commands for typos.\n if (commandName && !commandDefinition) {\n const suggestion = findBestCommandMatch(commandName, subcommands, aliases)\n if (suggestion) {\n process.exitCode = 2\n logger.fail(\n `Unknown command \"${commandName}\". Did you mean \"${suggestion}\"?`,\n )\n return\n }\n }\n }\n\n const lines = ['', 'Usage', ` $ ${name} <command>`]\n if (isRootCommand) {\n lines.push(\n ` $ ${name} scan create ${FLAG_JSON}`,\n ` $ ${name} package score ${NPM} lodash ${FLAG_MARKDOWN}`,\n )\n }\n lines.push('')\n if (isRootCommand) {\n // \"Bucket\" some commands for easier usage.\n const commands = new Set([\n 'analytics',\n 'audit-log',\n 'ci',\n 'cdxgen',\n 'config',\n 'dependencies',\n 'fix',\n 'install',\n //'json',\n 'license',\n 'login',\n 'logout',\n 'manifest',\n NPM,\n NPX,\n 'optimize',\n 'organization',\n 'package',\n //'patch',\n // PNPM,\n 'raw-npm',\n 'raw-npx',\n 'repository',\n 'scan',\n //'security',\n 'threat-feed',\n 'uninstall',\n 'wrapper',\n // YARN,\n ])\n Object.entries(subcommands)\n .filter(([_name, subcommand]) => !subcommand.hidden)\n .map(([name]) => name)\n .forEach(name => {\n if (commands.has(name)) {\n commands.delete(name)\n } else {\n logger.fail('Received an unknown command:', name)\n }\n })\n if (commands.size) {\n logger.fail(\n 'Found commands in the list that were not marked as public or not defined at all:',\n // Node < 22 will print 'Object (n)' before the array. So to have consistent\n // test snapshots we use joinAnd.\n joinAnd(\n Array.from(commands)\n .sort(naturalCompare)\n .map(c => `'${c}'`),\n ),\n )\n }\n lines.push(\n 'Note: All commands have their own --help',\n '',\n 'Main commands',\n ` socket login ${description(subcommands['login'])}`,\n ` socket scan create Create a new Socket scan and report`,\n ` socket npm/lodash@4.17.21 Request the Socket score of a package`,\n ` socket fix ${description(subcommands['fix'])}`,\n ` socket optimize ${description(subcommands['optimize'])}`,\n ` socket cdxgen ${description(subcommands['cdxgen'])}`,\n ` socket ci ${description(subcommands['ci'])}`,\n ``,\n 'Socket API',\n ` analytics ${description(subcommands['analytics'])}`,\n ` audit-log ${description(subcommands['audit-log'])}`,\n ` organization ${description(subcommands['organization'])}`,\n ` package ${description(subcommands['package'])}`,\n ` repository ${description(subcommands['repository'])}`,\n ` scan ${description(subcommands['scan'])}`,\n ` threat-feed ${description(subcommands['threat-feed'])}`,\n ``,\n 'Local tools',\n ` manifest ${description(subcommands['manifest'])}`,\n ` npm ${description(subcommands[NPM])}`,\n ` npx ${description(subcommands[NPX])}`,\n ` raw-npm ${description(subcommands['raw-npm'])}`,\n ` raw-npx ${description(subcommands['raw-npx'])}`,\n '',\n 'CLI configuration',\n ` config ${description(subcommands['config'])}`,\n ` install ${description(subcommands['install'])}`,\n ` login Socket API login and CLI setup`,\n ` logout ${description(subcommands['logout'])}`,\n ` uninstall ${description(subcommands['uninstall'])}`,\n ` wrapper ${description(subcommands['wrapper'])}`,\n )\n } else {\n lines.push('Commands')\n lines.push(\n ` ${getHelpListOutput(\n {\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(subcommands).filter(\n ({ 1: subcommand }) => !subcommand.hidden,\n ),\n ),\n ),\n ...toSortedObject(\n Object.fromEntries(\n Object.entries(aliases).filter(({ 1: alias }) => {\n const { hidden } = alias\n const cmdName = hidden ? '' : alias.argv[0]\n const subcommand = cmdName ? subcommands[cmdName] : undefined\n return subcommand && !subcommand.hidden\n }),\n ),\n ),\n },\n { indent: HELP_INDENT, padName: HELP_PAD_NAME },\n )}`,\n )\n }\n\n lines.push('', 'Options')\n if (isRootCommand) {\n lines.push(\n ' Note: All commands have these flags even when not displayed in their help',\n '',\n )\n } else {\n lines.push('')\n }\n lines.push(\n ` ${getFlagListOutput(\n {\n ...flags,\n // Explicitly document the negated --no-banner variant.\n noBanner: {\n ...flags['banner'],\n hidden: false,\n } as MeowFlag,\n // Explicitly document the negated --no-spinner variant.\n noSpinner: {\n ...flags['spinner'],\n hidden: false,\n } as MeowFlag,\n },\n { indent: HELP_INDENT, padName: HELP_PAD_NAME },\n )}`,\n )\n if (isRootCommand) {\n // Check if we should show full help with environment variables.\n const showFullHelp = argv.includes(FLAG_HELP_FULL)\n\n if (showFullHelp) {\n // Show full help with environment variables.\n lines.push(\n '',\n 'Environment variables',\n ' SOCKET_CLI_API_TOKEN Set the Socket API token',\n ' SOCKET_CLI_CONFIG A JSON stringified Socket configuration object',\n ' SOCKET_CLI_GITHUB_API_URL Change the base URL for GitHub REST API calls',\n ' SOCKET_CLI_GIT_USER_EMAIL The git config `user.email` used by Socket CLI',\n ` ${colors.italic('Defaults:')} github-actions[bot]@users.noreply.github.com`,\n ' SOCKET_CLI_GIT_USER_NAME The git config `user.name` used by Socket CLI',\n ` ${colors.italic('Defaults:')} github-actions[bot]`,\n ` SOCKET_CLI_GITHUB_TOKEN A classic or fine-grained ${terminalLink('GitHub personal access token', 'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens')}`,\n ` ${colors.italic('Aliases:')} GITHUB_TOKEN`,\n ' SOCKET_CLI_NO_API_TOKEN Make the default API token `undefined`',\n ' SOCKET_CLI_NPM_PATH The absolute location of the npm directory',\n ' SOCKET_CLI_ORG_SLUG Specify the Socket organization slug',\n '',\n ' SOCKET_CLI_ACCEPT_RISKS Accept risks of a Socket wrapped npm/npx run',\n ' SOCKET_CLI_VIEW_ALL_RISKS View all risks of a Socket wrapped npm/npx run',\n '',\n 'Environment variables for development',\n ' SOCKET_CLI_API_BASE_URL Change the base URL for Socket API calls',\n ` ${colors.italic('Defaults:')} The \"apiBaseUrl\" value of socket/settings local app data`,\n ` if present, else ${API_V0_URL}`,\n ' SOCKET_CLI_API_PROXY Set the proxy Socket API requests are routed through, e.g. if set to',\n ` ${terminalLink('http://127.0.0.1:9090', 'https://docs.proxyman.io/troubleshooting/couldnt-see-any-requests-from-3rd-party-network-libraries')} then all request are passed through that proxy`,\n ` ${colors.italic('Aliases:')} HTTPS_PROXY, https_proxy, HTTP_PROXY, and http_proxy`,\n ' SOCKET_CLI_API_TIMEOUT Set the timeout in milliseconds for Socket API requests',\n ' SOCKET_CLI_DEBUG Enable debug logging in Socket CLI',\n ` DEBUG Enable debug logging based on the ${socketPackageLink('npm', 'debug', undefined, 'debug')} package`,\n )\n } else {\n // Show condensed help with hint about --help-full.\n lines.push(\n '',\n 'Environment variables [more...]',\n ` Use ${colors.bold(FLAG_HELP_FULL)} to view all environment variables`,\n )\n }\n }\n\n // Parse it again. Config overrides should now be applied (may affect help).\n // Note: this is displayed as help screen if the command does not override it\n // (which is the case for most sub-commands with sub-commands).\n const cli2 = meow({\n argv,\n importMeta,\n ...additionalOptions,\n flags,\n // Do not strictly check for flags here.\n allowUnknownFlags: true,\n // We will emit help when we're ready.\n // Plus, if we allow this then meow may exit here.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n help: lines.map(l => indentString(l, HELP_INDENT)).join('\\n'),\n })\n\n const { dryRun, help: helpFlag } = cli2.flags as {\n dryRun: boolean\n help: boolean\n }\n\n // ...else we provide basic instructions and help.\n if (!shouldSuppressBanner(cli2.flags)) {\n emitBanner(name, orgFlag, compactMode)\n // Meow will add newline so don't add stderr spacing here.\n }\n if (!helpFlag && dryRun) {\n process.exitCode = 0\n logger.log(`${constants.DRY_RUN_LABEL}: No-op, call a sub-command; ok`)\n } else {\n // When you explicitly request --help, the command should be successful\n // so we exit(0). If we do it because we need more input, we exit(2).\n cli2.showHelp(helpFlag ? 0 : 2)\n }\n}\n\nexport interface MeowOrExitConfig {\n argv: string[] | readonly string[]\n config: CliCommandConfig\n parentName: string\n importMeta: ImportMeta\n}\n\nexport type MeowOrExitOptions = {\n allowUnknownFlags?: boolean | undefined\n}\n\n/**\n * Create meow CLI instance or exit with help/error (meow will exit immediately\n * if it calls .showHelp()).\n * @param config Configuration object with argv, config, parentName, and importMeta.\n * @param options Optional settings like allowUnknownFlags.\n * @example\n * meowOrExit(\n * { argv, config, parentName, importMeta },\n * { allowUnknownFlags: false }\n * )\n */\nexport function meowOrExit(\n config: MeowOrExitConfig,\n options?: MeowOrExitOptions | undefined,\n): Result<MeowFlags> {\n const {\n argv,\n config: cliConfig,\n importMeta,\n parentName,\n } = { __proto__: null, ...config } as MeowOrExitConfig\n const { allowUnknownFlags = true } = {\n __proto__: null,\n ...options,\n } as MeowOrExitOptions\n const command = `${parentName} ${cliConfig.commandName}`\n lastSeenCommand = command\n\n // This exits if .printHelp() is called either by meow itself or by us.\n const cli = meow({\n argv,\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n // We want to detect whether a bool flag is given at all.\n booleanDefault: undefined,\n collectUnknownFlags: true,\n description: cliConfig.description,\n flags: cliConfig.flags,\n help: trimNewlines(cliConfig.help(command, cliConfig)),\n importMeta,\n })\n\n const {\n compactHeader: compactHeaderFlag,\n help: helpFlag,\n org: orgFlag,\n spinner: spinnerFlag,\n version: versionFlag,\n } = cli.flags as {\n compactHeader: boolean\n help: boolean\n org: string\n spinner: boolean\n version: boolean | undefined\n }\n\n const compactMode =\n compactHeaderFlag || (constants.ENV.CI && !constants.ENV.VITEST)\n const noSpinner = spinnerFlag === false || isDebug()\n\n // Use CI spinner style when --no-spinner is passed.\n // This prevents the spinner from interfering with debug output.\n if (noSpinner) {\n constants.spinner.spinner = getCliSpinners('ci')!\n }\n\n if (!shouldSuppressBanner(cli.flags)) {\n emitBanner(command, orgFlag, compactMode)\n // Add newline in stderr.\n // Meow help adds a newline too so we do it here.\n logger.error('')\n }\n\n // As per https://github.com/sindresorhus/meow/issues/178\n // Setting `allowUnknownFlags: false` makes it reject camel cased flags.\n // if (!allowUnknownFlags) {\n // // Run meow specifically with the flag setting. It will exit(2) if an\n // // invalid flag is set and print a message.\n // meow({\n // argv,\n // allowUnknownFlags: false,\n // // Prevent meow from potentially exiting early.\n // autoHelp: false,\n // autoVersion: false,\n // description: config.description,\n // flags: config.flags,\n // help: trimNewlines(config.help(command, config)),\n // importMeta,\n // })\n // }\n\n if (helpFlag) {\n cli.showHelp(0)\n }\n\n // Meow doesn't detect 'version' as an unknown flag, so we do the leg work here.\n if (versionFlag && !hasOwn(cliConfig.flags, 'version')) {\n // Use `console.error` here instead of `logger.error` to match Meow behavior.\n console.error('Unknown flag\\n--version')\n // eslint-disable-next-line n/no-process-exit\n process.exit(2)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n }\n\n // Now test for help state. Run Meow again. If it exits now, it must be due\n // to wanting to print the help screen. But it would exit(0) and we want a\n // consistent exit(2) for that case (missing input).\n process.exitCode = 2\n meow({\n argv,\n // As per https://github.com/sindresorhus/meow/issues/178\n // Setting `allowUnknownFlags: false` makes it reject camel cased flags.\n allowUnknownFlags: Boolean(allowUnknownFlags),\n // Prevent meow from potentially exiting early.\n autoHelp: false,\n autoVersion: false,\n description: cliConfig.description,\n help: trimNewlines(cliConfig.help(command, cliConfig)),\n importMeta,\n flags: cliConfig.flags,\n })\n // Ok, no help, reset to default.\n process.exitCode = 0\n\n return cli\n}\n","export function msAtHome(isoTimeStamp: string): string {\n const timeStart = Date.parse(isoTimeStamp)\n const timeEnd = Date.now()\n\n const rtf = new Intl.RelativeTimeFormat('en', {\n numeric: 'always',\n style: 'short',\n })\n\n const delta = timeEnd - timeStart\n if (delta < 60 * 60 * 1000) {\n return rtf.format(-Math.round(delta / (60 * 1000)), 'minute')\n // return Math.round(delta / (60 * 1000)) + ' min ago'\n } else if (delta < 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (60 * 60 * 1000)).toFixed(1), 'hour')\n // return (delta / (60 * 60 * 1000)).toFixed(1) + ' hr ago'\n } else if (delta < 7 * 24 * 60 * 60 * 1000) {\n return rtf.format(-(delta / (24 * 60 * 60 * 1000)).toFixed(1), 'day')\n // return (delta / (24 * 60 * 60 * 1000)).toFixed(1) + ' day ago'\n } else {\n return isoTimeStamp.slice(0, 10)\n }\n}\n","import { handleApiCall } from '../../utils/api.mts'\nimport { setupSdk } from '../../utils/sdk.mts'\n\nimport type { CResult } from '../../types.mts'\nimport type { SetupSdkOptions } from '../../utils/sdk.mts'\nimport type { SocketSdk, SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport type FetchOrganizationOptions = {\n description?: string | undefined\n sdk?: SocketSdk | undefined\n sdkOpts?: SetupSdkOptions | undefined\n}\n\nexport type EnterpriseOrganization = Omit<Organization, 'plan'> & {\n plan: `enterprise${string}`\n}\n\nexport type EnterpriseOrganizations = EnterpriseOrganization[]\n\nexport type Organization =\n SocketSdkSuccessResult<'getOrganizations'>['data']['organizations'][string]\n\nexport type Organizations = Organization[]\n\nexport type OrganizationsData = { organizations: Organizations }\n\nexport type OrganizationsCResult = CResult<OrganizationsData>\n\nexport async function fetchOrganization(\n options?: FetchOrganizationOptions | undefined,\n): Promise<OrganizationsCResult> {\n const {\n description = 'organization list',\n sdk,\n sdkOpts,\n } = {\n __proto__: null,\n ...options,\n } as FetchOrganizationOptions\n\n let sockSdk = sdk\n if (!sockSdk) {\n const sockSdkCResult = await setupSdk(sdkOpts)\n if (!sockSdkCResult.ok) {\n return sockSdkCResult\n }\n sockSdk = sockSdkCResult.data\n }\n\n const orgsCResult = await handleApiCall(sockSdk.getOrganizations(), {\n description,\n })\n if (!orgsCResult.ok) {\n return orgsCResult\n }\n\n return {\n ...orgsCResult,\n data: {\n organizations: Object.values(orgsCResult.data.organizations),\n },\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nexport async function suggestOrgSlug(): Promise<string | void> {\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n logger.fail(\n 'Failed to lookup organization list from API, unable to suggest',\n )\n return undefined\n }\n\n // Ignore a failed request here. It was not the primary goal of\n // running this command and reporting it only leads to end-user confusion.\n const { organizations } = orgsCResult.data\n const proceed = await select<string>({\n message:\n 'Missing org name; do you want to use any of these orgs for this scan?',\n choices: [\n ...organizations.map(o => {\n const name = o.name ?? o.slug\n return {\n name: `Yes [${name}]`,\n value: name,\n description: `Use \"${name}\" as the organization`,\n }\n }),\n {\n name: 'No',\n value: '',\n description:\n 'Do not use any of these organizations (will end in a no-op)',\n },\n ],\n })\n\n if (proceed) {\n return proceed\n }\n return undefined\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\nimport { select } from '@socketsecurity/registry/lib/prompts'\n\nimport { getConfigValue, updateConfigValue } from '../../utils/config.mts'\n\nexport async function suggestToPersistOrgSlug(orgSlug: string): Promise<void> {\n const skipAsk = getConfigValue('skipAskToPersistDefaultOrg')\n if (!skipAsk.ok || skipAsk.data) {\n // Don't ask to store it when disabled before, or when reading config fails.\n return\n }\n\n const result = await select<string>({\n message: `Would you like to use this org (${orgSlug}) as the default org for future calls?`,\n choices: [\n {\n name: 'Yes',\n value: 'yes',\n description: 'Stores it in your config',\n },\n {\n name: 'No',\n value: 'no',\n description: 'Do not persist this org as default org',\n },\n {\n name: \"No and don't ask again\",\n value: 'sush',\n description:\n 'Do not store as default org and do not ask again to persist it',\n },\n ],\n })\n if (result === 'yes') {\n const updateResult = updateConfigValue('defaultOrg', orgSlug)\n if (updateResult.ok) {\n logger.success('Updated default org config to:', orgSlug)\n } else {\n logger.fail(\n '(Non blocking) Failed to update default org in config:',\n updateResult.cause,\n )\n }\n } else if (result === 'sush') {\n const updateResult = updateConfigValue('skipAskToPersistDefaultOrg', true)\n if (updateResult.ok) {\n logger.info('Default org not changed. Will not ask to persist again.')\n } else {\n logger.fail(\n `(Non blocking) Failed to store preference; will ask to persist again next time. Reason: ${updateResult.cause}`,\n )\n }\n }\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport {\n CONFIG_KEY_DEFAULT_ORG,\n V1_MIGRATION_GUIDE_URL,\n} from '../constants.mts'\nimport { getConfigValueOrUndef } from './config.mts'\nimport { webLink } from './terminal-link.mts'\nimport { suggestOrgSlug } from '../commands/scan/suggest-org-slug.mts'\nimport { suggestToPersistOrgSlug } from '../commands/scan/suggest-to-persist-orgslug.mts'\n\nexport async function determineOrgSlug(\n orgFlag: string,\n interactive: boolean,\n dryRun: boolean,\n): Promise<[string, string | undefined]> {\n const defaultOrgSlug = getConfigValueOrUndef(CONFIG_KEY_DEFAULT_ORG)\n let orgSlug = String(orgFlag || defaultOrgSlug || '')\n if (!orgSlug) {\n if (!interactive) {\n logger.warn(\n 'Note: This command requires an org slug because the Socket API endpoint does.',\n )\n logger.warn('')\n logger.warn(\n 'It seems no default org was setup and the `--org` flag was not used.',\n )\n logger.warn(\n \"Additionally, `--no-interactive` was set so we can't ask for it.\",\n )\n logger.warn(\n 'Since v1.0.0 the org _argument_ for all commands was dropped in favor of an',\n )\n logger.warn(\n 'implicit default org setting, which will be setup when you run `socket login`.',\n )\n logger.warn('')\n logger.warn(\n 'Note: When running in CI, you probably want to set the `--org` flag.',\n )\n logger.warn('')\n logger.warn(\n `For details, see the ${webLink(V1_MIGRATION_GUIDE_URL, 'v1 migration guide')}`,\n )\n logger.warn('')\n logger.warn(\n 'This command will exit now because the org slug is required to proceed.',\n )\n return ['', undefined]\n }\n\n logger.warn(\n 'Unable to determine the target org. Trying to auto-discover it now...',\n )\n logger.info('Note: Run `socket login` to set a default org.')\n logger.error(' Use the --org flag to override the default org.')\n logger.error('')\n if (dryRun) {\n logger.fail('Skipping auto-discovery of org in dry-run mode')\n } else {\n orgSlug = (await suggestOrgSlug()) || ''\n if (orgSlug) {\n await suggestToPersistOrgSlug(orgSlug)\n }\n }\n }\n\n return [orgSlug, defaultOrgSlug]\n}\n","import { debugFn } from '@socketsecurity/registry/lib/debug'\n\nimport constants from '../../constants.mts'\nimport { getConfigValueOrUndef } from '../../utils/config.mts'\nimport { fetchOrganization } from '../organization/fetch-organization-list.mts'\n\nimport type { CResult } from '../../types.mts'\n\n// Use the config defaultOrg when set, otherwise discover from remote.\nexport async function getDefaultOrgSlug(): Promise<CResult<string>> {\n const defaultOrgResult = getConfigValueOrUndef('defaultOrg')\n if (defaultOrgResult) {\n debugFn(\n 'notice',\n 'use: org from \"defaultOrg\" value of socket/settings local app data',\n defaultOrgResult,\n )\n return { ok: true, data: defaultOrgResult }\n }\n\n const envOrgSlug = constants.ENV.SOCKET_CLI_ORG_SLUG\n if (envOrgSlug) {\n debugFn(\n 'notice',\n 'use: org from SOCKET_CLI_ORG_SLUG environment variable',\n envOrgSlug,\n )\n return { ok: true, data: envOrgSlug }\n }\n\n const orgsCResult = await fetchOrganization()\n if (!orgsCResult.ok) {\n return orgsCResult\n }\n\n const { organizations } = orgsCResult.data\n const keys = Object.keys(organizations)\n if (!keys.length) {\n return {\n ok: false,\n message: 'Failed to establish identity',\n data: `No organization associated with the Socket API token. Unable to continue.`,\n }\n }\n\n const slug = (organizations as any)[keys[0]!]?.name ?? undefined\n if (!slug) {\n return {\n ok: false,\n message: 'Failed to establish identity',\n data: `Cannot determine the default organization for the API token. Unable to continue.`,\n }\n }\n\n debugFn('notice', 'resolve: org from Socket API', slug)\n\n return {\n ok: true,\n message: 'Retrieved default org from server',\n data: slug,\n }\n}\n","import constants from '../constants.mts'\n\n/**\n * Sanitizes a name to comply with repository naming constraints.\n * Constraints: 100 or less A-Za-z0-9 characters only with non-repeating,\n * non-leading or trailing ., _ or - only.\n *\n * @param name - The name to sanitize\n * @returns Sanitized name that complies with repository naming rules, or empty string if no valid characters\n */\nfunction sanitizeName(name: string): string {\n if (!name) {\n return ''\n }\n\n // Replace sequences of illegal characters with underscores.\n const sanitized = name\n // Replace any sequence of non-alphanumeric characters (except ., _, -) with underscore.\n .replace(/[^A-Za-z0-9._-]+/g, '_')\n // Replace sequences of multiple allowed special chars with single underscore.\n .replace(/[._-]{2,}/g, '_')\n // Remove leading special characters.\n .replace(/^[._-]+/, '')\n // Remove trailing special characters.\n .replace(/[._-]+$/, '')\n // Truncate to 100 characters max.\n .slice(0, 100)\n\n return sanitized\n}\n\n/**\n * Extracts and sanitizes a repository name.\n *\n * @param name - The repository name to extract and sanitize\n * @returns Sanitized repository name, or default repository name if empty\n */\nexport function extractName(name: string): string {\n const sanitized = sanitizeName(name)\n return sanitized || constants.SOCKET_DEFAULT_REPOSITORY\n}\n\n/**\n * Extracts and sanitizes a repository owner name.\n *\n * @param owner - The repository owner name to extract and sanitize\n * @returns Sanitized repository owner name, or undefined if input is empty\n */\nexport function extractOwner(owner: string): string | undefined {\n if (!owner) {\n return undefined\n }\n const sanitized = sanitizeName(owner)\n return sanitized || undefined\n}\n","/**\n * Git utilities for Socket CLI.\n * Provides git operations for repository management, branch handling, and commits.\n *\n * Branch Operations:\n * - gitCheckoutBranch: Switch to branch\n * - gitCreateBranch: Create new local branch\n * - gitDeleteBranch: Delete local branch\n * - gitDeleteRemoteBranch: Delete remote branch\n * - gitPushBranch: Push branch to remote with --force\n *\n * Commit Operations:\n * - gitCleanFdx: Remove untracked files\n * - gitCommit: Stage files and create commit\n * - gitEnsureIdentity: Configure git user.name/email\n * - gitResetHard: Reset to branch/commit\n *\n * Remote URL Parsing:\n * - parseGitRemoteUrl: Extract owner/repo from SSH or HTTPS URLs\n *\n * Repository Information:\n * - detectDefaultBranch: Find default branch (main/master/develop/etc)\n * - getBaseBranch: Determine base branch (respects GitHub Actions env)\n * - getRepoInfo: Extract owner/repo from git remote URL\n * - gitBranch: Get current branch or commit hash\n */\n\nimport { debugDir, debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\nimport { isSpawnError, spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants, { FLAG_QUIET } from '../constants.mts'\nimport { debugGit } from './debug.mts'\nimport { extractName, extractOwner } from './extract-names.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { SpawnOptions } from '@socketsecurity/registry/lib/spawn'\n\n// Listed in order of check preference.\nconst COMMON_DEFAULT_BRANCH_NAMES = [\n // Modern default (GitHub, GitLab, Bitbucket have switched to this).\n 'main',\n // Historic default in Git (pre-2020, still used in many repos).\n 'master',\n // Common in Git Flow workflows (main for stable, develop for ongoing work).\n 'develop',\n // Used by teams adopting trunk-based development practices.\n 'trunk',\n // Used in some older enterprise setups and tools.\n 'default',\n]\n\nexport async function getBaseBranch(cwd = process.cwd()): Promise<string> {\n const { GITHUB_BASE_REF, GITHUB_REF_NAME, GITHUB_REF_TYPE } = constants.ENV\n // 1. In a pull request, this is always the base branch.\n if (GITHUB_BASE_REF) {\n return GITHUB_BASE_REF\n }\n // 2. If it's a branch (not a tag), GITHUB_REF_TYPE should be 'branch'.\n if (GITHUB_REF_TYPE === 'branch' && GITHUB_REF_NAME) {\n return GITHUB_REF_NAME\n }\n // 3. Try to resolve the default remote branch using 'git remote show origin'.\n // This handles detached HEADs or workflows triggered by tags/releases.\n try {\n const originDetails = (\n await spawn('git', ['remote', 'show', 'origin'], { cwd })\n ).stdout\n\n const match = /(?<=HEAD branch: ).+/.exec(originDetails)\n if (match?.[0]) {\n return match[0].trim()\n }\n } catch {}\n // GitHub and GitLab default to branch name \"main\"\n // https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches#about-the-default-branch\n return 'main'\n}\n\nexport type RepoInfo = {\n owner: string\n repo: string\n}\n\nexport async function getRepoInfo(\n cwd = process.cwd(),\n): Promise<RepoInfo | undefined> {\n let info\n try {\n const remoteUrl = (\n await spawn('git', ['remote', 'get-url', 'origin'], { cwd })\n ).stdout\n info = parseGitRemoteUrl(remoteUrl)\n if (!info) {\n debugFn('warn', `Unmatched git remote URL format: ${remoteUrl}`)\n debugDir('warn', { remoteUrl })\n }\n } catch (e) {\n // Expected failure when not in a git repo.\n debugDir('inspect', { message: 'git remote get-url failed', error: e })\n }\n return info\n}\n\nexport async function getRepoName(cwd = process.cwd()): Promise<string> {\n const repoInfo = await getRepoInfo(cwd)\n return repoInfo?.repo\n ? extractName(repoInfo.repo)\n : constants.SOCKET_DEFAULT_REPOSITORY\n}\n\nexport async function getRepoOwner(\n cwd = process.cwd(),\n): Promise<string | undefined> {\n const repoInfo = await getRepoInfo(cwd)\n return repoInfo?.owner ? extractOwner(repoInfo.owner) : undefined\n}\n\nexport async function gitBranch(\n cwd = process.cwd(),\n): Promise<string | undefined> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n // Try symbolic-ref first which returns the branch name or fails in a\n // detached HEAD state.\n try {\n const gitSymbolicRefResult = await spawn(\n 'git',\n ['symbolic-ref', '--short', 'HEAD'],\n stdioPipeOptions,\n )\n return gitSymbolicRefResult.stdout\n } catch (e) {\n // Expected in detached HEAD state, fallback to rev-parse.\n debugDir('inspect', { message: 'In detached HEAD state', error: e })\n }\n // Fallback to using rev-parse to get the short commit hash in a\n // detached HEAD state.\n try {\n const gitRevParseResult = await spawn(\n 'git',\n ['rev-parse', '--short', 'HEAD'],\n stdioPipeOptions,\n )\n return gitRevParseResult.stdout\n } catch (e) {\n // Both methods failed, likely not in a git repo.\n debugDir('inspect', { message: 'Unable to determine git branch', error: e })\n }\n return undefined\n}\n\n/**\n * Try to detect the default branch name by checking common patterns.\n * Returns the first branch that exists in the repository.\n */\nexport async function detectDefaultBranch(\n cwd = process.cwd(),\n): Promise<string> {\n // First pass: check all local branches\n for (const branch of COMMON_DEFAULT_BRANCH_NAMES) {\n // eslint-disable-next-line no-await-in-loop\n if (await gitLocalBranchExists(branch, cwd)) {\n return branch\n }\n }\n // Second pass: check remote branches only if no local branch found\n for (const branch of COMMON_DEFAULT_BRANCH_NAMES) {\n // eslint-disable-next-line no-await-in-loop\n if (await gitRemoteBranchExists(branch, cwd)) {\n return branch\n }\n }\n return constants.SOCKET_DEFAULT_BRANCH\n}\n\nexport type GitCreateAndPushBranchOptions = {\n cwd?: string | undefined\n email?: string | undefined\n user?: string | undefined\n}\n\nexport async function gitCleanFdx(cwd = process.cwd()): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['clean', '-fdx'], stdioIgnoreOptions)\n debugGit('clean -fdx', true)\n return true\n } catch (e) {\n debugGit('clean -fdx', false, { error: e })\n }\n return false\n}\n\nexport async function gitCheckoutBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['checkout', branch], stdioIgnoreOptions)\n debugGit(`checkout ${branch}`, true)\n return true\n } catch (e) {\n debugGit(`checkout ${branch}`, false, { error: e })\n }\n return false\n}\n\nexport async function gitCreateBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n if (await gitLocalBranchExists(branch)) {\n return true\n }\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['branch', branch], stdioIgnoreOptions)\n debugGit(`branch ${branch}`, true)\n return true\n } catch (e) {\n debugGit(`branch ${branch}`, false, { error: e })\n }\n return false\n}\n\nexport async function gitPushBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn(\n 'git',\n ['push', '--force', '--set-upstream', 'origin', branch],\n stdioIgnoreOptions,\n )\n debugGit(`push ${branch}`, true)\n return true\n } catch (e) {\n if (isSpawnError(e) && e.code === 128) {\n debugFn(\n 'error',\n \"Push denied: token requires write permissions for 'contents' and 'pull-requests'\",\n )\n debugDir('error', e)\n debugDir('inspect', { branch })\n } else {\n debugGit(`push ${branch}`, false, { error: e })\n }\n }\n return false\n}\n\nexport async function gitCommit(\n commitMsg: string,\n filepaths: string[],\n options?: GitCreateAndPushBranchOptions | undefined,\n): Promise<boolean> {\n if (!filepaths.length) {\n debugFn('notice', `miss: no filepaths to add`)\n return false\n }\n const {\n cwd = process.cwd(),\n email = constants.ENV.SOCKET_CLI_GIT_USER_EMAIL,\n user = constants.ENV.SOCKET_CLI_GIT_USER_NAME,\n } = { __proto__: null, ...options } as GitCreateAndPushBranchOptions\n\n await gitEnsureIdentity(user, email, cwd)\n\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['add', ...filepaths], stdioIgnoreOptions)\n debugGit('add', true, { count: filepaths.length })\n } catch (e) {\n debugGit('add', false, { error: e })\n debugDir('inspect', { filepaths })\n return false\n }\n\n try {\n await spawn('git', ['commit', '-m', commitMsg], stdioIgnoreOptions)\n debugGit('commit', true)\n return true\n } catch (e) {\n debugGit('commit', false, { error: e })\n debugDir('inspect', { commitMsg })\n }\n return false\n}\n\nexport async function gitDeleteBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n // Will throw with exit code 1 if branch does not exist.\n await spawn('git', ['branch', '-D', branch], stdioIgnoreOptions)\n return true\n } catch (e) {\n // Expected failure when branch doesn't exist.\n debugDir('inspect', {\n message: `Branch deletion failed (may not exist): ${branch}`,\n error: e,\n })\n }\n return false\n}\n\nexport async function gitDeleteRemoteBranch(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n // Will throw with exit code 1 if branch does not exist.\n await spawn(\n 'git',\n ['push', 'origin', '--delete', branch],\n stdioIgnoreOptions,\n )\n return true\n } catch (e) {\n // Expected failure when remote branch doesn't exist.\n debugDir('inspect', {\n message: `Remote branch deletion failed (may not exist): ${branch}`,\n error: e,\n })\n }\n return false\n}\n\nexport async function gitEnsureIdentity(\n name: string,\n email: string,\n cwd = process.cwd(),\n): Promise<void> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n const identEntries: Array<[string, string]> = [\n ['user.email', email],\n ['user.name', name],\n ]\n await Promise.all(\n identEntries.map(async ({ 0: prop, 1: value }) => {\n let configValue\n try {\n // Will throw with exit code 1 if the config property is not set.\n const gitConfigResult = await spawn(\n 'git',\n ['config', '--get', prop],\n stdioPipeOptions,\n )\n configValue = gitConfigResult.stdout\n } catch (e) {\n // Expected when config property is not set.\n debugDir('inspect', {\n message: `Git config property not set: ${prop}`,\n error: e,\n })\n }\n if (configValue !== value) {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['config', prop, value], stdioIgnoreOptions)\n } catch (e) {\n debugFn('warn', `Failed to set git config: ${prop}`)\n debugDir('warn', e)\n debugDir('inspect', { value })\n }\n }\n }),\n )\n}\n\nexport async function gitLocalBranchExists(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n // Will throw with exit code 1 if the branch does not exist.\n await spawn(\n 'git',\n ['show-ref', FLAG_QUIET, `refs/heads/${branch}`],\n stdioIgnoreOptions,\n )\n return true\n } catch {\n // Expected when branch doesn't exist - no logging needed.\n }\n return false\n}\n\nexport async function gitRemoteBranchExists(\n branch: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n try {\n const lsRemoteResult = await spawn(\n 'git',\n ['ls-remote', '--heads', 'origin', branch],\n stdioPipeOptions,\n )\n return lsRemoteResult.stdout.length > 0\n } catch (e) {\n // Expected when remote is not accessible or branch doesn't exist.\n debugDir('inspect', {\n message: `Remote branch check failed: ${branch}`,\n error: e,\n })\n }\n return false\n}\n\nexport async function gitResetAndClean(\n branch = 'HEAD',\n cwd = process.cwd(),\n): Promise<void> {\n // Discards tracked changes.\n await gitResetHard(branch, cwd)\n // Deletes all untracked files and directories.\n await gitCleanFdx(cwd)\n}\n\nexport async function gitResetHard(\n branch = 'HEAD',\n cwd = process.cwd(),\n): Promise<boolean> {\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n try {\n await spawn('git', ['reset', '--hard', branch], stdioIgnoreOptions)\n debugGit(`reset --hard ${branch}`, true)\n return true\n } catch (e) {\n debugGit(`reset --hard ${branch}`, false, { error: e })\n }\n return false\n}\n\nexport async function gitUnstagedModifiedFiles(\n cwd = process.cwd(),\n): Promise<CResult<string[]>> {\n const stdioPipeOptions: SpawnOptions = { cwd }\n try {\n const gitDiffResult = await spawn(\n 'git',\n ['diff', '--name-only'],\n stdioPipeOptions,\n )\n const changedFilesDetails = gitDiffResult.stdout\n const relPaths = changedFilesDetails.split('\\n')\n return {\n ok: true,\n data: relPaths.map(p => normalizePath(p)),\n }\n } catch (e) {\n debugFn('error', 'Failed to get unstaged modified files')\n debugDir('error', e)\n return {\n ok: false,\n message: 'Git Error',\n cause: 'Unexpected error while trying to ask git whether repo is dirty',\n }\n }\n}\n\nconst parsedGitRemoteUrlCache = new Map<string, RepoInfo | undefined>()\n\nexport function parseGitRemoteUrl(remoteUrl: string): RepoInfo | undefined {\n let result = parsedGitRemoteUrlCache.get(remoteUrl)\n if (result) {\n return { ...result }\n }\n // Handle SSH-style\n const sshMatch = /^git@[^:]+:([^/]+)\\/(.+?)(?:\\.git)?$/.exec(remoteUrl)\n // 1. Handle SSH-style, e.g. git@github.com:owner/repo.git\n if (sshMatch) {\n result = { owner: sshMatch[1]!, repo: sshMatch[2]! }\n } else {\n // 2. Handle HTTPS/URL-style, e.g. https://github.com/owner/repo.git\n try {\n const parsed = new URL(remoteUrl)\n // Remove leading slashes from pathname and split by \"/\" to extract segments.\n const segments = parsed.pathname.replace(/^\\/+/, '').split('/')\n // The second-to-last segment is expected to be the owner (e.g., \"owner\" in /owner/repo.git).\n const owner = segments.at(-2)\n // The last segment is expected to be the repo name, so we remove the \".git\" suffix if present.\n const repo = segments.at(-1)?.replace(/\\.git$/, '')\n if (owner && repo) {\n result = { owner, repo }\n }\n } catch {}\n }\n parsedGitRemoteUrlCache.set(remoteUrl, result)\n return result ? { ...result } : result\n}\n","/**\n * Package URL (PURL) utilities for Socket CLI.\n * Implements the PURL specification for universal package identification.\n *\n * PURL Format:\n * pkg:type/namespace/name@version?qualifiers#subpath\n *\n * Key Functions:\n * - createPurlObject: Create PURL from components\n * - isPurl: Check if string is valid PURL\n * - normalizePurl: Normalize PURL format\n * - parsePurl: Parse PURL string to object\n * - purlToString: Convert PURL object to string\n *\n * Supported Types:\n * - cargo: Rust packages\n * - gem: Ruby packages\n * - go: Go modules\n * - maven: Java packages\n * - npm: Node.js packages\n * - pypi: Python packages\n *\n * See: https://github.com/package-url/purl-spec\n */\n\nimport { PackageURL, type PurlQualifiers } from '@socketregistry/packageurl-js'\nimport { isObjectObject } from '@socketsecurity/registry/lib/objects'\n\nimport type { SocketArtifact } from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\n\nexport type PurlObject<T> = T & { type: PURL_Type }\n\nexport type PurlLike = string | PackageURL | SocketArtifact\n\nexport type CreatePurlObjectOptions = {\n type?: string | undefined\n namespace?: string | undefined\n name?: string | undefined\n version?: string | undefined\n qualifiers?: PurlQualifiers | undefined\n subpath?: string | undefined\n throws?: boolean | undefined\n}\n\nexport type CreatePurlOptionsWithThrows = CreatePurlObjectOptions & {\n throws?: true | undefined\n}\n\nexport type CreatePurlOptionsNoThrows = CreatePurlObjectOptions & {\n throws: false\n}\n\nexport function createPurlObject(\n options: CreatePurlOptionsWithThrows,\n): PurlObject<PackageURL>\nexport function createPurlObject(\n options: CreatePurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function createPurlObject(\n type: string | CreatePurlObjectOptions,\n options?: CreatePurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function createPurlObject(\n type: string | CreatePurlObjectOptions,\n options: CreatePurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function createPurlObject(\n type: string | CreatePurlObjectOptions,\n options?: CreatePurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function createPurlObject(\n type: string,\n name: string,\n options: CreatePurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function createPurlObject(\n type: string,\n name: string,\n options?: CreatePurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function createPurlObject(\n type: string | CreatePurlObjectOptions,\n name?: string | CreatePurlObjectOptions | undefined,\n options?: CreatePurlObjectOptions | undefined,\n): PurlObject<PackageURL> | undefined {\n let opts: CreatePurlObjectOptions | undefined\n if (isObjectObject(type)) {\n opts = { __proto__: null, ...type } as CreatePurlObjectOptions\n type = opts.type as string\n name = opts.name as string\n } else if (isObjectObject(name)) {\n opts = { __proto__: null, ...name } as CreatePurlObjectOptions\n name = opts.name as string\n } else {\n opts = { __proto__: null, ...options } as CreatePurlObjectOptions\n if (typeof name !== 'string') {\n name = opts.name as string\n }\n }\n const { namespace, qualifiers, subpath, throws, version } = opts\n const shouldThrow = throws === undefined || !!throws\n try {\n return new PackageURL(\n type,\n namespace,\n name,\n version,\n qualifiers,\n subpath,\n ) as PurlObject<PackageURL>\n } catch (e) {\n if (shouldThrow) {\n throw e\n }\n }\n return undefined\n}\n\nexport type PurlObjectOptions = {\n throws?: boolean | undefined\n}\n\nexport type PurlOptionsWithThrows = PurlObjectOptions & {\n throws?: true | undefined\n}\n\nexport type PurlOptionsNoThrows = PurlObjectOptions & { throws: false }\n\nexport function getPurlObject(\n purl: string,\n options?: PurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function getPurlObject(\n purl: string,\n options: PurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function getPurlObject(\n purl: PackageURL,\n options?: PurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL>\nexport function getPurlObject(\n purl: PackageURL,\n options: PurlOptionsNoThrows,\n): PurlObject<PackageURL> | undefined\nexport function getPurlObject(\n purl: SocketArtifact,\n options?: PurlOptionsWithThrows | undefined,\n): PurlObject<SocketArtifact>\nexport function getPurlObject(\n purl: SocketArtifact,\n options: PurlOptionsNoThrows,\n): PurlObject<SocketArtifact> | undefined\nexport function getPurlObject(\n purl: PurlLike,\n options?: PurlOptionsWithThrows | undefined,\n): PurlObject<PackageURL | SocketArtifact>\nexport function getPurlObject(\n purl: PurlLike,\n options?: PurlObjectOptions | undefined,\n): PurlObject<PackageURL | SocketArtifact> | undefined {\n const { throws } = { __proto__: null, ...options } as PurlObjectOptions\n const shouldThrow = throws === undefined || !!throws\n try {\n return typeof purl === 'string'\n ? (PackageURL.fromString(normalizePurl(purl)) as PurlObject<PackageURL>)\n : (purl as PurlObject<PackageURL | SocketArtifact>)\n } catch (e) {\n if (shouldThrow) {\n throw e\n }\n return undefined\n }\n}\n\nexport function normalizePurl(rawPurl: string): string {\n return rawPurl.startsWith('pkg:') ? rawPurl : `pkg:${rawPurl}`\n}\n","/**\n * Socket.dev URL utilities for Socket CLI.\n * Generates URLs for Socket.dev website features and resources.\n *\n * Key Functions:\n * - getPkgFullNameFromPurl: Extract full package name from PURL\n * - getSocketDevAlertUrl: Generate alert type documentation URL\n * - getSocketDevPackageOverviewUrl: Generate package overview URL\n * - getSocketDevPackageOverviewUrlFromPurl: Generate overview URL from PURL\n * - getSocketDevPackageUrl: Generate package detail URL\n * - getSocketDevPackageUrlFromPurl: Generate package URL from PURL\n * - getSocketDevReportUrl: Generate scan report URL\n *\n * URL Generation:\n * - Package overview and detail pages\n * - Security alert documentation\n * - Scan report links\n * - Ecosystem-specific URL formatting\n */\n\nimport constants from '../constants.mts'\nimport { getPurlObject } from './purl.mts'\n\nimport type { SocketArtifact } from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\nimport type { PackageURL } from '@socketregistry/packageurl-js'\n\nexport function getPkgFullNameFromPurl(\n purl: string | PackageURL | SocketArtifact,\n): string {\n const purlObj = getPurlObject(purl)\n const { name, namespace } = purlObj\n return namespace\n ? `${namespace}${purlObj.type === 'maven' ? ':' : '/'}${name}`\n : name!\n}\n\nexport function getSocketDevAlertUrl(alertType: string): string {\n return `${constants.SOCKET_WEBSITE_URL}/alerts/${alertType}`\n}\n\nexport function getSocketDevPackageOverviewUrlFromPurl(\n purl: string | PackageURL | SocketArtifact,\n): string {\n const purlObj = getPurlObject(purl)\n const fullName = getPkgFullNameFromPurl(purlObj)\n return getSocketDevPackageOverviewUrl(purlObj.type, fullName, purlObj.version)\n}\n\nexport function getSocketDevPackageOverviewUrl(\n ecosystem: PURL_Type,\n fullName: string,\n version?: string | undefined,\n): string {\n const url = `${constants.SOCKET_WEBSITE_URL}/${ecosystem}/package/${fullName}`\n return ecosystem === 'golang'\n ? `${url}${version ? `?section=overview&version=${version}` : ''}`\n : `${url}${version ? `/overview/${version}` : ''}`\n}\n","interface NestedRecord<T> {\n [key: string]: T | NestedRecord<T>\n}\n\n/**\n * Convert a Map<string, Map|string> to a nested object of similar shape.\n * The goal is to serialize it with JSON.stringify, which Map can't do.\n */\nexport function mapToObject<T>(\n map: Map<string, T | Map<string, T | Map<string, T>>>,\n): NestedRecord<T> {\n return Object.fromEntries(\n Array.from(map.entries()).map(([k, v]) => [\n k,\n v instanceof Map ? mapToObject(v) : v,\n ]),\n )\n}\n","type NestedMap<T> = Map<string, T | NestedMap<T>>\n\nexport function* walkNestedMap<T>(\n map: NestedMap<T>,\n keys: string[] = [],\n): Generator<{ keys: string[]; value: T }> {\n for (const { 0: key, 1: value } of map.entries()) {\n if (value instanceof Map) {\n yield* walkNestedMap(value as NestedMap<T>, [...keys, key])\n } else {\n yield { keys: [...keys, key], value: value }\n }\n }\n}\n","/**\n * Coana integration utilities for Socket CLI.\n * Manages reachability analysis via Coana tech CLI.\n *\n * Key Functions:\n * - extractTier1ReachabilityScanId: Extract scan ID from socket facts file\n *\n * Integration:\n * - Works with @coana-tech/cli for reachability analysis\n * - Processes socket facts JSON files\n * - Extracts tier 1 reachability scan identifiers\n */\n\nimport { readJsonSync } from '@socketsecurity/registry/lib/fs'\n\nexport function extractTier1ReachabilityScanId(\n socketFactsFile: string,\n): string | undefined {\n const json = readJsonSync(socketFactsFile, { throws: false })\n const tier1ReachabilityScanId = String(\n json?.['tier1ReachabilityScanId'] ?? '',\n ).trim()\n return tier1ReachabilityScanId.length > 0\n ? tier1ReachabilityScanId\n : undefined\n}\n","/**\n * File system utilities for Socket CLI.\n * Provides file and directory search functionality.\n *\n * Key Functions:\n * - findUp: Search for files/directories up the directory tree\n *\n * Features:\n * - Upward directory traversal\n * - Supports file and directory searching\n * - Abort signal support for cancellation\n * - Multiple name search support\n *\n * Usage:\n * - Finding configuration files (package.json, lockfiles)\n * - Locating project root directories\n * - Searching for specific files in parent directories\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nexport type FindUpOptions = {\n cwd?: string | undefined\n onlyDirectories?: boolean | undefined\n onlyFiles?: boolean | undefined\n signal?: AbortSignal | undefined\n}\n\nexport async function findUp(\n name: string | string[],\n options?: FindUpOptions | undefined,\n): Promise<string | undefined> {\n const opts = { __proto__: null, ...options }\n const { cwd = process.cwd(), signal = constants.abortSignal } = opts\n let { onlyDirectories = false, onlyFiles = true } = opts\n if (onlyDirectories) {\n onlyFiles = false\n }\n if (onlyFiles) {\n onlyDirectories = false\n }\n let dir = path.resolve(cwd)\n const { root } = path.parse(dir)\n const names = [name].flat()\n while (dir && dir !== root) {\n for (const name of names) {\n if (signal?.aborted) {\n return undefined\n }\n const thePath = path.join(dir, name)\n try {\n // eslint-disable-next-line no-await-in-loop\n const stats = await fs.stat(thePath)\n if (!onlyDirectories && stats.isFile()) {\n return thePath\n }\n if (!onlyFiles && stats.isDirectory()) {\n return thePath\n }\n } catch {}\n }\n dir = path.dirname(dir)\n }\n return undefined\n}\n","import path from 'node:path'\n\nimport fastGlob from 'fast-glob'\nimport ignore from 'ignore'\nimport micromatch from 'micromatch'\nimport { parse as yamlParse } from 'yaml'\n\nimport { isDirSync, safeReadFile } from '@socketsecurity/registry/lib/fs'\nimport { defaultIgnore } from '@socketsecurity/registry/lib/globs'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { transform } from '@socketsecurity/registry/lib/streams'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { NODE_MODULES, PNPM } from '../constants.mts'\n\nimport type { Agent } from './package-environment.mts'\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\nimport type { Options as GlobOptions } from 'fast-glob'\n\nconst DEFAULT_IGNORE_FOR_GIT_IGNORE = defaultIgnore.filter(\n p => !p.endsWith('.gitignore'),\n)\n\nconst IGNORED_DIRS = [\n // Taken from ignore-by-default:\n // https://github.com/novemberborn/ignore-by-default/blob/v2.1.0/index.js\n '.git', // Git repository files, see <https://git-scm.com/>\n '.log', // Log files emitted by tools such as `tsserver`, see <https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29>\n '.nyc_output', // Temporary directory where nyc stores coverage data, see <https://github.com/bcoe/nyc>\n '.sass-cache', // Cache folder for node-sass, see <https://github.com/sass/node-sass>\n '.yarn', // Where node modules are installed when using Yarn, see <https://yarnpkg.com/>\n 'bower_components', // Where Bower packages are installed, see <http://bower.io/>\n 'coverage', // Standard output directory for code coverage reports, see <https://github.com/gotwarlost/istanbul>\n NODE_MODULES, // Where Node modules are installed, see <https://nodejs.org/>\n // Taken from globby:\n // https://github.com/sindresorhus/globby/blob/v14.0.2/ignore.js#L11-L16\n 'flow-typed',\n] as const\n\nconst IGNORED_DIR_PATTERNS = IGNORED_DIRS.map(i => `**/${i}`)\n\nasync function getWorkspaceGlobs(\n agent: Agent,\n cwd = process.cwd(),\n): Promise<string[]> {\n let workspacePatterns\n if (agent === PNPM) {\n const workspacePath = path.join(cwd, 'pnpm-workspace.yaml')\n const yml = await safeReadFile(workspacePath)\n if (yml) {\n try {\n workspacePatterns = yamlParse(yml)?.packages\n } catch {}\n }\n } else {\n workspacePatterns = (await readPackageJson(cwd, { throws: false }))?.[\n 'workspaces'\n ]\n }\n return Array.isArray(workspacePatterns)\n ? workspacePatterns\n .filter(isNonEmptyString)\n .map(workspacePatternToGlobPattern)\n : []\n}\n\nfunction ignoreFileLinesToGlobPatterns(\n lines: string[] | readonly string[],\n filepath: string,\n cwd: string,\n): string[] {\n const base = path.relative(cwd, path.dirname(filepath)).replace(/\\\\/g, '/')\n const patterns = []\n for (let i = 0, { length } = lines; i < length; i += 1) {\n const pattern = lines[i]!.trim()\n if (pattern.length > 0 && pattern.charCodeAt(0) !== 35 /*'#'*/) {\n patterns.push(\n ignorePatternToMinimatch(\n pattern.length && pattern.charCodeAt(0) === 33 /*'!'*/\n ? `!${path.posix.join(base, pattern.slice(1))}`\n : path.posix.join(base, pattern),\n ),\n )\n }\n }\n return patterns\n}\n\nfunction ignoreFileToGlobPatterns(\n content: string,\n filepath: string,\n cwd: string,\n): string[] {\n return ignoreFileLinesToGlobPatterns(content.split(/\\r?\\n/), filepath, cwd)\n}\n\n// Based on `@eslint/compat` convertIgnorePatternToMinimatch.\n// Apache v2.0 licensed\n// Copyright Nicholas C. Zakas\n// https://github.com/eslint/rewrite/blob/compat-v1.2.1/packages/compat/src/ignore-file.js#L28\nfunction ignorePatternToMinimatch(pattern: string): string {\n const isNegated = pattern.startsWith('!')\n const negatedPrefix = isNegated ? '!' : ''\n const patternToTest = (isNegated ? pattern.slice(1) : pattern).trimEnd()\n // Special cases.\n if (\n patternToTest === '' ||\n patternToTest === '**' ||\n patternToTest === '/**' ||\n patternToTest === '**'\n ) {\n return `${negatedPrefix}${patternToTest}`\n }\n const firstIndexOfSlash = patternToTest.indexOf('/')\n const matchEverywherePrefix =\n firstIndexOfSlash === -1 || firstIndexOfSlash === patternToTest.length - 1\n ? '**/'\n : ''\n const patternWithoutLeadingSlash =\n firstIndexOfSlash === 0 ? patternToTest.slice(1) : patternToTest\n // Escape `{` and `(` because in gitignore patterns they are just\n // literal characters without any specific syntactic meaning,\n // while in minimatch patterns they can form brace expansion or extglob syntax.\n //\n // For example, gitignore pattern `src/{a,b}.js` ignores file `src/{a,b}.js`.\n // But, the same minimatch pattern `src/{a,b}.js` ignores files `src/a.js` and `src/b.js`.\n // Minimatch pattern `src/\\{a,b}.js` is equivalent to gitignore pattern `src/{a,b}.js`.\n const escapedPatternWithoutLeadingSlash =\n patternWithoutLeadingSlash.replaceAll(\n /(?=((?:\\\\.|[^{(])*))\\1([{(])/guy,\n '$1\\\\$2',\n )\n const matchInsideSuffix = patternToTest.endsWith('/**') ? '/*' : ''\n return `${negatedPrefix}${matchEverywherePrefix}${escapedPatternWithoutLeadingSlash}${matchInsideSuffix}`\n}\n\nfunction workspacePatternToGlobPattern(workspace: string): string {\n const { length } = workspace\n if (!length) {\n return ''\n }\n // If the workspace ends with \"/\"\n if (workspace.charCodeAt(length - 1) === 47 /*'/'*/) {\n return `${workspace}/*/package.json`\n }\n // If the workspace ends with \"/**\"\n if (\n workspace.charCodeAt(length - 1) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 2) === 42 /*'*'*/ &&\n workspace.charCodeAt(length - 3) === 47 /*'/'*/\n ) {\n return `${workspace}/*/**/package.json`\n }\n // Things like \"packages/a\" or \"packages/*\"\n return `${workspace}/package.json`\n}\n\nexport function filterBySupportedScanFiles(\n filepaths: string[] | readonly string[],\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n): string[] {\n const patterns = getSupportedFilePatterns(supportedFiles)\n return filepaths.filter(p => micromatch.some(p, patterns))\n}\n\nexport function getSupportedFilePatterns(\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n): string[] {\n const patterns: string[] = []\n for (const key of Object.keys(supportedFiles)) {\n const supported = supportedFiles[key]\n if (supported) {\n patterns.push(...Object.values(supported).map(p => `**/${p.pattern}`))\n }\n }\n return patterns\n}\n\ntype GlobWithGitIgnoreOptions = GlobOptions & {\n socketConfig?: SocketYml | undefined\n}\n\nexport async function globWithGitIgnore(\n patterns: string[] | readonly string[],\n options: GlobWithGitIgnoreOptions,\n): Promise<string[]> {\n const {\n cwd = process.cwd(),\n socketConfig,\n ...additionalOptions\n } = { __proto__: null, ...options } as GlobWithGitIgnoreOptions\n\n const ignores = new Set<string>(IGNORED_DIR_PATTERNS)\n\n const projectIgnorePaths = socketConfig?.projectIgnorePaths\n if (Array.isArray(projectIgnorePaths)) {\n const ignorePatterns = ignoreFileLinesToGlobPatterns(\n projectIgnorePaths,\n path.join(cwd, '.gitignore'),\n cwd,\n )\n for (const pattern of ignorePatterns) {\n ignores.add(pattern)\n }\n }\n\n const gitIgnoreStream = fastGlob.globStream(['**/.gitignore'], {\n absolute: true,\n cwd,\n ignore: DEFAULT_IGNORE_FOR_GIT_IGNORE,\n })\n for await (const ignorePatterns of transform(\n gitIgnoreStream,\n async (filepath: string) =>\n ignoreFileToGlobPatterns(\n (await safeReadFile(filepath)) ?? '',\n filepath,\n cwd,\n ),\n { concurrency: 8 },\n )) {\n for (const p of ignorePatterns) {\n ignores.add(p)\n }\n }\n\n let hasNegatedPattern = false\n for (const p of ignores) {\n if (p.charCodeAt(0) === 33 /*'!'*/) {\n hasNegatedPattern = true\n break\n }\n }\n\n const globOptions = {\n __proto__: null,\n absolute: true,\n cwd,\n dot: true,\n ignore: hasNegatedPattern ? defaultIgnore : [...ignores],\n ...additionalOptions,\n } as GlobOptions\n\n if (!hasNegatedPattern) {\n return await fastGlob.glob(patterns as string[], globOptions)\n }\n\n // Add support for negated \"ignore\" patterns which many globbing libraries,\n // including 'fast-glob', 'globby', and 'tinyglobby', lack support for.\n const filtered: string[] = []\n const ig = ignore().add([...ignores])\n const stream = fastGlob.globStream(\n patterns as string[],\n globOptions,\n ) as AsyncIterable<string>\n for await (const p of stream) {\n // Note: the input files must be INSIDE the cwd. If you get strange looking\n // relative path errors here, most likely your path is outside the given cwd.\n const relPath = globOptions.absolute ? path.relative(cwd, p) : p\n if (!ig.ignores(relPath)) {\n filtered.push(p)\n }\n }\n return filtered\n}\n\nexport async function globWorkspace(\n agent: Agent,\n cwd = process.cwd(),\n): Promise<string[]> {\n const workspaceGlobs = await getWorkspaceGlobs(agent, cwd)\n return workspaceGlobs.length\n ? await fastGlob.glob(workspaceGlobs, {\n absolute: true,\n cwd,\n ignore: defaultIgnore,\n })\n : []\n}\n\nexport function isReportSupportedFile(\n filepath: string,\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n) {\n const patterns = getSupportedFilePatterns(supportedFiles)\n return micromatch.some(filepath, patterns)\n}\n\nexport function pathsToGlobPatterns(\n paths: string[] | readonly string[],\n cwd?: string | undefined,\n): string[] {\n // TODO: Does not support `~/` paths.\n return paths.map(p => {\n // Convert current directory references to glob patterns.\n if (p === '.' || p === './') {\n return '**/*'\n }\n const absolutePath = path.isAbsolute(p)\n ? p\n : path.resolve(cwd ?? process.cwd(), p)\n // If the path is a directory, scan it recursively for all files.\n if (isDirSync(absolutePath)) {\n return `${p}/**/*`\n }\n return p\n })\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport {\n resolveBinPathSync,\n whichBinSync,\n} from '@socketsecurity/registry/lib/bin'\nimport { isDirSync } from '@socketsecurity/registry/lib/fs'\n\nimport constants, { NODE_MODULES, NPM } from '../constants.mts'\nimport {\n filterBySupportedScanFiles,\n globWithGitIgnore,\n pathsToGlobPatterns,\n} from './glob.mts'\n\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport function findBinPathDetailsSync(binName: string): {\n name: string\n path: string | undefined\n shadowed: boolean\n} {\n const rawBinPaths =\n whichBinSync(binName, {\n all: true,\n nothrow: true,\n }) ?? []\n // whichBinSync may return a string when only one result is found, even with all: true.\n // This handles both the current published version and future versions.\n const binPaths = Array.isArray(rawBinPaths)\n ? rawBinPaths\n : typeof rawBinPaths === 'string'\n ? [rawBinPaths]\n : []\n const { shadowBinPath } = constants\n let shadowIndex = -1\n let theBinPath: string | undefined\n for (let i = 0, { length } = binPaths; i < length; i += 1) {\n const binPath = binPaths[i]!\n // Skip our bin directory if it's in the front.\n if (path.dirname(binPath) === shadowBinPath) {\n shadowIndex = i\n } else {\n theBinPath = resolveBinPathSync(binPath)\n break\n }\n }\n return { name: binName, path: theBinPath, shadowed: shadowIndex !== -1 }\n}\n\nexport function findNpmDirPathSync(npmBinPath: string): string | undefined {\n const { WIN32 } = constants\n let thePath = npmBinPath\n while (true) {\n const libNmNpmPath = path.join(thePath, `lib/${NODE_MODULES}/${NPM}`)\n // mise, which uses opaque binaries, puts its npm bin in a path like:\n // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/bin/npm.\n // HOWEVER, the location of the npm install is:\n // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/lib/node_modules/npm.\n if (\n // Use existsSync here because statsSync, even with { throwIfNoEntry: false },\n // will throw an ENOTDIR error for paths like ./a-file-that-exists/a-directory-that-does-not.\n // See https://github.com/nodejs/node/issues/56993.\n isDirSync(libNmNpmPath)\n ) {\n thePath = libNmNpmPath\n }\n const hasNmInCurrPath = isDirSync(path.join(thePath, NODE_MODULES))\n const hasNmInParentPath =\n !hasNmInCurrPath && isDirSync(path.join(thePath, `../${NODE_MODULES}`))\n if (\n // npm bin paths may look like:\n // /usr/local/share/npm/bin/npm\n // /Users/SomeUsername/.nvm/versions/node/vX.X.X/bin/npm\n // C:\\Users\\SomeUsername\\AppData\\Roaming\\npm\\bin\\npm.cmd\n // OR\n // C:\\Program Files\\nodejs\\npm.cmd\n //\n // In practically all cases the npm path contains a node_modules folder:\n // /usr/local/share/npm/bin/npm/node_modules\n // C:\\Program Files\\nodejs\\node_modules\n (hasNmInCurrPath ||\n // In some bespoke cases the node_modules folder is in the parent directory.\n hasNmInParentPath) &&\n // Optimistically look for the default location.\n (path.basename(thePath) === NPM ||\n // Chocolatey installs npm bins in the same directory as node bins.\n (WIN32 && existsSync(path.join(thePath, `${NPM}.cmd`))))\n ) {\n return hasNmInParentPath ? path.dirname(thePath) : thePath\n }\n const parent = path.dirname(thePath)\n if (parent === thePath) {\n return undefined\n }\n thePath = parent\n }\n}\n\nexport type PackageFilesForScanOptions = {\n cwd?: string | undefined\n config?: SocketYml | undefined\n}\n\nexport async function getPackageFilesForScan(\n inputPaths: string[],\n supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],\n options?: PackageFilesForScanOptions | undefined,\n): Promise<string[]> {\n const { config: socketConfig, cwd = process.cwd() } = {\n __proto__: null,\n ...options,\n } as PackageFilesForScanOptions\n\n const filepaths = await globWithGitIgnore(\n pathsToGlobPatterns(inputPaths, options?.cwd),\n {\n cwd,\n socketConfig,\n },\n )\n\n return filterBySupportedScanFiles(filepaths!, supportedFiles)\n}\n","import { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { YARN } from '../constants.mts'\nimport { findBinPathDetailsSync } from './path-resolve.mts'\n\nfunction exitWithBinPathError(binName: string): never {\n logger.fail(\n `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`,\n )\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n}\n\nlet _yarnBinPath: string | undefined\nexport function getYarnBinPath(): string {\n if (_yarnBinPath === undefined) {\n _yarnBinPath = getYarnBinPathDetails().path\n if (!_yarnBinPath) {\n exitWithBinPathError(YARN)\n }\n }\n return _yarnBinPath\n}\n\nlet _yarnBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nexport function getYarnBinPathDetails(): ReturnType<\n typeof findBinPathDetailsSync\n> {\n if (_yarnBinPathDetails === undefined) {\n _yarnBinPathDetails = findBinPathDetailsSync(YARN)\n }\n return _yarnBinPathDetails\n}\n\nexport function isYarnBinPathShadowed(): boolean {\n return getYarnBinPathDetails().shadowed\n}\n","import { spawnSync } from '@socketsecurity/registry/lib/spawn'\n\nimport { getYarnBinPath } from './yarn-paths.mts'\nimport constants, { FLAG_VERSION, UTF8 } from '../constants.mts'\n\nlet _isYarnBerry: boolean | undefined\nexport function isYarnBerry(): boolean {\n if (_isYarnBerry === undefined) {\n try {\n const yarnBinPath = getYarnBinPath()\n const result = spawnSync(yarnBinPath, [FLAG_VERSION], {\n encoding: UTF8,\n // On Windows, yarn is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\n shell: constants.WIN32,\n })\n\n if (result.status === 0 && result.stdout) {\n const version = result.stdout\n // Yarn Berry starts from version 2.x\n const majorVersion = parseInt(version.split('.')[0]!, 10)\n _isYarnBerry = majorVersion >= 2\n } else {\n _isYarnBerry = false\n }\n } catch {\n _isYarnBerry = false\n }\n }\n return _isYarnBerry\n}\n","/**\n * DLX execution utilities for Socket CLI.\n * Manages package execution via npx/pnpm dlx/yarn dlx commands.\n *\n * Key Functions:\n * - spawnCdxgenDlx: Execute CycloneDX generator via dlx\n * - spawnCoanaDlx: Execute Coana CLI tool via dlx\n * - spawnDlx: Execute packages using dlx-style commands\n * - spawnSynpDlx: Execute Synp converter via dlx\n *\n * Package Manager Detection:\n * - Auto-detects npm, pnpm, or yarn based on lockfiles\n * - Supports force-refresh and silent execution modes\n *\n * Integration:\n * - Works with shadow binaries for security scanning\n * - Handles version pinning and cache management\n * - Configures environment for third-party tools\n */\n\nimport { createRequire } from 'node:module'\n\nimport { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport { getDefaultOrgSlug } from '../commands/ci/fetch-default-org-slug.mts'\nimport constants, {\n FLAG_QUIET,\n FLAG_SILENT,\n NPM,\n PNPM,\n YARN,\n} from '../constants.mts'\nimport { getErrorCause } from './errors.mts'\nimport { findUp } from './fs.mts'\nimport { getDefaultApiToken, getDefaultProxyUrl } from './sdk.mts'\nimport { isYarnBerry } from './yarn-version.mts'\n\nimport type { ShadowBinOptions, ShadowBinResult } from '../shadow/npm-base.mts'\nimport type { CResult } from '../types.mts'\nimport type { SpawnExtra } from '@socketsecurity/registry/lib/spawn'\n\nconst require = createRequire(import.meta.url)\n\nconst { PACKAGE_LOCK_JSON, PNPM_LOCK_YAML, YARN_LOCK } = constants\n\nexport type DlxOptions = ShadowBinOptions & {\n force?: boolean | undefined\n agent?: 'npm' | 'pnpm' | 'yarn' | undefined\n silent?: boolean | undefined\n}\n\nexport type DlxPackageSpec = {\n name: string\n version: string\n}\n\n/**\n * Regex to check if a version string contains range operators.\n * Matches any version with range operators: ~, ^, >, <, =, x, X, *, spaces, or ||.\n */\nconst rangeOperatorsRegExp = /[~^><=xX* ]|\\|\\|/\n\n/**\n * Spawns a package using dlx-style execution (npx/pnpm dlx/yarn dlx).\n * Automatically detects the appropriate package manager if not specified.\n * Uses force/update flags to ensure the latest version within the range is fetched.\n */\nexport async function spawnDlx(\n packageSpec: DlxPackageSpec,\n args: string[] | readonly string[],\n options?: DlxOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<ShadowBinResult> {\n // If version is not pinned exactly, default to force and silent for better UX.\n const isNotPinned = rangeOperatorsRegExp.test(packageSpec.version)\n\n const {\n agent,\n force = false,\n silent = isNotPinned,\n ...shadowOptions\n } = options ?? {}\n\n let finalShadowOptions = shadowOptions\n\n let pm = agent\n\n // Auto-detect package manager if not specified.\n if (!pm) {\n const pnpmLockPath = await findUp(PNPM_LOCK_YAML, { onlyFiles: true })\n const yarnLockPath = pnpmLockPath\n ? undefined\n : await findUp(YARN_LOCK, { onlyFiles: true })\n const npmLockPath =\n pnpmLockPath || yarnLockPath\n ? undefined\n : await findUp(PACKAGE_LOCK_JSON, { onlyFiles: true })\n\n if (pnpmLockPath) {\n pm = PNPM\n } else if (yarnLockPath) {\n pm = YARN\n } else if (npmLockPath) {\n pm = NPM\n } else {\n // Default to npm if no lockfile found.\n pm = NPM\n }\n }\n\n const packageString = `${packageSpec.name}@${packageSpec.version}`\n\n // Build command args based on package manager.\n let spawnArgs: string[]\n\n if (pm === PNPM) {\n spawnArgs = []\n // The --silent flag must come before dlx, not after.\n if (silent) {\n spawnArgs.push(FLAG_SILENT)\n }\n spawnArgs.push('dlx')\n if (force) {\n // For pnpm, set dlx-cache-max-age to 0 via env to force fresh download.\n // This ensures we always get the latest version within the range.\n finalShadowOptions = {\n ...finalShadowOptions,\n env: {\n ...getOwn(finalShadowOptions, 'env'),\n // Set dlx cache max age to 0 minutes to bypass cache.\n // The npm_config_ prefix is how pnpm reads config from environment variables.\n // See: https://pnpm.io/npmrc#settings\n npm_config_dlx_cache_max_age: '0',\n },\n }\n }\n spawnArgs.push(packageString, ...args)\n\n const shadowPnpmBin = /*@__PURE__*/ require(constants.shadowPnpmBinPath)\n return await shadowPnpmBin(spawnArgs, finalShadowOptions, spawnExtra)\n } else if (pm === YARN && isYarnBerry()) {\n spawnArgs = ['dlx']\n // Yarn dlx runs in a temporary environment by design and should always fetch fresh.\n if (silent) {\n spawnArgs.push(FLAG_QUIET)\n }\n spawnArgs.push(packageString, ...args)\n\n const shadowYarnBin = /*@__PURE__*/ require(constants.shadowYarnBinPath)\n return await shadowYarnBin(spawnArgs, finalShadowOptions, spawnExtra)\n } else {\n // Use npm exec/npx.\n // For consistency, we'll use npx which is more commonly used for one-off execution.\n spawnArgs = ['--yes']\n if (force) {\n // Use --force to bypass cache and get latest within range.\n spawnArgs.push('--force')\n }\n if (silent) {\n spawnArgs.push(FLAG_SILENT)\n }\n spawnArgs.push(packageString, ...args)\n\n const shadowNpxBin = /*@__PURE__*/ require(constants.shadowNpxBinPath)\n return await shadowNpxBin(spawnArgs, finalShadowOptions, spawnExtra)\n }\n}\n\n/**\n * Helper to spawn coana with dlx.\n * Automatically uses force and silent when version is not pinned exactly.\n * Returns a CResult with stdout extraction for backward compatibility.\n *\n * If SOCKET_CLI_COANA_LOCAL_PATH environment variable is set, uses the local\n * Coana CLI at that path instead of downloading from npm.\n */\nexport async function spawnCoanaDlx(\n args: string[] | readonly string[],\n orgSlug?: string,\n options?: DlxOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<CResult<string>> {\n const {\n env: spawnEnv,\n ipc,\n ...dlxOptions\n } = {\n __proto__: null,\n ...options,\n } as DlxOptions\n\n const mixinsEnv: Record<string, string> = {\n SOCKET_CLI_VERSION: constants.ENV.INLINED_SOCKET_CLI_VERSION,\n }\n const defaultApiToken = getDefaultApiToken()\n if (defaultApiToken) {\n mixinsEnv['SOCKET_CLI_API_TOKEN'] = defaultApiToken\n }\n\n if (orgSlug) {\n mixinsEnv['SOCKET_ORG_SLUG'] = orgSlug\n } else {\n const orgSlugCResult = await getDefaultOrgSlug()\n if (orgSlugCResult.ok) {\n mixinsEnv['SOCKET_ORG_SLUG'] = orgSlugCResult.data\n }\n }\n\n const proxyUrl = getDefaultProxyUrl()\n if (proxyUrl) {\n mixinsEnv['SOCKET_CLI_API_PROXY'] = proxyUrl\n }\n\n try {\n const localCoanaPath = process.env['SOCKET_CLI_COANA_LOCAL_PATH']\n // Use local Coana CLI if path is provided.\n if (localCoanaPath) {\n const isBinary =\n !localCoanaPath.endsWith('.js') && !localCoanaPath.endsWith('.mjs')\n\n const finalEnv = {\n ...process.env,\n ...constants.processEnv,\n ...mixinsEnv,\n ...spawnEnv,\n }\n\n const spawnArgs = isBinary ? args : [localCoanaPath, ...args]\n const spawnResult = await spawn(\n isBinary ? localCoanaPath : 'node',\n spawnArgs,\n {\n cwd: dlxOptions.cwd,\n env: finalEnv,\n stdio: spawnExtra?.['stdio'] || 'inherit',\n },\n )\n\n return { ok: true, data: spawnResult.stdout }\n }\n\n // Use npm/dlx version.\n const result = await spawnDlx(\n {\n name: '@coana-tech/cli',\n version: constants.ENV.INLINED_SOCKET_CLI_COANA_TECH_CLI_VERSION,\n },\n args,\n {\n force: true,\n silent: true,\n ...dlxOptions,\n env: {\n ...process.env,\n ...constants.processEnv,\n ...mixinsEnv,\n ...spawnEnv,\n },\n ipc: {\n [constants.SOCKET_CLI_SHADOW_ACCEPT_RISKS]: true,\n [constants.SOCKET_CLI_SHADOW_API_TOKEN]:\n constants.SOCKET_PUBLIC_API_TOKEN,\n [constants.SOCKET_CLI_SHADOW_SILENT]: true,\n ...ipc,\n },\n },\n spawnExtra,\n )\n const output = await result.spawnPromise\n return { ok: true, data: output.stdout }\n } catch (e) {\n const stderr = (e as any)?.stderr\n const cause = getErrorCause(e)\n const message = stderr || cause\n return {\n ok: false,\n data: e,\n message,\n }\n }\n}\n\n/**\n * Helper to spawn cdxgen with dlx.\n */\nexport async function spawnCdxgenDlx(\n args: string[] | readonly string[],\n options?: DlxOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<ShadowBinResult> {\n return await spawnDlx(\n {\n name: '@cyclonedx/cdxgen',\n version: constants.ENV.INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION,\n },\n args,\n { force: false, silent: true, ...options },\n spawnExtra,\n )\n}\n\n/**\n * Helper to spawn synp with dlx.\n */\nexport async function spawnSynpDlx(\n args: string[] | readonly string[],\n options?: DlxOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<ShadowBinResult> {\n return await spawnDlx(\n {\n name: 'synp',\n version: `${constants.ENV.INLINED_SOCKET_CLI_SYNP_VERSION}`,\n },\n args,\n { force: false, silent: true, ...options },\n spawnExtra,\n )\n}\n","import type {\n EnterpriseOrganizations,\n Organizations,\n} from '../commands/organization/fetch-organization-list.mts'\n\nexport function getEnterpriseOrgs(\n orgs: Organizations,\n): EnterpriseOrganizations {\n return orgs.filter(o =>\n o.plan.includes('enterprise'),\n ) as EnterpriseOrganizations\n}\n\nexport function getOrgSlugs(orgs: Organizations): string[] {\n return orgs.map(o => o.slug)\n}\n\nexport function hasEnterpriseOrgPlan(orgs: Organizations): boolean {\n return orgs.some(o => o.plan.includes('enterprise'))\n}\n","/**\n * Socket JSON utilities for Socket CLI.\n * Manages .socket/socket.json configuration and scan metadata.\n *\n * Key Functions:\n * - loadDotSocketDirectory: Load .socket directory configuration\n * - saveSocketJson: Persist scan configuration to .socket/socket.json\n * - validateSocketJson: Validate socket.json structure\n *\n * File Structure:\n * - Contains scan metadata and configuration\n * - Stores scan IDs and repository information\n * - Tracks CLI version and scan timestamps\n *\n * Directory Management:\n * - Creates .socket directory as needed\n * - Handles nested directory structures\n * - Supports both read and write operations\n */\n\nimport { existsSync, promises as fs, readFileSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { formatErrorWithDetail } from './errors.mts'\nimport { findUp } from './fs.mts'\nimport { SOCKET_JSON, SOCKET_WEBSITE_URL } from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\n\nexport interface SocketJson {\n ' _____ _ _ ': string\n '| __|___ ___| |_ ___| |_ ': string\n \"|__ | . | _| '_| -_| _| \": string\n '|_____|___|___|_,_|___|_|.dev': string\n version: number\n\n defaults?: {\n manifest?: {\n conda?: {\n disabled?: boolean | undefined\n infile?: string | undefined\n outfile?: string | undefined\n stdin?: boolean | undefined\n stdout?: boolean | undefined\n target?: string | undefined\n verbose?: boolean | undefined\n }\n gradle?: {\n disabled?: boolean | undefined\n bin?: string | undefined\n gradleOpts?: string | undefined\n verbose?: boolean | undefined\n }\n sbt?: {\n disabled?: boolean | undefined\n infile?: string | undefined\n stdin?: boolean | undefined\n bin?: string | undefined\n outfile?: string | undefined\n sbtOpts?: string | undefined\n stdout?: boolean | undefined\n verbose?: boolean | undefined\n }\n }\n scan?: {\n create?: {\n autoManifest?: boolean | undefined\n repo?: string | undefined\n report?: boolean | undefined\n branch?: string | undefined\n }\n github?: {\n all?: boolean | undefined\n githubApiUrl?: string | undefined\n orgGithub?: string | undefined\n repos?: string | undefined\n }\n }\n }\n}\n\nexport function readOrDefaultSocketJson(cwd: string): SocketJson {\n const jsonCResult = readSocketJsonSync(cwd, true)\n return jsonCResult.ok\n ? jsonCResult.data\n : // This should be unreachable but it makes TS happy.\n getDefaultSocketJson()\n}\n\nexport async function findSocketJsonUp(\n cwd: string,\n): Promise<string | undefined> {\n return await findUp(SOCKET_JSON, { onlyFiles: true, cwd })\n}\n\nexport async function readOrDefaultSocketJsonUp(\n cwd: string,\n): Promise<SocketJson> {\n const socketJsonPath = await findSocketJsonUp(cwd)\n if (socketJsonPath) {\n const socketJsonDir = path.dirname(socketJsonPath)\n const jsonCResult = readSocketJsonSync(socketJsonDir, true)\n return jsonCResult.ok ? jsonCResult.data : getDefaultSocketJson()\n }\n return getDefaultSocketJson()\n}\n\nexport function getDefaultSocketJson(): SocketJson {\n return {\n ' _____ _ _ ': `Local config file for Socket CLI tool ( ${SOCKET_WEBSITE_URL}/npm/package/${SOCKET_JSON.replace('.json', '')} ), to work with ${SOCKET_WEBSITE_URL}`,\n '| __|___ ___| |_ ___| |_ ':\n ' The config in this file is used to set as defaults for flags or command args when using the CLI',\n \"|__ | . | _| '_| -_| _| \":\n ' in this dir, often a repo root. You can choose commit or .ignore this file, both works.',\n '|_____|___|___|_,_|___|_|.dev': `Warning: This file may be overwritten without warning by \\`${SOCKET_JSON.replace('.json', '')} manifest setup\\` or other commands`,\n version: 1,\n }\n}\n\nexport async function readSocketJson(\n cwd: string,\n defaultOnError = false,\n): Promise<CResult<SocketJson>> {\n const sockJsonPath = path.join(cwd, SOCKET_JSON)\n if (!existsSync(sockJsonPath)) {\n debugFn('notice', `miss: ${SOCKET_JSON} not found at ${cwd}`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n\n let json = null\n try {\n json = await fs.readFile(sockJsonPath, 'utf8')\n } catch (e) {\n if (defaultOnError) {\n logger.warn(`Failed to read ${SOCKET_JSON}, using default`)\n debugFn('warn', `Failed to read ${SOCKET_JSON}`)\n debugDir('warn', e)\n return { ok: true, data: getDefaultSocketJson() }\n }\n const cause = formatErrorWithDetail(\n `An error occurred while trying to read ${SOCKET_JSON}`,\n e,\n )\n debugFn('error', `Failed to read ${SOCKET_JSON}`)\n debugDir('error', e)\n return {\n ok: false,\n message: `Failed to read ${SOCKET_JSON}`,\n cause,\n }\n }\n\n let obj\n try {\n obj = JSON.parse(json)\n } catch (e) {\n debugFn('error', `Failed to parse ${SOCKET_JSON} as JSON`)\n debugDir('inspect', { json })\n debugDir('error', e)\n if (defaultOnError) {\n logger.warn(`Failed to parse ${SOCKET_JSON}, using default`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n return {\n ok: false,\n message: `Failed to parse ${SOCKET_JSON}`,\n cause: `${SOCKET_JSON} does not contain valid JSON, please verify`,\n }\n }\n\n if (!obj) {\n logger.warn('Warning: file contents was empty, using default')\n return { ok: true, data: getDefaultSocketJson() }\n }\n\n // Do we really care to validate? All properties are optional so code will have\n // to check every step of the way regardless. Who cares about validation here...?\n return { ok: true, data: obj }\n}\n\nexport function readSocketJsonSync(\n cwd: string,\n defaultOnError = false,\n): CResult<SocketJson> {\n const sockJsonPath = path.join(cwd, SOCKET_JSON)\n if (!existsSync(sockJsonPath)) {\n debugFn('notice', `miss: ${SOCKET_JSON} not found at ${cwd}`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n let jsonContent = null\n try {\n jsonContent = readFileSync(sockJsonPath, 'utf8')\n } catch (e) {\n if (defaultOnError) {\n logger.warn(`Failed to read ${SOCKET_JSON}, using default`)\n debugFn('warn', `Failed to read ${SOCKET_JSON} sync`)\n debugDir('warn', e)\n return { ok: true, data: getDefaultSocketJson() }\n }\n const cause = formatErrorWithDetail(\n `An error occurred while trying to read ${SOCKET_JSON}`,\n e,\n )\n debugFn('error', `Failed to read ${SOCKET_JSON} sync`)\n debugDir('error', e)\n return {\n ok: false,\n message: `Failed to read ${SOCKET_JSON}`,\n cause,\n }\n }\n\n let jsonObj\n try {\n jsonObj = JSON.parse(jsonContent)\n } catch (e) {\n debugFn('error', `Failed to parse ${SOCKET_JSON} as JSON (sync)`)\n debugDir('inspect', { jsonContent })\n debugDir('error', e)\n if (defaultOnError) {\n logger.warn(`Failed to parse ${SOCKET_JSON}, using default`)\n return { ok: true, data: getDefaultSocketJson() }\n }\n return {\n ok: false,\n message: `Failed to parse ${SOCKET_JSON}`,\n cause: `${SOCKET_JSON} does not contain valid JSON, please verify`,\n }\n }\n\n if (!jsonObj) {\n logger.warn('Warning: file contents was empty, using default')\n return { ok: true, data: getDefaultSocketJson() }\n }\n\n // TODO: Do we need to validate? All properties are optional so code will have\n // to check every step of the way regardless.\n return { ok: true, data: jsonObj }\n}\n\nexport async function writeSocketJson(\n cwd: string,\n sockJson: SocketJson,\n): Promise<CResult<undefined>> {\n let jsonContent = ''\n try {\n jsonContent = JSON.stringify(sockJson, null, 2)\n } catch (e) {\n debugFn('error', `Failed to serialize ${SOCKET_JSON} to JSON`)\n debugDir('inspect', { sockJson })\n debugDir('error', e)\n return {\n ok: false,\n message: 'Failed to serialize to JSON',\n cause: `There was an unexpected problem converting the ${SOCKET_JSON} object to a JSON string. Unable to store it.`,\n }\n }\n\n const filepath = path.join(cwd, SOCKET_JSON)\n await fs.writeFile(filepath, `${jsonContent}\\n`, 'utf8')\n\n return { ok: true, data: undefined }\n}\n","/**\n * GitHub utilities for Socket CLI.\n * Provides GitHub API integration for repository operations and GHSA vulnerability data.\n *\n * Authentication:\n * - getGitHubToken: Retrieve GitHub token from env/git config\n * - getOctokit: Get authenticated Octokit instance\n * - getOctokitGraphql: Get authenticated GraphQL client\n *\n * Caching:\n * - 5-minute TTL for API responses\n * - Automatic cache invalidation\n * - Persistent cache in node_modules/.cache\n *\n * GHSA Operations:\n * - cacheFetch: Cache API responses with TTL\n * - fetchGhsaDetails: Fetch GitHub Security Advisory details\n * - getGhsaUrl: Generate GHSA advisory URL\n * - readCache/writeCache: Persistent cache operations\n *\n * Repository Operations:\n * - GraphQL queries for complex operations\n * - Integration with Octokit REST API\n * - Support for GitHub Actions environment variables\n */\n\nimport { existsSync, promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport {\n GraphqlResponseError,\n graphql as OctokitGraphql,\n} from '@octokit/graphql'\nimport { Octokit } from '@octokit/rest'\n\nimport { debugDir, debugFn, isDebug } from '@socketsecurity/registry/lib/debug'\nimport {\n readJson,\n safeStatsSync,\n writeJson,\n} from '@socketsecurity/registry/lib/fs'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { parseUrl } from '@socketsecurity/registry/lib/url'\n\nimport { formatErrorWithDetail } from './errors.mts'\nimport constants from '../constants.mts'\n\nimport type { components } from '@octokit/openapi-types'\nimport type { JsonContent } from '@socketsecurity/registry/lib/fs'\nimport type { SpawnOptions } from '@socketsecurity/registry/lib/spawn'\n\nexport type Pr = components['schemas']['pull-request']\n\nasync function readCache(\n key: string,\n // 5 minute in milliseconds time to live (TTL).\n ttlMs = 5 * 60 * 1000,\n): Promise<JsonContent | undefined> {\n const cacheJsonPath = path.join(constants.githubCachePath, `${key}.json`)\n const stat = safeStatsSync(cacheJsonPath)\n if (stat) {\n const isExpired = Date.now() - stat.mtimeMs > ttlMs\n if (!isExpired) {\n return await readJson(cacheJsonPath)\n }\n }\n return undefined\n}\n\nexport async function writeCache(\n key: string,\n data: JsonContent,\n): Promise<void> {\n const { githubCachePath } = constants\n const cacheJsonPath = path.join(githubCachePath, `${key}.json`)\n if (!existsSync(githubCachePath)) {\n await fs.mkdir(githubCachePath, { recursive: true })\n }\n await writeJson(cacheJsonPath, data as JsonContent)\n}\n\nexport async function cacheFetch<T>(\n key: string,\n fetcher: () => Promise<T>,\n ttlMs?: number | undefined,\n): Promise<T> {\n // Optionally disable cache.\n if (constants.ENV.DISABLE_GITHUB_CACHE) {\n return await fetcher()\n }\n let data = (await readCache(key, ttlMs)) as T\n if (!data) {\n data = await fetcher()\n await writeCache(key, data as JsonContent)\n }\n return data\n}\n\nexport type GhsaDetails = {\n ghsaId: string\n cveId?: string | undefined\n summary: string\n severity: string\n publishedAt: string\n withdrawnAt?: string | undefined\n references: Array<{\n url: string\n }>\n vulnerabilities: {\n nodes: Array<{\n package: {\n ecosystem: string\n name: string\n }\n vulnerableVersionRange: string\n }>\n }\n}\n\nexport async function fetchGhsaDetails(\n ids: string[],\n): Promise<Map<string, GhsaDetails>> {\n const results = new Map<string, GhsaDetails>()\n if (!ids.length) {\n return results\n }\n\n const octokitGraphql = getOctokitGraphql()\n try {\n const gqlCacheKey = `${ids.join('-')}-graphql-snapshot`\n\n const aliases = ids\n .map(\n (id, index) =>\n `advisory${index}: securityAdvisory(ghsaId: \"${id}\") {\n ghsaId\n summary\n severity\n publishedAt\n withdrawnAt\n vulnerabilities(first: 10) {\n nodes {\n package {\n ecosystem\n name\n }\n vulnerableVersionRange\n }\n }\n }`,\n )\n .join('\\n')\n\n const gqlResp = await cacheFetch(gqlCacheKey, () =>\n octokitGraphql(`\n query {\n ${aliases}\n }\n `),\n )\n\n for (let i = 0, { length } = ids; i < length; i += 1) {\n const id = ids[i]!\n const advisoryKey = `advisory${i}`\n const advisory = (gqlResp as any)?.[advisoryKey]\n if (advisory && advisory.ghsaId) {\n results.set(id, advisory as GhsaDetails)\n } else {\n debugFn('notice', `miss: no advisory found for ${id}`)\n }\n }\n } catch (e) {\n debugFn('error', formatErrorWithDetail('Failed to fetch GHSA details', e))\n debugDir('error', e)\n }\n\n return results\n}\n\nlet _octokit: Octokit | undefined\nexport function getOctokit(): Octokit {\n if (_octokit === undefined) {\n const { SOCKET_CLI_GITHUB_TOKEN } = constants.ENV\n if (!SOCKET_CLI_GITHUB_TOKEN) {\n debugFn('notice', 'miss: SOCKET_CLI_GITHUB_TOKEN env var')\n }\n const octokitOptions = {\n auth: SOCKET_CLI_GITHUB_TOKEN,\n baseUrl: constants.ENV.GITHUB_API_URL,\n }\n debugDir('inspect', { octokitOptions })\n _octokit = new Octokit(octokitOptions)\n }\n return _octokit\n}\n\nlet _octokitGraphql: typeof OctokitGraphql | undefined\nexport function getOctokitGraphql(): typeof OctokitGraphql {\n if (!_octokitGraphql) {\n const { SOCKET_CLI_GITHUB_TOKEN } = constants.ENV\n if (!SOCKET_CLI_GITHUB_TOKEN) {\n debugFn('notice', 'miss: SOCKET_CLI_GITHUB_TOKEN env var')\n }\n _octokitGraphql = OctokitGraphql.defaults({\n headers: {\n authorization: `token ${SOCKET_CLI_GITHUB_TOKEN}`,\n },\n })\n }\n return _octokitGraphql\n}\n\nexport type PrAutoMergeState = {\n enabled: boolean\n details?: string[] | undefined\n}\n\nexport async function enablePrAutoMerge({\n node_id: prId,\n}: Pr): Promise<PrAutoMergeState> {\n const octokitGraphql = getOctokitGraphql()\n try {\n const gqlResp = await octokitGraphql(\n `\n mutation EnableAutoMerge($pullRequestId: ID!) {\n enablePullRequestAutoMerge(input: {\n pullRequestId: $pullRequestId,\n mergeMethod: SQUASH\n }) {\n pullRequest {\n number\n }\n }\n }`,\n { pullRequestId: prId },\n )\n const respPrNumber = (gqlResp as any)?.enablePullRequestAutoMerge\n ?.pullRequest?.number\n if (respPrNumber) {\n return { enabled: true }\n }\n } catch (e) {\n if (\n e instanceof GraphqlResponseError &&\n Array.isArray(e.errors) &&\n e.errors.length\n ) {\n const details = e.errors.map(({ message: m }) => m.trim())\n return { enabled: false, details }\n }\n }\n return { enabled: false }\n}\n\nexport async function prExistForBranch(\n owner: string,\n repo: string,\n branch: string,\n): Promise<boolean> {\n const octokit = getOctokit()\n try {\n const { data: prs } = await octokit.pulls.list({\n owner,\n repo,\n head: `${owner}:${branch}`,\n state: 'all',\n per_page: 1,\n })\n return prs.length > 0\n } catch {}\n return false\n}\n\nexport async function setGitRemoteGithubRepoUrl(\n owner: string,\n repo: string,\n token: string,\n cwd = process.cwd(),\n): Promise<boolean> {\n const { GITHUB_SERVER_URL } = constants.ENV\n const urlObj = parseUrl(GITHUB_SERVER_URL)\n const host = urlObj?.host\n if (!host) {\n debugFn('error', 'invalid: GITHUB_SERVER_URL env var')\n debugDir('inspect', { GITHUB_SERVER_URL })\n return false\n }\n const url = `https://x-access-token:${token}@${host}/${owner}/${repo}`\n const stdioIgnoreOptions: SpawnOptions = {\n cwd,\n stdio: isDebug('stdio') ? 'inherit' : 'ignore',\n }\n const quotedCmd = `\\`git remote set-url origin ${url}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n await spawn('git', ['remote', 'set-url', 'origin', url], stdioIgnoreOptions)\n return true\n } catch (e) {\n debugFn('error', `Git command failed: ${quotedCmd}`)\n debugDir('inspect', { cmd: quotedCmd })\n debugDir('error', e)\n }\n return false\n}\n","import { getErrorCause } from './errors.mts'\nimport { cacheFetch, getOctokit } from './github.mts'\n\nimport type { CResult } from '../types.mts'\n\n/**\n * Converts CVE IDs to GHSA IDs using GitHub API.\n * CVE to GHSA mappings are permanent, so we cache for 30 days.\n */\nexport async function convertCveToGhsa(\n cveId: string,\n): Promise<CResult<string>> {\n try {\n const cacheKey = `cve-to-ghsa-${cveId}`\n const octokit = getOctokit()\n\n const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000\n\n const response = await cacheFetch(\n cacheKey,\n () =>\n octokit.rest.securityAdvisories.listGlobalAdvisories({\n cve_id: cveId,\n per_page: 1,\n }),\n THIRTY_DAYS_MS,\n )\n\n if (!response.data.length) {\n return {\n ok: false,\n message: `No GHSA found for CVE ${cveId}`,\n }\n }\n\n return {\n ok: true,\n data: response.data[0]!.ghsa_id,\n }\n } catch (e) {\n const errorCause = getErrorCause(e)\n const errorLower = errorCause.toLowerCase()\n // Detect GitHub API rate limit and network errors.\n const isRateLimitOrNetworkError =\n errorLower.includes('rate limit') ||\n errorLower.includes('epipe') ||\n errorLower.includes('econnreset') ||\n errorLower.includes('status: 403') ||\n errorLower.includes('status code 403')\n\n return {\n ok: false,\n message: isRateLimitOrNetworkError\n ? 'GitHub API rate limit exceeded while converting CVE to GHSA. Wait an hour or set SOCKET_CLI_GITHUB_TOKEN environment variable with a personal access token for higher limits.'\n : `Failed to convert CVE to GHSA: ${errorCause}`,\n }\n }\n}\n","import { cacheFetch, getOctokit } from './github.mts'\nimport { getPurlObject } from './purl.mts'\nimport { LATEST } from '../constants.mts'\nimport { getErrorCause } from './errors.mts'\n\nimport type { CResult } from '../types.mts'\n\nconst PURL_TO_GITHUB_ECOSYSTEM_MAPPING = {\n __proto__: null,\n // GitHub Advisory Database supported ecosystems\n cargo: 'rust',\n composer: 'composer',\n gem: 'rubygems',\n go: 'go',\n golang: 'go',\n maven: 'maven',\n npm: 'npm',\n nuget: 'nuget',\n pypi: 'pip',\n swift: 'swift',\n} as unknown as Record<string, string>\n\n/**\n * Converts PURL to GHSA IDs using GitHub API.\n */\nexport async function convertPurlToGhsas(\n purl: string,\n): Promise<CResult<string[]>> {\n try {\n const purlObj = getPurlObject(purl, { throws: false })\n if (!purlObj) {\n return {\n ok: false,\n message: `Invalid PURL format: ${purl}`,\n }\n }\n\n const { name, type: ecosystem, version } = purlObj\n\n // Map PURL ecosystem to GitHub ecosystem.\n const githubEcosystem = PURL_TO_GITHUB_ECOSYSTEM_MAPPING[ecosystem]\n if (!githubEcosystem) {\n return {\n ok: false,\n message: `Unsupported PURL ecosystem: ${ecosystem}`,\n }\n }\n\n // Search for advisories affecting this package.\n const cacheKey = `purl-to-ghsa-${ecosystem}-${name}-${version || LATEST}`\n const octokit = getOctokit()\n const affects = version ? `${name}@${version}` : name\n\n const response = await cacheFetch(cacheKey, () =>\n octokit.rest.securityAdvisories.listGlobalAdvisories({\n ecosystem: githubEcosystem as any,\n affects,\n }),\n )\n\n return {\n ok: true,\n data: response.data.map(a => a.ghsa_id),\n }\n } catch (e) {\n return {\n ok: false,\n message: `Failed to convert PURL to GHSA: ${getErrorCause(e)}`,\n }\n }\n}\n","/**\n * Command-line utilities for Socket CLI.\n * Handles argument parsing, flag processing, and command formatting.\n *\n * Argument Handling:\n * - Handles both long (--flag) and short (-f) formats\n * - Preserves special characters and escaping\n * - Properly quotes arguments containing spaces\n *\n * Command Names:\n * - commandNameFromCamel: Convert camelCase to kebab-case command names\n * - commandNameFromKebab: Convert kebab-case to camelCase\n *\n * Flag Processing:\n * - cmdFlagsToString: Format arguments for display with proper escaping\n * - cmdPrefixMessage: Generate command prefix message\n * - stripConfigFlags: Remove --config flags from argument list\n * - stripDebugFlags: Remove debug-related flags\n * - stripHelpFlags: Remove help flags (-h, --help)\n */\n\nimport { FLAG_CONFIG, FLAG_HELP } from '../constants.mts'\nimport { camelToKebab } from './strings.mts'\n\nconst CONFIG_FLAG_LONG_NAME = FLAG_CONFIG\nconst CONFIG_FLAG_ASSIGNMENT = `${CONFIG_FLAG_LONG_NAME}=`\nconst CONFIG_FLAG_ASSIGNMENT_LENGTH = CONFIG_FLAG_ASSIGNMENT.length\n\nconst configFlags = new Set([FLAG_CONFIG])\nconst helpFlags = new Set([FLAG_HELP, '-h'])\n\n/**\n * Convert flag values to array format for processing.\n */\nexport function cmdFlagValueToArray(value: any): string[] {\n if (typeof value === 'string') {\n return value.trim().split(/, */).filter(Boolean)\n }\n if (Array.isArray(value)) {\n return value.flatMap(cmdFlagValueToArray)\n }\n return []\n}\n\n/**\n * Convert command arguments to a properly formatted string representation.\n */\nexport function cmdFlagsToString(args: string[] | readonly string[]): string {\n const result = []\n for (let i = 0, { length } = args; i < length; i += 1) {\n const arg = args[i]!.trim()\n if (arg.startsWith('--')) {\n const nextArg = i + 1 < length ? args[i + 1]!.trim() : undefined\n // Check if the next item exists and is NOT another flag.\n if (nextArg && !nextArg.startsWith('--') && !nextArg.startsWith('-')) {\n result.push(`${arg}=${nextArg}`)\n i += 1\n } else {\n result.push(arg)\n }\n } else {\n // Include non-flag arguments (commands, package names, etc.).\n result.push(arg)\n }\n }\n return result.join(' ')\n}\n\n/**\n * Add command name prefix to message text.\n */\nexport function cmdPrefixMessage(cmdName: string, text: string): string {\n const cmdPrefix = cmdName ? `${cmdName}: ` : ''\n return `${cmdPrefix}${text}`\n}\n\n/**\n * Filter out Socket flags from argv before passing to subcommands.\n */\nexport function filterFlags(\n argv: readonly string[],\n flagsToFilter: Record<string, any>,\n exceptions?: string[] | undefined,\n): string[] {\n const filtered: string[] = []\n\n // Build set of flags to filter from the provided flag objects.\n const flagsToFilterSet = new Set<string>()\n const flagsWithValueSet = new Set<string>()\n\n for (const [flagName, flag] of Object.entries(flagsToFilter)) {\n const longFlag = `--${camelToKebab(flagName)}`\n // Special case for negated booleans.\n if (flagName === 'spinner' || flagName === 'banner') {\n flagsToFilterSet.add(`--no-${flagName}`)\n } else {\n flagsToFilterSet.add(longFlag)\n }\n if (flag?.shortFlag) {\n flagsToFilterSet.add(`-${flag.shortFlag}`)\n }\n // Track flags that take values.\n if (flag.type !== 'boolean') {\n flagsWithValueSet.add(longFlag)\n if (flag?.shortFlag) {\n flagsWithValueSet.add(`-${flag.shortFlag}`)\n }\n }\n }\n\n for (let i = 0, { length } = argv; i < length; i += 1) {\n const arg = argv[i]!\n // Check if this flag should be kept as an exception.\n if (exceptions?.includes(arg)) {\n filtered.push(arg)\n // Handle flags that take values.\n if (flagsWithValueSet.has(arg)) {\n // Include the next argument (the flag value).\n i += 1\n if (i < length) {\n filtered.push(argv[i]!)\n }\n }\n } else if (flagsToFilterSet.has(arg)) {\n // Skip flags that take values.\n if (flagsWithValueSet.has(arg)) {\n // Skip the next argument (the flag value).\n i += 1\n }\n // Skip boolean flags (no additional argument to skip).\n } else if (\n arg &&\n Array.from(flagsWithValueSet).some(flag => arg.startsWith(`${flag}=`))\n ) {\n // Skip --flag=value format for Socket flags unless it's an exception.\n if (exceptions?.some(exc => arg.startsWith(`${exc}=`))) {\n filtered.push(arg)\n }\n // Otherwise skip it.\n } else {\n filtered.push(arg!)\n }\n }\n return filtered\n}\n\n/**\n * Extract config flag value from command arguments.\n */\nexport function getConfigFlag(\n argv: string[] | readonly string[],\n): string | undefined {\n for (let i = 0, { length } = argv; i < length; i += 1) {\n const arg = argv[i]!.trim()\n // Handle --config=value format.\n if (arg.startsWith(CONFIG_FLAG_ASSIGNMENT)) {\n return arg.slice(CONFIG_FLAG_ASSIGNMENT_LENGTH)\n }\n // Handle --config value format.\n if (arg === CONFIG_FLAG_LONG_NAME && i + 1 < length) {\n return argv[i + 1]\n }\n }\n return undefined\n}\n\n/**\n * Check if command is an add command (adds new dependencies).\n * Supported by: pnpm, yarn.\n * Note: npm uses 'install' with package names instead of 'add'.\n */\nexport function isAddCommand(command: string): boolean {\n return command === 'add'\n}\n\n/**\n * Check if argument is a config flag.\n */\nexport function isConfigFlag(cmdArg: string): boolean {\n return configFlags.has(cmdArg) || cmdArg.startsWith(CONFIG_FLAG_ASSIGNMENT)\n}\n\n/**\n * Check if argument is a help flag.\n */\nexport function isHelpFlag(cmdArg: string): boolean {\n return helpFlags.has(cmdArg)\n}\n\n/**\n * Check if npm command requires lockfile scanning.\n * npm uses: install, i, update\n */\nexport function isNpmLockfileScanCommand(command: string): boolean {\n return command === 'install' || command === 'i' || command === 'update'\n}\n\n/**\n * Check if pnpm command requires lockfile scanning.\n * pnpm uses: install, i, update, up\n */\nexport function isPnpmLockfileScanCommand(command: string): boolean {\n return (\n command === 'install' ||\n command === 'i' ||\n command === 'update' ||\n command === 'up'\n )\n}\n\n/**\n * Check if yarn command requires lockfile scanning.\n * yarn uses: install, up, upgrade, upgrade-interactive\n */\nexport function isYarnLockfileScanCommand(command: string): boolean {\n return (\n command === 'install' ||\n command === 'up' ||\n command === 'upgrade' ||\n command === 'upgrade-interactive'\n )\n}\n","import semver from 'semver'\n\nimport type { SemVer } from 'semver'\n\nexport const RangeStyles = ['pin', 'preserve']\n\nexport type RangeStyle = 'pin' | 'preserve'\n\nexport type { SemVer }\n\nexport function getMajor(version: unknown): number | undefined {\n try {\n const coerced = semver.coerce(version as string)\n return coerced ? semver.major(coerced) : undefined\n } catch {}\n return undefined\n}\n\nexport function getMinVersion(range: unknown): SemVer | undefined {\n try {\n return semver.minVersion(range as string) ?? undefined\n } catch {}\n return undefined\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\n\nexport const COMPLETION_CMD_PREFIX = 'complete -F _socket_completion'\n\nexport function getCompletionSourcingCommand(): CResult<string> {\n // Note: this is exported to distPath in .config/rollup.dist.config.mjs\n const completionScriptExportPath = path.join(\n constants.distPath,\n 'socket-completion.bash',\n )\n\n if (!fs.existsSync(completionScriptExportPath)) {\n return {\n ok: false,\n message: 'Tab Completion script not found',\n cause: `Expected to find completion script at \\`${completionScriptExportPath}\\` but it was not there`,\n }\n }\n\n return { ok: true, data: `source ${completionScriptExportPath}` }\n}\n\nexport function getBashrcDetails(targetCommandName: string): CResult<{\n completionCommand: string\n sourcingCommand: string\n toAddToBashrc: string\n targetName: string\n targetPath: string\n}> {\n const sourcingCommand = getCompletionSourcingCommand()\n if (!sourcingCommand.ok) {\n return sourcingCommand\n }\n\n const { socketAppDataPath } = constants\n if (!socketAppDataPath) {\n return {\n ok: false,\n message: 'Could not determine config directory',\n cause: 'Failed to get config path',\n }\n }\n\n // _socket_completion is the function defined in our completion bash script\n const completionCommand = `${COMPLETION_CMD_PREFIX} ${targetCommandName}`\n\n // Location of completion script in config after installing\n const completionScriptPath = path.join(\n path.dirname(socketAppDataPath),\n 'completion',\n 'socket-completion.bash',\n )\n\n const bashrcContent = `# Socket CLI completion for \"${targetCommandName}\"\nif [ -f \"${completionScriptPath}\" ]; then\n # Load the tab completion script\n source \"${completionScriptPath}\"\n # Tell bash to use this function for tab completion of this function\n ${completionCommand}\nfi\n`\n\n return {\n ok: true,\n data: {\n sourcingCommand: sourcingCommand.data,\n completionCommand,\n toAddToBashrc: bashrcContent,\n targetName: targetCommandName,\n targetPath: completionScriptPath,\n },\n }\n}\n","import npmPackageArg from 'npm-package-arg'\n\nexport type {\n AliasResult,\n FileResult,\n HostedGit,\n HostedGitResult,\n RegistryResult,\n Result,\n URLResult,\n} from 'npm-package-arg'\n\n/**\n * Safe wrapper for npm-package-arg that doesn't throw.\n * Returns undefined if parsing fails.\n */\nexport function safeNpa(\n ...args: Parameters<typeof npmPackageArg>\n): ReturnType<typeof npmPackageArg> | undefined {\n try {\n return Reflect.apply(npmPackageArg, undefined, args)\n } catch {}\n return undefined\n}\n","import { existsSync } from 'node:fs'\nimport Module from 'node:module'\nimport path from 'node:path'\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport constants, { NODE_MODULES, NPM } from '../constants.mts'\nimport { findBinPathDetailsSync, findNpmDirPathSync } from './path-resolve.mts'\n\nfunction exitWithBinPathError(binName: string): never {\n logger.fail(\n `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`,\n )\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n}\n\nlet _npmBinPath: string | undefined\nexport function getNpmBinPath(): string {\n if (_npmBinPath === undefined) {\n _npmBinPath = getNpmBinPathDetails().path\n if (!_npmBinPath) {\n exitWithBinPathError(NPM)\n }\n }\n return _npmBinPath\n}\n\nlet _npmBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nfunction getNpmBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {\n if (_npmBinPathDetails === undefined) {\n _npmBinPathDetails = findBinPathDetailsSync(NPM)\n }\n return _npmBinPathDetails\n}\n\nlet _npmDirPath: string | undefined\nexport function getNpmDirPath() {\n if (_npmDirPath === undefined) {\n const npmBinPath = getNpmBinPath()\n _npmDirPath = npmBinPath ? findNpmDirPathSync(npmBinPath) : undefined\n if (!_npmDirPath) {\n _npmDirPath = constants.ENV.SOCKET_CLI_NPM_PATH || undefined\n }\n if (!_npmDirPath) {\n let message = 'Unable to find npm CLI install directory.'\n if (npmBinPath) {\n message += `\\nSearched parent directories of ${path.dirname(npmBinPath)}.`\n }\n message +=\n '\\n\\nThis is may be a bug with socket-npm related to changes to the npm CLI.'\n message += `\\nPlease report to ${constants.SOCKET_CLI_ISSUES_URL}.`\n logger.fail(message)\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n }\n }\n return _npmDirPath\n}\n\nlet _npmRequire: NodeJS.Require | undefined\nexport function getNpmRequire(): NodeJS.Require {\n if (_npmRequire === undefined) {\n const npmDirPath = getNpmDirPath()\n const npmNmPath = path.join(npmDirPath, `${NODE_MODULES}/npm`)\n _npmRequire = Module.createRequire(\n path.join(\n existsSync(npmNmPath) ? npmNmPath : npmDirPath,\n '<dummy-basename>',\n ),\n )\n }\n return _npmRequire\n}\n\nlet _npxBinPath: string | undefined\nexport function getNpxBinPath(): string {\n if (_npxBinPath === undefined) {\n _npxBinPath = getNpxBinPathDetails().path\n if (!_npxBinPath) {\n exitWithBinPathError('npx')\n }\n }\n return _npxBinPath\n}\n\nlet _npxBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nfunction getNpxBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {\n if (_npxBinPathDetails === undefined) {\n _npxBinPathDetails = findBinPathDetailsSync('npx')\n }\n return _npxBinPathDetails\n}\n\nexport function isNpmBinPathShadowed() {\n return getNpmBinPathDetails().shadowed\n}\n\nexport function isNpxBinPathShadowed() {\n return getNpxBinPathDetails().shadowed\n}\n","import {\n isNpmAuditFlag,\n isNpmFundFlag,\n isNpmLoglevelFlag,\n isNpmProgressFlag,\n resolveBinPathSync,\n} from '@socketsecurity/registry/lib/agent'\nimport { isDebug } from '@socketsecurity/registry/lib/debug'\nimport { getOwn, isObject } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\n\nimport constants, { FLAG_LOGLEVEL, NPM } from '../../constants.mts'\nimport { getNpmBinPath } from '../../utils/npm-paths.mts'\n\nimport type { SpawnResult } from '@socketsecurity/registry/lib/spawn'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\nexport type ShadowNpmInstallOptions = SpawnOption & {\n agentExecPath?: string | undefined\n args?: string[] | readonly string[] | undefined\n ipc?: object | undefined\n spinner?: Spinner | undefined\n}\n\nexport function shadowNpmInstall(\n options?: ShadowNpmInstallOptions | undefined,\n): SpawnResult<string, Record<any, any> | undefined> {\n const {\n agentExecPath = getNpmBinPath(),\n args = [],\n ipc,\n spinner,\n ...spawnOpts\n } = { __proto__: null, ...options } as ShadowNpmInstallOptions\n const useDebug = isDebug('stdio')\n const terminatorPos = args.indexOf('--')\n const rawBinArgs = terminatorPos === -1 ? args : args.slice(0, terminatorPos)\n const binArgs = rawBinArgs.filter(\n a => !isNpmAuditFlag(a) && !isNpmFundFlag(a) && !isNpmProgressFlag(a),\n )\n const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos)\n const progressArg = rawBinArgs.findLast(isNpmProgressFlag) !== '--no-progress'\n const isSilent = !useDebug && !binArgs.some(isNpmLoglevelFlag)\n const logLevelArgs = isSilent ? [FLAG_LOGLEVEL, 'silent'] : []\n const useIpc = isObject(ipc)\n\n // Include 'ipc' in the spawnOpts.stdio when an options.ipc object is provided.\n // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166\n // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.\n let stdio = getOwn(spawnOpts, 'stdio')\n if (typeof stdio === 'string') {\n stdio = useIpc ? [stdio, stdio, stdio, 'ipc'] : [stdio, stdio, stdio]\n } else if (Array.isArray(stdio)) {\n if (useIpc && !stdio.includes('ipc')) {\n stdio = stdio.concat('ipc')\n }\n } else {\n stdio = useIpc ? ['pipe', 'pipe', 'pipe', 'ipc'] : 'pipe'\n }\n\n const spawnPromise = spawn(\n constants.execPath,\n [\n ...constants.nodeNoWarningsFlags,\n ...constants.nodeDebugFlags,\n ...constants.nodeHardenFlags,\n ...constants.nodeMemoryFlags,\n ...(constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD\n ? ['--require', constants.instrumentWithSentryPath]\n : []),\n '--require',\n constants.shadowNpmInjectPath,\n resolveBinPathSync(agentExecPath),\n 'install',\n // Avoid code paths for 'audit' and 'fund'.\n '--no-audit',\n '--no-fund',\n // Add '--no-progress' to fix input being swallowed by the npm spinner.\n '--no-progress',\n // Add 'FLAG_LOGLEVEL silent' if a loglevel flag is not provided and the\n // SOCKET_CLI_DEBUG environment variable is not truthy.\n ...logLevelArgs,\n ...binArgs,\n ...otherArgs,\n ],\n {\n ...spawnOpts,\n env: {\n ...process.env,\n ...constants.processEnv,\n ...getOwn(spawnOpts, 'env'),\n },\n spinner,\n stdio,\n },\n )\n\n if (useIpc) {\n spawnPromise.process.send({\n [constants.SOCKET_IPC_HANDSHAKE]: {\n [constants.SOCKET_CLI_SHADOW_BIN]: NPM,\n [constants.SOCKET_CLI_SHADOW_PROGRESS]: progressArg,\n ...ipc,\n },\n })\n }\n\n return spawnPromise\n}\n","/**\n * Package manager agent utilities for Socket CLI.\n * Manages package installation via different package managers.\n *\n * Key Functions:\n * - runAgentInstall: Execute package installation with detected agent\n *\n * Supported Agents:\n * - npm: Node Package Manager\n * - pnpm: Fast, disk space efficient package manager\n * - yarn: Alternative package manager\n *\n * Features:\n * - Automatic agent detection\n * - Shadow installation for security scanning\n * - Spinner support for progress indication\n */\n\nimport { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nimport constants, { NPM, PNPM } from '../constants.mts'\nimport { cmdFlagsToString } from './cmd.mts'\nimport { shadowNpmInstall } from '../shadow/npm/install.mts'\n\nimport type { EnvDetails } from './package-environment.mts'\n\ntype SpawnOption = Exclude<Parameters<typeof spawn>[2], undefined>\n\nexport type AgentInstallOptions = SpawnOption & {\n args?: string[] | readonly string[] | undefined\n spinner?: Spinner | undefined\n}\n\nexport type AgentSpawnResult = ReturnType<typeof spawn>\n\nexport function runAgentInstall(\n pkgEnvDetails: EnvDetails,\n options?: AgentInstallOptions | undefined,\n): AgentSpawnResult {\n const { agent, agentExecPath, pkgPath } = pkgEnvDetails\n const isNpm = agent === NPM\n const isPnpm = agent === PNPM\n // All package managers support the \"install\" command.\n if (isNpm) {\n return shadowNpmInstall({\n agentExecPath,\n cwd: pkgPath,\n ...options,\n })\n }\n const {\n args = [],\n spinner,\n ...spawnOpts\n } = { __proto__: null, ...options } as AgentInstallOptions\n const skipNodeHardenFlags = isPnpm && pkgEnvDetails.agentVersion.major < 11\n // In CI mode, pnpm uses --frozen-lockfile by default, which prevents lockfile updates.\n // We need to explicitly disable it when updating the lockfile with overrides.\n // Also add --config.confirmModulesPurge=false to avoid interactive prompts.\n const installArgs = isPnpm\n ? [\n 'install',\n '--config.confirmModulesPurge=false',\n '--no-frozen-lockfile',\n ...args,\n ]\n : ['install', ...args]\n\n return spawn(agentExecPath, installArgs, {\n cwd: pkgPath,\n // On Windows, package managers are often .cmd files that require shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\n shell: constants.WIN32,\n spinner,\n stdio: 'inherit',\n ...spawnOpts,\n env: {\n ...process.env,\n ...constants.processEnv,\n // Set CI for pnpm to ensure non-interactive mode and consistent behavior.\n ...(isPnpm ? { CI: '1' } : {}),\n NODE_OPTIONS: cmdFlagsToString([\n ...(skipNodeHardenFlags ? [] : constants.nodeHardenFlags),\n ...constants.nodeNoWarningsFlags,\n ]),\n ...getOwn(spawnOpts, 'env'),\n },\n })\n}\n","/**\n * Package environment detection utilities for Socket CLI.\n * Analyzes project environment and package manager configuration.\n *\n * Key Functions:\n * - getPackageEnvironment: Detect package manager and project details\n * - makeConcurrentExecLimit: Calculate concurrent execution limits\n *\n * Environment Detection:\n * - Detects npm, pnpm, yarn, bun package managers\n * - Analyzes lockfiles for version information\n * - Determines Node.js and engine requirements\n * - Identifies workspace configurations\n *\n * Features:\n * - Browser target detection via browserslist\n * - Engine compatibility checking\n * - Package manager version detection\n * - Workspace and monorepo support\n *\n * Usage:\n * - Auto-detecting appropriate package manager\n * - Validating environment compatibility\n * - Configuring concurrent execution limits\n */\n\nimport { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport browserslist from 'browserslist'\nimport semver from 'semver'\n\nimport { parse as parseBunLockb } from '@socketregistry/hyrious__bun.lockb/index.cjs'\nimport { whichBin } from '@socketsecurity/registry/lib/bin'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { readFileBinary, readFileUtf8 } from '@socketsecurity/registry/lib/fs'\nimport { Logger } from '@socketsecurity/registry/lib/logger'\nimport { readPackageJson } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\nimport { spawn } from '@socketsecurity/registry/lib/spawn'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { cmdPrefixMessage } from './cmd.mts'\nimport { findUp } from './fs.mts'\nimport constants, {\n FLAG_VERSION,\n PACKAGE_LOCK_JSON,\n PNPM_LOCK_YAML,\n YARN_LOCK,\n} from '../constants.mts'\n\nimport type { CResult } from '../types.mts'\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type { EditablePackageJson } from '@socketsecurity/registry/lib/packages'\nimport type { SemVer } from 'semver'\n\nconst {\n BUN,\n BUN_LOCK,\n BUN_LOCKB,\n DOT_PACKAGE_LOCK_JSON,\n EXT_LOCK,\n EXT_LOCKB,\n NODE_MODULES,\n NPM,\n NPM_BUGGY_OVERRIDES_PATCHED_VERSION,\n NPM_SHRINKWRAP_JSON,\n PACKAGE_JSON,\n PNPM,\n VLT,\n VLT_LOCK_JSON,\n YARN,\n YARN_BERRY,\n YARN_CLASSIC,\n} = constants\n\nexport const AGENTS = [BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT] as const\n\nconst binByAgent = new Map<Agent, string>([\n [BUN, BUN],\n [NPM, NPM],\n [PNPM, PNPM],\n [YARN_BERRY, YARN],\n [YARN_CLASSIC, YARN],\n [VLT, VLT],\n])\n\nexport type Agent = (typeof AGENTS)[number]\n\nexport type EnvBase = {\n agent: Agent\n agentExecPath: string\n agentSupported: boolean\n features: {\n // Fixed by https://github.com/npm/cli/pull/8089.\n // Landed in npm v11.2.0.\n npmBuggyOverrides: boolean\n }\n nodeSupported: boolean\n nodeVersion: SemVer\n npmExecPath: string\n pkgRequirements: {\n agent: string\n node: string\n }\n pkgSupports: {\n agent: boolean\n node: boolean\n }\n}\n\nexport type EnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer\n editablePkgJson: EditablePackageJson\n lockName: string\n lockPath: string\n lockSrc: string\n pkgPath: string\n }\n >\n>\n\nexport type DetectAndValidateOptions = {\n cmdName?: string | undefined\n logger?: Logger | undefined\n prod?: boolean | undefined\n}\n\nexport type DetectOptions = {\n cwd?: string | undefined\n onUnknown?: (pkgManager: string | undefined) => void\n}\n\nexport type PartialEnvDetails = Readonly<\n Remap<\n EnvBase & {\n agentVersion: SemVer | undefined\n editablePkgJson: EditablePackageJson | undefined\n lockName: string | undefined\n lockPath: string | undefined\n lockSrc: string | undefined\n pkgPath: string | undefined\n }\n >\n>\n\nexport type ReadLockFile =\n | ((lockPath: string) => Promise<string | undefined>)\n | ((lockPath: string, agentExecPath: string) => Promise<string | undefined>)\n | ((\n lockPath: string,\n agentExecPath: string,\n cwd: string,\n ) => Promise<string | undefined>)\n\nconst readLockFileByAgent: Map<Agent, ReadLockFile> = (() => {\n function wrapReader<T extends (...args: any[]) => Promise<any>>(\n reader: T,\n ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>> | undefined> {\n return async (...args: any[]): Promise<any> => {\n try {\n return await reader(...args)\n } catch {}\n return undefined\n }\n }\n\n const binaryReader = wrapReader(readFileBinary)\n\n const defaultReader = wrapReader(\n async (lockPath: string) => await readFileUtf8(lockPath),\n )\n\n return new Map([\n [\n BUN,\n wrapReader(\n async (\n lockPath: string,\n agentExecPath: string,\n cwd = process.cwd(),\n ) => {\n const ext = path.extname(lockPath)\n if (ext === EXT_LOCK) {\n return await defaultReader(lockPath)\n }\n if (ext === EXT_LOCKB) {\n const lockBuffer = await binaryReader(lockPath)\n if (lockBuffer) {\n try {\n return parseBunLockb(lockBuffer)\n } catch {}\n }\n // To print a Yarn lockfile to your console without writing it to disk\n // use `bun bun.lockb`.\n // https://bun.sh/guides/install/yarnlock\n return (\n await spawn(agentExecPath, [lockPath], {\n cwd,\n // On Windows, bun is often a .cmd file that requires shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\n shell: constants.WIN32,\n })\n ).stdout\n }\n return undefined\n },\n ),\n ],\n [NPM, defaultReader],\n [PNPM, defaultReader],\n [VLT, defaultReader],\n [YARN_BERRY, defaultReader],\n [YARN_CLASSIC, defaultReader],\n ])\n})()\n\n// The order of LOCKS properties IS significant as it affects iteration order.\nconst LOCKS: Record<string, Agent> = {\n [BUN_LOCK]: BUN,\n [BUN_LOCKB]: BUN,\n // If both package-lock.json and npm-shrinkwrap.json are present in the root\n // of a project, npm-shrinkwrap.json will take precedence and package-lock.json\n // will be ignored.\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson\n [NPM_SHRINKWRAP_JSON]: NPM,\n [PACKAGE_LOCK_JSON]: NPM,\n [PNPM_LOCK_YAML]: PNPM,\n [YARN_LOCK]: YARN_CLASSIC,\n [VLT_LOCK_JSON]: VLT,\n // Lastly, look for a hidden lockfile which is present if .npmrc has package-lock=false:\n // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles\n //\n // Unlike the other LOCKS keys this key contains a directory AND filename so\n // it has to be handled differently.\n [`${NODE_MODULES}/${DOT_PACKAGE_LOCK_JSON}`]: NPM,\n}\n\nasync function getAgentExecPath(agent: Agent): Promise<string> {\n const binName = binByAgent.get(agent)!\n if (binName === NPM) {\n // Try to use constants.npmExecPath first, but verify it exists.\n const npmPath = constants.npmExecPath\n if (existsSync(npmPath)) {\n return npmPath\n }\n // If npmExecPath doesn't exist, try common locations.\n // Check npm in the same directory as node.\n const nodeDir = path.dirname(process.execPath)\n const npmInNodeDir = path.join(nodeDir, NPM)\n if (existsSync(npmInNodeDir)) {\n return npmInNodeDir\n }\n // Fall back to whichBin.\n return (await whichBin(binName, { nothrow: true })) ?? binName\n }\n if (binName === PNPM) {\n // Try to use constants.pnpmExecPath first, but verify it exists.\n const pnpmPath = constants.pnpmExecPath\n if (existsSync(pnpmPath)) {\n return pnpmPath\n }\n // Fall back to whichBin.\n return (await whichBin(binName, { nothrow: true })) ?? binName\n }\n return (await whichBin(binName, { nothrow: true })) ?? binName\n}\n\nasync function getAgentVersion(\n agent: Agent,\n agentExecPath: string,\n cwd: string,\n): Promise<SemVer | undefined> {\n let result\n const quotedCmd = `\\`${agent} ${FLAG_VERSION}\\``\n debugFn('stdio', `spawn: ${quotedCmd}`)\n try {\n result =\n // Coerce version output into a valid semver version by passing it through\n // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),\n // and tildes (~).\n semver.coerce(\n // All package managers support the \"--version\" flag.\n (\n await spawn(agentExecPath, [FLAG_VERSION], {\n cwd,\n // On Windows, package managers are often .cmd files that require shell execution.\n // The spawn function from @socketsecurity/registry will handle this properly\n // when shell is true.\n shell: constants.WIN32,\n })\n ).stdout,\n ) ?? undefined\n } catch (e) {\n debugFn('error', `Package manager command failed: ${quotedCmd}`)\n debugDir('inspect', { cmd: quotedCmd })\n debugDir('error', e)\n }\n return result\n}\n\nexport async function detectPackageEnvironment({\n cwd = process.cwd(),\n onUnknown,\n}: DetectOptions = {}): Promise<EnvDetails | PartialEnvDetails> {\n let lockPath = await findUp(Object.keys(LOCKS), { cwd })\n let lockName = lockPath ? path.basename(lockPath) : undefined\n const isHiddenLockFile = lockName === DOT_PACKAGE_LOCK_JSON\n const pkgJsonPath = lockPath\n ? path.resolve(\n lockPath,\n `${isHiddenLockFile ? '../' : ''}../${PACKAGE_JSON}`,\n )\n : await findUp(PACKAGE_JSON, { cwd })\n const pkgPath =\n pkgJsonPath && existsSync(pkgJsonPath)\n ? path.dirname(pkgJsonPath)\n : undefined\n const editablePkgJson = pkgPath\n ? await readPackageJson(pkgPath, { editable: true })\n : undefined\n // Read Corepack `packageManager` field in package.json:\n // https://nodejs.org/api/packages.html#packagemanager\n const pkgManager = isNonEmptyString(editablePkgJson?.content?.packageManager)\n ? editablePkgJson.content.packageManager\n : undefined\n\n let agent: Agent | undefined\n if (pkgManager) {\n // A valid \"packageManager\" field value is \"<package manager name>@<version>\".\n // https://nodejs.org/api/packages.html#packagemanager\n const atSignIndex = pkgManager.lastIndexOf('@')\n if (atSignIndex !== -1) {\n const name = pkgManager.slice(0, atSignIndex) as Agent\n const version = pkgManager.slice(atSignIndex + 1)\n if (version && AGENTS.includes(name)) {\n agent = name\n }\n }\n }\n if (\n agent === undefined &&\n !isHiddenLockFile &&\n typeof pkgJsonPath === 'string' &&\n typeof lockName === 'string'\n ) {\n agent = LOCKS[lockName] as Agent\n }\n if (agent === undefined) {\n agent = NPM\n onUnknown?.(pkgManager)\n }\n const agentExecPath = await getAgentExecPath(agent)\n const agentVersion = await getAgentVersion(agent, agentExecPath, cwd)\n if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {\n agent = YARN_BERRY\n }\n const { maintainedNodeVersions } = constants\n const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent)!\n const minSupportedNodeMajor = semver.major(maintainedNodeVersions.last)\n const minSupportedNodeVersion = `${minSupportedNodeMajor}.0.0`\n const minSupportedNodeRange = `>=${minSupportedNodeMajor}`\n const nodeVersion = semver.coerce(process.version)!\n let lockSrc: string | undefined\n let pkgAgentRange: string | undefined\n let pkgNodeRange: string | undefined\n let pkgMinAgentVersion = minSupportedAgentVersion\n let pkgMinNodeVersion = minSupportedNodeVersion\n if (editablePkgJson?.content) {\n const { engines } = editablePkgJson.content\n const engineAgentRange = engines?.[agent]\n const engineNodeRange = engines?.['node']\n if (isNonEmptyString(engineAgentRange)) {\n pkgAgentRange = engineAgentRange\n // Roughly check agent range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n const coerced = semver.coerce(pkgAgentRange)\n if (coerced && semver.lt(coerced, pkgMinAgentVersion)) {\n pkgMinAgentVersion = coerced.version\n }\n }\n if (isNonEmptyString(engineNodeRange)) {\n pkgNodeRange = engineNodeRange\n // Roughly check Node range as semver.coerce will strip leading\n // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).\n const coerced = semver.coerce(pkgNodeRange)\n if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {\n pkgMinNodeVersion = coerced.version\n }\n }\n const browserslistQuery = editablePkgJson.content['browserslist'] as\n | string[]\n | undefined\n if (Array.isArray(browserslistQuery)) {\n // List Node targets in ascending version order.\n const browserslistNodeTargets = browserslist(browserslistQuery)\n .filter(v => /^node /i.test(v))\n .map(v => v.slice(5 /*'node '.length*/))\n .sort(naturalCompare)\n if (browserslistNodeTargets.length) {\n // browserslistNodeTargets[0] is the lowest Node target version.\n const coerced = semver.coerce(browserslistNodeTargets[0])\n if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {\n pkgMinNodeVersion = coerced.version\n }\n }\n }\n lockSrc =\n typeof lockPath === 'string'\n ? await readLockFileByAgent.get(agent)!(lockPath, agentExecPath, cwd)\n : undefined\n } else {\n lockName = undefined\n lockPath = undefined\n }\n\n // Does the system agent version meet our minimum supported agent version?\n const agentSupported =\n !!agentVersion &&\n semver.satisfies(agentVersion, `>=${minSupportedAgentVersion}`)\n // Does the system Node version meet our minimum supported Node version?\n const nodeSupported = semver.satisfies(nodeVersion, minSupportedNodeRange)\n\n const npmExecPath =\n agent === NPM ? agentExecPath : await getAgentExecPath(NPM)\n const npmBuggyOverrides =\n agent === NPM &&\n !!agentVersion &&\n semver.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION)\n\n const pkgMinAgentRange = `>=${pkgMinAgentVersion}`\n const pkgMinNodeRange = `>=${semver.major(pkgMinNodeVersion)}`\n\n return {\n agent,\n agentExecPath,\n agentSupported,\n agentVersion,\n editablePkgJson,\n features: { npmBuggyOverrides },\n lockName,\n lockPath,\n lockSrc,\n nodeSupported,\n nodeVersion,\n npmExecPath,\n pkgPath,\n pkgRequirements: {\n agent: pkgAgentRange ?? pkgMinAgentRange,\n node: pkgNodeRange ?? pkgMinNodeRange,\n },\n pkgSupports: {\n // Does our minimum supported agent version meet the package's requirements?\n agent: semver.satisfies(minSupportedAgentVersion, pkgMinAgentRange),\n // Does our supported Node versions meet the package's requirements?\n node: maintainedNodeVersions.some(v =>\n semver.satisfies(v, pkgMinNodeRange),\n ),\n },\n }\n}\n\nexport async function detectAndValidatePackageEnvironment(\n cwd: string,\n options?: DetectAndValidateOptions | undefined,\n): Promise<CResult<EnvDetails>> {\n const {\n cmdName = '',\n logger,\n prod,\n } = {\n __proto__: null,\n ...options,\n } as DetectAndValidateOptions\n const details = await detectPackageEnvironment({\n cwd,\n onUnknown(pkgManager: string | undefined) {\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to ${NPM}`,\n ),\n )\n },\n })\n const { agent, nodeVersion, pkgRequirements } = details\n const agentVersion = details.agentVersion ?? 'unknown'\n if (!details.agentSupported) {\n const minVersion = constants.minimumVersionByAgent.get(agent)!\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`,\n ),\n }\n }\n if (!details.nodeSupported) {\n const minVersion = constants.maintainedNodeVersions.last\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`,\n ),\n }\n }\n if (!details.pkgSupports.agent) {\n return {\n ok: false,\n message: 'Engine mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Package engine \"${agent}\" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`,\n ),\n }\n }\n if (!details.pkgSupports.node) {\n return {\n ok: false,\n message: 'Version mismatch',\n cause: cmdPrefixMessage(\n cmdName,\n `Package engine \"node\" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`,\n ),\n }\n }\n const lockName = details.lockName ?? 'lockfile'\n if (details.lockName === undefined || details.lockSrc === undefined) {\n return {\n ok: false,\n message: 'Missing lockfile',\n cause: cmdPrefixMessage(cmdName, `No ${lockName} found`),\n }\n }\n if (details.lockSrc.trim() === '') {\n return {\n ok: false,\n message: 'Empty lockfile',\n cause: cmdPrefixMessage(cmdName, `${lockName} is empty`),\n }\n }\n if (details.pkgPath === undefined) {\n return {\n ok: false,\n message: 'Missing package.json',\n cause: cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`),\n }\n }\n if (prod && (agent === BUN || agent === YARN_BERRY)) {\n return {\n ok: false,\n message: 'Bad input',\n cause: cmdPrefixMessage(\n cmdName,\n `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`,\n ),\n }\n }\n if (\n details.lockPath &&\n path.relative(cwd, details.lockPath).startsWith('.')\n ) {\n // Note: In tests we return <redacted> because otherwise snapshots will fail.\n logger?.warn(\n cmdPrefixMessage(\n cmdName,\n `Package ${lockName} found at ${constants.ENV.VITEST ? constants.REDACTED : details.lockPath}`,\n ),\n )\n }\n return { ok: true, data: details as EnvDetails }\n}\n","/**\n * Ecosystem type utilities for Socket CLI.\n * Manages package ecosystem identifiers and mappings.\n *\n * Constants:\n * - ALL_ECOSYSTEMS: Complete list of supported ecosystems\n * - ECOSYSTEM_MAP: Map ecosystem strings to PURL types\n *\n * Type Definitions:\n * - PURL_Type: Package URL type from Socket SDK\n *\n * Supported Ecosystems:\n * - apk, bitbucket, cargo, chrome, cocoapods, composer\n * - conan, conda, cran, deb, docker, gem, generic\n * - github, gitlab, go, hackage, hex, huggingface\n * - maven, mlflow, npm, nuget, oci, pub, pypi, rpm, swift\n *\n * Usage:\n * - Validates ecosystem types\n * - Maps between different ecosystem representations\n * - Ensures type safety for ecosystem operations\n */\n\nimport { NPM } from '../constants.mts'\n\nimport type { EcosystemString } from '@socketsecurity/registry'\nimport type { components } from '@socketsecurity/sdk/types/api'\n\nexport type PURL_Type = components['schemas']['SocketPURL_Type']\n\ntype ExpectNever<T extends never> = T\n\ntype MissingInEcosystemString = Exclude<PURL_Type, EcosystemString>\ntype ExtraInEcosystemString = Exclude<EcosystemString, PURL_Type>\n\nexport type _Check_EcosystemString_has_all_purl_types =\n ExpectNever<MissingInEcosystemString>\nexport type _Check_EcosystemString_has_no_extras =\n ExpectNever<ExtraInEcosystemString>\n\nexport const ALL_ECOSYSTEMS = [\n 'apk',\n 'bitbucket',\n 'cargo',\n 'chrome',\n 'cocoapods',\n 'composer',\n 'conan',\n 'conda',\n 'cran',\n 'deb',\n 'docker',\n 'gem',\n 'generic',\n 'github',\n 'golang',\n 'hackage',\n 'hex',\n 'huggingface',\n 'maven',\n 'mlflow',\n NPM,\n 'nuget',\n 'oci',\n 'pub',\n 'pypi',\n 'qpkg',\n 'rpm',\n 'swift',\n 'swid',\n 'unknown',\n] as const satisfies readonly PURL_Type[]\n\ntype AllEcosystemsUnion = (typeof ALL_ECOSYSTEMS)[number]\ntype MissingInAllEcosystems = Exclude<PURL_Type, AllEcosystemsUnion>\ntype ExtraInAllEcosystems = Exclude<AllEcosystemsUnion, PURL_Type>\n\nexport type _Check_ALL_ECOSYSTEMS_has_all_purl_types =\n ExpectNever<MissingInAllEcosystems>\nexport type _Check_ALL_ECOSYSTEMS_has_no_extras =\n ExpectNever<ExtraInAllEcosystems>\n\nexport const ALL_SUPPORTED_ECOSYSTEMS = new Set<string>(ALL_ECOSYSTEMS)\n\nexport function getEcosystemChoicesForMeow(): string[] {\n return [...ALL_ECOSYSTEMS]\n}\n\nexport function isValidEcosystem(value: string): value is PURL_Type {\n return ALL_SUPPORTED_ECOSYSTEMS.has(value)\n}\n\nexport function parseEcosystems(\n value: string | string[] | undefined,\n): PURL_Type[] {\n if (!value) {\n return []\n }\n const values =\n typeof value === 'string'\n ? value.split(',').map(v => v.trim().toLowerCase())\n : value.map(v => String(v).toLowerCase())\n\n return values.filter(isValidEcosystem)\n}\n","/**\n * Temporary package executor detection utilities for Socket CLI.\n * Identifies and handles temporary execution contexts.\n *\n * Key Functions:\n * - isRunningInTemporaryExecutor: Detects if running in npx/dlx/exec context\n * - shouldSkipShadow: Determines if shadow installation should be skipped\n *\n * Temporary Execution Contexts:\n * - npm exec/npx: Runs packages in temporary npm cache\n * - pnpm dlx: Executes packages in temporary pnpm store\n * - yarn dlx: Runs packages in temporary yarn environment\n *\n * Detection Methods:\n * - Environment variable analysis (npm_config_user_agent)\n * - Path pattern matching for temporary directories\n * - Cache directory identification\n *\n * Usage:\n * - Prevents shadow installation in temporary contexts\n * - Avoids PATH pollution in ephemeral environments\n * - Ensures package manager commands work correctly\n */\n\nimport path from 'node:path'\n\nimport { normalizePath } from '@socketsecurity/registry/lib/path'\n\nimport constants from '../constants.mts'\n\n/**\n * Detects if the current process is running in a temporary package execution context\n * such as npm exec, npx, pnpm dlx, or yarn dlx.\n *\n * When package managers run commands via exec/npx/dlx, they execute in temporary directories\n * that are cleaned up after execution. Creating persistent shadows or modifying PATH\n * in these contexts can break subsequent package manager commands.\n *\n * @returns true if running in an exec/npx/dlx context, false otherwise\n */\nexport function isRunningInTemporaryExecutor(): boolean {\n // Check environment variable for exec/npx/dlx indicators.\n const userAgent = constants.ENV.npm_config_user_agent\n if (\n userAgent?.includes('exec') ||\n userAgent?.includes('npx') ||\n userAgent?.includes('dlx')\n ) {\n return true\n }\n\n // Normalize the __dirname path for consistent checking across platforms.\n const normalizedDirname = normalizePath(__dirname)\n\n // Check if running from npm's npx cache.\n const npmCache = constants.ENV.npm_config_cache\n if (npmCache && normalizedDirname.includes(normalizePath(npmCache))) {\n return true\n }\n\n // Check common temporary execution path patterns.\n const tempPatterns = [\n '_npx', // npm's npx cache directory\n '.pnpm-store', // pnpm dlx temporary store\n 'dlx-', // Common dlx directory prefix\n '.yarn/$$', // Yarn Berry PnP virtual packages\n path.sep === '\\\\'\n ? 'AppData\\\\Local\\\\Temp\\\\xfs-'\n : 'AppData/Local/Temp/xfs-', // Yarn on Windows\n ]\n\n return tempPatterns.some(pattern => normalizedDirname.includes(pattern))\n}\n\nexport type ShadowInstallationOptions = {\n cwd?: string | undefined\n win32?: boolean | undefined\n}\n\n/**\n * Determines if shadow binaries should be installed.\n * Shadows should NOT be installed when:\n * - Running in a temporary execution context (exec/npx/dlx)\n * - On Windows with an existing binary path (required for Windows to function)\n *\n * @param binPath - Path to the binary being shadowed\n * @param options - Configuration options\n * @param options.cwd - Current working directory path to check\n * @param options.win32 - Whether running on Windows\n * @returns true if shadow installation should be skipped\n */\nexport function shouldSkipShadow(\n binPath: string,\n options: ShadowInstallationOptions,\n): boolean {\n const { cwd = process.cwd(), win32 = false } = {\n __proto__: null,\n ...options,\n } as ShadowInstallationOptions\n\n // Windows compatibility: Skip shadow installation if binary is already found.\n //\n // This check is required because Windows handles executables differently than Unix:\n // 1. File locking - Windows locks running executables, so cmd-shim creation would\n // fail with EBUSY/EACCES errors when trying to create wrapper files.\n // 2. PATH conflicts - Attempting to shadow an already-resolved binary can create\n // circular references or ambiguous command resolution.\n // 3. Registry integration - Windows package managers often use system-level\n // integrations beyond just PATH that our shadowing would interfere with.\n //\n // Without this check, users would see \"Access Denied\" or file locking errors\n // that are difficult to debug. This is not a performance optimization - the\n // shadow installation will fail without it.\n if (win32 && binPath) {\n return true\n }\n\n // Check environment variable for exec/npx/dlx indicators.\n const userAgent = constants.ENV.npm_config_user_agent\n if (\n userAgent?.includes('exec') ||\n userAgent?.includes('npx') ||\n userAgent?.includes('dlx')\n ) {\n return true\n }\n\n // Normalize the cwd path for consistent checking across platforms.\n const normalizedCwd = normalizePath(cwd)\n\n // Check if running from npm's npx cache.\n const npmCache = constants.ENV.npm_config_cache\n if (npmCache && normalizedCwd.includes(normalizePath(npmCache))) {\n return true\n }\n\n // Check common temporary execution path patterns.\n const tempPatterns = [\n '_npx', // npm's npx cache directory\n '.pnpm-store', // pnpm dlx temporary store\n 'dlx-', // Common dlx directory prefix\n '.yarn/$$', // Yarn Berry PnP virtual packages\n path.sep === '\\\\'\n ? 'AppData\\\\Local\\\\Temp\\\\xfs-'\n : 'AppData/Local/Temp/xfs-', // Yarn on Windows\n ]\n\n return tempPatterns.some(pattern => normalizedCwd.includes(pattern))\n}\n","/**\n * PNPM path resolution utilities for Socket CLI.\n * Locates and caches PNPM binary paths.\n *\n * Key Functions:\n * - getPnpmBinPath: Get cached PNPM binary path\n * - getPnpmBinPathDetails: Get detailed PNPM path information\n *\n * Error Handling:\n * - Exits with code 127 if PNPM not found\n * - Provides clear error messages for missing binaries\n *\n * Caching:\n * - Caches binary path lookups for performance\n * - Prevents repeated PATH searches\n */\n\nimport { logger } from '@socketsecurity/registry/lib/logger'\n\nimport { findBinPathDetailsSync } from './path-resolve.mts'\n\nfunction exitWithBinPathError(binName: string): never {\n logger.fail(\n `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`,\n )\n // The exit code 127 indicates that the command or binary being executed\n // could not be found.\n // eslint-disable-next-line n/no-process-exit\n process.exit(127)\n // This line is never reached in production, but helps tests.\n throw new Error('process.exit called')\n}\n\nlet _pnpmBinPath: string | undefined\nexport function getPnpmBinPath(): string {\n if (_pnpmBinPath === undefined) {\n _pnpmBinPath = getPnpmBinPathDetails().path\n if (!_pnpmBinPath) {\n exitWithBinPathError('pnpm')\n }\n }\n return _pnpmBinPath\n}\n\nlet _pnpmBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined\nexport function getPnpmBinPathDetails(): ReturnType<\n typeof findBinPathDetailsSync\n> {\n if (_pnpmBinPathDetails === undefined) {\n _pnpmBinPathDetails = findBinPathDetailsSync('pnpm')\n }\n return _pnpmBinPathDetails\n}\n\nexport function isPnpmBinPathShadowed(): boolean {\n return getPnpmBinPathDetails().shadowed\n}\n","/**\n * Shadow binary link installation utilities for Socket CLI.\n * Manages installation of shadow binaries for package managers.\n *\n * Key Functions:\n * - installNpmLinks: Install shadow links for npm binary\n * - installNpxLinks: Install shadow links for npx binary\n * - installPnpmLinks: Install shadow links for pnpm binary\n * - installYarnLinks: Install shadow links for yarn binary\n *\n * Shadow Installation:\n * - Creates symlinks/cmd-shims to intercept package manager commands\n * - Modifies PATH to prioritize shadow binaries\n * - Skips installation in temporary execution contexts\n *\n * Security Integration:\n * - Enables security scanning before package operations\n * - Transparent interception of package manager commands\n * - Preserves original binary functionality\n */\n\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport cmdShim from 'cmd-shim'\n\nimport constants from '../constants.mts'\nimport { shouldSkipShadow } from './dlx-detection.mts'\nimport {\n getNpmBinPath,\n getNpxBinPath,\n isNpmBinPathShadowed,\n isNpxBinPathShadowed,\n} from './npm-paths.mts'\nimport { getPnpmBinPath, isPnpmBinPathShadowed } from './pnpm-paths.mts'\nimport { getYarnBinPath, isYarnBinPathShadowed } from './yarn-paths.mts'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\nexport async function installNpmLinks(shadowBinPath: string): Promise<string> {\n // Find npm being shadowed by this process.\n const binPath = getNpmBinPath()\n const { WIN32 } = constants\n\n // Skip shadow installation when in temporary execution context or when required for Windows.\n if (shouldSkipShadow(binPath, { cwd: __dirname, win32: WIN32 })) {\n return binPath\n }\n const shadowed = isNpmBinPathShadowed()\n // Move our bin directory to front of PATH so its found first.\n if (!shadowed) {\n if (WIN32) {\n await cmdShim(\n path.join(constants.distPath, 'npm-cli.js'),\n path.join(shadowBinPath, 'npm'),\n )\n }\n const { env } = process\n env['PATH'] = `${shadowBinPath}${path.delimiter}${env['PATH']}`\n }\n return binPath\n}\n\nexport async function installNpxLinks(shadowBinPath: string): Promise<string> {\n // Find npx being shadowed by this process.\n const binPath = getNpxBinPath()\n const { WIN32 } = constants\n\n // Skip shadow installation when in temporary execution context or when required for Windows.\n if (shouldSkipShadow(binPath, { cwd: __dirname, win32: WIN32 })) {\n return binPath\n }\n const shadowed = isNpxBinPathShadowed()\n // Move our bin directory to front of PATH so its found first.\n if (!shadowed) {\n if (WIN32) {\n await cmdShim(\n path.join(constants.distPath, 'npx-cli.js'),\n path.join(shadowBinPath, 'npx'),\n )\n }\n const { env } = process\n env['PATH'] = `${shadowBinPath}${path.delimiter}${env['PATH']}`\n }\n return binPath\n}\n\nexport async function installPnpmLinks(shadowBinPath: string): Promise<string> {\n // Find pnpm being shadowed by this process.\n const binPath = getPnpmBinPath()\n const { WIN32 } = constants\n\n // Skip shadow installation when in temporary execution context or when required for Windows.\n if (shouldSkipShadow(binPath, { cwd: __dirname, win32: WIN32 })) {\n return binPath\n }\n\n const shadowed = isPnpmBinPathShadowed()\n\n // Move our bin directory to front of PATH so its found first.\n if (!shadowed) {\n if (WIN32) {\n await cmdShim(\n path.join(constants.distPath, 'pnpm-cli.js'),\n path.join(shadowBinPath, 'pnpm'),\n )\n }\n const { env } = process\n env['PATH'] = `${shadowBinPath}${path.delimiter}${env['PATH']}`\n }\n\n return binPath\n}\n\nexport async function installYarnLinks(shadowBinPath: string): Promise<string> {\n const binPath = getYarnBinPath()\n const { WIN32 } = constants\n\n // Skip shadow installation when in temporary execution context or when required for Windows.\n if (shouldSkipShadow(binPath, { cwd: __dirname, win32: WIN32 })) {\n return binPath\n }\n\n const shadowed = isYarnBinPathShadowed()\n\n if (!shadowed) {\n if (WIN32) {\n await cmdShim(\n path.join(constants.distPath, 'yarn-cli.js'),\n path.join(shadowBinPath, 'yarn'),\n )\n }\n const { env } = process\n env['PATH'] = `${shadowBinPath}${path.delimiter}${env['PATH']}`\n }\n\n return binPath\n}\n","/**\n * Filter configuration utilities for Socket CLI.\n * Manages filter configuration normalization for security scanning.\n *\n * Key Functions:\n * - toFilterConfig: Normalize filter configuration objects\n *\n * Usage:\n * - Normalizes user-provided filter objects\n * - Ensures proper structure for filter configuration\n * - Validates boolean and array values\n */\n\nimport { isObject } from '@socketsecurity/registry/lib/objects'\n\nexport type FilterConfig = {\n [key: string]: boolean | string[]\n}\n\nexport function toFilterConfig(obj: any): FilterConfig {\n const normalized = { __proto__: null } as unknown as FilterConfig\n const keys = isObject(obj) ? Object.keys(obj) : []\n for (const key of keys) {\n const value = obj[key]\n if (typeof value === 'boolean' || Array.isArray(value)) {\n normalized[key] = value\n }\n }\n return normalized\n}\n","import semver from 'semver'\n\nimport { NPM } from '../constants.mts'\nimport { stripPnpmPeerSuffix } from './pnpm.mts'\n\nimport type { PackageURL } from '@socketregistry/packageurl-js'\n\nexport function idToNpmPurl(id: string): string {\n return `pkg:${NPM}/${id}`\n}\n\nexport function idToPurl(id: string, type: string): string {\n return `pkg:${type}/${id}`\n}\n\nexport function resolvePackageVersion(purlObj: PackageURL): string {\n const { version } = purlObj\n if (!version) {\n return ''\n }\n const { type } = purlObj\n return (\n semver.coerce(type === NPM ? stripPnpmPeerSuffix(version) : version)\n ?.version ?? ''\n )\n}\n","import { existsSync } from 'node:fs'\n\nimport yaml from 'js-yaml'\nimport semver from 'semver'\n\nimport { readFileUtf8 } from '@socketsecurity/registry/lib/fs'\nimport { isObjectObject } from '@socketsecurity/registry/lib/objects'\nimport { stripBom } from '@socketsecurity/registry/lib/strings'\n\nimport { idToNpmPurl } from './spec.mts'\n\nimport type { LockfileObject, PackageSnapshot } from '@pnpm/lockfile.fs'\nimport type { SemVer } from 'semver'\n\nexport function extractOverridesFromPnpmLockSrc(lockfileContent: any): string {\n let match\n if (typeof lockfileContent === 'string') {\n match = /^overrides:(?:\\r?\\n {2}.+)+(?:\\r?\\n)*/m.exec(lockfileContent)?.[0]\n }\n return match ?? ''\n}\n\nexport async function extractPurlsFromPnpmLockfile(\n lockfile: LockfileObject,\n): Promise<string[]> {\n const packages = lockfile?.packages ?? {}\n const seen = new Set<string>()\n const visit = (pkgPath: string) => {\n if (seen.has(pkgPath)) {\n return\n }\n const pkg = (packages as any)[pkgPath] as PackageSnapshot\n if (!pkg) {\n return\n }\n seen.add(pkgPath)\n const deps: { [name: string]: string } = {\n __proto__: null,\n ...pkg.dependencies,\n ...pkg.optionalDependencies,\n ...(pkg as any).devDependencies,\n }\n for (const depName in deps) {\n const ref = deps[depName]!\n const subKey = isPnpmDepPath(ref) ? ref : `/${depName}@${ref}`\n visit(subKey)\n }\n }\n for (const pkgPath of Object.keys(packages)) {\n visit(pkgPath)\n }\n return Array.from(seen).map(p =>\n idToNpmPurl(stripPnpmPeerSuffix(stripLeadingPnpmDepPathSlash(p))),\n )\n}\n\nexport function isPnpmDepPath(maybeDepPath: string): boolean {\n return maybeDepPath.length > 0 && maybeDepPath.charCodeAt(0) === 47 /*'/'*/\n}\n\nexport function parsePnpmLockfile(\n lockfileContent: unknown,\n): LockfileObject | null {\n let result\n if (typeof lockfileContent === 'string') {\n try {\n result = yaml.load(stripBom(lockfileContent))\n } catch {}\n }\n return isObjectObject(result) ? (result as LockfileObject) : null\n}\n\nexport function parsePnpmLockfileVersion(version: unknown): SemVer | undefined {\n try {\n return semver.coerce(version as string) ?? undefined\n } catch {}\n return undefined\n}\n\nexport async function readPnpmLockfile(\n lockfilePath: string,\n): Promise<string | undefined> {\n return existsSync(lockfilePath) ? await readFileUtf8(lockfilePath) : undefined\n}\n\nexport function stripLeadingPnpmDepPathSlash(depPath: string): string {\n return isPnpmDepPath(depPath) ? depPath.slice(1) : depPath\n}\n\nexport function stripPnpmPeerSuffix(depPath: string): string {\n const parenIndex = depPath.indexOf('(')\n const index = parenIndex === -1 ? depPath.indexOf('_') : parenIndex\n return index === -1 ? depPath : depPath.slice(0, index)\n}\n","import constants from '../../constants.mts'\n\nimport type { Remap } from '@socketsecurity/registry/lib/objects'\nimport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n SocketArtifact,\n SocketArtifactAlert,\n} from '@socketsecurity/sdk'\n\nexport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n SocketArtifact,\n SocketArtifactAlert,\n}\n\nexport type CVE_ALERT_TYPE = 'cve' | 'mediumCVE' | 'mildCVE' | 'criticalCVE'\n\nexport type ArtifactAlertCve = Remap<\n Omit<CompactSocketArtifactAlert, 'type'> & {\n type: CVE_ALERT_TYPE\n }\n>\n\nexport type ArtifactAlertCveFixable = Remap<\n Omit<CompactSocketArtifactAlert, 'props' | 'type'> & {\n type: CVE_ALERT_TYPE\n props: CveProps\n }\n>\n\nexport type ArtifactAlertUpgrade = Remap<\n Omit<CompactSocketArtifactAlert, 'type'> & {\n type: 'socketUpgradeAvailable'\n }\n>\n\nexport type CveProps = {\n firstPatchedVersionIdentifier?: string | undefined\n vulnerableVersionRange: string\n [key: string]: any\n}\n\nexport function isArtifactAlertCve(\n alert: CompactSocketArtifactAlert,\n): alert is ArtifactAlertCve {\n const { type } = alert\n return (\n type === constants.ALERT_TYPE_CVE ||\n type === constants.ALERT_TYPE_MEDIUM_CVE ||\n type === constants.ALERT_TYPE_MILD_CVE ||\n type === constants.ALERT_TYPE_CRITICAL_CVE\n )\n}\n","export function createEnum<const T extends Record<string, any>>(\n obj: T,\n): Readonly<T> {\n return Object.freeze({ __proto__: null, ...obj }) as any\n}\n\nexport function pick<T extends Record<string, any>, K extends keyof T>(\n input: T,\n keys: K[] | readonly K[],\n): Pick<T, K> {\n const result: Partial<Pick<T, K>> = {}\n for (const key of keys) {\n result[key] = input[key]\n }\n return result as Pick<T, K>\n}\n","import { createEnum } from '../objects.mts'\n\nexport const ALERT_FIX_TYPE = createEnum({\n cve: 'cve',\n remove: 'remove',\n upgrade: 'upgrade',\n})\n","import { joinAnd } from '@socketsecurity/registry/lib/arrays'\n\nimport { createEnum, pick } from '../objects.mts'\n\nimport type { SocketSdkSuccessResult } from '@socketsecurity/sdk'\n\nexport const ALERT_SEVERITY = createEnum({\n critical: 'critical',\n high: 'high',\n middle: 'middle',\n low: 'low',\n})\n\nexport type SocketSdkAlertList =\n SocketSdkSuccessResult<'getIssuesByNPMPackage'>['data']\n\nexport type SocketSdkAlert = SocketSdkAlertList[number]['value'] extends\n | infer U\n | undefined\n ? U\n : never\n\n// Ordered from most severe to least.\nexport const ALERT_SEVERITIES_SORTED: ReadonlyArray<\n SocketSdkAlert['severity']\n> = Object.freeze(['critical', 'high', 'middle', 'low'])\n\nfunction getDesiredSeverities(\n lowestToInclude: SocketSdkAlert['severity'] | undefined,\n): Array<SocketSdkAlert['severity']> {\n const result: Array<SocketSdkAlert['severity']> = []\n for (const severity of ALERT_SEVERITIES_SORTED) {\n result.push(severity)\n if (severity === lowestToInclude) {\n break\n }\n }\n return result\n}\n\nexport function formatSeverityCount(\n severityCount: Record<SocketSdkAlert['severity'], number>,\n): string {\n const summary: string[] = []\n for (const severity of ALERT_SEVERITIES_SORTED) {\n if (severityCount[severity]) {\n summary.push(`${severityCount[severity]} ${severity}`)\n }\n }\n return joinAnd(summary)\n}\n\nexport function getSeverityCount(\n issues: SocketSdkAlertList,\n lowestToInclude: SocketSdkAlert['severity'] | undefined,\n): Record<SocketSdkAlert['severity'], number> {\n const severityCount = pick(\n { low: 0, middle: 0, high: 0, critical: 0 },\n getDesiredSeverities(lowestToInclude),\n ) as Record<SocketSdkAlert['severity'], number>\n\n for (const issue of issues) {\n const { value } = issue\n if (!value) {\n continue\n }\n const { severity } = value\n if (severityCount[severity] !== undefined) {\n severityCount[severity] += 1\n }\n }\n return severityCount\n}\n","/**\n * Color and markdown formatting utilities for Socket CLI.\n * Provides dual-mode formatting for terminal colors or markdown output.\n *\n * Key Class:\n * - ColorOrMarkdown: Dual-mode formatter for terminal/markdown output\n *\n * Formatting Methods:\n * - bold: Bold text formatting\n * - codeBlock: Code block formatting\n * - codeInline: Inline code formatting\n * - header: Section headers\n * - hyperlink: Clickable links\n * - indent: Text indentation\n * - italic: Italic text formatting\n * - list: Bullet list formatting\n * - table: Table formatting\n *\n * Usage:\n * - Switches between terminal colors and markdown based on output format\n * - Supports both interactive terminal and report generation\n * - Handles hyperlink fallbacks for terminals without link support\n */\n\nimport terminalLink from 'terminal-link'\nimport colors from 'yoctocolors-cjs'\n\nimport indentString from '@socketregistry/indent-string/index.cjs'\n\nexport class ColorOrMarkdown {\n public useMarkdown: boolean\n\n constructor(useMarkdown: boolean) {\n this.useMarkdown = !!useMarkdown\n }\n\n bold(text: string): string {\n return this.useMarkdown ? `**${text}**` : colors.bold(`${text}`)\n }\n\n header(text: string, level = 1): string {\n return this.useMarkdown\n ? `\\n${''.padStart(level, '#')} ${text}\\n`\n : colors.underline(`\\n${level === 1 ? colors.bold(text) : text}\\n`)\n }\n\n hyperlink(\n text: string,\n url: string | undefined,\n {\n fallback = true,\n fallbackToUrl,\n }: {\n fallback?: boolean | undefined\n fallbackToUrl?: boolean | undefined\n } = {},\n ) {\n if (url) {\n return this.useMarkdown\n ? `[${text}](${url})`\n : terminalLink(text, url, {\n fallback: fallbackToUrl ? (_text, url) => url : fallback,\n })\n }\n return text\n }\n\n indent(\n ...args: Parameters<typeof indentString>\n ): ReturnType<typeof indentString> {\n return indentString(...args)\n }\n\n italic(text: string): string {\n return this.useMarkdown ? `_${text}_` : colors.italic(`${text}`)\n }\n\n json(value: any): string {\n return this.useMarkdown\n ? '```json\\n' + JSON.stringify(value) + '\\n```'\n : JSON.stringify(value)\n }\n\n list(items: string[]): string {\n const indentedContent = items.map(item => this.indent(item).trimStart())\n return this.useMarkdown\n ? `* ${indentedContent.join('\\n* ')}\\n`\n : `${indentedContent.join('\\n')}\\n`\n }\n}\n","import { createRequire } from 'node:module'\nimport path from 'node:path'\n\nimport constants from '../constants.mts'\n\nconst require = createRequire(import.meta.url)\n\nlet _translations:\n | Readonly<typeof import('../../translations.json')>\n | undefined\n\nexport function getTranslations() {\n if (_translations === undefined) {\n _translations = /*@__PURE__*/ require(\n path.join(constants.rootPath, 'translations.json'),\n )\n }\n return _translations!\n}\n","/**\n * Socket package alert utilities for Socket CLI.\n * Handles security alerts, vulnerabilities, and package risk assessment.\n *\n * Key Functions:\n * - addArtifactToAlertsMap: Add security alert to package map\n * - logAlertsMap: Display alerts in formatted output\n * - shouldSkipPackageAlert: Filter alerts based on criteria\n *\n * Alert Types:\n * - CVE: Common Vulnerabilities and Exposures\n * - GHSA: GitHub Security Advisories\n * - Package quality issues\n * - Supply chain risks\n *\n * Features:\n * - Alert severity classification (critical/high/medium/low)\n * - Fix type detection (major/minor/patch/none)\n * - Alert filtering and suppression\n * - Colorized terminal output\n */\n\nimport semver from 'semver'\nimport colors from 'yoctocolors-cjs'\n\nimport { getManifestData } from '@socketsecurity/registry'\nimport { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'\nimport { getOwn, hasOwn } from '@socketsecurity/registry/lib/objects'\nimport { resolvePackageName } from '@socketsecurity/registry/lib/packages'\nimport { naturalCompare } from '@socketsecurity/registry/lib/sorts'\n\nimport { isArtifactAlertCve } from './alert/artifact.mts'\nimport { ALERT_FIX_TYPE } from './alert/fix.mts'\nimport { ALERT_SEVERITY } from './alert/severity.mts'\nimport { ColorOrMarkdown } from './color-or-markdown.mts'\nimport { toFilterConfig } from './filter-config.mts'\nimport { createEnum } from './objects.mts'\nimport { createPurlObject, getPurlObject } from './purl.mts'\nimport { getMajor } from './semver.mts'\nimport { getSocketDevPackageOverviewUrl } from './socket-url.mts'\nimport { getTranslations } from './translations.mts'\n\nimport type {\n ALERT_ACTION,\n ALERT_TYPE,\n CompactSocketArtifact,\n CompactSocketArtifactAlert,\n CveProps,\n} from './alert/artifact.mts'\nimport type { PURL_Type } from './ecosystem.mts'\nimport type { SocketYml } from '@socketsecurity/config'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport const ALERT_SEVERITY_COLOR = createEnum({\n critical: 'magenta',\n high: 'red',\n middle: 'yellow',\n low: 'white',\n})\n\nexport const ALERT_SEVERITY_ORDER = createEnum({\n critical: 0,\n high: 1,\n middle: 2,\n low: 3,\n none: 4,\n})\n\nexport type SocketPackageAlert = {\n name: string\n version: string\n key: string\n type: string\n blocked: boolean\n critical: boolean\n ecosystem: PURL_Type\n fixable: boolean\n raw: CompactSocketArtifactAlert\n upgradable: boolean\n}\n\nexport type AlertsByPurl = Map<string, SocketPackageAlert[]>\n\nconst MIN_ABOVE_THE_FOLD_COUNT = 3\n\nconst MIN_ABOVE_THE_FOLD_ALERT_COUNT = 1\n\nconst format = new ColorOrMarkdown(false)\n\nexport type RiskCounts = {\n critical: number\n high: number\n middle: number\n low: number\n}\n\nfunction getHiddenRiskCounts(hiddenAlerts: SocketPackageAlert[]): RiskCounts {\n const riskCounts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0,\n }\n for (const alert of hiddenAlerts) {\n switch (getAlertSeverityOrder(alert)) {\n case ALERT_SEVERITY_ORDER.critical:\n riskCounts.critical += 1\n break\n case ALERT_SEVERITY_ORDER.high:\n riskCounts.high += 1\n break\n case ALERT_SEVERITY_ORDER.middle:\n riskCounts.middle += 1\n break\n case ALERT_SEVERITY_ORDER.low:\n riskCounts.low += 1\n break\n }\n }\n return riskCounts\n}\n\nfunction getHiddenRisksDescription(riskCounts: RiskCounts): string {\n const descriptions: string[] = []\n if (riskCounts.critical) {\n descriptions.push(`${riskCounts.critical} ${getSeverityLabel('critical')}`)\n }\n if (riskCounts.high) {\n descriptions.push(`${riskCounts.high} ${getSeverityLabel('high')}`)\n }\n if (riskCounts.middle) {\n descriptions.push(`${riskCounts.middle} ${getSeverityLabel('middle')}`)\n }\n if (riskCounts.low) {\n descriptions.push(`${riskCounts.low} ${getSeverityLabel('low')}`)\n }\n return `(${descriptions.join('; ')})`\n}\n\nexport type AlertFilter = {\n actions?: ALERT_ACTION[] | undefined\n blocked?: boolean | undefined\n critical?: boolean | undefined\n cve?: boolean | undefined\n existing?: boolean | undefined\n fixable?: boolean | undefined\n upgradable?: boolean | undefined\n}\n\nexport type AddArtifactToAlertsMapOptions = {\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n overrides?: { [key: string]: string } | undefined\n socketYml?: SocketYml | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function addArtifactToAlertsMap<T extends AlertsByPurl>(\n artifact: CompactSocketArtifact,\n alertsByPurl: T,\n options?: AddArtifactToAlertsMapOptions | undefined,\n): Promise<T> {\n // Make TypeScript happy.\n if (!artifact.name || !artifact.version || !artifact.alerts?.length) {\n return alertsByPurl\n }\n\n const { type: ecosystem, version } = artifact\n\n const {\n consolidate = false,\n overrides,\n socketYml,\n } = {\n __proto__: null,\n ...options,\n } as AddArtifactToAlertsMapOptions\n\n const name = resolvePackageName(\n artifact as {\n name: string\n namespace?: string | undefined\n },\n )\n\n const filterConfig = toFilterConfig({\n blocked: true,\n critical: true,\n cve: true,\n ...getOwn(options, 'filter'),\n }) as AlertFilter\n\n const enabledState = {\n __proto__: null,\n ...socketYml?.issueRules,\n } as Partial<Record<ALERT_TYPE, boolean>>\n\n let sockPkgAlerts: SocketPackageAlert[] = []\n for (const alert of artifact.alerts) {\n const action = alert.action ?? ''\n const enabledFlag = enabledState[alert.type]\n if (\n (action === 'ignore' && enabledFlag !== true) ||\n enabledFlag === false\n ) {\n continue\n }\n const blocked = action === 'error'\n const critical = alert.severity === ALERT_SEVERITY.critical\n const cve = isArtifactAlertCve(alert)\n const fixType = alert.fix?.type ?? ''\n const fixableCve = fixType === ALERT_FIX_TYPE.cve\n const fixableUpgrade = fixType === ALERT_FIX_TYPE.upgrade\n const fixable = fixableCve || fixableUpgrade\n const upgradable = fixableUpgrade && !hasOwn(overrides, name)\n if (\n (filterConfig.blocked && blocked) ||\n (filterConfig.critical && critical) ||\n (filterConfig.cve && cve) ||\n (filterConfig.fixable && fixable) ||\n (filterConfig.upgradable && upgradable)\n ) {\n sockPkgAlerts.push({\n name,\n version,\n key: alert.key,\n type: alert.type,\n blocked,\n critical,\n ecosystem,\n fixable,\n raw: alert,\n upgradable,\n })\n }\n }\n if (!sockPkgAlerts.length) {\n return alertsByPurl\n }\n const purl = `pkg:${ecosystem}/${name}@${version}`\n const major = getMajor(version)!\n if (consolidate) {\n type HighestVersionByMajor = Map<\n number,\n { alert: SocketPackageAlert; version: string }\n >\n const highestForCve: HighestVersionByMajor = new Map()\n const highestForUpgrade: HighestVersionByMajor = new Map()\n const unfixableAlerts: SocketPackageAlert[] = []\n for (const sockPkgAlert of sockPkgAlerts) {\n const alert = sockPkgAlert.raw\n const fixType = alert.fix?.type ?? ''\n if (fixType === ALERT_FIX_TYPE.cve) {\n // An alert with alert.fix.type of 'cve' should have a\n // alert.props.firstPatchedVersionIdentifier property value.\n // We're just being cautious.\n const firstPatchedVersionIdentifier = (alert.props as CveProps)\n ?.firstPatchedVersionIdentifier\n const patchedMajor = firstPatchedVersionIdentifier\n ? getMajor(firstPatchedVersionIdentifier)\n : null\n if (typeof patchedMajor === 'number') {\n // Consolidate to the highest \"first patched version\" by each major\n // version number.\n const highest = highestForCve.get(patchedMajor)?.version ?? '0.0.0'\n if (semver.gt(firstPatchedVersionIdentifier!, highest)) {\n highestForCve.set(patchedMajor, {\n alert: sockPkgAlert,\n version: firstPatchedVersionIdentifier!,\n })\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n } else if (fixType === ALERT_FIX_TYPE.upgrade) {\n // For Socket Optimize upgrades we assume the highest version available\n // is compatible. This may change in the future.\n const highest = highestForUpgrade.get(major)?.version ?? '0.0.0'\n if (semver.gt(version, highest)) {\n highestForUpgrade.set(major, { alert: sockPkgAlert, version })\n }\n } else {\n unfixableAlerts.push(sockPkgAlert)\n }\n }\n sockPkgAlerts = [\n // Sort CVE alerts by severity: critical, high, middle, then low.\n ...Array.from(highestForCve.values())\n .map(d => d.alert)\n .sort(alertSeverityComparator),\n ...Array.from(highestForUpgrade.values()).map(d => d.alert),\n ...unfixableAlerts,\n ]\n } else {\n sockPkgAlerts.sort((a, b) => naturalCompare(a.type, b.type))\n }\n if (sockPkgAlerts.length) {\n alertsByPurl.set(purl, sockPkgAlerts)\n }\n return alertsByPurl\n}\n\nexport function alertsHaveBlocked(alerts: SocketPackageAlert[]): boolean {\n return alerts.find(a => a.blocked) !== undefined\n}\n\nexport function alertsHaveSeverity(\n alerts: SocketPackageAlert[],\n severity: `${keyof typeof ALERT_SEVERITY}`,\n): boolean {\n return alerts.find(a => a.raw.severity === severity) !== undefined\n}\n\nexport function alertSeverityComparator(\n a: SocketPackageAlert,\n b: SocketPackageAlert,\n): number {\n // Put the most severe first.\n return getAlertSeverityOrder(a) - getAlertSeverityOrder(b)\n}\n\nexport function getAlertSeverityOrder(alert: SocketPackageAlert): number {\n // The more severe, the lower the sort number.\n const { severity } = alert.raw\n return severity === ALERT_SEVERITY.critical\n ? 0\n : severity === ALERT_SEVERITY.high\n ? 1\n : severity === ALERT_SEVERITY.middle\n ? 2\n : severity === ALERT_SEVERITY.low\n ? 3\n : 4\n}\n\nexport function getAlertsSeverityOrder(alerts: SocketPackageAlert[]): number {\n return alertsHaveBlocked(alerts) ||\n alertsHaveSeverity(alerts, ALERT_SEVERITY.critical)\n ? 0\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.high)\n ? 1\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.middle)\n ? 2\n : alertsHaveSeverity(alerts, ALERT_SEVERITY.low)\n ? 3\n : 4\n}\n\nexport type CveFilter = {\n upgradable?: boolean | undefined\n}\n\nexport type CveInfoByAlertKey = Map<\n string,\n {\n firstPatchedVersionIdentifier: string\n vulnerableVersionRange: string\n }\n>\n\nexport type CveInfoByPartialPurl = Map<string, CveInfoByAlertKey>\n\nexport type GetCveInfoByPackageOptions = {\n filter?: CveFilter | undefined\n}\n\nexport function getCveInfoFromAlertsMap(\n alertsMap: AlertsByPurl,\n options?: GetCveInfoByPackageOptions | undefined,\n): CveInfoByPartialPurl | null {\n const filterConfig = toFilterConfig(getOwn(options, 'filter')) as CveFilter\n\n let infoByPartialPurl: CveInfoByPartialPurl | null = null\n alertsMapLoop: for (const { 0: purl, 1: sockPkgAlerts } of alertsMap) {\n const purlObj = getPurlObject(purl, { throws: false })\n if (!purlObj) {\n debugFn('error', 'invalid PURL')\n debugDir('inspect', { purl })\n continue alertsMapLoop\n }\n const partialPurl = createPurlObject({\n type: purlObj.type,\n namespace: purlObj.namespace,\n name: purlObj.name,\n }).toString()\n const name = resolvePackageName(purlObj)\n sockPkgAlertsLoop: for (const sockPkgAlert of sockPkgAlerts) {\n const alert = sockPkgAlert.raw\n if (\n alert.fix?.type !== ALERT_FIX_TYPE.cve ||\n (filterConfig.upgradable === false &&\n getManifestData(sockPkgAlert.ecosystem as any, name))\n ) {\n continue sockPkgAlertsLoop\n }\n if (!infoByPartialPurl) {\n infoByPartialPurl = new Map()\n }\n let infos = infoByPartialPurl.get(partialPurl)\n if (!infos) {\n infos = new Map()\n infoByPartialPurl.set(partialPurl, infos)\n }\n const { key } = alert\n if (!infos.has(key)) {\n // An alert with alert.fix.type of 'cve' should have a\n // alert.props.firstPatchedVersionIdentifier property value.\n // We're just being cautious.\n const firstPatchedVersionIdentifier = (alert.props as CveProps)\n ?.firstPatchedVersionIdentifier\n const vulnerableVersionRange = (alert.props as CveProps)\n ?.vulnerableVersionRange\n let error: unknown\n if (firstPatchedVersionIdentifier && vulnerableVersionRange) {\n try {\n infos.set(key, {\n firstPatchedVersionIdentifier,\n vulnerableVersionRange: new semver.Range(\n // Replace ', ' in a range like '>= 1.0.0, < 1.8.2' with ' ' so that\n // semver.Range will parse it without erroring.\n vulnerableVersionRange\n .replace(/, +/g, ' ')\n .replace(/; +/g, ' || '),\n ).format(),\n })\n continue sockPkgAlertsLoop\n } catch (e) {\n error = e\n }\n }\n debugFn('error', 'fail: invalid SocketPackageAlert')\n debugDir('inspect', { alert })\n debugDir('error', error)\n }\n }\n }\n return infoByPartialPurl\n}\n\nexport function getSeverityLabel(\n severity: `${keyof typeof ALERT_SEVERITY}`,\n): string {\n return severity === 'middle' ? 'moderate' : severity\n}\n\nexport type LogAlertsMapOptions = {\n hideAt?: `${keyof typeof ALERT_SEVERITY}` | 'none' | undefined\n output?: NodeJS.WriteStream | undefined\n}\n\nexport function logAlertsMap(\n alertsMap: AlertsByPurl,\n options: LogAlertsMapOptions,\n) {\n const { hideAt = 'middle', output = process.stderr } = {\n __proto__: null,\n ...options,\n } as LogAlertsMapOptions\n\n const translations = getTranslations()\n const sortedEntries = Array.from(alertsMap.entries()).sort(\n (a, b) => getAlertsSeverityOrder(a[1]) - getAlertsSeverityOrder(b[1]),\n )\n\n const aboveTheFoldPurls = new Set<string>()\n const viewableAlertsByPurl = new Map<string, SocketPackageAlert[]>()\n const hiddenAlertsByPurl = new Map<string, SocketPackageAlert[]>()\n\n for (let i = 0, { length } = sortedEntries; i < length; i += 1) {\n const { 0: purl, 1: alerts } = sortedEntries[i]!\n const hiddenAlerts: typeof alerts = []\n const viewableAlerts = alerts.filter(a => {\n const keep =\n a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER[hideAt]\n if (!keep) {\n hiddenAlerts.push(a)\n }\n return keep\n })\n if (hiddenAlerts.length) {\n hiddenAlertsByPurl.set(purl, hiddenAlerts.sort(alertSeverityComparator))\n }\n if (!viewableAlerts.length) {\n continue\n }\n viewableAlerts.sort(alertSeverityComparator)\n viewableAlertsByPurl.set(purl, viewableAlerts)\n if (\n viewableAlerts.find(\n (a: SocketPackageAlert) =>\n a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER.middle,\n )\n ) {\n aboveTheFoldPurls.add(purl)\n }\n }\n\n // If MIN_ABOVE_THE_FOLD_COUNT is NOT met add more from viewable pkg ids.\n for (const { 0: purl } of viewableAlertsByPurl.entries()) {\n if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {\n break\n }\n aboveTheFoldPurls.add(purl)\n }\n // If MIN_ABOVE_THE_FOLD_COUNT is STILL NOT met add more from hidden pkg ids.\n for (const { 0: purl, 1: hiddenAlerts } of hiddenAlertsByPurl.entries()) {\n if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {\n break\n }\n aboveTheFoldPurls.add(purl)\n const viewableAlerts = viewableAlertsByPurl.get(purl) ?? []\n if (viewableAlerts.length < MIN_ABOVE_THE_FOLD_ALERT_COUNT) {\n const neededCount = MIN_ABOVE_THE_FOLD_ALERT_COUNT - viewableAlerts.length\n let removedHiddenAlerts: SocketPackageAlert[] | undefined\n if (hiddenAlerts.length - neededCount > 0) {\n removedHiddenAlerts = hiddenAlerts.splice(\n 0,\n MIN_ABOVE_THE_FOLD_ALERT_COUNT,\n )\n } else {\n removedHiddenAlerts = hiddenAlerts\n hiddenAlertsByPurl.delete(purl)\n }\n viewableAlertsByPurl.set(purl, [\n ...viewableAlerts,\n ...removedHiddenAlerts,\n ])\n }\n }\n\n const mentionedPurlsWithHiddenAlerts = new Set<string>()\n for (\n let i = 0,\n prevAboveTheFold = true,\n entries = Array.from(viewableAlertsByPurl.entries()),\n { length } = entries;\n i < length;\n i += 1\n ) {\n const { 0: purl, 1: alerts } = entries[i]!\n const lines = new Set<string>()\n for (const alert of alerts) {\n const { type } = alert\n const severity = alert.raw.severity ?? ''\n const attributes = [\n ...(severity\n ? [colors[ALERT_SEVERITY_COLOR[severity]](getSeverityLabel(severity))]\n : []),\n ...(alert.blocked ? [colors.bold(colors.red('blocked'))] : []),\n ...(alert.fixable ? ['fixable'] : []),\n ]\n const maybeAttributes = attributes.length\n ? ` ${colors.italic(`(${attributes.join('; ')})`)}`\n : ''\n // Based data from { pageProps: { alertTypes } } of:\n // https://socket.dev/_next/data/9a6db8224b68b6da0eb9f7dbb17aff7e51568ac2/en-US.json\n const info = (translations.alerts as any)[type]\n const title = info?.title ?? type\n const maybeDesc = info?.description ? ` - ${info.description}` : ''\n const content = `${title}${maybeAttributes}${maybeDesc}`\n // TODO: An added emoji seems to mis-align terminals sometimes.\n lines.add(` ${content}`)\n }\n const purlObj = getPurlObject(purl)\n const pkgName = resolvePackageName(purlObj)\n const hyperlink = format.hyperlink(\n `${pkgName}@${purlObj.version}`,\n getSocketDevPackageOverviewUrl(purlObj.type, pkgName, purlObj.version),\n )\n const isAboveTheFold = aboveTheFoldPurls.has(purl)\n if (isAboveTheFold) {\n aboveTheFoldPurls.add(purl)\n output.write(`${i ? '\\n' : ''}${hyperlink}:\\n`)\n } else {\n output.write(`${prevAboveTheFold ? '\\n' : ''}${hyperlink}:\\n`)\n }\n for (const line of lines) {\n output.write(`${line}\\n`)\n }\n const hiddenAlerts = hiddenAlertsByPurl.get(purl) ?? []\n const { length: hiddenAlertsCount } = hiddenAlerts\n if (hiddenAlertsCount) {\n mentionedPurlsWithHiddenAlerts.add(purl)\n if (hiddenAlertsCount === 1) {\n output.write(\n ` ${colors.dim(`+1 Hidden ${getSeverityLabel(hiddenAlerts[0]!.raw.severity ?? 'low')} risk alert`)}\\n`,\n )\n } else {\n output.write(\n ` ${colors.dim(`+${hiddenAlertsCount} Hidden alerts ${colors.italic(getHiddenRisksDescription(getHiddenRiskCounts(hiddenAlerts)))}`)}\\n`,\n )\n }\n }\n prevAboveTheFold = isAboveTheFold\n }\n\n const additionalHiddenCount =\n hiddenAlertsByPurl.size - mentionedPurlsWithHiddenAlerts.size\n if (additionalHiddenCount) {\n const totalRiskCounts = {\n critical: 0,\n high: 0,\n middle: 0,\n low: 0,\n }\n for (const { 0: purl, 1: alerts } of hiddenAlertsByPurl.entries()) {\n if (mentionedPurlsWithHiddenAlerts.has(purl)) {\n continue\n }\n const riskCounts = getHiddenRiskCounts(alerts)\n totalRiskCounts.critical += riskCounts.critical\n totalRiskCounts.high += riskCounts.high\n totalRiskCounts.middle += riskCounts.middle\n totalRiskCounts.low += riskCounts.low\n }\n output.write(\n `${aboveTheFoldPurls.size ? '\\n' : ''}${colors.dim(`${aboveTheFoldPurls.size ? '+' : ''}${additionalHiddenCount} Packages with hidden alerts ${colors.italic(getHiddenRisksDescription(totalRiskCounts))}`)}\\n`,\n )\n }\n output.write('\\n')\n}\n","/**\n * Alerts map utilities for Socket CLI.\n * Manages security alerts and vulnerability mappings for packages.\n *\n * Key Functions:\n * - getAlertsMapFromPnpmLockfile: Extract alerts from pnpm lockfile\n * - getAlertsMapFromPurls: Get alerts for specific package URLs\n * - processAlertsApiResponse: Process API response into alerts map\n *\n * Alert Processing:\n * - Filters alerts based on socket.yml configuration\n * - Maps package URLs to security vulnerabilities\n * - Supports batch processing for performance\n *\n * Integration:\n * - Works with pnpm lockfiles for dependency scanning\n * - Uses Socket API for vulnerability data\n * - Respects filter configurations from socket.yml\n */\n\nimport { arrayUnique } from '@socketsecurity/registry/lib/arrays'\nimport { debugDir } from '@socketsecurity/registry/lib/debug'\nimport { logger } from '@socketsecurity/registry/lib/logger'\nimport { getOwn } from '@socketsecurity/registry/lib/objects'\nimport { isNonEmptyString } from '@socketsecurity/registry/lib/strings'\n\nimport { findSocketYmlSync } from './config.mts'\nimport { toFilterConfig } from './filter-config.mts'\nimport { extractPurlsFromPnpmLockfile } from './pnpm.mts'\nimport { setupSdk } from './sdk.mts'\nimport { addArtifactToAlertsMap } from './socket-package-alert.mts'\n\nimport type { CompactSocketArtifact } from './alert/artifact.mts'\nimport type { AlertFilter, AlertsByPurl } from './socket-package-alert.mts'\nimport type { LockfileObject } from '@pnpm/lockfile.fs'\nimport type { Spinner } from '@socketsecurity/registry/lib/spinner'\n\nexport type GetAlertsMapFromPnpmLockfileOptions = {\n apiToken?: string | undefined\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n overrides?: { [key: string]: string } | undefined\n nothrow?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPnpmLockfile(\n lockfile: LockfileObject,\n options?: GetAlertsMapFromPnpmLockfileOptions | undefined,\n): Promise<AlertsByPurl> {\n const purls = await extractPurlsFromPnpmLockfile(lockfile)\n return await getAlertsMapFromPurls(purls, {\n overrides: lockfile.overrides,\n ...options,\n })\n}\n\nexport type GetAlertsMapFromPurlsOptions = {\n apiToken?: string | undefined\n consolidate?: boolean | undefined\n filter?: AlertFilter | undefined\n onlyFixable?: boolean | undefined\n overrides?: { [key: string]: string } | undefined\n nothrow?: boolean | undefined\n spinner?: Spinner | undefined\n}\n\nexport async function getAlertsMapFromPurls(\n purls: string[] | readonly string[],\n options?: GetAlertsMapFromPurlsOptions | undefined,\n): Promise<AlertsByPurl> {\n const uniqPurls = arrayUnique(purls)\n debugDir('silly', { purls: uniqPurls })\n\n let { length: remaining } = uniqPurls\n const alertsByPurl: AlertsByPurl = new Map()\n\n if (!remaining) {\n return alertsByPurl\n }\n\n const opts = {\n __proto__: null,\n consolidate: false,\n nothrow: false,\n ...options,\n filter: toFilterConfig(getOwn(options, 'filter')),\n } as GetAlertsMapFromPurlsOptions & { filter: AlertFilter }\n\n if (opts.onlyFixable) {\n opts.filter.fixable = true\n }\n\n const { apiToken, spinner } = opts\n\n const getText = () => `Looking up data for ${remaining} packages`\n\n spinner?.start(getText())\n\n const sockSdkCResult = await setupSdk({ apiToken })\n if (!sockSdkCResult.ok) {\n spinner?.stop()\n throw new Error('Auth error: Run `socket login` first.')\n }\n const sockSdk = sockSdkCResult.data\n const socketYmlResult = findSocketYmlSync()\n const socketYml =\n socketYmlResult.ok && socketYmlResult.data\n ? socketYmlResult.data.parsed\n : undefined\n\n const alertsMapOptions = {\n consolidate: opts.consolidate,\n filter: opts.filter,\n overrides: opts.overrides,\n socketYml,\n spinner,\n }\n\n try {\n for await (const batchResult of sockSdk.batchPackageStream(\n {\n components: uniqPurls.map(purl => ({ purl })),\n },\n {\n queryParams: {\n alerts: 'true',\n compact: 'true',\n ...(opts.onlyFixable ? { fixable: 'true ' } : {}),\n ...(Array.isArray(opts.filter.actions)\n ? { actions: opts.filter.actions.join(',') }\n : {}),\n },\n },\n )) {\n if (batchResult.success) {\n const artifact = batchResult.data as CompactSocketArtifact\n await addArtifactToAlertsMap(artifact, alertsByPurl, alertsMapOptions)\n } else if (!opts.nothrow) {\n spinner?.stop()\n if (isNonEmptyString(batchResult.error)) {\n throw new Error(batchResult.error)\n }\n const statusCode = batchResult.status ?? 'unknown'\n throw new Error(\n `Socket API server error (${statusCode}): No status message`,\n )\n } else {\n spinner?.stop()\n logger.fail(\n `Received a ${batchResult.status} response from Socket API which we consider a permanent failure:`,\n batchResult.error,\n batchResult.cause ? `( ${batchResult.cause} )` : '',\n )\n debugDir('inspect', { batchResult })\n break\n }\n remaining -= 1\n if (remaining > 0) {\n spinner?.start(getText())\n }\n }\n } catch (e) {\n spinner?.stop()\n throw e\n }\n\n spinner?.stop()\n\n return alertsByPurl\n}\n","/**\n * npm package specification utilities for Socket CLI.\n * Parses and handles various npm package specification formats.\n *\n * Supported Formats:\n * - Regular packages: lodash, lodash@4.17.21\n * - Scoped packages: @types/node, @types/node@20.0.0\n * - Version ranges: lodash@^4.0.0, lodash@~4.17.0\n * - Git URLs: git+https://github.com/user/repo.git\n * - File paths: file:../local-package\n * - Aliases: my-alias@npm:real-package@1.0.0\n *\n * Key Functions:\n * - safeNpa: Safe wrapper for npm-package-arg\n * - safeNpmSpecToPurl: Convert npm spec to PURL\n * - safeParseNpmSpec: Parse npm spec to name/version\n *\n * Error Handling:\n * - Returns undefined for invalid specs\n * - Fallback parsing for edge cases\n * - Safe against malformed input\n */\n\nimport npmPackageArg from 'npm-package-arg'\n\nimport { NPM } from '../constants.mts'\nimport { createPurlObject } from './purl.mts'\n\n// @ts-expect-error - Result is re-exported below.\nimport type { Result } from 'npm-package-arg'\n\nexport type {\n AliasResult,\n FileResult,\n HostedGit,\n HostedGitResult,\n RegistryResult,\n Result,\n URLResult,\n} from 'npm-package-arg'\n\nexport type ParsedPackageSpec = {\n name: string\n version: string | undefined\n}\n\n/**\n * Safe wrapper for npm-package-arg that doesn't throw.\n * Returns undefined if parsing fails.\n */\nexport function safeNpa(\n ...args: Parameters<typeof npmPackageArg>\n): ReturnType<typeof npmPackageArg> | undefined {\n try {\n return Reflect.apply(npmPackageArg, undefined, args)\n } catch {}\n return undefined\n}\n\n/**\n * Parse npm package specification into name and version.\n * Uses npm-package-arg for proper handling of various spec formats:\n * - Regular packages: lodash, lodash@4.17.21\n * - Scoped packages: @types/node, @types/node@20.0.0\n * - Version ranges: lodash@^4.0.0\n * - Git URLs, file paths, etc.\n *\n * Returns undefined if parsing fails.\n */\nexport function safeParseNpmSpec(\n pkgSpec: string,\n): ParsedPackageSpec | undefined {\n // Use npm-package-arg for proper spec parsing.\n const parsed = safeNpa(pkgSpec)\n\n if (!parsed) {\n // Fallback to simple parsing if npm-package-arg fails.\n // Handle scoped packages first to avoid confusion with version delimiter.\n if (pkgSpec.startsWith('@')) {\n const scopedMatch = pkgSpec.match(/^(@[^/@]+\\/[^/@]+)(?:@(.+))?$/)\n if (scopedMatch) {\n return {\n name: scopedMatch[1]!,\n version: scopedMatch[2],\n }\n }\n }\n\n // Handle regular packages.\n const atIndex = pkgSpec.indexOf('@')\n if (atIndex === -1) {\n return { name: pkgSpec, version: undefined }\n }\n\n return {\n name: pkgSpec.slice(0, atIndex),\n version: pkgSpec.slice(atIndex + 1),\n }\n }\n\n // Extract name and version from parsed spec.\n const name = parsed.name || pkgSpec\n let version: string | undefined\n\n // Handle different spec types from npm-package-arg.\n if (\n parsed.type === 'tag' ||\n parsed.type === 'version' ||\n parsed.type === 'range'\n ) {\n // For npm registry packages:\n // - type 'tag': latest, beta, etc.\n // - type 'version': exact version like 1.0.0\n // - type 'range': version range like ^1.0.0, ~1.0.0, or * for bare names\n // Don't include '*' as a version - it means \"any version\".\n if (parsed.fetchSpec && parsed.fetchSpec !== '*') {\n version = parsed.fetchSpec\n } else if (\n parsed.rawSpec &&\n parsed.rawSpec !== '*' &&\n parsed.rawSpec !== parsed.name\n ) {\n version = parsed.rawSpec\n }\n } else if (\n parsed.type === 'git' ||\n parsed.type === 'remote' ||\n parsed.type === 'file'\n ) {\n // For non-registry specs, use rawSpec if different from name.\n if (parsed.rawSpec && parsed.rawSpec !== parsed.name) {\n version = parsed.rawSpec\n }\n }\n\n return { name, version }\n}\n\n/**\n * Convert npm package spec to PURL string.\n * Handles various npm spec formats and converts them to standardized PURLs.\n * Returns undefined if conversion fails.\n */\nexport function safeNpmSpecToPurl(pkgSpec: string): string | undefined {\n const parsed = safeParseNpmSpec(pkgSpec)\n if (!parsed) {\n return undefined\n }\n\n const { name, version } = parsed\n\n // Create PURL object to ensure proper formatting.\n const purlObj = createPurlObject({\n type: NPM,\n name,\n version,\n throws: false,\n })\n\n return (\n purlObj?.toString() ?? `pkg:${NPM}/${name}${version ? `@${version}` : ''}`\n )\n}\n\n/**\n * Convert npm package spec to PURL string.\n * Handles various npm spec formats and converts them to standardized PURLs.\n * Throws if conversion fails.\n */\nexport function npmSpecToPurl(pkgSpec: string): string {\n const purl = safeNpmSpecToPurl(pkgSpec)\n if (!purl) {\n throw new Error(`Failed to convert ${NPM} spec to PURL: ${pkgSpec}`)\n }\n return purl\n}\n"],"names":["logger","debugFn","phase","details","git_op","getSentry","constructor","socketAppDataPath","debugConfig","updateConfigValue","mkdirSync","recursive","ok","data","yml","path","parsed","debugDir","prevDir","message","cause","_configFromFlag","_cachedConfig","localConfig","wasDeleted","_pendingSave","writeFileSync","_requirements","_defaultToken","__proto__","apiProxy","proxy","baseUrl","timeout","name","version","homepage","hooks","debugApiRequest","spinner","socketSdkErrorResult","sdkResult","method","headers","Authorization","result","status","code","body","mw2","lines","cols","length","cws","url","msg","indent","keyPrefix","padName","bestScore","bestMatch","REDACTED","matrix","subcommands","type","hidden","description","commandOrAliasName","argv","allowUnknownFlags","autoHelp","autoVersion","booleanDefault","compactHeader","config","org","configOverrideResult","emitBanner","parentName","Object","commands","noBanner","noSpinner","help","collectUnknownFlags","importMeta","cli","console","process","meow","numeric","style","sdkOpts","organizations","value","choices","orgSlug","GITHUB_REF_TYPE","cwd","info","remoteUrl","error","stdio","debugGit","branch","email","user","filepaths","commitMsg","owner","repo","parsedGitRemoteUrlCache","opts","throws","namespace","keys","onlyDirectories","onlyFiles","root","dir","NODE_MODULES","workspacePatterns","ignores","absolute","ignore","concurrency","hasNegatedPattern","dot","filtered","all","nothrow","shadowBinPath","shadowIndex","theBinPath","WIN32","thePath","socketConfig","_yarnBinPath","_yarnBinPathDetails","encoding","_isYarnBerry","YARN_LOCK","force","silent","pm","spawnArgs","finalShadowOptions","env","npm_config_dlx_cache_max_age","SOCKET_CLI_VERSION","mixinsEnv","ipc","getDefaultSocketJson","jsonContent","jsonObj","sockJson","ttlMs","githubCachePath","results","SOCKET_CLI_GITHUB_TOKEN","auth","octokitOptions","_octokit","_octokitGraphql","node_id","pullRequestId","enabled","GITHUB_SERVER_URL","cmd","cve_id","per_page","cargo","composer","gem","go","golang","maven","npm","nuget","pypi","swift","ecosystem","affects","i","flagsToFilterSet","flagsWithValueSet","toAddToBashrc","targetName","targetPath","_npmBinPath","_npmBinPathDetails","_npmDirPath","_npxBinPath","_npxBinPathDetails","args","spawnPromise","pkgPath","CI","NODE_OPTIONS","YARN_CLASSIC","semver","onUnknown","editable","agent","maintainedNodeVersions","engines","pkgAgentRange","pkgNodeRange","lockName","lockPath","features","npmBuggyOverrides","pkgRequirements","pkgSupports","node","cmdName","prod","win32","_pnpmBinPath","_pnpmBinPathDetails","normalized","seen","cve","remove","upgrade","critical","high","middle","low","header","hyperlink","fallback","fallbackToUrl","_translations","none","descriptions","consolidate","socketYml","blocked","raw","upgradable","highestForCve","alert","unfixableAlerts","highestForUpgrade","sockPkgAlerts","alertsByPurl","severity","hideAt","hiddenAlerts","viewableAlerts","viewableAlertsByPurl","aboveTheFoldPurls","removedHiddenAlerts","hiddenAlertsByPurl","output","mentionedPurlsWithHiddenAlerts","prevAboveTheFold","totalRiskCounts","purls","apiToken","components","purl","queryParams","alerts","compact","fixable","batchResult","remaining"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAOA;AACA;AACA;AACA;AACO;AAKL;;AAEEA;AAGF;AACF;;AAEA;AACA;AACA;AACA;AACO;AAQL;AACE;AACF;AAEA;AACEA;AAGA;AACEA;AAGF;AACF;;;AAKE;AACEA;AAGF;AACF;AACF;;AAEA;AACA;AACA;AACA;AACO;AAKL;;;;;AAKE;AACF;;AAEA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAKL;AACE;AACE;AACEC;AACF;AACA;AACF;AACE;AACEA;AACF;AACA;AACF;AACEA;AAIA;AACF;;AAEIC;AACAC;AACF;AACA;AACJ;AACF;;AAEA;AACA;AACA;AACO;AAKL;;;;AAIE;;AAEAF;AACF;AACEA;AACF;AACF;;AAEA;AACA;AACA;AACA;AACO;;;AAODG;;AAEF;;AAKA;AACAH;AACF;AACEA;AACF;AACF;;ACjLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAQA;;AAEE;AAA+DI;AAAU;AAC3E;AAIO;AAEA;AAGLC;;;AAGA;AACF;AAEO;AAIL;AACA;;AAEA;AACF;AAEO;AAIL;;AAEE;AACF;AACAL;AACA;AACF;;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AAEP;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;;AAEF;;ACzIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA0CA;AAIA;AAsBA;AAGA;AAEA;;AAEI;;;AAEQM;AAAkB;AAC1B;AACE;AACA;;;AAMIC;;AAEAR;AACAQ;AACF;AACA;AACA;AACA;AACE;;AAEAC;AACF;AACF;AACEC;AAA6CC;AAAgB;AAC/D;AACF;AACF;AACA;AACF;AAEA;AAGE;AACA;AACA;AACA;AAMA;;AAEIC;;AAEAC;;AAEJ;;AACSD;AAAUC;;AACrB;AAOO;;;;AAMH;;;AAGEC;AACF;AACA;;;AAGMF;AACAC;AACEE;AACAC;AACF;;;AAGFf;AACAgB;;AAEEL;;;;AAIJ;AACF;AACAM;;AAEF;;AACSN;AAAUC;;AACrB;AAEO;AAGL;AACA;AACA;AACE;AACF;;AACSD;AAAUC;;AACrB;;AAEA;AACA;AACO;AAGL;AACA;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACA;AACE;;AAEF;AAEO;;AAEP;AAEO;;AAEP;AAEO;AACL;AACF;AAEO;AACL;AACF;AAEO;AACL;AACF;AAEA;AACA;AACA;AAEO;AACLZ;AAEA;;;AAGE;AACE;AACA;;AAEEW;AACAO;AACAC;;AAGJ;AACF;AACE;;AAEAC;;AAGET;AACAO;AACAC;;AAGJ;;AAEA;AACAE;AACAD;;AAEA;AACA;AACE;AACErB;AAGF;AACAsB;;AAEF;;AAESV;AAAUC;;AACrB;AAEO;AACLZ;AACA;AACAqB;AACE;AACA;;;;AAEFD;AACF;AAEA;AACO;AAIL;AACA;AACA;AACE;AACF;AACA;AACA;AACA;;AAEE;AACEE;AACF;;AAEEC;AACF;AACF;;AAEIxB;AAGF;AACAuB;AACF;AACA;;AAEIX;;AAEAC;;AAEJ;;AAGEY;;AAEEA;;AACQlB;AAAkB;AAC1B;AACEmB;AAIF;AACF;AACF;;AAGEd;;AAEAC;;AAEJ;;ACrWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAOA;AAEA;AAIO;;AAEHc;AAGF;AACA;AACF;;AAEA;AACA;AACA;AACO;AACL;AACF;;ACvCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAsBA;AACA;AACA;;AAEA;AACO;;AAIL;AACF;;AAEA;AACO;;AAIL;AACF;;AAEA;AACA;AACO;AACL;AACEC;AACA;AACF;AAEA;;AAMA;AACF;AAEO;AACL;AAKF;AAEO;AACL;AACA;AAMF;AAEO;AACL;AACF;AAQO;AAGL;AAAeC;;;;;AACuB;;;AAIlCV;AAEF;AACAS;AACF;;;AAIIhB;AACAO;AACAC;;AAEJ;;AAEMU;AAAS;AACf;;AAEA;;;AAE4C;;AAE5C;AACA;;AAKA;AACE;;AAAyCC;;;AACzC;AAAmBC;;AACnBC;;AAEEC;AACAC;AACAC;AACF;AACA;AACA;AAEMC;;AAEIC;;;;AAWF;AACF;;;AAKR;;AAIA;;;AAKE1B;AACAC;;AAEJ;;AC7LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA+BA;AAOA;AACA;AACA;AACA;;AAII;AACF;AAEA;AACA;AACA;AACF;;AAEA;AACA;AACA;AACA;AACE;AACA;AACE;AACF;AAEAb;AACA;AACEA;AACF;AACAA;AACF;;AAEA;AACO;;AAIL;AACE;AACF;AACA;AACA;AACF;;AAEA;AACA;AACA;AACO;;AAEH;AACF;AACA;AACE;AACF;;AAEE;AACF;;AAEE;AACF;;AAEF;AAYA;AACA;AACA;AACO;;;;AAI6BuC;AAAQ;AACxCV;;;AAIF;AACEU;AACF;;AAEA;AAEA;;;;AAIE;AACE;;AAEEvC;AACF;AACEA;AACF;AACF;;;AAGA;AACEY;AACAO;;;AAGF;AACEnB;AACF;;AACsBwC;AAAqB;AAC3C;AACF;;AAEA;AACA;AACE;;;AAEsBC;AAAU;;AAGhC;AACA;AACA;AACA;AAEA;AACE7B;AACAO;;AAEAN;;AAEA;;;AAGF;AACA;;AAEA;AAEA;AACF;AACA;AACED;;;AAGF;AACF;AAEO;AAIL;;;;AAIEX;AACAgB;AAEA;;AAEA;;;AAIEL;;AAEA;AAAcQ;;;AAElB;;AAEA;AACA;AACEnB;;AACsBwC;AAAU;;AAGhC;AAGA;AACA;AACA;;AAIE7B;AACAO;;AAEAN;;AAEA;;AAEJ;;;AAGID;;;AAGJ;AACF;AAEA;AACE;;AAEE;AACF;AAEA;AACA;AACE8B;AACAC;AACEC;AACF;AACF;AACA;AACF;;AAEA;AACA;AACA;AACO;AAKL;;;AAGIhC;AACAO;AACAC;;AAGJ;;AAEQmB;AAAQ;AAEhB;AACEA;;AAEF;AAEA;AACA;;AAEEM;;;AAUA;AACEN;AAGF;;;AAGA;AACEA;;AAIF;AAEAtC;AACAgB;AAEA;;AAEA;;;AAIEL;;AAEA;AAAcQ;;;AAElB;AAEA;;AACU0B;AAAO;;AAMblC;AACAO;;AAEAN;AACEkC;AACF;;AAEJ;;AAGE;;AAEEnC;AACAC;;;AAGFZ;AACAgB;;AAGEL;AACAO;AACAC;;AAEJ;AACF;;AAEA;AACA;AACA;AACO;;AAML;AACE;AACF;;;AAIIR;AACAC;;;;AAIAD;AACAO;AACAC;;AAEJ;AACF;AASA;AACA;AACA;AACO;AAIL;;;AAGIR;AACAO;AACAC;;AAGJ;AAEA;;;AAGIR;AACAO;AACAC;;AAGJ;;;;;AAEwCsB;AAAO;AAC7Cb;;;;AAGMU;AAAQ;AAEhB;AACEA;AACF;AAEA;;AAEE;;AAEEI;;AAEE;;AAEF;AAAaK;;;;AAOf;AACET;AAGF;;AAEA;AACEA;AAGF;AAEAtC;AACAgB;AAEA;;AAEA;;;AAIEL;;AAEA;AAAcQ;;;AAElB;AAEA;;AACU0B;AAAO;AACf;AACA;;AAEA;;AAEElC;AACAO;;AAEAN;AACEkC;AACF;;AAEJ;;AAGE;;AAEEnC;AACAC;;;AAGFZ;AACAgB;;AAEEL;AACAO;AACAC;;AAEJ;AACF;;ACzgBO;;AAOL;AACA;AACF;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEO;AAKL;AACA;AACA;AACA;AACA;AACA;AACA;AAAa;AAAQ;AAAS;;AAE5B6B;AACF;;;AAIAC;AACA;AAAa;AAAQ;AAAS;;AAI9B;AACAA;AAEA;AACF;AAEO;AAEL;AACA;AACAC;AAGA;;AAGA;AACE;AAAkBC;;AAChB;AACA;AACAC;AAKF;AACF;;AAGA;;AAEA;;AAGA;AAAkBD;;;AAElB;;AAGA;AACEJ;AACA;AAAkBI;;AAChB;AACA;;AAEF;AACAJ;AACF;AAEA;AACF;AAEO;AAEL;AACA;AACAG;AAEA;;;;;AAMA;;AAGA;;AAEA;;AAGA;AAAkBC;;;AAElB;;;AAIEJ;;;AAGAA;AACF;AAEA;AACF;;ACxHA;AACA;AACO;AACL;;;AAGuBnC;AAAK;;AAE1B;AACA;AACA;AACED;AACAO;AACAC;AAEF;AACF;;AAGE;;;;AAOApB;AACAC;AACAgB;;AAEA;AACA;AACEL;AACAO;AACAC;AACF;AACF;AACF;;ACrCA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;;AAIF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;;AAEP;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;AAIF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AAIL;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AAML;AACA;AACE;AACA;AACEkC;AACF;AACEA;AACF;AACF;AACEA;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACF;;ACnGO;;AAWH;AACF;AAEA;AACA;AACE;AACA;AACE;AACF;;;AAEQF;AAAkB;;AAExB;AACF;AACA;AACA;AACA;AACA;;AAEA;AACE;;AAEF;AACAG;;AAEEA;AACF;AACF;;AAEA;AACA;AACA;;;AAIEvD;AAEIY;AACAO;;AAEF;AAEJ;AACEnB;AACF;AAEA;AACF;;ACpEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMO;AACL;AACE;AACF;AACA;AACE;AACF;AACA;AACF;;AC9BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEO;AACL;AACF;;ACfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA2BO;;AAIGwD;AAAW;AACjB3B;;;AAGF;AACA;AACA;;AAEA;AACE;AACA;AACA;;;AAGEqB;AACF;;;;AAIA;AACAL;AACF;AACA;AACF;AAEO;;AAIGY;AAAiB;AACvB5B;;;AAGF;;AAGE;AACE;AAAY4B;AAAU;AAE5B;AAEO;;AAKHD;AACAC;AACAC;AACF;AACE7B;;;;AAIF;AACA;AACE;AACA;;AAEE;AACF;;;AAIAgB;;AAGA;AACEA;AACF;AACAA;AACF;AACA;AACF;;AC3HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAQO;;AAKP;;AC4CA;AACA;AACA;;AA2BA;AAEA;;AAEA;AACA;AACA;AACA;AACE;AACA;;AAGF;;AAEA;AACA;AACA;AACA;;;AAOE;AACA;AACE;AAIA;AACA;;AAEEc;AACAC;AACF;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AACE;AACE;AACF;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACA;AACA;AAKE;;AACQC;AAAS;AACjB;;AAEA;AACA;AACA;AACA;;AAOA;AACA;;;AAGA;AACA;;AAEA;AACA;AAQA;;AAEA;AACA;;AAQA;AACA;;AAME;;AAIF;;AAEA;AACA;AACA;AACF;AACA;AACA;AACA;AACA;AACE;;AAEF;;AAEA;AACA;AACA;AACA;AACE;AAA4BT;AAAqB;AAGjD;AACEU;AACF;AACA;AACEA;AACF;AACA;AACE;AACE;;AAEE;;AAEA;;AAEA;AACAA;AAEJ;AACF;;AAEF;;AAEA;AACA;AACA;AACA;AACE;AAGF;;AAEA;AACA;AACA;AACO;AAKL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEF;;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;;;;AAI2BC;AAAY;AAC1ClC;;;;;;;AAOF;AAAMA;;;AACN;AACE;AACAM;AACE6B;AACAC;AACAC;;AAEF;;AAGF;;AAEA;AACEC;AACF;;AAEA;AACA;;AAIA;;AAEE;AACEnE;;;;;AAE0D+D;;AAG5D;AACA;AACA;;AAEE/D;;;AAIIoE;;AAOAL;;AAIN;AACF;AAEA;AACE;;;AAIEE;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKAA;;;;AAKJ;;;;AAIA;;AAEA;AACA;;;;AAIE;;AAEA;AACAI;AACA;AACAC;AACAC;AACA;AACAC;AACF;;AAGEC;AACAC;AACAC;AACApC;;AAQF;;;AAIA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;;AAGEqC;AACF;AAEA;AACE;AACA;;AAEF;AACE;AACA;AACE;AACA;;AAEF;AACF;AAEA;AACE;AACEC;AACA;AACA7E;AACF;AACAA;;AAEA;AACF;;AAEA;AACA;AACE;AACA;;AAIA;;AAEA;AACA;AACE;AACA;;AAEE8E;AACF;AACF;;AAEA;AACA;;AAEE;;;AAKE;AACF;AACF;AACF;;AAGA;AACE5B;AAIF;AACAA;AACA;AACE;;AAUE;AACA;AASA;AACA;AACA;AAIA;;AAIA;AAAA;AAEF6B;AAII;AACEC;AACF;AACEhF;AACF;AACF;;;AAIE;AACA;;AAOJ;;AAqCF;AACEkD;AACAA;AAGM;AAGS;AAAc;AAIvB;AAEsC;AAAS;;AACjCe;AAAO;;;AAGf;AACF;AAGN;AACET;AAAqBE;;AAG7B;AAEAR;AACA;AACEA;AAIF;AACEA;AACF;AACAA;AAGM;AACA;AACA+B;;AAEEhB;;AAEF;AACAiB;;AAEEjB;AACF;AACF;AACET;AAAqBE;;AAG3B;AACE;AACA;AAEA;AACE;;AA+BF;AACE;AACAR;AAKF;AACF;;AAEA;AACA;AACA;;;;AAIE;;AAEA;AACAmB;AACA;AACA;AACAC;AACAC;AACA;AACAC;AACAW;AACF;;;AAEgBA;;;AAKhB;AACA;AACEN;AACA;AACF;AACA;;;AAGA;AACE;AACA;;AAEF;AACF;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;;AAMHH;;AAEAI;AACF;AAAMjD;;;;AACEwC;AAAyB;AAC/BxC;;;;;AAMF;;;AAGE;AACAyC;AACAC;AACA;AACAC;AACAY;;;;AAIAC;AACF;;AAGEZ;AACAU;AACAR;AACApC;AACAJ;;AASF;;;AAIA;AACA;AACA;;AAEA;AAEA;AACE0C;AACA;AACA;AACA7E;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACEsF;AACF;;AAEA;;AAEE;AACAC;AACA;AACAC;AACA;AACA;AACF;;AAEA;AACA;AACA;;AAEAC;;AAEE;AACA;AACApB;AACA;AACAC;AACAC;;;;;AAKF;AACA;;AAGA;AACF;;ACp4BO;AACL;AACA;;AAGEmB;AACAC;AACF;AAEA;AACA;AACE;AACA;;;AAGA;AACF;;AAEE;AACF;AACE;AACF;AACF;;ACMO;;AAIHzB;;AAEA0B;AACF;AACE/D;;;;;AAMA;AACA;AACE;AACF;;AAEF;;AAGEqC;AACF;AACA;AACE;AACF;;AAGE;AACArD;;AAEA;;AAEJ;;ACzDO;AACL;AACA;AACEb;AAGA;AACF;;AAEA;AACA;;AACQ6F;;AACR;AACE1E;;;;;AAOM2E;;;AAGJ;AAEE5D;AACA4D;AACA5B;;AAIN;AAEA;AACE;AACF;AACA;AACF;;ACrCO;AACL;;AAEE;AACA;AACF;AAEA;;AAEE6B;AAEI7D;AACA4D;AACA5B;AACF;AAEEhC;AACA4D;AACA5B;AACF;AAEEhC;AACA4D;AACA5B;;AAIN;;AAEE;;AAEElE;AACF;;AAKA;AACF;AACE;;AAEEA;AACF;;AAIA;AACF;AACF;;AC1CO;AAKL;;;;AAIIA;AAGAA;AACAA;AAGAA;AAGAA;AAGAA;AAGAA;AACAA;AAGAA;;AAIAA;AACAA;AAGA;AACF;AAEAA;AAGAA;AACAA;AACAA;AACA;AACEA;AACF;AACEgG;AACA;;AAEA;AACF;AACF;AAEA;AACF;;AC5DA;AACO;AACL;AACA;AACE/F;;AAKSW;AAAUC;;AACrB;AAEA;AACA;AACEZ;;AAKSW;AAAUC;;AACrB;AAEA;AACA;AACE;AACF;;AAEQgF;;AACR;AACA;;AAEIjF;AACAO;AACAN;;AAEJ;AAEA;;;AAGID;AACAO;AACAN;;AAEJ;AAEAZ;;AAGEW;AACAO;AACAN;;AAEJ;;AC3DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEI;AACF;;AAEA;AACA;AACE;AAAA;AAEA;AAAA;AAEA;AAAA;AAEA;AAAA;AAEA;AAAA;AAGF;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AACL;AACA;AACF;;ACxCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAaA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGK;;;;AACqCoF;;AAC1C;AACA;AACE;AACF;AACA;AACA;AACE;AACF;AACA;AACA;;AAEE;AACqDC;;AAGrD;AACA;AACE;AACF;;AAEF;AACA;AACA;AACF;AAOO;AAGL;;AAEE;AACwDA;;AAExDC;;AAEElG;;AACmBmG;AAAU;AAC/B;;AAEA;;AACsBjF;AAAsCkF;AAAS;AACvE;AACA;AACF;AAEO;AACL;AACA;AAGF;AASO;AAGL;AAAyCH;;AACzC;AACA;;AAEE;;;AAOA;;AACsB/E;AAAmCkF;AAAS;AACpE;AACA;AACA;;AAEE;;;AAOA;;AACsBlF;AAA2CkF;AAAS;AAC5E;AACA;AACF;;AAEA;AACA;AACA;AACA;AACO;AAGL;AACA;AACE;AACA;AACE;AACF;AACF;AACA;AACA;AACE;AACA;AACE;AACF;AACF;;AAEF;AAQO;AACL;;AAEEC;;;;AAIAC;AACA;;AAEAA;AAAgCF;AAAS;AAC3C;AACA;AACF;AAEO;AAIL;;AAEEC;;;;AAIAC;AACA;;AAEAA;AAAwCF;AAAS;AACnD;AACA;AACF;AAEO;AAIL;AACE;AACF;AACA;;AAEEC;;;;AAIAC;AACA;;AAEAA;AAAsCF;AAAS;AACjD;AACA;AACF;AAEO;AAIL;;AAEEC;;;AAGA;AAKAC;AACA;;;AAGEtG;AAIAgB;;AACsBuF;AAAO;AAC/B;AACED;AAAoCF;AAAS;AAC/C;AACF;AACA;AACF;AAEO;AAKL;AACEpG;AACA;AACF;;AAEEiG;AACAO;AACAC;AACF;AAAM7E;;;AAEN;AAEA;;AAEEyE;;;AAGA;AACAC;;AAAgD;;AAEhDA;AAAyBF;AAAS;;AACZM;AAAU;AAChC;AACF;;AAGE;AACAJ;AACA;;AAEAA;AAA4BF;AAAS;;AACfO;AAAU;AAClC;AACA;AACF;AAEO;AAIL;;AAEEN;;;AAGA;AACA;AACA;;AAEA;;;AAGED;AACF;AACF;AACA;AACF;AAEO;AAIL;;AAEEC;;;AAGA;AACA;AAKA;;AAEA;;;AAGED;AACF;AACF;AACA;AACF;AAEO;AAKL;AAAyCH;;AACzC;;AAK4B;AAAS;AAAS;AAC1C;;AAEE;AACA;;;AAOA;;;AAGEG;AACF;AACF;;AAEE;;AAEEC;;;AAGA;;AAEArG;AACAgB;;AACsB6E;AAAM;AAC9B;AACF;AACF;AAEJ;AAEO;AAIL;;AAEEQ;;;AAGA;AACA;AAKA;AACF;AACE;AAAA;AAEF;AACF;AAEO;AAIL;AAAyCJ;;;AAEvC;AAKA;;AAEA;;;AAGEG;AACF;AACF;AACA;AACF;AAEO;AAIL;AACA;AACA;;AAEF;AAEO;AAIL;;AAEEC;;;AAGA;AACAC;AACA;;AAEAA;AAA4CF;AAAS;AACvD;AACA;AACF;AAEO;AAGL;AAAyCH;;;AAEvC;AAKA;AACA;;AAEEtF;;;;AAIFX;AACAgB;;AAEEL;AACAO;AACAC;;AAEJ;AACF;AAEA;AAEO;AACL;AACA;;;;AAEA;AACA;AACA;AACA;AACA;AACEyB;AAAWgE;;;AACb;AACE;;AAEE;AACA;AACA;AACA;;AAEA;AACA;;AAEEhE;;AAAkBiE;;AACpB;;AAEJ;AACAC;AACA;;AAA4B;AAC9B;;AChhBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA0DO;AAKL;AACA;AACEC;AAASnF;;;;;AAGX;AACEmF;AAASnF;;;;AAEX;AACEmF;AAASnF;;;AACT;;AAEA;AACF;;;;;;AACgDM;AAAQ;;;AAGtD;;AASA;AACE;AACF;AACF;AACA;AACF;AAwCO;;AAIG8E;AAAO;AAAMpF;;;;;AAGnB;;AAIA;AACE;AACF;AACA;AACF;AACF;AAEO;;AAEP;;ACjLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASO;AAGL;;;AACcqF;AAAU;AACxB;AAGF;AAMO;AAGL;AACA;;AAEF;AAEO;;;AASP;;ACtDA;AACA;AACA;AACA;AACO;AAGL;AAMF;;ACfO;AAIL;AAAa;AAAQ;AAAS;;;AAG5B;;AACUC;AAAsBrB;;AAChC;AACF;AACF;;ACbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIO;AAGL;AAA6CmB;AAAc;AAC3D;;AAMF;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAcO;AAIL;AAAepF;;;;AACPqE;;AAAoD;;AACtDkB;AAAyBC;AAAiB;AAChD;AACEA;AACF;AACA;AACED;AACF;AACA;;AACQE;AAAK;;AAEb;AACE;;AAEI;AACF;;;AAGE;;;AAGE;AACF;;AAEE;AACF;;AAEJ;AACAC;AACF;AACA;AACF;;AC/CA;AAIA;AACE;AACA;AACA;AAAQ;AACR;AAAQ;AACR;AAAe;AACf;AAAe;AACf;AAAS;AACT;AAAoB;AACpB;AAAY;AACZC;AAAc;AACd;AACA;AACA;AAGF;AAEA;AAIE;;;AAGE;AACA;;AAEIC;;AAEJ;AACF;AACEA;AAAkDR;;AAGpD;AACA;AAKF;AAEA;;;AAOE;AAAkB7D;;;AAEhB;;AAQA;AACF;AACA;AACF;AAEA;AAKE;AACF;;AAEA;AACA;AACA;AACA;AACA;AACE;AACA;AACA;AACA;AACA;AAME;AACF;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AAQF;AAEA;;AACUA;AAAO;;AAEb;AACF;AACA;;;AAGA;AACA;AACA;;AAMA;AACA;;AAEF;AAEO;AAIL;AACA;AACF;AAEO;;;AAKH;AACA;;AAEA;AACF;AACA;AACF;AAMO;;AAKH8C;;;AAGF;AAAMrE;;;AAEN;AAEA;AACA;AACE;AAKA;AACE6F;AACF;AACF;;AAGEC;;AAEAC;AACF;;AASIC;AAAe;AAEjB;AACEH;AACF;AACF;;AAGA;;AAEII;AACA;AACF;AACF;AAEA;AACEjG;AACA8F;;AAEAI;;;;;;AAOF;;AAEA;AACA;;AAEA;;AAKA;AACE;AACA;AACA;AACA;AACEC;AACF;AACF;AACA;AACF;AAEO;;;AAOCL;;AAEAC;;AAGR;AAEO;AAIL;AACA;AACF;AAEO;AAIL;AACA;AACE;AACA;AACE;AACF;;AAIA;AACA;;AAEA;AACA;AACF;AACF;;ACjSO;AAKL;AAEIK;AACAC;;AAEJ;AACA;;;AAMQC;AAAc;;AAEtB;AACA;AAAkB/E;;AAChB;AACA;;AAEEgF;AACF;AACEC;AACA;AACF;AACF;;AACSnG;AAAenB;;;AAC1B;AAEO;;AACGuH;AAAM;;AAEd;AACE;AACA;AACA;AACA;AACA;AACA;AACE;AACA;AACA;;AAGAC;AACF;AACA;AACA;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE;;AAEF;AACCxH;AACC;AACCuH;;AAGL;AACA;;AAEE;AACF;AACAC;AACF;AACF;AAOO;;AAKG7D;AAAsBwB;AAAoB;AAChDrE;;;AAIF;;AAII2G;AACF;AAGF;AACF;;ACxHA;AACExI;AAGA;AACA;AACA;AACAwF;AACA;AACA;AACF;AAEA;AACO;;AAEHiD;;;AAGA;AACF;AACA;AACF;AAEA;AACO;;AAIHC;AACF;AACA;AACF;AAEO;AACL;AACF;;ACnCA;AACO;;;AAGD;;AAEEC;AACA;AACA;AACA;;AAEF;;AAGE;AACA;AACA;;AAEF;AACEC;AACF;AACF;AACEA;AACF;AACF;AACA;AACF;;AC/BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAwBA;AAEA;;;AAA2CC;AAAU;AAarD;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACO;AAML;;;;AAKEC;AACAC;;AAEF;;;;AAMA;;AAEE;AAAoD1B;AAAgB;;AAGtCA;AAAgB;AAC9C;AAGwCA;AAAgB;AAExD;AACE2B;;AAEAA;;AAEAA;AACF;AACE;AACAA;AACF;AACF;;;AAIA;AACA;;AAGEC;AACA;AACA;AACEA;AACF;AACAA;AACA;AACE;AACA;AACAC;AACE;AACAC;AACE;AACA;AACA;AACA;AACAC;AACF;;AAEJ;AACAH;;;;;AAMA;AACA;AACEA;AACF;AACAA;;;AAIF;AACE;AACA;;AAEA;AACE;AACAA;AACF;AACA;AACEA;AACF;AACAA;;;AAIF;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AAOHE;;;AAGF;AACEtH;;;AAIF;AACEwH;;AAEF;AACA;AACEC;AACF;AAEA;AACEA;AACF;AACE;;AAEEA;AACF;AACF;AAEA;AACA;AACEA;AACF;;AAGE;AACA;AACA;AACE;AAGA;;;AAGE;;;;AAKF;;AAKIH;AACA7C;AACF;;AAGO1F;;;AACX;;AAEA;AACA;AAEIsB;AACAC;;AAIA2G;AACAC;AACA;AACAI;;;AAGE;;;AAGFI;AACE;AACA;AAEA;;AAEF;;AAIJ;;AACS3I;;;;AAET;AACA;AACA;;AAEEA;AACAC;AACAM;;AAEJ;AACF;;AAEA;AACA;AACA;AACO;;AAODe;AACAC;;AAGA2G;AAAcC;;;AAGpB;;AAEA;AACA;AACA;AACO;;AAOD7G;AACAC;;AAGA2G;AAAcC;;;AAGpB;;AC1TO;AAGL;AAGF;AAEO;;AAEP;AAEO;AACL;AACF;;ACnBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAkEO;AACL;AACA;AAEI;AACAS;AACN;AAEO;AAGL;AAAmCnC;AAAiBnB;AAAI;AAC1D;AAEO;AAGL;AACA;AACE;AACA;;AAEF;;AAEF;AAEO;;AAEH;AACA;AAEA;;AAGA/D;;AAEJ;AA+DO;;AAKL;;;AAEWvB;;;AACX;;;AAGE6I;;AAEA;AACEzJ;AACAC;AACAgB;;AACSL;;;AACX;;AAKAX;AACAgB;;AAEEL;;AAEAQ;;AAEJ;AAEA;;AAEEsI;;AAEAzJ;;AACsBwJ;AAAY;AAClCxI;AACA;AACEjB;;AACSY;;;AACX;;AAEEA;;;;AAIJ;;AAGEZ;;AACSY;;;AACX;;AAEA;AACA;;AACSA;AAAUC;;AACrB;AAEO;;;;;AAQHZ;;AACsB0J;AAAS;AAC/B1I;;AAEEL;AACAO;;;AAGJ;;;;AAKSP;AAAUC;;AACrB;;ACzQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA6BA;AAEE;AACA+I;AAEA;AACA;AACA;AACE;;AAEE;AACF;AACF;AACA;AACF;AAEO;;AAIGC;AAAgB;;AAExB;AACE;AAAkClJ;AAAgB;AACpD;AACA;AACF;AAEO;AAKL;AACA;;AAEA;;;AAGEE;AACA;AACF;AACA;AACF;AAuBO;AAGL;AACA;AACE;AACF;AAEA;;;AAIE;AAIJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA;AACA;AACA;AACA;AAGI;AAAkBuC;;AAChB;AACA;AACA;AACA;AACE0G;AACF;AACE7J;AACF;AACF;;;AAGAgB;AACF;AAEA;AACF;AAEA;AACO;;;AAEK8I;;;AAEN9J;AACF;AACA;AACE+J;AACAhI;;;AAEoBiI;AAAe;AACrCC;AACF;AACA;AACF;AAEA;AACO;;;AAEKH;;;AAEN9J;AACF;AACAkK;AACExH;;AAEA;AACF;AACF;AACA;AACF;AAOO;AACLyH;AACE;AACF;;AAEE;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACQC;AAAoB;;AAIxB;;AACWC;;AACX;;AAEA;;AAKkCnJ;AAAW;;AAClCmJ;AAAgBnK;;AAC3B;AACF;;AACSmK;;AACX;AAqBO;;AAMGC;;AACR;AACA;;AAEEtK;;AACsBsK;AAAkB;AACxC;AACF;;AAEA;;AAEEjE;;AAEF;AACArG;;AAEE;AACA;;AAEAA;;AACsBuK;AAAe;AACrCvJ;AACF;AACA;AACF;;AC1SA;AACA;AACA;AACA;AACO;;AAIH;AACA;;AAIA;AAIMwJ;AACAC;;AAKN;;AAEI9J;;;AAGJ;;AAGEA;AACAC;;;AAGF;AACA;AACA;AACA;;AAQED;AACAO;;AAIJ;AACF;;AClDA;AACEU;AACA;AACA8I;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACF;;AAEA;AACA;AACA;AACO;;AAIH;AAAsCnE;AAAc;;;AAGhDrG;;;AAGJ;;;AAEcoD;AAAiB7B;AAAQ;;AAEvC;AACA;;;AAGIvB;;;AAGJ;;AAEA;;AAEA;;AAGA;AAEIyK;AACAC;AACF;;AAIA1K;;;;;AAKAA;AACAO;;AAEJ;AACF;;ACtEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAUA;;AAEA;AACA;AACA;AACO;AACL;AACE;AACF;AACA;AACE;AACF;AACA;AACF;;AAEA;AACA;AACA;AACO;;AAEL;AAAkBiC;;;AAEhB;AACE;AACA;AACA;;AAEEmI;AACF;AACE1I;AACF;AACF;AACE;AACAA;AACF;AACF;AACA;AACF;;AAEA;AACA;AACA;AACO;;AAEL;AACF;;AAEA;AACA;AACA;AACO;;;AAOL;AACA;AACA;AAEA;AACE;AACA;AACA;AACE2I;AACF;AACEA;AACF;;;AAGA;AACA;AACA;AACEC;;;AAGA;AACF;AACF;AAEA;AAAkBrI;;AAChB;AACA;AACA;AACE4E;AACA;AACA;AACE;AACAuD;;AAEEvD;AACF;AACF;;AAEA;AACA;AACE;AACAuD;AACF;AACA;;AAKA;AACA;AACEvD;AACF;AACA;AACF;AACEA;AACF;AACF;AACA;AACF;;AAsBA;AACA;AACA;AACA;AACA;AACO;;AAEP;;AASA;AACA;AACA;AACO;AACL;AACF;;AAUA;AACA;AACA;AACA;AACO;AACL;AAMF;;;ACtMO;;AAEH;;;AAGF;AACF;;ACTO;AAEA;AACL;;AAMA;;AAEIpH;AACAO;;;AAGJ;;AAESP;;;AACX;AAEO;AAOL;AACA;AACE;AACF;;AAEQL;AAAkB;;;AAGtBK;AACAO;AACAC;;AAEJ;;AAEA;AACA;;AAEA;AACA;;AAOF;AACA;AACA;AACA;AACA;AACA;AACA;;AAGIR;AACAC;;;AAGE6K;AACAC;AACAC;AACF;;AAEJ;;ACjEA;AACA;AACA;AACA;AACO;;;;AAML;AACF;;ACdA;AACE5L;AAGA;AACA;AACA;AACAwF;AACA;AACA;AACF;AAEA;AACO;;AAEHqG;;;AAGA;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACF;AACA;AACF;AAEA;AACO;;AAEH;;;AAGEC;AACF;;;AAGE;;AAEA;AACA5K;AAEAA;AACAnB;AACA;AACA;AACA;AACAwF;AACA;AACA;AACF;AACF;AACA;AACF;AAEA;AACO;;AAEH;;;AAQF;AACA;AACF;AAEA;AACO;;AAEHwG;;;AAGA;AACF;AACA;AACF;AAEA;AACA;;AAEIC;AACF;AACA;AACF;AAEO;AACL;AACF;AAEO;AACL;AACF;;AClFO;;;AAKHC;;;;AAIF;AAAMrK;;;AACN;AACA;AACA;;AAIA;;;;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACEyE;;;AAGEA;AACF;AACF;AACEA;AACF;;AAgBI;AACA;AAEA;;AAEA;AACA;;AAMA;AACA6C;;;AAGE;;;AAGF7C;AACF;AAGF;AACE6F;;AAEI;AACA;;AAEF;AACF;AACF;AAEA;AACF;;AC9GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAqBO;;;;AAIyBC;AAAQ;AACtC;AACA;AACA;AACA;AACE;;AAEElG;;AAEF;AACF;;AAEEgG;;;AAGF;AAAMrK;;;;AAEN;AACA;AACA;;AAUA;AACEqE;AACA;AACA;AACA;;;AAGAI;AACA;AACA6C;;;AAGE;AACA;AAAekD;;AACfC;AAIA;AACF;AACF;AACF;;AC3FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAgCA;;;;;;;;;;;;;;;;;AAiBEC;AACF;AAEO;AAEP;AA+EA;;;;AAMQ;;AAEF;;AAEJ;AAEA;AAEA;;AAaQ;;AAEE;AACF;;AAEE;AACA;;;;AAIA;AACA;AACA;AACA;;;AAII;AACA;AACA;;;AAIN;AACA;AACF;AASR;;AAEA;AACA;;;AAGE;AACA;AACA;AACA;;;;;;AAMA;AACA;AACA;AACA;AACA;AACA;AACF;AAEA;AACE;;AAEE;AACA;AACA;AACE;AACF;AACA;AACA;;;AAGA;AACE;AACF;AACA;AACA;AAAkCrE;;AACpC;;AAEE;AACA;AACA;AACE;AACF;AACA;AACA;AAAkCA;;AACpC;AACA;AAAkCA;;AACpC;AAEA;AAKE;AACA;AACAjI;;;AAGI;AACA;AACA;AACAuM;AACE;;;AAII;AACA;AACA;;AAEF;;AAINvM;;AACsBuK;AAAe;AACrCvJ;AACF;AACA;AACF;AAEO;AACLiF;AACAuG;AACa;;AACqCvG;AAAI;;AAEtD;;AAMiCA;AAAI;AACrC;;AAKqCwG;;AAErC;AACA;AACA;AAIA;AACA;AACE;AACA;AACA;AACA;;;;AAIIC;AACF;AACF;AACF;AACA;AAMEA;AACF;;AAEEA;;AAEF;AACA;;AAEA;AACEA;AACF;;AACQC;AAAuB;;;AAG/B;AACA;;AAEA;AACA;AACA;;;;;AAIUC;;AACR;AACA;AACA;AACEC;AACA;AACA;AACA;;;AAGA;AACF;AACA;AACEC;AACA;AACA;AACA;;;AAGA;AACF;AACA;AAGA;AACE;AACA;;AAKE;;;;AAIA;AACF;AACF;;AAKF;AACEC;AACAC;AACF;;AAEA;AACA;AAGA;;AAGA;AAEA;AAKA;;;;;;;;AASEC;AAAYC;;;;;;;;;AAQZC;;;;AAIAC;AACE;;AAEA;AACAC;AAGF;;AAEJ;AAEO;;AAKHC;;AAEAC;AACF;AACE3L;;;AAGF;;;AAGI7B;AAMF;AACF;;;;AAC4BoN;AAAgB;AAC5C;AACA;;;AAGIxM;AACAO;;;AAMJ;AACA;AACE;;AAEEP;AACAO;;;AAMJ;AACA;;AAEIP;AACAO;AACAC;;AAKJ;AACA;;AAEIR;AACAO;;;AAMJ;AACA;;;AAGIP;AACAO;AACAC;;AAEJ;;;AAGIR;AACAO;AACAC;;AAEJ;AACA;;AAEIR;AACAO;AACAC;;AAEJ;;;AAGIR;AACAO;AACAC;;AAKJ;AACA;AAIE;;AAOF;;AACSR;AAAUC;;AACrB;;ACjkBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAmBO;AA0CiC;AAEjC;;AAEP;;ACtFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAyDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AAIGqF;AAAqBuH;AAAc;AACzC5L;;;;AAIF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEE;AACF;;AAEA;AACA;;AAME;AACF;;AAEA;AACA;;AAEA;AACA;;AAEE;AACF;;AAEA;;AAEU;;AACO;;AACP;;AACI;;;AAMd;AACF;;ACpJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA;AACE7B;AAGA;AACA;AACA;AACAwF;AACA;AACA;AACF;AAEA;AACO;;AAEHkI;;;AAGA;AACF;AACA;AACF;AAEA;AACO;;AAIHC;AACF;AACA;AACF;AAEO;AACL;AACF;;ACxDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAkBA;AACA;AAEO;AACL;AACA;;AACQrF;AAAM;;AAEd;;AACgCpC;AAAgBuH;AAAa;AAC3D;AACF;AACA;AACA;;AAEE;;AAKA;;AACQtE;AAAI;AACZA;AACF;AACA;AACF;AAEO;AACL;AACA;;AACQb;AAAM;;AAEd;;AACgCpC;AAAgBuH;AAAa;AAC3D;AACF;AACA;AACA;;AAEE;;AAKA;;AACQtE;AAAI;AACZA;AACF;AACA;AACF;AAEO;AACL;AACA;;AACQb;AAAM;;AAEd;;AACgCpC;AAAgBuH;AAAa;AAC3D;AACF;AAEA;;AAEA;;AAEE;;AAKA;;AACQtE;AAAI;AACZA;AACF;AAEA;AACF;AAEO;AACL;;AACQb;AAAM;;AAEd;;AACgCpC;AAAgBuH;AAAa;AAC3D;AACF;AAEA;;AAGE;;AAKA;;AACQtE;AAAI;AACZA;AACF;AAEA;AACF;;AC1IA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAQO;AACL;AAAqBtH;;AACrB;AACA;AACE;;AAEE+L;AACF;AACF;AACA;AACF;;ACtBO;AACL;AACF;;ACaO;AAGL;AACA;;AAEE;AACE;AACF;AACA;;AAEE;AACF;AACAC;AACA;AACEhM;;;AAGA;;AAEF;AACE;AACA;;AAEF;;;;AAIF;;AAIF;AAEO;AACL;AACF;AAEO;AAGL;AACA;;;;AAIA;AACA;AACF;AASO;;AAIP;AAEO;AACL;AACF;AAEO;AACL;AACA;AACA;AACF;;AC7CO;;AAGGmC;AAAK;;AAOf;;AC1DO;;AAGkBnC;;AAAwB;AACjD;;ACFO;AACLiM;AACAC;AACAC;AACF;;ACAO;AACLC;AACAC;AACAC;AACAC;AACF;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAOO;;AAIH;AACF;;AAGE;AACF;AAEAC;AACE;AAGF;AAEAC;AAIIC;AACAC;;AAMF;AACE;;AAII;AACN;AACA;AACF;;AAKE;AACF;;AAGE;AACF;;;AAMA;;AAGE;;AAIF;AACF;;ACpFA;AAEA;AAIO;;AAEHC;AAGF;AACA;AACF;;AClBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAiCO;AACLR;AACAC;AACAC;AACAC;AACF;AAEO;AACLH;AACAC;AACAC;AACAC;AACAM;AACF;AAiBA;AAEA;AAEA;AASA;AACE;AACET;AACAC;AACAC;AACAC;;AAEF;;;;AAIM;;;AAGA;;;AAGA;;;AAGA;AACJ;AACF;AACA;AACF;AAEA;;;AAGIO;AACF;;AAEEA;AACF;;AAEEA;AACF;;AAEEA;AACF;AACA;AACF;AAoBO;AAKL;AACA;AACE;AACF;;AAEQ3K;AAAiB7B;AAAQ;;AAG/ByM;;AAEAC;AACF;AACEhN;;;AAIF;;AAQEiN;AACAb;AACAH;AACA;AACF;AAEA;AACEjM;AACA;;;AAIF;AACE;AACA;;AAKE;AACF;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;;;;;;;;;AAgBIkN;AACAC;AACF;AACF;AACF;AACA;AACE;AACF;;AAEA;AACA;AAKE;AACA;;AAEA;AACE;;AAEA;AACE;AACA;AACA;AACA;;AAKA;AACE;AACA;;;AAGEC;AACEC;AACA/M;AACF;AACF;AACF;AACEgN;AACF;AACF;AACE;AACA;;;AAGEC;AAA+BF;AAAqB/M;AAAQ;AAC9D;AACF;AACEgN;AACF;AACF;AACAE;AACE;;AAOJ;AACEA;AACF;;AAEEC;AACF;AACA;AACF;AAEO;;AAEP;AAEO;AAIL;AACF;AAEO;AAIL;;AAEF;AAEO;AACL;;AACQC;;AACR;AASF;AAEO;;AAWP;AA6FO;AAGL;AACF;AAOO;;AAIGC;;AAA2C;AACjD3N;;;AAIF;AACA;AAIA;AACA;AACA;AAEA;AAAkBuB;;;AACR;AAAS;AAAU;;AAE3B;AACE;;AAGEqM;AACF;AACA;AACF;;;AAGA;AACA;AACE;AACF;AACAC;AACAC;AACA;AAMEC;AACF;AACF;;AAEA;AACA;AAAa;AAAQ;AACnB;AACE;AACF;AACAA;AACF;AACA;AACA;AAAa;AAAS;AAAgB;AACpC;AACE;AACF;AACAA;;AAEA;AACE;AACA;AACA;;AAKA;AACEC;AACAC;AACF;AACAH;AAIF;AACF;AAEA;;AAKMvM;;;AAII;AAAS;AAAU;AAC3B;AACA;;AACUY;AAAK;;;;AAYb;AACA;AACA;AACA;AACA;;AAEA;AACAd;AACF;AACA;AACA;;AAKA;AACA;AACE0M;AACAG;AACF;AACEA;AACF;AACA;AACEA;AACF;;;AAEQ3M;AAA0B;AAClC;AACE4M;;;AAKA;;AAIA;AACF;AACAC;AACF;;AAIA;AACE;AACEhC;AACAC;AACAC;AACAC;;AAEF;AAAa;AAAS;AAAU;AAC9B;AACE;AACF;AACA;AACA8B;AACAA;AACAA;AACAA;AACF;AACAH;AAGF;AACAA;AACF;;AC5mBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA4BO;AAIL;AACA;;;AAGA;AACF;AAYO;AAIL;;AACoBI;AAAiB;;AAE/B/M;AAAkB;AACxB;;AAGE;AACF;AAEA;AACEvB;AACA+M;AACA1G;AACA;;;;AAKAlB;AACF;;;AAEkBzE;AAAQ;AAE1B;AAEAA;AAEA;AAAwC6N;AAAS;AACjD;;AAEE;AACF;AACA;AACA;AACA;AAKA;;;;;AAKE7N;;;AAIA;AAEI8N;AAAqCC;AAAK;AAC5C;AAEEC;AACEC;AACAC;;AACyBC;;;;;AAI3B;AACF;;AAGE;AACA;AACF;;AAEE;AACE;AACF;AACA;AACA;AAGF;;;;AAOwBC;AAAY;AAClC;AACF;AACAC;;AAEErO;AACF;AACF;;;AAGA;AACF;;AAIA;AACF;;AC1KA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAOA;;AAkBA;AACA;AACA;AACA;AACO;;;;AAML;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AAGL;AACA;;AAGE;AACA;AACA;AACE;AACA;;AAEIL;;;AAGJ;AACF;;AAEA;AACA;AACA;;AACWA;AAAeC;;AAC1B;;;AAIEA;;AAEJ;;AAEA;AACA;AACA;;AAEA;AACA;AAKE;AACA;AACA;AACA;AACA;;;AAGA;;AAMA;AACF;AAKE;;;AAGA;AACF;;;AAEeA;;AACjB;;AAEA;AACA;AACA;AACA;AACA;AACO;AACL;;AAEE;AACF;;;AAEcA;AAAQ;;AAEtB;;AAEE6B;;;AAGAiD;AACF;AAEA;AAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","debugId":"9654f015-4a5a-4e8b-b934-395a88591a9"}
|