diff --git a/src/Api/Extensions/StringExtensions.cs b/src/Api/Extensions/StringExtensions.cs
new file mode 100644
index 00000000..f7c43667
--- /dev/null
+++ b/src/Api/Extensions/StringExtensions.cs
@@ -0,0 +1,63 @@
+using System.Text.RegularExpressions;
+
+namespace Void.Proxy.Api.Extensions;
+
+public static partial class StringExtensions
+{
+ private static readonly char[] DefaultDelimiters = [',', ';', '\n'];
+
+ ///
+ /// Splits a string by multiple delimiters with optional escape character support.
+ ///
+ /// The string to split.
+ /// Custom delimiters to use. If null, uses default delimiters: comma, semicolon, and newline.
+ /// Optional escape character (e.g., '\') to allow escaped delimiters in the input.
+ /// Whether to remove empty entries from the result. Defaults to true.
+ /// An array of strings split by the delimiters.
+ public static string[] SplitByDelimiters(this string input, char[]? delimiters = null, char? escapeCharacter = null, bool removeEmptyEntries = true)
+ {
+ if (string.IsNullOrWhiteSpace(input))
+ return [];
+
+ delimiters ??= DefaultDelimiters;
+
+ if (escapeCharacter is not null)
+ {
+ return SplitByDelimitersWithEscape(input, delimiters, escapeCharacter.Value, removeEmptyEntries);
+ }
+
+ var options = removeEmptyEntries ? StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries : StringSplitOptions.TrimEntries;
+ return input.Split(delimiters, options);
+ }
+
+ private static string[] SplitByDelimitersWithEscape(string input, char[] delimiters, char escapeCharacter, bool removeEmptyEntries)
+ {
+ var pattern = BuildEscapedDelimiterPattern(delimiters, escapeCharacter);
+ var regex = new Regex(pattern, RegexOptions.Compiled);
+ var parts = regex.Split(input);
+
+ var result = parts.Select(part => UnescapeDelimiters(part, delimiters, escapeCharacter).Trim()).ToArray();
+
+ if (removeEmptyEntries)
+ result = result.Where(part => !string.IsNullOrWhiteSpace(part)).ToArray();
+
+ return result;
+ }
+
+ private static string BuildEscapedDelimiterPattern(char[] delimiters, char escapeCharacter)
+ {
+ var escapedChar = Regex.Escape(escapeCharacter.ToString());
+ var delimiterPattern = string.Join("|", delimiters.Select(d => Regex.Escape(d.ToString())));
+ return $"(? logger, IRunOptions runOptions, IConsoleService console, HttpClient httpClient) : INuGetDependencyResolver, IEventListener
+public class NuGetDependencyResolver(ILogger logger, IRunOptions runOptions, IConsoleService console, HttpClient httpClient) : INuGetDependencyResolver, IEventListener
{
private static readonly Option RepositoryOption = new("--repository", "-r")
{
@@ -42,7 +42,7 @@ public partial class NuGetDependencyResolver(ILogger lo
private readonly NuGet.Common.ILogger _nugetLogger = console.GetOptionValue(EnableNugetLoggingOption) ? new NuGetLogger(logger) : NullLogger.Instance;
private readonly HashSet _repositories = [];
- private IEnumerable UriRepositories => UnescapedSemicolonRegex().Split(Environment.GetEnvironmentVariable("VOID_NUGET_REPOSITORIES") ?? "").Select(repo => repo.Replace(@"\;", ";")).Concat(_repositories.Concat(console.GetOptionValue(RepositoryOption) ?? [])).Where(uri => !string.IsNullOrWhiteSpace(uri));
+ private IEnumerable UriRepositories => (Environment.GetEnvironmentVariable("VOID_NUGET_REPOSITORIES") ?? "").SplitByDelimiters([';'], escapeCharacter: '\\').Concat(_repositories.Concat(console.GetOptionValue(RepositoryOption) ?? [])).Where(uri => !string.IsNullOrWhiteSpace(uri));
private IEnumerable Repositories
{
get
@@ -543,7 +543,4 @@ private async Task ProbeRepositoriesAsync(CancellationToken cancellationToken =
foreach (var (url, status) in statuses)
logger.LogInformation(" - {Url} [{Status}]", url, status);
}
-
- [GeneratedRegex(@"(?