diff --git a/test/upstreamGapAnalyzer.test.js b/test/upstreamGapAnalyzer.test.js
new file mode 100644
index 0000000..fa2e8d4
--- /dev/null
+++ b/test/upstreamGapAnalyzer.test.js
@@ -0,0 +1,136 @@
+const assert = require("assert");
+const {
+ analyzeUpstreamGaps,
+} = require("../util/upstreamGapAnalyzer");
+
+suite("upstreamGapAnalyzer", () => {
+ function createState(entries = {}) {
+ return {
+ groupedUpstreams: new Map(Object.entries(entries)),
+ };
+ }
+
+ test("classifies uncovered dependencies as reachable when a matching proxy exists", async () => {
+ const dependencies = [
+ {
+ name: "accepts",
+ version: "1.3.8",
+ format: "npm",
+ ecosystem: "npm",
+ cloudsmithStatus: "NOT_FOUND",
+ },
+ ];
+
+ const enriched = await analyzeUpstreamGaps(dependencies, "workspace-a", ["production"], {
+ upstreamChecker: {
+ async getRepositoryUpstreamState() {
+ return createState({
+ npm: [
+ { name: "npm", is_active: true },
+ ],
+ });
+ },
+ },
+ });
+
+ assert.strictEqual(enriched[0].upstreamStatus, "reachable");
+ assert.strictEqual(enriched[0].upstreamDetail, "npm proxy on production");
+ });
+
+ test("classifies supported formats with no proxy as no_proxy", async () => {
+ const dependencies = [
+ {
+ name: "requests",
+ version: "2.31.0",
+ format: "python",
+ ecosystem: "python",
+ cloudsmithStatus: "NOT_FOUND",
+ },
+ ];
+
+ const enriched = await analyzeUpstreamGaps(dependencies, "workspace-a", ["production"], {
+ upstreamChecker: {
+ async getRepositoryUpstreamState() {
+ return createState();
+ },
+ },
+ });
+
+ assert.strictEqual(enriched[0].upstreamStatus, "no_proxy");
+ assert.strictEqual(enriched[0].upstreamDetail, "No upstream proxy configured for python");
+ });
+
+ test("classifies unsupported formats as unreachable", async () => {
+ const dependencies = [
+ {
+ name: "custom-lib",
+ version: "1.0.0",
+ format: "custom",
+ ecosystem: "custom",
+ cloudsmithStatus: "NOT_FOUND",
+ },
+ ];
+
+ const enriched = await analyzeUpstreamGaps(dependencies, "workspace-a", ["production"], {
+ upstreamChecker: {
+ async getRepositoryUpstreamState() {
+ return createState();
+ },
+ },
+ });
+
+ assert.strictEqual(enriched[0].upstreamStatus, "unreachable");
+ assert.strictEqual(enriched[0].upstreamDetail, "Not available through Cloudsmith");
+ });
+
+ test("limits upstream repository lookups to five concurrent requests and emits one final patch", async () => {
+ const dependencies = [
+ {
+ name: "accepts",
+ version: "1.3.8",
+ format: "npm",
+ ecosystem: "npm",
+ cloudsmithStatus: "NOT_FOUND",
+ },
+ ];
+ const repositories = Array.from({ length: 12 }, (_, index) => `repo-${index + 1}`);
+ const progressEvents = [];
+ let inFlight = 0;
+ let maxInFlight = 0;
+
+ const enriched = await analyzeUpstreamGaps(dependencies, "workspace-a", repositories, {
+ onProgress: (patchMap, meta) => {
+ progressEvents.push({
+ size: patchMap.size,
+ completed: meta.completed,
+ total: meta.total,
+ });
+ },
+ upstreamChecker: {
+ async getRepositoryUpstreamState(_workspace, repo) {
+ inFlight += 1;
+ maxInFlight = Math.max(maxInFlight, inFlight);
+ await new Promise((resolve) => setTimeout(resolve, 5));
+ inFlight -= 1;
+
+ if (repo === "repo-9") {
+ return createState({
+ npm: [
+ { name: "npm", is_active: true },
+ ],
+ });
+ }
+
+ return createState();
+ },
+ },
+ });
+
+ assert.ok(maxInFlight <= 5);
+ assert.strictEqual(progressEvents.filter((event) => event.size > 0).length, 1);
+ assert.strictEqual(progressEvents[progressEvents.length - 1].completed, repositories.length);
+ assert.strictEqual(progressEvents[progressEvents.length - 1].size, 1);
+ assert.strictEqual(enriched[0].upstreamStatus, "reachable");
+ assert.strictEqual(enriched[0].upstreamDetail, "npm proxy on repo-9");
+ });
+});
diff --git a/test/upstreamPullService.test.js b/test/upstreamPullService.test.js
new file mode 100644
index 0000000..bad0b01
--- /dev/null
+++ b/test/upstreamPullService.test.js
@@ -0,0 +1,312 @@
+const assert = require("assert");
+const {
+ buildRegistryTriggerPlan,
+} = require("../util/registryEndpoints");
+const {
+ UpstreamPullService,
+} = require("../util/upstreamPullService");
+
+function createResponse(status, body, headers = {}) {
+ return {
+ status,
+ headers: {
+ get(name) {
+ const lowerName = String(name || "").toLowerCase();
+ return headers[lowerName] || headers[name] || null;
+ },
+ },
+ async text() {
+ return body;
+ },
+ };
+}
+
+suite("UpstreamPullService", () => {
+ test("builds canonical registry trigger URLs for supported formats", () => {
+ const mavenPlan = buildRegistryTriggerPlan("workspace", "repo", {
+ name: "com.example:demo-app",
+ version: "1.2.3",
+ format: "maven",
+ });
+ assert.strictEqual(
+ mavenPlan.request.url,
+ "https://dl.cloudsmith.io/basic/workspace/repo/maven/com/example/demo-app/1.2.3/demo-app-1.2.3.pom"
+ );
+
+ const npmPlan = buildRegistryTriggerPlan("workspace", "repo", {
+ name: "@scope/widget",
+ version: "4.5.6",
+ format: "npm",
+ });
+ assert.strictEqual(
+ npmPlan.request.url,
+ "https://npm.cloudsmith.io/workspace/repo/%40scope/widget/-/widget-4.5.6.tgz"
+ );
+
+ const goPlan = buildRegistryTriggerPlan("workspace", "repo", {
+ name: "github.com/MyOrg/MyModule",
+ version: "v1.0.0",
+ format: "go",
+ });
+ assert.strictEqual(
+ goPlan.request.url,
+ "https://golang.cloudsmith.io/workspace/repo/github.com/!my!org/!my!module/@v/v1.0.0.info"
+ );
+
+ const cargoPlan = buildRegistryTriggerPlan("workspace", "repo", {
+ name: "serde",
+ version: "1.0.0",
+ format: "cargo",
+ });
+ assert.strictEqual(
+ cargoPlan.request.url,
+ "https://cargo.cloudsmith.io/workspace/repo/se/rd/serde"
+ );
+ });
+
+ test("prepare builds a mixed-ecosystem confirmation with skipped formats", async () => {
+ const warnings = [];
+ const service = new UpstreamPullService({}, {
+ fetchRepositories: async () => [{ slug: "repo", name: "Repo" }],
+ upstreamChecker: {
+ async getRepositoryUpstreamState() {
+ return {
+ groupedUpstreams: new Map([
+ ["maven", [{ name: "Maven Central", is_active: true }]],
+ ]),
+ };
+ },
+ },
+ showQuickPick: async (items) => items[0],
+ showWarningMessage: async (message, _options, action) => {
+ warnings.push(message);
+ return action;
+ },
+ showErrorMessage: async () => {},
+ showInformationMessage: async () => {},
+ });
+
+ const prepared = await service.prepare({
+ workspace: "workspace",
+ repositoryHint: "repo",
+ dependencies: [
+ {
+ name: "com.example:demo-app",
+ version: "1.2.3",
+ format: "maven",
+ cloudsmithStatus: "NOT_FOUND",
+ },
+ {
+ name: "requests",
+ version: "2.31.0",
+ format: "python",
+ cloudsmithStatus: "NOT_FOUND",
+ },
+ ],
+ });
+
+ assert.ok(prepared);
+ assert.strictEqual(prepared.plan.pullableDependencies.length, 1);
+ assert.strictEqual(prepared.plan.skippedDependencies.length, 1);
+ assert.strictEqual(warnings.length, 1);
+ assert.match(warnings[0], /Pull 1 of 2 dependencies through repo\?/);
+ assert.match(warnings[0], /1 Maven will be pulled\./);
+ assert.match(
+ warnings[0],
+ /1 Python will be skipped \(no matching upstream is configured on this repository\)\./
+ );
+ });
+
+ test("pulls Python dependencies via same-host redirects using manual auth-preserving requests", async () => {
+ const calls = [];
+ const initialIndexUrl = "https://dl.cloudsmith.io/basic/workspace/repo/python/simple/requests/";
+ const redirectedIndexUrl = "https://dl.cloudsmith.io/basic/workspace/repo/python/simple/requests/index.html";
+ const artifactUrl = "https://dl.cloudsmith.io/basic/workspace/repo/python/packages/requests-2.31.0-py3-none-any.whl";
+ const authorizationHeader = `Basic ${Buffer.from("token:api-key").toString("base64")}`;
+ const service = new UpstreamPullService({}, {
+ credentialManager: {
+ async getApiKey() {
+ return "api-key";
+ },
+ },
+ fetchImpl: async (url, options) => {
+ calls.push({ url, options });
+ if (url === initialIndexUrl) {
+ return createResponse(302, "", {
+ location: redirectedIndexUrl,
+ });
+ }
+ if (url === redirectedIndexUrl) {
+ return createResponse(200, 'requests');
+ }
+ if (url === artifactUrl) {
+ return createResponse(200, "");
+ }
+ throw new Error(`Unexpected URL: ${url}`);
+ },
+ showErrorMessage: async () => {},
+ showInformationMessage: async () => {},
+ showWarningMessage: async () => {},
+ });
+
+ const result = await service.execute({
+ workspace: "workspace",
+ repository: { slug: "repo" },
+ plan: {
+ pullableDependencies: [{
+ name: "requests",
+ version: "2.31.0",
+ format: "python",
+ cloudsmithStatus: "NOT_FOUND",
+ }],
+ skippedDependencies: [],
+ },
+ });
+
+ assert.strictEqual(result.canceled, false);
+ assert.strictEqual(result.pullResult.cached, 1);
+ assert.strictEqual(calls.length, 3);
+ assert.deepStrictEqual(
+ calls.map((call) => call.url),
+ [initialIndexUrl, redirectedIndexUrl, artifactUrl]
+ );
+ assert.strictEqual(calls.every((call) => call.options.redirect === "manual"), true);
+ assert.strictEqual(
+ calls.every((call) => call.options.headers.Authorization === authorizationHeader),
+ true
+ );
+ });
+
+ test("rejects redirects to untrusted hosts before forwarding credentials", async () => {
+ const calls = [];
+ const service = new UpstreamPullService({}, {
+ credentialManager: {
+ async getApiKey() {
+ return "api-key";
+ },
+ },
+ fetchImpl: async (url, options) => {
+ calls.push({ url, options });
+ return createResponse(302, "", {
+ location: "https://example.com/requests-2.31.0.whl",
+ });
+ },
+ showErrorMessage: async () => {},
+ showInformationMessage: async () => {},
+ showWarningMessage: async () => {},
+ });
+
+ const result = await service.execute({
+ workspace: "workspace",
+ repository: { slug: "repo" },
+ plan: {
+ pullableDependencies: [{
+ name: "requests",
+ version: "2.31.0",
+ format: "python",
+ cloudsmithStatus: "NOT_FOUND",
+ }],
+ skippedDependencies: [],
+ },
+ });
+
+ assert.strictEqual(result.canceled, false);
+ assert.strictEqual(result.pullResult.cached, 0);
+ assert.strictEqual(result.pullResult.errors, 1);
+ assert.strictEqual(calls.length, 1);
+ assert.match(result.pullResult.details[0].errorMessage, /redirect target was rejected/i);
+ });
+
+ test("stops after three authentication failures before expanding concurrency", async () => {
+ const calls = [];
+ const errors = [];
+ const service = new UpstreamPullService({}, {
+ credentialManager: {
+ async getApiKey() {
+ return "api-key";
+ },
+ },
+ fetchImpl: async (url) => {
+ calls.push(url);
+ return createResponse(401, "");
+ },
+ showErrorMessage: async (message) => {
+ errors.push(message);
+ },
+ showInformationMessage: async () => {},
+ showWarningMessage: async () => {},
+ });
+
+ const dependencies = Array.from({ length: 5 }, (_, index) => ({
+ name: `package-${index}`,
+ version: "1.0.0",
+ format: "npm",
+ cloudsmithStatus: "NOT_FOUND",
+ }));
+
+ const result = await service.execute({
+ workspace: "workspace",
+ repository: { slug: "repo" },
+ plan: {
+ pullableDependencies: dependencies,
+ skippedDependencies: [],
+ },
+ });
+
+ assert.strictEqual(calls.length, 3);
+ assert.strictEqual(result.pullResult.errors, 5);
+ assert.strictEqual(result.pullResult.authFailed, 5);
+ assert.deepStrictEqual(errors, [
+ "Authentication failed. Check your API key in Cloudsmith settings.",
+ ]);
+ });
+
+ test("prepareSingle only offers repositories with a matching upstream", async () => {
+ const quickPickCalls = [];
+ let warningCalls = 0;
+ const service = new UpstreamPullService({}, {
+ fetchRepositories: async () => [
+ { slug: "repo-a", name: "Repo A" },
+ { slug: "repo-b", name: "Repo B" },
+ ],
+ upstreamChecker: {
+ async getRepositoryUpstreamState(_workspace, repo) {
+ return {
+ groupedUpstreams: new Map([
+ ["python", repo === "repo-b" ? [{ name: "PyPI", is_active: true }] : []],
+ ]),
+ };
+ },
+ },
+ showQuickPick: async (items) => {
+ quickPickCalls.push(items);
+ return items[0];
+ },
+ showErrorMessage: async () => {},
+ showInformationMessage: async () => {},
+ showWarningMessage: async () => {
+ warningCalls += 1;
+ },
+ });
+
+ const prepared = await service.prepareSingle({
+ workspace: "workspace",
+ repositoryHint: "repo-b",
+ dependency: {
+ name: "requests",
+ version: "2.31.0",
+ format: "python",
+ cloudsmithStatus: "NOT_FOUND",
+ },
+ });
+
+ assert.ok(prepared);
+ assert.strictEqual(prepared.repository.slug, "repo-b");
+ assert.strictEqual(prepared.plan.pullableDependencies.length, 1);
+ assert.strictEqual(quickPickCalls.length, 1);
+ assert.strictEqual(quickPickCalls[0].length, 1);
+ assert.strictEqual(quickPickCalls[0][0].label, "repo-b");
+ assert.match(quickPickCalls[0][0].detail, /Python upstream \(PyPI\)/);
+ assert.strictEqual(warningCalls, 0);
+ });
+});
diff --git a/util/registryEndpoints.js b/util/registryEndpoints.js
new file mode 100644
index 0000000..3475759
--- /dev/null
+++ b/util/registryEndpoints.js
@@ -0,0 +1,600 @@
+// Copyright 2026 Cloudsmith Ltd. All rights reserved.
+const {
+ canonicalFormat,
+ sanitizePackageNameInput,
+} = require("./packageNameNormalizer");
+
+const CLOUDSMITH_HOST_SUFFIX = ".cloudsmith.io";
+const MAX_REGISTRY_VALUE_LENGTH = 4096;
+
+const UNSUPPORTED_PULL_FORMATS = new Set([
+ "alpine",
+ "conda",
+ "deb",
+ "generic",
+ "huggingface",
+ "raw",
+ "rpm",
+]);
+
+const DOCKER_MANIFEST_ACCEPT =
+ "application/vnd.docker.distribution.manifest.v2+json, "
+ + "application/vnd.docker.distribution.manifest.list.v2+json, "
+ + "application/vnd.oci.image.manifest.v1+json, "
+ + "application/vnd.oci.image.index.v1+json";
+
+function formatForEcosystem(ecosystemOrFormat) {
+ const normalized = canonicalFormat(ecosystemOrFormat);
+ return normalized || null;
+}
+
+function formatForDependency(dependency) {
+ return formatForEcosystem(dependency && (dependency.format || dependency.ecosystem));
+}
+
+function isPullUnsupportedFormat(format) {
+ const normalized = formatForEcosystem(format);
+ return Boolean(normalized && UNSUPPORTED_PULL_FORMATS.has(normalized));
+}
+
+function encodePathSegment(value) {
+ const normalized = String(value == null ? "" : value)
+ .replace(/\0/g, "")
+ .trim();
+
+ if (!normalized || normalized.length > MAX_REGISTRY_VALUE_LENGTH) {
+ return "";
+ }
+
+ if (normalized === ".") {
+ return "%2E";
+ }
+
+ if (normalized === "..") {
+ return "%2E%2E";
+ }
+
+ return encodeURIComponent(normalized);
+}
+
+function encodePath(value) {
+ return String(value == null ? "" : value)
+ .replace(/\0/g, "")
+ .trim()
+ .split("/")
+ .filter(Boolean)
+ .map((segment) => encodePathSegment(segment))
+ .join("/");
+}
+
+function normalizePythonName(name) {
+ return sanitizePackageNameInput(name).toLowerCase().replace(/[-_.]+/g, "-");
+}
+
+function encodeGoModulePath(modulePath) {
+ return [...String(modulePath || "")]
+ .map((character) => {
+ if (character === "!") {
+ return "!!";
+ }
+ if (character >= "A" && character <= "Z") {
+ return `!${character.toLowerCase()}`;
+ }
+ return character;
+ })
+ .join("");
+}
+
+function cargoIndexPath(crateName) {
+ const normalized = String(crateName || "").trim().toLowerCase();
+ if (!normalized) {
+ return null;
+ }
+
+ if (normalized.length <= 2) {
+ return encodePathSegment(normalized);
+ }
+
+ if (normalized.length === 3) {
+ return `1/${encodePathSegment(normalized)}`;
+ }
+
+ return [
+ encodePathSegment(normalized.slice(0, 2)),
+ encodePathSegment(normalized.slice(2, 4)),
+ encodePathSegment(normalized),
+ ].join("/");
+}
+
+function buildNpmPackagePath(name) {
+ const rawName = sanitizePackageNameInput(name);
+ if (!rawName) {
+ return null;
+ }
+
+ if (!rawName.startsWith("@")) {
+ if (rawName.includes("/")) {
+ return null;
+ }
+
+ const encodedName = encodePathSegment(rawName);
+ return {
+ segments: [encodedName],
+ tarballBaseName: encodedName,
+ };
+ }
+
+ const separatorIndex = rawName.indexOf("/");
+ if (
+ separatorIndex <= 1
+ || separatorIndex === rawName.length - 1
+ || rawName.indexOf("/", separatorIndex + 1) !== -1
+ ) {
+ return null;
+ }
+
+ const scope = rawName.slice(0, separatorIndex);
+ const packageName = rawName.slice(separatorIndex + 1);
+
+ return {
+ segments: [encodePathSegment(scope), encodePathSegment(packageName)],
+ tarballBaseName: encodePathSegment(packageName),
+ };
+}
+
+function buildMavenCoordinates(dependency) {
+ const name = sanitizePackageNameInput(dependency && dependency.name);
+ const version = String(dependency && dependency.version || "")
+ .replace(/\0/g, "")
+ .trim();
+ const coordinates = name.split(":", 3);
+
+ if (coordinates.length < 2 || !version) {
+ return null;
+ }
+
+ const groupId = coordinates[0].trim();
+ const artifactId = coordinates[1].trim();
+ if (!groupId || !artifactId) {
+ return null;
+ }
+
+ const groupPath = groupId
+ .split(".")
+ .filter(Boolean)
+ .map((segment) => encodePathSegment(segment))
+ .join("/");
+
+ if (!groupPath) {
+ return null;
+ }
+
+ return {
+ groupPath,
+ artifactId: encodePathSegment(artifactId),
+ version: encodePathSegment(version),
+ };
+}
+
+function buildComposerCoordinates(name) {
+ const rawName = sanitizePackageNameInput(name);
+ const separatorIndex = rawName.indexOf("/");
+ if (
+ separatorIndex <= 0
+ || separatorIndex === rawName.length - 1
+ || rawName.indexOf("/", separatorIndex + 1) !== -1
+ ) {
+ return null;
+ }
+
+ const vendor = rawName.slice(0, separatorIndex);
+ const packageName = rawName.slice(separatorIndex + 1);
+
+ return {
+ vendor: encodePathSegment(vendor),
+ package: encodePathSegment(packageName),
+ packageName: `${vendor}/${packageName}`,
+ };
+}
+
+function buildSwiftCoordinates(name) {
+ const parts = sanitizePackageNameInput(name).split("/").filter(Boolean);
+ if (parts.length < 2) {
+ return null;
+ }
+
+ return {
+ scope: parts.slice(0, -1).map((part) => encodePathSegment(part)).join("/"),
+ name: encodePathSegment(parts[parts.length - 1]),
+ };
+}
+
+function buildRegistryTriggerPlan(workspace, repo, dependency) {
+ const format = formatForDependency(dependency);
+ if (!format || isPullUnsupportedFormat(format)) {
+ return null;
+ }
+
+ const safeWorkspace = encodePathSegment(workspace);
+ const safeRepo = encodePathSegment(repo);
+ const version = encodePathSegment(dependency && dependency.version);
+
+ switch (format) {
+ case "maven": {
+ const coordinates = buildMavenCoordinates(dependency);
+ if (!coordinates) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://dl.cloudsmith.io/basic/${safeWorkspace}/${safeRepo}/maven/${coordinates.groupPath}/${coordinates.artifactId}/${coordinates.version}/${coordinates.artifactId}-${coordinates.version}.pom`,
+ headers: {},
+ },
+ };
+ }
+ case "npm": {
+ const packagePath = buildNpmPackagePath(dependency && dependency.name);
+ if (!packagePath || !version) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://npm.cloudsmith.io/${safeWorkspace}/${safeRepo}/${packagePath.segments.join("/")}/-/${packagePath.tarballBaseName}-${version}.tgz`,
+ headers: {},
+ },
+ };
+ }
+ case "python": {
+ const normalizedName = normalizePythonName(dependency && dependency.name);
+ if (!normalizedName) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "python-simple-index",
+ request: {
+ method: "GET",
+ url: `https://dl.cloudsmith.io/basic/${safeWorkspace}/${safeRepo}/python/simple/${encodePathSegment(normalizedName)}/`,
+ headers: {},
+ },
+ };
+ }
+ case "go": {
+ const modulePath = encodeGoModulePath(String(dependency && dependency.name || "").trim());
+ if (!modulePath || !version) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://golang.cloudsmith.io/${safeWorkspace}/${safeRepo}/${modulePath}/@v/${version}.info`,
+ headers: {},
+ },
+ };
+ }
+ case "cargo": {
+ const indexPath = cargoIndexPath(dependency && dependency.name);
+ if (!indexPath) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://cargo.cloudsmith.io/${safeWorkspace}/${safeRepo}/${indexPath}`,
+ headers: {},
+ },
+ };
+ }
+ case "ruby": {
+ const name = encodePathSegment(dependency && dependency.name);
+ if (!name || !version) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://dl.cloudsmith.io/basic/${safeWorkspace}/${safeRepo}/ruby/gems/${name}-${version}.gem`,
+ headers: {},
+ },
+ };
+ }
+ case "nuget": {
+ const name = encodePathSegment(dependency && dependency.name);
+ if (!name || !version) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://nuget.cloudsmith.io/${safeWorkspace}/${safeRepo}/v3/package/${name}/${version}/${name}.${version}.nupkg`,
+ headers: {},
+ },
+ };
+ }
+ case "docker": {
+ const image = encodePath(dependency && dependency.name);
+ if (!image || !version) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://docker.cloudsmith.io/v2/${safeWorkspace}/${safeRepo}/${image}/manifests/${version}`,
+ headers: {
+ Accept: DOCKER_MANIFEST_ACCEPT,
+ },
+ },
+ };
+ }
+ case "helm": {
+ const name = encodePathSegment(dependency && dependency.name);
+ if (!name || !version) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://dl.cloudsmith.io/basic/${safeWorkspace}/${safeRepo}/helm/charts/${name}-${version}.tgz`,
+ headers: {},
+ },
+ };
+ }
+ case "dart": {
+ const name = encodePathSegment(dependency && dependency.name);
+ if (!name) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "dart-api",
+ request: {
+ method: "GET",
+ url: `https://dart.cloudsmith.io/${safeWorkspace}/${safeRepo}/api/packages/${name}`,
+ headers: {},
+ },
+ };
+ }
+ case "composer": {
+ const coordinates = buildComposerCoordinates(dependency && dependency.name);
+ if (!coordinates) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "composer-p2",
+ packageName: coordinates.packageName,
+ request: {
+ method: "GET",
+ url: `https://composer.cloudsmith.io/${safeWorkspace}/${safeRepo}/p2/${coordinates.vendor}/${coordinates.package}.json`,
+ headers: {},
+ },
+ };
+ }
+ case "hex": {
+ const name = encodePathSegment(dependency && dependency.name);
+ if (!name || !version) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://dl.cloudsmith.io/basic/${safeWorkspace}/${safeRepo}/hex/tarballs/${name}-${version}.tar`,
+ headers: {},
+ },
+ };
+ }
+ case "swift": {
+ const coordinates = buildSwiftCoordinates(dependency && dependency.name);
+ if (!coordinates || !coordinates.scope || !coordinates.name || !version) {
+ return null;
+ }
+ return {
+ format,
+ strategy: "direct",
+ request: {
+ method: "GET",
+ url: `https://dl.cloudsmith.io/basic/${safeWorkspace}/${safeRepo}/swift/${coordinates.scope}/${coordinates.name}/${version}.zip`,
+ headers: {},
+ },
+ };
+ }
+ default:
+ return null;
+ }
+}
+
+function isTrustedCloudsmithHost(host) {
+ const normalizedHost = String(host || "").trim().toLowerCase();
+ return normalizedHost === "cloudsmith.io" || normalizedHost.endsWith(CLOUDSMITH_HOST_SUFFIX);
+}
+
+function isTrustedRegistryUrl(candidateUrl) {
+ try {
+ const parsed = new URL(candidateUrl);
+ return parsed.protocol === "https:" && isTrustedCloudsmithHost(parsed.host);
+ } catch {
+ return false;
+ }
+}
+
+function resolveAndValidateRegistryUrl(candidate, baseUrl) {
+ if (!candidate) {
+ return null;
+ }
+
+ let resolved;
+ try {
+ resolved = new URL(candidate, baseUrl);
+ } catch {
+ return null;
+ }
+
+ if (!isTrustedRegistryUrl(resolved.toString())) {
+ return null;
+ }
+
+ return resolved.toString();
+}
+
+function collectHrefValues(html) {
+ const hrefs = [];
+ const pattern = /]*\bhref\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+))/gi;
+ let match = pattern.exec(String(html || ""));
+
+ while (match) {
+ hrefs.push(match[1] || match[2] || match[3] || "");
+ match = pattern.exec(String(html || ""));
+ }
+
+ return hrefs.filter(Boolean);
+}
+
+function scorePythonArtifact(url, version) {
+ const normalizedVersion = String(version || "").trim().toLowerCase();
+ const fileName = decodeURIComponent(String(url || "").split("/").pop() || "").toLowerCase();
+
+ if (!fileName) {
+ return -1;
+ }
+
+ let score = 0;
+ if (normalizedVersion) {
+ if (!fileName.includes(normalizedVersion)) {
+ return -1;
+ }
+ score += 10;
+ }
+
+ if (fileName.endsWith(".whl")) {
+ score += 2;
+ } else if (fileName.endsWith(".tar.gz") || fileName.endsWith(".zip")) {
+ score += 1;
+ }
+
+ return score;
+}
+
+function findPythonDistributionUrl(html, version, baseUrl) {
+ const candidates = collectHrefValues(html)
+ .map((href) => resolveAndValidateRegistryUrl(href, baseUrl))
+ .filter(Boolean)
+ .map((url) => ({
+ url,
+ score: scorePythonArtifact(url, version),
+ }))
+ .filter((candidate) => candidate.score >= 0)
+ .sort((left, right) => right.score - left.score || left.url.localeCompare(right.url));
+
+ return candidates.length > 0 ? candidates[0].url : null;
+}
+
+function parseDartArchiveUrl(body, version, baseUrl) {
+ let payload;
+ try {
+ payload = JSON.parse(String(body || ""));
+ } catch {
+ return null;
+ }
+
+ const wantedVersion = String(version || "").trim();
+ const candidates = [];
+
+ if (payload && payload.latest && payload.latest.version === wantedVersion && payload.latest.archive_url) {
+ candidates.push(payload.latest.archive_url);
+ }
+
+ if (Array.isArray(payload && payload.versions)) {
+ for (const entry of payload.versions) {
+ if (entry && entry.version === wantedVersion && entry.archive_url) {
+ candidates.push(entry.archive_url);
+ }
+ }
+ } else if (payload && payload.versions && typeof payload.versions === "object") {
+ const entry = payload.versions[wantedVersion];
+ if (entry && entry.archive_url) {
+ candidates.push(entry.archive_url);
+ }
+ }
+
+ if (payload && payload.version === wantedVersion && payload.archive_url) {
+ candidates.push(payload.archive_url);
+ }
+
+ for (const candidate of candidates) {
+ const resolved = resolveAndValidateRegistryUrl(candidate, baseUrl);
+ if (resolved) {
+ return resolved;
+ }
+ }
+
+ return null;
+}
+
+function parseComposerDistUrl(body, packageName, version, baseUrl) {
+ let payload;
+ try {
+ payload = JSON.parse(String(body || ""));
+ } catch {
+ return null;
+ }
+
+ const entries = [];
+ const normalizedPackageName = sanitizePackageNameInput(packageName);
+
+ if (payload && payload.packages && typeof payload.packages === "object") {
+ if (Array.isArray(payload.packages[normalizedPackageName])) {
+ entries.push(...payload.packages[normalizedPackageName]);
+ } else {
+ for (const value of Object.values(payload.packages)) {
+ if (Array.isArray(value)) {
+ entries.push(...value);
+ }
+ }
+ }
+ }
+
+ if (Array.isArray(payload)) {
+ entries.push(...payload);
+ }
+
+ const matchedEntry = entries.find((entry) => entry && entry.version === version)
+ || entries.find(Boolean);
+ const distUrl = matchedEntry
+ && matchedEntry.dist
+ && typeof matchedEntry.dist === "object"
+ ? matchedEntry.dist.url
+ : null;
+
+ return resolveAndValidateRegistryUrl(distUrl, baseUrl);
+}
+
+module.exports = {
+ buildRegistryTriggerPlan,
+ findPythonDistributionUrl,
+ formatForDependency,
+ isPullUnsupportedFormat,
+ isTrustedRegistryUrl,
+ parseComposerDistUrl,
+ parseDartArchiveUrl,
+ resolveAndValidateRegistryUrl,
+};
diff --git a/util/upstreamChecker.js b/util/upstreamChecker.js
index 354693c..943de51 100644
--- a/util/upstreamChecker.js
+++ b/util/upstreamChecker.js
@@ -1,6 +1,4 @@
-// Upstream proxy resolution checker.
-// Provides a "what if I pull this?" dry run for packages that don't exist locally.
-
+// Copyright 2026 Cloudsmith Ltd. All rights reserved.
const { CloudsmithAPI } = require("./cloudsmithAPI");
const { CredentialManager } = require("./credentialManager");
const { SearchQueryBuilder } = require("./searchQueryBuilder");
@@ -156,6 +154,19 @@ function isAbortError(error) {
return error && (error.name === "AbortError" || error.code === "ABORT_ERR");
}
+function getActiveUpstreamsFromRepositoryState(state, format) {
+ if (!state || !(state.groupedUpstreams instanceof Map)) {
+ return [];
+ }
+
+ const upstreams = state.groupedUpstreams.get(format);
+ if (!Array.isArray(upstreams)) {
+ return [];
+ }
+
+ return upstreams.filter((upstream) => upstream && upstream.is_active !== false);
+}
+
function getUpstreamRequestOptions(apiKey, signal) {
const headers = {
accept: "application/json",
@@ -450,6 +461,11 @@ class UpstreamChecker {
return state.upstreams;
}
+ async getActiveRepositoryUpstreamsForFormat(workspace, repo, format, options = {}) {
+ const state = await this.getRepositoryUpstreamState(workspace, repo, options);
+ return getActiveUpstreamsFromRepositoryState(state, format);
+ }
+
/**
* Orchestrate a full upstream resolution preview.
* Checks local existence and upstream configs for a package preview.
@@ -492,7 +508,7 @@ class UpstreamChecker {
}
_isCacheObjectRecord(value) {
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
+ return isCacheObjectRecord(value);
}
_logRepositoryUpstreamCacheError(action, workspace, repo, error) {
@@ -749,98 +765,15 @@ class UpstreamChecker {
}
_getRequestOptions(apiKey, signal) {
- const headers = {
- accept: "application/json",
- "content-type": "application/json",
- };
-
- if (apiKey) {
- headers["X-Api-Key"] = apiKey;
- }
-
- const requestOptions = {
- method: "GET",
- headers,
- };
-
- if (signal) {
- requestOptions.signal = signal;
- }
-
- return requestOptions;
+ return getUpstreamRequestOptions(apiKey, signal);
}
_isAbortError(error) {
- return error && (error.name === "AbortError" || error.code === "ABORT_ERR");
+ return isAbortError(error);
}
_isWarningWorthyFormatError(message) {
- const normalized = typeof message === "string" ? message.toLowerCase() : "";
- if (!normalized) {
- return true;
- }
-
- const benignKeywords = [
- "response status: 404",
- "not found",
- "unsupported",
- "not applicable",
- "unknown format",
- "no upstream",
- "does not exist",
- ];
- if (benignKeywords.some((keyword) => normalized.includes(keyword))) {
- return false;
- }
-
- const statusMatch = normalized.match(/response status:\s*(\d{3})/);
- if (statusMatch) {
- const statusCode = Number(statusMatch[1]);
- if (
- statusCode === 401 ||
- statusCode === 403 ||
- statusCode === 407 ||
- statusCode === 408 ||
- statusCode === 429
- ) {
- return true;
- }
- if (statusCode >= 500) {
- return true;
- }
- if (statusCode >= 400) {
- return true;
- }
- }
-
- const warningKeywords = [
- "blocked ",
- "redirect",
- "fetch failed",
- "network",
- "timed out",
- "timeout",
- "unauthorized",
- "forbidden",
- "permission",
- "access denied",
- "server error",
- "bad gateway",
- "service unavailable",
- "gateway timeout",
- "econn",
- "enotfound",
- "eai_again",
- "socket",
- "tls",
- "certificate",
- ];
-
- if (warningKeywords.some((keyword) => normalized.includes(keyword))) {
- return true;
- }
-
- return true;
+ return isWarningWorthyUpstreamFormatError(message);
}
}
@@ -857,6 +790,7 @@ async function getUpstreamDataForFormats(context, workspace, repo, formats, opti
module.exports = {
getAllUpstreamData,
getUpstreamDataForFormats,
+ getActiveUpstreamsFromRepositoryState,
isBenignUpstreamFormatError,
SUPPORTED_UPSTREAM_FORMATS,
UpstreamChecker,
diff --git a/util/upstreamGapAnalyzer.js b/util/upstreamGapAnalyzer.js
new file mode 100644
index 0000000..2dac342
--- /dev/null
+++ b/util/upstreamGapAnalyzer.js
@@ -0,0 +1,213 @@
+// Copyright 2026 Cloudsmith Ltd. All rights reserved.
+const { canonicalFormat, normalizePackageName } = require("./packageNameNormalizer");
+const { normalizeUpstreamFormat } = require("./upstreamFormats");
+const { UpstreamChecker } = require("./upstreamChecker");
+
+const UPSTREAM_REPO_CONCURRENCY = 5;
+
+function getUncoveredDependencyKey(dependency) {
+ const format = canonicalFormat(dependency && (dependency.format || dependency.ecosystem));
+ const normalizedName = normalizePackageName(dependency && dependency.name, format);
+ const version = String(dependency && dependency.version || "").trim().toLowerCase();
+
+ if (!format || !normalizedName) {
+ return null;
+ }
+
+ return `${format}:${normalizedName}:${version}`;
+}
+
+function formatLabel(format) {
+ const normalized = String(format || "").trim();
+ if (!normalized) {
+ return "package";
+ }
+ if (normalized === "npm") {
+ return "npm";
+ }
+ if (normalized === "python") {
+ return "PyPI";
+ }
+ if (normalized === "go") {
+ return "Go";
+ }
+ return normalized.charAt(0).toUpperCase() + normalized.slice(1);
+}
+
+function buildProxyLabel(upstream, format) {
+ const configuredName = String(upstream && upstream.name || "").trim();
+ if (!configuredName) {
+ return `${formatLabel(format)} proxy`;
+ }
+ return configuredName.toLowerCase().includes("proxy")
+ ? configuredName
+ : `${configuredName} proxy`;
+}
+
+function buildReachableDetail(snapshot, upstream, format) {
+ return `${buildProxyLabel(upstream, format)} on ${snapshot.repo}`;
+}
+
+function classifyDependency(dependency, snapshots) {
+ const key = getUncoveredDependencyKey(dependency);
+ const format = canonicalFormat(dependency && (dependency.format || dependency.ecosystem));
+ if (!key || !format) {
+ return {
+ upstreamStatus: "unreachable",
+ upstreamDetail: "Not available through Cloudsmith",
+ };
+ }
+
+ const upstreamFormat = normalizeUpstreamFormat(format);
+ if (!upstreamFormat) {
+ return {
+ upstreamStatus: "unreachable",
+ upstreamDetail: "Not available through Cloudsmith",
+ };
+ }
+
+ for (const snapshot of snapshots) {
+ const formatUpstreams = Array.isArray(snapshot.groupedUpstreams.get(upstreamFormat))
+ ? snapshot.groupedUpstreams.get(upstreamFormat)
+ : [];
+ const activeUpstream = formatUpstreams.find((upstream) => upstream.is_active !== false);
+ if (!activeUpstream) {
+ continue;
+ }
+
+ return {
+ upstreamStatus: "reachable",
+ upstreamDetail: buildReachableDetail(snapshot, activeUpstream, upstreamFormat),
+ };
+ }
+
+ return {
+ upstreamStatus: "no_proxy",
+ upstreamDetail: `No upstream proxy configured for ${upstreamFormat}`,
+ };
+}
+
+function buildGapPatch(uncoveredDependencies, snapshots) {
+ const patchMap = new Map();
+
+ for (const dependency of Array.isArray(uncoveredDependencies) ? uncoveredDependencies : []) {
+ if (dependency.cloudsmithStatus !== "NOT_FOUND") {
+ continue;
+ }
+
+ const key = getUncoveredDependencyKey(dependency);
+ if (!key || patchMap.has(key)) {
+ continue;
+ }
+
+ patchMap.set(key, classifyDependency(dependency, snapshots));
+ }
+
+ return patchMap;
+}
+
+function applyGapPatch(dependencies, patchMap) {
+ return (Array.isArray(dependencies) ? dependencies : []).map((dependency) => {
+ const key = getUncoveredDependencyKey(dependency);
+ if (!key || !patchMap.has(key)) {
+ return dependency;
+ }
+
+ const gap = patchMap.get(key);
+ return {
+ ...dependency,
+ upstreamStatus: gap.upstreamStatus,
+ upstreamDetail: gap.upstreamDetail,
+ };
+ });
+}
+
+async function analyzeUpstreamGaps(uncoveredDependencies, workspace, repositories, options = {}) {
+ const onProgress = typeof options.onProgress === "function" ? options.onProgress : null;
+ const cancellationToken = options.cancellationToken || null;
+ const upstreamChecker = options.upstreamChecker || new UpstreamChecker(options.context);
+ const repositoriesToInspect = Array.isArray(repositories)
+ ? repositories.map((repo) => String(repo || "").trim()).filter(Boolean)
+ : [];
+
+ if (repositoriesToInspect.length === 0) {
+ const emptyPatch = buildGapPatch(uncoveredDependencies, []);
+ if (onProgress && emptyPatch.size > 0) {
+ onProgress(new Map(emptyPatch), {
+ completed: 0,
+ total: 0,
+ workspace,
+ stage: "upstream",
+ });
+ }
+ return applyGapPatch(uncoveredDependencies, emptyPatch);
+ }
+
+ const repoUpstreamStates = new Map();
+ let completed = 0;
+
+ await runPromisePool(repositoriesToInspect, UPSTREAM_REPO_CONCURRENCY, async (repo) => {
+ if (cancellationToken && cancellationToken.isCancellationRequested) {
+ return;
+ }
+
+ const state = await upstreamChecker.getRepositoryUpstreamState(workspace, repo);
+ repoUpstreamStates.set(repo, {
+ repo,
+ groupedUpstreams: state && state.groupedUpstreams instanceof Map
+ ? state.groupedUpstreams
+ : new Map(),
+ });
+
+ completed += 1;
+ if (onProgress) {
+ onProgress(new Map(), {
+ completed,
+ total: repositoriesToInspect.length,
+ workspace,
+ stage: "upstream",
+ });
+ }
+ });
+
+ const snapshots = repositoriesToInspect
+ .filter((repo) => repoUpstreamStates.has(repo))
+ .map((repo) => repoUpstreamStates.get(repo));
+
+ const patchMap = buildGapPatch(uncoveredDependencies, snapshots);
+ if (onProgress && patchMap.size > 0) {
+ onProgress(new Map(patchMap), {
+ completed,
+ total: repositoriesToInspect.length,
+ workspace,
+ stage: "upstream",
+ });
+ }
+ return applyGapPatch(uncoveredDependencies, patchMap);
+}
+
+async function runPromisePool(items, concurrency, worker) {
+ const workers = [];
+ let index = 0;
+ const size = Math.max(1, Math.min(concurrency, items.length || 1));
+
+ for (let workerIndex = 0; workerIndex < size; workerIndex += 1) {
+ workers.push((async () => {
+ while (index < items.length) {
+ const item = items[index];
+ index += 1;
+ if (item === undefined) {
+ break;
+ }
+ await worker(item);
+ }
+ })());
+ }
+
+ await Promise.all(workers);
+}
+
+module.exports = {
+ analyzeUpstreamGaps,
+ getUncoveredDependencyKey,
+};
diff --git a/util/upstreamPullService.js b/util/upstreamPullService.js
new file mode 100644
index 0000000..cac515f
--- /dev/null
+++ b/util/upstreamPullService.js
@@ -0,0 +1,1231 @@
+// Copyright 2026 Cloudsmith Ltd. All rights reserved.
+const vscode = require("vscode");
+const { CloudsmithAPI } = require("./cloudsmithAPI");
+const { CredentialManager } = require("./credentialManager");
+const { PaginatedFetch } = require("./paginatedFetch");
+const {
+ buildRegistryTriggerPlan,
+ findPythonDistributionUrl,
+ formatForDependency,
+ isPullUnsupportedFormat,
+ isTrustedRegistryUrl,
+ parseComposerDistUrl,
+ parseDartArchiveUrl,
+ resolveAndValidateRegistryUrl,
+} = require("./registryEndpoints");
+const { canonicalFormat } = require("./packageNameNormalizer");
+const { UpstreamChecker } = require("./upstreamChecker");
+const { normalizeUpstreamFormat } = require("./upstreamFormats");
+
+const MAX_CONCURRENT_PULLS = 5;
+const INITIAL_AUTH_PROBE_CONCURRENCY = 3;
+const MAX_REGISTRY_REDIRECTS = 5;
+const REQUEST_TIMEOUT_MS = 30 * 1000;
+const WORKSPACE_REPOSITORY_PAGE_SIZE = 500;
+
+const PULL_STATUS = Object.freeze({
+ PENDING: "pending",
+ PULLING: "pulling",
+ CACHED: "cached",
+ ALREADY_EXISTS: "exists",
+ NOT_FOUND: "not_found",
+ AUTH_FAILED: "auth_failed",
+ FORMAT_MISMATCH: "mismatch",
+ ERROR: "error",
+ SKIPPED: "skipped",
+});
+
+const PULL_SKIP_REASON = Object.freeze({
+ NO_ACTIVE_UPSTREAM: "no_active_upstream",
+ NO_PULL_SUPPORT: "no_pull_support",
+ NO_TRIGGER_URL: "no_trigger_url",
+});
+
+class UpstreamPullService {
+ constructor(context, options = {}) {
+ this.context = context;
+ this._api = options.api || new CloudsmithAPI(context);
+ this._credentialManager = options.credentialManager || new CredentialManager(context);
+ this._fetchImpl = options.fetchImpl || fetch;
+ this._fetchRepositories = options.fetchRepositories || this._fetchWorkspaceRepositories.bind(this);
+ this._showQuickPick = options.showQuickPick || vscode.window.showQuickPick.bind(vscode.window);
+ this._showErrorMessage = options.showErrorMessage || vscode.window.showErrorMessage.bind(vscode.window);
+ this._showInformationMessage = options.showInformationMessage || vscode.window.showInformationMessage.bind(vscode.window);
+ this._showWarningMessage = options.showWarningMessage || vscode.window.showWarningMessage.bind(vscode.window);
+ this._upstreamChecker = options.upstreamChecker || new UpstreamChecker(context);
+ }
+
+ async run(options) {
+ const prepared = await this.prepare(options);
+ if (!prepared) {
+ return null;
+ }
+
+ const execution = await this.execute(prepared, options);
+ if (!execution) {
+ return null;
+ }
+
+ return {
+ ...prepared,
+ ...execution,
+ };
+ }
+
+ async prepare({
+ workspace,
+ repositoryHint,
+ dependencies,
+ }) {
+ const uncoveredDependencies = dedupePullDependencies(
+ (Array.isArray(dependencies) ? dependencies : [])
+ .filter((dependency) => dependency && dependency.cloudsmithStatus !== "FOUND")
+ );
+
+ if (!workspace) {
+ await this._showErrorMessage("Run a dependency scan against a Cloudsmith workspace first.");
+ return null;
+ }
+
+ if (uncoveredDependencies.length === 0) {
+ await this._showInformationMessage("No uncovered dependencies are available to pull.");
+ return null;
+ }
+
+ const projectFormats = [...new Set(
+ uncoveredDependencies
+ .map((dependency) => normalizeUpstreamFormat(formatForDependency(dependency)))
+ .filter(Boolean)
+ )];
+
+ if (projectFormats.length === 0) {
+ await this._showInformationMessage(
+ "Pull-through caching is not available for the uncovered dependency formats in this project."
+ );
+ return null;
+ }
+
+ let repositories;
+ try {
+ repositories = await this._fetchRepositories(workspace);
+ } catch (error) {
+ const message = error && error.message ? error.message : "Could not fetch workspace repositories.";
+ await this._showErrorMessage(message);
+ return null;
+ }
+
+ const repositoryMatches = await this._findMatchingRepositories(workspace, repositories, projectFormats);
+ if (repositoryMatches.length === 0) {
+ await this._showInformationMessage(
+ `No repositories have upstream proxies configured for the dependency formats in this project (${formatListLabel(projectFormats)}). Configure an upstream proxy in Cloudsmith to enable pull-through caching.`
+ );
+ return null;
+ }
+
+ const orderedMatches = sortRepositoryMatches(repositoryMatches, repositoryHint);
+ const selected = await this._showQuickPick(
+ orderedMatches.map((match) => ({
+ label: match.repo.slug || match.repo.name,
+ description: match.repo.name && match.repo.name !== match.repo.slug ? match.repo.name : "",
+ detail: `${formatListLabel(match.activeFormats)} upstream${match.activeFormats.length === 1 ? "" : "s"} configured`,
+ _match: match,
+ })),
+ {
+ placeHolder: "Select a repository to pull dependencies through",
+ matchOnDescription: true,
+ matchOnDetail: true,
+ }
+ );
+
+ if (!selected || !selected._match) {
+ return null;
+ }
+
+ const repository = selected._match.repo;
+ const plan = buildPullExecutionPlan(
+ workspace,
+ repository.slug,
+ uncoveredDependencies,
+ selected._match.activeFormats
+ );
+
+ if (plan.pullableDependencies.length === 0) {
+ await this._showInformationMessage(buildPullPlanErrorMessage(repository.slug, plan));
+ return null;
+ }
+
+ const confirmation = await this._showWarningMessage(
+ buildPullConfirmationMessage(plan, repository.slug),
+ { modal: true },
+ "Pull dependencies"
+ );
+
+ if (confirmation !== "Pull dependencies") {
+ return null;
+ }
+
+ return {
+ workspace,
+ repository,
+ plan,
+ };
+ }
+
+ async prepareSingle({
+ workspace,
+ repositoryHint,
+ dependency,
+ }) {
+ const normalizedDependency = normalizeSingleDependency(dependency);
+ if (!workspace) {
+ await this._showErrorMessage("Run a dependency scan against a Cloudsmith workspace first.");
+ return null;
+ }
+
+ if (!normalizedDependency) {
+ await this._showWarningMessage("Could not determine the dependency details to pull.");
+ return null;
+ }
+
+ const dependencyFormat = normalizeUpstreamFormat(formatForDependency(normalizedDependency));
+ if (!dependencyFormat) {
+ await this._showInformationMessage(
+ `Pull-through caching is not available for ${formatDisplayName(normalizedDependency.format)} dependencies.`
+ );
+ return null;
+ }
+
+ let repositories;
+ try {
+ repositories = await this._fetchRepositories(workspace);
+ } catch (error) {
+ const message = error && error.message ? error.message : "Could not fetch workspace repositories.";
+ await this._showErrorMessage(message);
+ return null;
+ }
+
+ const repositoryMatches = await this._findMatchingRepositories(workspace, repositories, [dependencyFormat]);
+ if (repositoryMatches.length === 0) {
+ await this._showInformationMessage(
+ `No repositories have a ${formatDisplayName(dependencyFormat)} upstream configured. Add one in Cloudsmith to pull this dependency.`
+ );
+ return null;
+ }
+
+ const orderedMatches = sortRepositoryMatches(repositoryMatches, repositoryHint);
+ const selected = await this._showQuickPick(
+ orderedMatches.map((match) => ({
+ label: match.repo.slug || match.repo.name,
+ description: match.repo.name && match.repo.name !== match.repo.slug ? match.repo.name : "",
+ detail: buildSingleDependencyRepositoryDetail(match, dependencyFormat),
+ _match: match,
+ })),
+ {
+ placeHolder: `Select a repository to pull ${buildDependencyLabel(normalizedDependency)} through`,
+ matchOnDescription: true,
+ matchOnDetail: true,
+ }
+ );
+
+ if (!selected || !selected._match) {
+ return null;
+ }
+
+ const repository = selected._match.repo;
+ const plan = buildPullExecutionPlan(
+ workspace,
+ repository.slug,
+ [normalizedDependency],
+ selected._match.activeFormats
+ );
+
+ if (plan.pullableDependencies.length === 0) {
+ await this._showInformationMessage(buildPullPlanErrorMessage(repository.slug, plan));
+ return null;
+ }
+
+ return {
+ workspace,
+ repository,
+ plan,
+ dependency: normalizedDependency,
+ };
+ }
+
+ async execute(prepared, options = {}) {
+ const apiKey = await this._credentialManager.getApiKey();
+ if (!apiKey) {
+ await this._showErrorMessage("Authentication failed. Check your API key in Cloudsmith settings.");
+ return null;
+ }
+
+ const progress = options.progress || null;
+ const token = options.token || null;
+ const onStatus = typeof options.onStatus === "function" ? options.onStatus : null;
+ const queue = prepared.plan.pullableDependencies.slice();
+ let nextDependencyIndex = 0;
+ const details = [];
+ const counts = createResultCounts(prepared.plan.pullableDependencies.length);
+ const state = {
+ authFailureCount: 0,
+ nonAuthOutcomeCount: 0,
+ stopForAuthFailure: false,
+ canceled: false,
+ allowedConcurrency: Math.min(
+ prepared.plan.pullableDependencies.length || 1,
+ INITIAL_AUTH_PROBE_CONCURRENCY
+ ),
+ expandedConcurrency: false,
+ };
+ const pending = new Set();
+ let activeCount = 0;
+ let launchedCount = 0;
+
+ const takeNextDependency = () => {
+ if (nextDependencyIndex >= queue.length) {
+ return null;
+ }
+
+ const dependency = queue[nextDependencyIndex];
+ nextDependencyIndex += 1;
+ return dependency;
+ };
+
+ const processNext = async () => {
+ if (token && token.isCancellationRequested) {
+ state.canceled = true;
+ return;
+ }
+
+ if (state.stopForAuthFailure) {
+ return;
+ }
+
+ const dependency = takeNextDependency();
+ if (!dependency) {
+ return;
+ }
+
+ activeCount += 1;
+
+ try {
+ const pullingDetail = {
+ dependency,
+ status: PULL_STATUS.PULLING,
+ errorMessage: null,
+ requestUrl: buildPullRequestUrl(prepared.workspace, prepared.repository.slug, dependency),
+ };
+ if (onStatus) {
+ await onStatus(pullingDetail);
+ }
+
+ const result = await this._pullDependency(
+ prepared.workspace,
+ prepared.repository.slug,
+ dependency,
+ apiKey,
+ token
+ );
+
+ if (result.canceled) {
+ state.canceled = true;
+ return;
+ }
+
+ details.push(result);
+ updateResultCounts(counts, result);
+
+ if (result.status === PULL_STATUS.AUTH_FAILED) {
+ state.authFailureCount += 1;
+ } else {
+ state.nonAuthOutcomeCount += 1;
+ }
+
+ if (
+ result.status === PULL_STATUS.AUTH_FAILED
+ && state.authFailureCount >= INITIAL_AUTH_PROBE_CONCURRENCY
+ && state.nonAuthOutcomeCount === 0
+ ) {
+ state.stopForAuthFailure = true;
+ }
+
+ if (
+ !state.expandedConcurrency
+ && state.nonAuthOutcomeCount > 0
+ && state.allowedConcurrency < MAX_CONCURRENT_PULLS
+ ) {
+ state.allowedConcurrency = Math.min(MAX_CONCURRENT_PULLS, counts.total);
+ state.expandedConcurrency = true;
+ }
+
+ if (progress) {
+ progress.report({
+ message: buildProgressMessage(counts),
+ increment: counts.total > 0 ? 100 / counts.total : 100,
+ });
+ }
+
+ if (onStatus) {
+ await onStatus(result);
+ }
+ } finally {
+ activeCount -= 1;
+ fillConcurrency();
+ }
+ };
+
+ const fillConcurrency = () => {
+ while (
+ activeCount < state.allowedConcurrency
+ && (state.expandedConcurrency || launchedCount < INITIAL_AUTH_PROBE_CONCURRENCY)
+ && nextDependencyIndex < queue.length
+ && !(token && token.isCancellationRequested)
+ && !state.stopForAuthFailure
+ ) {
+ launchedCount += 1;
+ const promise = processNext();
+ pending.add(promise);
+ promise.finally(() => pending.delete(promise));
+ }
+ };
+
+ fillConcurrency();
+
+ while (pending.size > 0) {
+ await Promise.race([...pending]);
+ }
+
+ if (state.stopForAuthFailure) {
+ while (nextDependencyIndex < queue.length) {
+ const dependency = queue[nextDependencyIndex];
+ nextDependencyIndex += 1;
+ details.push({
+ dependency,
+ status: PULL_STATUS.AUTH_FAILED,
+ errorMessage: "Skipped after repeated authentication failures.",
+ requestUrl: buildPullRequestUrl(prepared.workspace, prepared.repository.slug, dependency),
+ networkError: false,
+ });
+ }
+ recomputeResultCounts(counts, details);
+ await this._showErrorMessage("Authentication failed. Check your API key in Cloudsmith settings.");
+ } else if (state.canceled) {
+ return {
+ canceled: true,
+ };
+ } else if (
+ counts.completed > 0
+ && counts.completed === counts.errors
+ && counts.networkErrors === counts.errors
+ ) {
+ await this._showErrorMessage("Cannot reach the Cloudsmith registry. Check your network connection.");
+ }
+
+ return {
+ canceled: false,
+ pullResult: {
+ total: counts.total,
+ cached: counts.cached,
+ alreadyExisted: counts.alreadyExisted,
+ notFound: counts.notFound,
+ formatMismatched: counts.formatMismatched,
+ errors: counts.errors,
+ networkErrors: counts.networkErrors,
+ authFailed: counts.authFailed,
+ skipped: counts.skipped,
+ details,
+ },
+ };
+ }
+
+ async _findMatchingRepositories(workspace, repositories, projectFormats) {
+ const matches = [];
+
+ await runPromisePool(repositories, 5, async (repo) => {
+ const repoSlug = repo && repo.slug ? repo.slug : null;
+ if (!repoSlug) {
+ return;
+ }
+
+ const state = await this._upstreamChecker.getRepositoryUpstreamState(workspace, repoSlug);
+ const activeUpstreamsByFormat = new Map();
+ const activeFormats = projectFormats.filter((format) => {
+ const upstreams = state && state.groupedUpstreams instanceof Map
+ ? state.groupedUpstreams.get(format)
+ : [];
+ const activeUpstreams = Array.isArray(upstreams)
+ ? upstreams.filter((upstream) => upstream && upstream.is_active !== false)
+ : [];
+ if (activeUpstreams.length > 0) {
+ activeUpstreamsByFormat.set(format, activeUpstreams);
+ return true;
+ }
+ return false;
+ });
+
+ if (activeFormats.length === 0) {
+ return;
+ }
+
+ matches.push({
+ repo,
+ activeFormats,
+ activeUpstreamsByFormat,
+ });
+ });
+
+ return matches.sort((left, right) => {
+ const leftSlug = String(left.repo.slug || left.repo.name || "");
+ const rightSlug = String(right.repo.slug || right.repo.name || "");
+ return leftSlug.localeCompare(rightSlug, undefined, { sensitivity: "base" });
+ });
+ }
+
+ async _fetchWorkspaceRepositories(workspace) {
+ const paginatedFetch = new PaginatedFetch(this._api);
+ const endpoint = `repos/${workspace}/?sort=name`;
+ const repositories = [];
+ let page = 1;
+
+ while (true) {
+ const result = await paginatedFetch.fetchPage(endpoint, page, WORKSPACE_REPOSITORY_PAGE_SIZE);
+ if (result.error) {
+ throw new Error(`Could not fetch workspace repositories. ${result.error}`);
+ }
+
+ repositories.push(...(Array.isArray(result.data) ? result.data : []));
+
+ const pageTotal = result.pagination && result.pagination.pageTotal
+ ? result.pagination.pageTotal
+ : 1;
+ if (page >= pageTotal) {
+ break;
+ }
+ page += 1;
+ }
+
+ return repositories;
+ }
+
+ async _pullDependency(workspace, repo, dependency, apiKey, token) {
+ const plan = buildRegistryTriggerPlan(workspace, repo, dependency);
+ const format = formatForDependency(dependency);
+
+ if (!plan) {
+ const errorMessage = isPullUnsupportedFormat(format)
+ ? `Pull-through caching is not supported for ${formatDisplayName(format)} dependencies.`
+ : `No registry trigger URL is available for ${formatDisplayName(format)} dependencies.`;
+
+ return {
+ dependency,
+ status: PULL_STATUS.FORMAT_MISMATCH,
+ errorMessage,
+ requestUrl: null,
+ networkError: false,
+ };
+ }
+
+ const metadataAttempt = await this._requestRegistry(plan.request, apiKey, token);
+ if (metadataAttempt.canceled) {
+ return metadataAttempt;
+ }
+
+ if (plan.strategy === "direct") {
+ return mapRegistryAttempt(dependency, metadataAttempt, plan.request.url, format);
+ }
+
+ if (metadataAttempt.statusCode === 401 || metadataAttempt.statusCode === 403) {
+ return mapRegistryAttempt(dependency, metadataAttempt, plan.request.url, format);
+ }
+
+ if (metadataAttempt.statusCode === 404) {
+ return mapRegistryAttempt(dependency, metadataAttempt, plan.request.url, format);
+ }
+
+ if (metadataAttempt.statusCode < 200 || metadataAttempt.statusCode >= 300) {
+ return mapRegistryAttempt(dependency, metadataAttempt, plan.request.url, format);
+ }
+
+ let artifactUrl = null;
+ if (plan.strategy === "python-simple-index") {
+ artifactUrl = findPythonDistributionUrl(metadataAttempt.body, dependency.version, plan.request.url);
+ } else if (plan.strategy === "dart-api") {
+ artifactUrl = parseDartArchiveUrl(metadataAttempt.body, dependency.version, plan.request.url);
+ } else if (plan.strategy === "composer-p2") {
+ artifactUrl = parseComposerDistUrl(
+ metadataAttempt.body,
+ plan.packageName || dependency.name,
+ dependency.version,
+ plan.request.url
+ );
+ }
+
+ if (!artifactUrl) {
+ return {
+ dependency,
+ status: PULL_STATUS.NOT_FOUND,
+ errorMessage: missingArtifactMessage(plan.strategy, dependency.version),
+ requestUrl: plan.request.url,
+ networkError: false,
+ };
+ }
+
+ const artifactAttempt = await this._requestRegistry(
+ {
+ method: "GET",
+ url: artifactUrl,
+ headers: {},
+ },
+ apiKey,
+ token
+ );
+
+ if (artifactAttempt.canceled) {
+ return artifactAttempt;
+ }
+
+ return mapRegistryAttempt(dependency, artifactAttempt, artifactUrl, format);
+ }
+
+ async _requestRegistry(request, apiKey, token) {
+ const controller = new AbortController();
+ let didTimeout = false;
+ const timeoutHandle = setTimeout(() => {
+ didTimeout = true;
+ controller.abort();
+ }, REQUEST_TIMEOUT_MS);
+
+ const cancellationDisposable = token && typeof token.onCancellationRequested === "function"
+ ? token.onCancellationRequested(() => controller.abort())
+ : null;
+
+ try {
+ const response = await this._fetchRegistryResponse(
+ request,
+ apiKey,
+ controller.signal,
+ 0
+ );
+
+ return {
+ statusCode: response.status,
+ body: await response.text(),
+ };
+ } catch (error) {
+ if (token && token.isCancellationRequested) {
+ return { canceled: true };
+ }
+
+ return {
+ statusCode: 0,
+ body: "",
+ errorMessage: didTimeout
+ ? "Registry request timed out."
+ : buildRegistryErrorMessage(request.url, error),
+ networkError: isNetworkError(error) || didTimeout,
+ };
+ } finally {
+ clearTimeout(timeoutHandle);
+ if (cancellationDisposable && typeof cancellationDisposable.dispose === "function") {
+ cancellationDisposable.dispose();
+ }
+ }
+ }
+
+ async _fetchRegistryResponse(request, apiKey, signal, redirectCount) {
+ if (!request || !isTrustedRegistryUrl(request.url)) {
+ throw new Error("Refused to send Cloudsmith credentials to an untrusted registry host.");
+ }
+
+ const response = await this._fetchImpl(request.url, {
+ method: request.method || "GET",
+ headers: {
+ Authorization: buildBasicAuthHeader(apiKey),
+ ...(request.headers || {}),
+ },
+ redirect: "manual",
+ signal,
+ });
+
+ if (!isRedirectStatus(response.status)) {
+ return response;
+ }
+
+ if (redirectCount >= MAX_REGISTRY_REDIRECTS) {
+ throw new Error("Registry request exceeded the redirect limit.");
+ }
+
+ const location = response.headers && typeof response.headers.get === "function"
+ ? response.headers.get("location")
+ : "";
+ const redirectUrl = resolveAndValidateRegistryUrl(location, request.url);
+ if (!redirectUrl || !isTrustedRegistryUrl(redirectUrl)) {
+ throw new Error("Registry redirect target was rejected.");
+ }
+
+ return this._fetchRegistryResponse(
+ {
+ ...request,
+ url: redirectUrl,
+ },
+ apiKey,
+ signal,
+ redirectCount + 1
+ );
+ }
+}
+
+function buildPullExecutionPlan(workspace, repo, dependencies, activeUpstreamFormats) {
+ const normalizedActiveFormats = [...new Set(
+ (Array.isArray(activeUpstreamFormats) ? activeUpstreamFormats : [])
+ .map((format) => normalizeUpstreamFormat(format))
+ .filter(Boolean)
+ )];
+
+ const uniqueDependencies = dedupePullDependencies(dependencies);
+ const skippedDependencies = [];
+ const pullableDependencies = [];
+
+ for (const dependency of uniqueDependencies) {
+ const format = canonicalFormat(formatForDependency(dependency) || dependency.ecosystem || "");
+ const triggerPlan = buildRegistryTriggerPlan(workspace, repo, dependency);
+
+ if (isPullUnsupportedFormat(format)) {
+ skippedDependencies.push({
+ dependency,
+ format,
+ reason: PULL_SKIP_REASON.NO_PULL_SUPPORT,
+ message: `Pull-through caching is not supported for ${formatDisplayName(format)} dependencies.`,
+ });
+ continue;
+ }
+
+ if (!triggerPlan) {
+ skippedDependencies.push({
+ dependency,
+ format,
+ reason: PULL_SKIP_REASON.NO_TRIGGER_URL,
+ message: `No registry trigger URL is available for ${formatDisplayName(format)} dependencies.`,
+ });
+ continue;
+ }
+
+ if (!normalizedActiveFormats.includes(normalizeUpstreamFormat(format))) {
+ skippedDependencies.push({
+ dependency,
+ format,
+ reason: PULL_SKIP_REASON.NO_ACTIVE_UPSTREAM,
+ message: `No ${formatDisplayName(format)} upstream is configured on this repository.`,
+ });
+ continue;
+ }
+
+ pullableDependencies.push(dependency);
+ }
+
+ return {
+ dependencies: uniqueDependencies,
+ pullableDependencies,
+ skippedDependencies,
+ activeUpstreamFormats: normalizedActiveFormats,
+ };
+}
+
+function buildPullConfirmationMessage(plan, repositoryLabel) {
+ const totalCount = plan.dependencies.length;
+ const pullableCount = plan.pullableDependencies.length;
+ const header = plan.skippedDependencies.length > 0
+ ? `Pull ${pullableCount} of ${totalCount} dependencies through ${repositoryLabel}?`
+ : singleFormatPullHeader(plan.pullableDependencies, repositoryLabel);
+ const pulledLine = buildPullableSummary(plan.pullableDependencies, plan.skippedDependencies.length > 0);
+ const skippedLine = buildSkippedSummary(plan.skippedDependencies);
+
+ return [
+ header,
+ pulledLine,
+ skippedLine,
+ "Packages not already cached will be fetched from the upstream source.",
+ ].filter(Boolean).join("\n");
+}
+
+function buildPullPlanErrorMessage(repositoryLabel, plan) {
+ const noUpstreamFormats = [...new Set(
+ plan.skippedDependencies
+ .filter((entry) => entry.reason === PULL_SKIP_REASON.NO_ACTIVE_UPSTREAM)
+ .map((entry) => entry.format)
+ .filter(Boolean)
+ )];
+
+ if (plan.pullableDependencies.length === 0 && noUpstreamFormats.length > 0) {
+ return `No ${formatListLabel(noUpstreamFormats)} upstream${noUpstreamFormats.length === 1 ? "" : "s"} are configured on ${repositoryLabel}.`;
+ }
+
+ return "Pull-through caching is not available for the uncovered dependencies in this project.";
+}
+
+function buildPullSummaryMessage(result, skippedCount) {
+ const pulledCount = result.cached + result.alreadyExisted;
+ const parts = [
+ `Pulled ${pulledCount} of ${result.total} dependencies.`,
+ `${result.cached} cached`,
+ `${result.alreadyExisted} already existed`,
+ `${result.notFound} not found upstream`,
+ ];
+
+ if (skippedCount > 0) {
+ parts.push(`${skippedCount} skipped`);
+ }
+
+ if (result.errors > 0) {
+ parts.push(`${result.errors} errors`);
+ }
+
+ return `${parts.shift()} ${parts.join(", ")}.`;
+}
+
+function buildProgressMessage(counts) {
+ const parts = [`Pulling dependencies... ${counts.completed}/${counts.total}`];
+ const detail = [];
+
+ if (counts.cached > 0) {
+ detail.push(`${counts.cached} cached`);
+ }
+ if (counts.notFound > 0) {
+ detail.push(`${counts.notFound} not found`);
+ }
+ if (counts.errors > 0) {
+ detail.push(`${counts.errors} errors`);
+ }
+
+ if (detail.length > 0) {
+ parts.push(`(${detail.join(", ")})`);
+ }
+
+ return parts.join(" ");
+}
+
+function createResultCounts(total) {
+ return {
+ total,
+ completed: 0,
+ cached: 0,
+ alreadyExisted: 0,
+ notFound: 0,
+ formatMismatched: 0,
+ errors: 0,
+ networkErrors: 0,
+ authFailed: 0,
+ skipped: 0,
+ };
+}
+
+function updateResultCounts(counts, result) {
+ counts.completed += 1;
+ switch (result.status) {
+ case PULL_STATUS.CACHED:
+ counts.cached += 1;
+ break;
+ case PULL_STATUS.ALREADY_EXISTS:
+ counts.alreadyExisted += 1;
+ break;
+ case PULL_STATUS.NOT_FOUND:
+ counts.notFound += 1;
+ break;
+ case PULL_STATUS.FORMAT_MISMATCH:
+ counts.formatMismatched += 1;
+ break;
+ case PULL_STATUS.AUTH_FAILED:
+ counts.authFailed += 1;
+ counts.errors += 1;
+ break;
+ case PULL_STATUS.SKIPPED:
+ counts.skipped += 1;
+ break;
+ case PULL_STATUS.ERROR:
+ counts.errors += 1;
+ if (result.networkError) {
+ counts.networkErrors += 1;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+function recomputeResultCounts(counts, results) {
+ const next = createResultCounts(results.length);
+ for (const result of results) {
+ updateResultCounts(next, result);
+ }
+
+ Object.assign(counts, next);
+}
+
+function mapRegistryAttempt(dependency, attempt, requestUrl, format) {
+ if (attempt.statusCode >= 200 && attempt.statusCode < 300) {
+ return {
+ dependency,
+ status: PULL_STATUS.CACHED,
+ errorMessage: null,
+ requestUrl,
+ networkError: false,
+ };
+ }
+
+ if (attempt.statusCode === 304 || attempt.statusCode === 409) {
+ return {
+ dependency,
+ status: PULL_STATUS.ALREADY_EXISTS,
+ errorMessage: null,
+ requestUrl,
+ networkError: false,
+ };
+ }
+
+ if (attempt.statusCode === 401 || attempt.statusCode === 403) {
+ return {
+ dependency,
+ status: PULL_STATUS.AUTH_FAILED,
+ errorMessage: "Authentication failed.",
+ requestUrl,
+ networkError: false,
+ };
+ }
+
+ if (attempt.statusCode === 404) {
+ return {
+ dependency,
+ status: PULL_STATUS.NOT_FOUND,
+ errorMessage: defaultNotFoundMessage(format),
+ requestUrl,
+ networkError: false,
+ };
+ }
+
+ if (attempt.statusCode === 0) {
+ return {
+ dependency,
+ status: PULL_STATUS.ERROR,
+ errorMessage: attempt.errorMessage || "Registry request failed.",
+ requestUrl,
+ networkError: Boolean(attempt.networkError),
+ };
+ }
+
+ return {
+ dependency,
+ status: PULL_STATUS.ERROR,
+ errorMessage: `Registry request returned HTTP ${attempt.statusCode}.`,
+ requestUrl,
+ networkError: false,
+ };
+}
+
+function defaultNotFoundMessage(format) {
+ switch (format) {
+ case "docker":
+ return "Image manifest not found upstream.";
+ case "go":
+ return "Go module metadata not found upstream.";
+ case "cargo":
+ return "Cargo index entry not found upstream.";
+ case "helm":
+ return "Chart archive not found upstream.";
+ default:
+ return "Package not found upstream.";
+ }
+}
+
+function missingArtifactMessage(strategy, version) {
+ switch (strategy) {
+ case "python-simple-index":
+ return `No distribution file was found for version ${version}.`;
+ case "dart-api":
+ return `No Dart archive URL was found for version ${version}.`;
+ case "composer-p2":
+ return `No Composer dist URL was found for version ${version}.`;
+ default:
+ return "No downloadable artifact was found.";
+ }
+}
+
+function buildRegistryErrorMessage(url, error) {
+ if (isNetworkError(error)) {
+ return "Cannot reach the Cloudsmith registry. Check your network connection.";
+ }
+
+ const host = safeHost(url);
+ const message = error && error.message ? error.message : "Registry request failed.";
+ return host ? `${message} (${host})` : message;
+}
+
+function isNetworkError(error) {
+ const code = error && (
+ error.code
+ || (error.cause && error.cause.code)
+ || (error.errno)
+ );
+
+ if (["ECONNREFUSED", "ECONNRESET", "ENOTFOUND", "EHOSTUNREACH", "ETIMEDOUT"].includes(code)) {
+ return true;
+ }
+
+ const message = String(error && error.message || "").toLowerCase();
+ return message.includes("fetch failed")
+ || message.includes("network")
+ || message.includes("timed out")
+ || message.includes("econnrefused")
+ || message.includes("enotfound");
+}
+
+function buildBasicAuthHeader(apiKey) {
+ return `Basic ${Buffer.from(`token:${apiKey}`).toString("base64")}`;
+}
+
+function isRedirectStatus(statusCode) {
+ return Number.isInteger(statusCode) && statusCode >= 300 && statusCode < 400;
+}
+
+function safeHost(url) {
+ try {
+ return new URL(url).host;
+ } catch {
+ return "";
+ }
+}
+
+function buildPullRequestUrl(workspace, repo, dependency) {
+ const plan = buildRegistryTriggerPlan(workspace, repo, dependency);
+ return plan && plan.request ? plan.request.url : null;
+}
+
+function dedupePullDependencies(dependencies) {
+ const unique = new Map();
+
+ for (const dependency of Array.isArray(dependencies) ? dependencies : []) {
+ const key = pullDependencyKey(dependency);
+ if (!unique.has(key)) {
+ unique.set(key, dependency);
+ }
+ }
+
+ return [...unique.values()];
+}
+
+function pullDependencyKey(dependency) {
+ return [
+ String(canonicalFormat(dependency && (dependency.format || dependency.ecosystem)) || "").toLowerCase(),
+ String(dependency && dependency.name || "").toLowerCase(),
+ String(dependency && dependency.version || "").toLowerCase(),
+ ].join(":");
+}
+
+function singleFormatPullHeader(dependencies, repositoryLabel) {
+ const formats = [...new Set(
+ dependencies.map((dependency) => canonicalFormat(formatForDependency(dependency))).filter(Boolean)
+ )];
+
+ if (formats.length === 1) {
+ return `Pull ${dependencies.length} ${formatDisplayName(formats[0])} dependenc${dependencies.length === 1 ? "y" : "ies"} through ${repositoryLabel}?`;
+ }
+
+ return `Pull ${dependencies.length} dependencies through ${repositoryLabel}?`;
+}
+
+function buildPullableSummary(dependencies, forceSummary) {
+ const groups = groupCountsByFormat(dependencies);
+ if (groups.length === 0) {
+ return "";
+ }
+
+ if (!forceSummary && groups.length === 1) {
+ return "";
+ }
+
+ return `${groups.map(({ count, format }) => `${count} ${formatDisplayName(format)}`).join(" + ")} will be pulled.`;
+}
+
+function buildSkippedSummary(skippedDependencies) {
+ const groups = groupCountsByFormat(skippedDependencies.map((entry) => ({
+ format: entry.format,
+ })));
+
+ if (groups.length === 0) {
+ return "";
+ }
+
+ const reason = skippedDependencies.every((entry) => entry.reason === PULL_SKIP_REASON.NO_ACTIVE_UPSTREAM)
+ ? "no matching upstream is configured on this repository"
+ : "pull-through is not available for these formats";
+
+ return `${groups.map(({ count, format }) => `${count} ${formatDisplayName(format)}`).join(" + ")} will be skipped (${reason}).`;
+}
+
+function groupCountsByFormat(dependencies) {
+ const counts = new Map();
+
+ for (const dependency of Array.isArray(dependencies) ? dependencies : []) {
+ const format = canonicalFormat(
+ dependency && (dependency.format || dependency.ecosystem || formatForDependency(dependency))
+ );
+ if (!format) {
+ continue;
+ }
+ counts.set(format, (counts.get(format) || 0) + 1);
+ }
+
+ return [...counts.entries()]
+ .map(([format, count]) => ({ format, count }))
+ .sort((left, right) => {
+ if (right.count !== left.count) {
+ return right.count - left.count;
+ }
+ return formatDisplayName(left.format).localeCompare(formatDisplayName(right.format), undefined, {
+ sensitivity: "base",
+ });
+ });
+}
+
+function formatDisplayName(format) {
+ const normalized = String(canonicalFormat(format) || format || "").trim().toLowerCase();
+ switch (normalized) {
+ case "npm":
+ return "npm";
+ case "python":
+ return "Python";
+ case "go":
+ return "Go";
+ case "nuget":
+ return "NuGet";
+ default:
+ return normalized ? normalized.charAt(0).toUpperCase() + normalized.slice(1) : "Unknown";
+ }
+}
+
+function formatListLabel(formats) {
+ return [...new Set(
+ (Array.isArray(formats) ? formats : [])
+ .map((format) => formatDisplayName(format))
+ .filter(Boolean)
+ )].join(", ");
+}
+
+function normalizeSingleDependency(dependency) {
+ if (!dependency || typeof dependency !== "object") {
+ return null;
+ }
+
+ const format = canonicalFormat(formatForDependency(dependency) || dependency.format || dependency.ecosystem);
+ const name = String(dependency.name || "").trim();
+ if (!name || !format) {
+ return null;
+ }
+
+ return {
+ ...dependency,
+ name,
+ version: dependency.version || dependency.declaredVersion || "",
+ format,
+ ecosystem: dependency.ecosystem || format,
+ };
+}
+
+function buildDependencyLabel(dependency) {
+ const name = String(dependency && dependency.name || "").trim() || "dependency";
+ const version = String(dependency && dependency.version || "").trim();
+ return version ? `${name}@${version}` : name;
+}
+
+function buildSingleDependencyRepositoryDetail(match, format) {
+ const upstreams = match && match.activeUpstreamsByFormat instanceof Map
+ ? match.activeUpstreamsByFormat.get(format)
+ : [];
+ const activeUpstream = Array.isArray(upstreams) ? upstreams[0] : null;
+ const configuredName = String(activeUpstream && activeUpstream.name || "").trim();
+ const sourceLabel = configuredName || defaultUpstreamSourceLabel(format);
+ if (!sourceLabel) {
+ return `${formatDisplayName(format)} upstream configured`;
+ }
+ return `${formatDisplayName(format)} upstream (${sourceLabel})`;
+}
+
+function defaultUpstreamSourceLabel(format) {
+ switch (canonicalFormat(format)) {
+ case "cargo":
+ return "crates.io";
+ case "composer":
+ return "Packagist";
+ case "conda":
+ return "Conda";
+ case "dart":
+ return "pub.dev";
+ case "docker":
+ return "Docker";
+ case "go":
+ return "Go";
+ case "helm":
+ return "Helm";
+ case "hex":
+ return "Hex";
+ case "maven":
+ return "Maven";
+ case "npm":
+ return "npm";
+ case "nuget":
+ return "NuGet";
+ case "python":
+ return "PyPI";
+ case "ruby":
+ return "RubyGems";
+ case "swift":
+ return "Swift";
+ default:
+ return null;
+ }
+}
+
+function sortRepositoryMatches(matches, repositoryHint) {
+ const hint = String(repositoryHint || "").trim().toLowerCase();
+ if (!hint) {
+ return matches;
+ }
+
+ return matches.slice().sort((left, right) => {
+ const leftSlug = String(left.repo.slug || "").toLowerCase();
+ const rightSlug = String(right.repo.slug || "").toLowerCase();
+ const leftPriority = leftSlug === hint ? 0 : 1;
+ const rightPriority = rightSlug === hint ? 0 : 1;
+ if (leftPriority !== rightPriority) {
+ return leftPriority - rightPriority;
+ }
+ return leftSlug.localeCompare(rightSlug, undefined, { sensitivity: "base" });
+ });
+}
+
+async function runPromisePool(items, concurrency, worker) {
+ const workers = [];
+ let index = 0;
+ const size = Math.max(1, Math.min(concurrency, items.length || 1));
+
+ for (let workerIndex = 0; workerIndex < size; workerIndex += 1) {
+ workers.push((async () => {
+ while (index < items.length) {
+ const item = items[index];
+ index += 1;
+ if (item === undefined) {
+ break;
+ }
+ await worker(item);
+ }
+ })());
+ }
+
+ await Promise.all(workers);
+}
+
+module.exports = {
+ PULL_SKIP_REASON,
+ PULL_STATUS,
+ UpstreamPullService,
+ buildPullSummaryMessage,
+};