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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 67x 78x 9x 68x 68x 8x 60x 4x 56x 60x 9x 62x 62x 9x 59x 9x 15x 4x 11x 11x 1x 10x 3x 7x 7x 1x 6x | /**
* Smart project identifier handling utilities
* Handles both numeric IDs and namespace paths (URL-encoded or not)
*/
/**
* Smart project identifier that can be numeric ID or namespace path
*/
export interface ProjectIdentifier {
/** The normalized project identifier for API calls */
identifier: string;
/** Whether this is a numeric ID (true) or namespace path (false) */
isNumericId: boolean;
/** Original input value */
originalValue: string;
}
/**
* Detects if a string is URL-encoded by checking for common encoded characters
*/
function isUrlEncoded(str: string): boolean {
// Check for common URL encoded patterns: %20, %2F, %3A, etc.
return /%[0-9A-Fa-f]{2}/.test(str);
}
/**
* Detects if a string is a numeric ID
*/
function isNumericId(str: string): boolean {
return /^\d+$/.test(str.trim());
}
/**
* Smart processing of project identifier that handles:
* - Numeric IDs: "123" -> stays as "123"
* - URL-encoded paths: "group%2Fproject" -> stays as "group%2Fproject" (for API)
* - Regular paths: "group/project" -> converts to "group%2Fproject"
* - Already properly encoded paths: detected and preserved
*/
export function processProjectIdentifier(input: string): ProjectIdentifier {
const trimmedInput = input.trim();
// Check if it's a numeric ID
if (isNumericId(trimmedInput)) {
return {
identifier: trimmedInput,
isNumericId: true,
originalValue: input,
};
}
// For namespace paths, we need to ensure proper encoding for GitLab API
let identifier: string;
if (isUrlEncoded(trimmedInput)) {
// Already URL-encoded, use as-is (avoid double encoding)
identifier = trimmedInput;
} else {
// Regular path, needs URL encoding
identifier = encodeURIComponent(trimmedInput);
}
return {
identifier,
isNumericId: false,
originalValue: input,
};
}
/**
* Safe URL component encoding that avoids double-encoding
* If input is already encoded, returns as-is
* If input needs encoding, applies encodeURIComponent
*/
export function safeEncodeProjectId(projectId: string): string {
const processed = processProjectIdentifier(projectId);
return processed.identifier;
}
/**
* Normalize project identifier for API calls
* Ensures the identifier is properly formatted for GitLab REST API
*/
export function normalizeProjectId(projectId: string): string {
return safeEncodeProjectId(projectId);
}
/**
* Validate project identifier format
* Returns error message if invalid, null if valid
*/
export function validateProjectIdentifier(input: string): string | null {
if (!input || typeof input !== "string") {
return "Project identifier is required and must be a string";
}
const trimmed = input.trim();
if (trimmed.length === 0) {
return "Project identifier cannot be empty";
}
// Check if it's numeric ID
if (isNumericId(trimmed)) {
return null; // Valid numeric ID
}
// Check if it's a valid namespace path (with or without encoding)
const decoded = isUrlEncoded(trimmed) ? decodeURIComponent(trimmed) : trimmed;
// Basic validation for namespace/project format
if (!/^[a-zA-Z0-9\-_./]+$/.test(decoded)) {
return "Invalid project identifier format. Use numeric ID or namespace/project path.";
}
return null; // Valid
}
|