Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 35x 35x 43x 1x 42x 42x 4x 38x 35x 95x 35x 95x 77x 77x 4x 4x 2x 18x 18x 2x 2x 1x 101x 101x 101x 101x 99x 2x 35x 87x 87x | import { enhancedFetch } from "./fetch";
/**
* Extract namespace (group path) from a full project path.
*
* Examples:
* - "group/project" -> "group"
* - "group/subgroup/project" -> "group/subgroup"
* - "myproject" (single segment) -> "myproject" (root-level project)
* - "" (empty) -> undefined
*
* @param projectPath - Full project path (e.g., "group/project")
* @returns Namespace path or undefined if projectPath is empty
*/
export function extractNamespaceFromPath(projectPath: string): string | undefined {
if (!projectPath) {
return undefined;
}
const pathParts = projectPath.split("/");
// Single segment = root-level project, namespace equals project path
if (pathParts.length === 1) {
return projectPath;
}
// Multiple segments = namespace is everything except the last part
return pathParts.slice(0, -1).join("/");
}
/**
* Simple heuristic to determine if a path likely represents a project
* Projects typically contain a slash (group/project), while groups usually don't
*/
export function isLikelyProjectPath(namespacePath: string): boolean {
return namespacePath.includes("/");
}
/**
* Detect namespace type by attempting to fetch from GitLab API
* Tries both project and group endpoints to determine which one exists
*/
export async function detectNamespaceType(namespacePath: string): Promise<"project" | "group"> {
// First try heuristic for common cases
if (isLikelyProjectPath(namespacePath)) {
// Try project first, fallback to group if needed
const isProject = await verifyNamespaceType(namespacePath, "project");
if (isProject) return "project";
const isGroup = await verifyNamespaceType(namespacePath, "group");
if (isGroup) return "group";
// Default fallback for paths with slash
return "project";
} else {
// Try group first, fallback to project if needed
const isGroup = await verifyNamespaceType(namespacePath, "group");
if (isGroup) return "group";
const isProject = await verifyNamespaceType(namespacePath, "project");
if (isProject) return "project";
// Default fallback for paths without slash
return "group";
}
}
/**
* Verify if a namespace exists as the specified type by making a lightweight API call
*/
async function verifyNamespaceType(
namespacePath: string,
type: "project" | "group"
): Promise<boolean> {
try {
const entityType = type === "project" ? "projects" : "groups";
const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/${entityType}/${encodeURIComponent(namespacePath)}`;
const response = await enhancedFetch(apiUrl);
return response.ok;
} catch {
// If API call fails, return false
return false;
}
}
/**
* Determine the appropriate entity type and path for GitLab API calls
* Returns the entity type ('projects' or 'groups') and ensures proper encoding
*/
export async function resolveNamespaceForAPI(namespacePath: string): Promise<{
entityType: "projects" | "groups";
encodedPath: string;
}> {
const namespaceType = await detectNamespaceType(namespacePath);
return {
entityType: namespaceType === "project" ? "projects" : "groups",
encodedPath: encodeURIComponent(namespacePath),
};
}
|