diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/ApiProtocolPatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/ApiProtocolPatch.cs
new file mode 100644
index 0000000..6e7f7a2
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/ApiProtocolPatch.cs
@@ -0,0 +1,32 @@
+using System.Text;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public class ApiProtocolPatch : IPatch {
+ public int GetOrder() => 0;
+
+ public string GetName() => "API: Use GLOBAL_ENV.API_PROTOCOL instead of hardcoded https:";
+ public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string _, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ // TODO: regex
+ stringContent = stringContent
+ .Replace(
+ "return \"https:\" + window.GLOBAL_ENV.API_ENDPOINT + (e ? \"/v\".concat(window.GLOBAL_ENV.API_VERSION) : \"\");",
+ "return window.GLOBAL_ENV.API_PROTOCOL + window.GLOBAL_ENV.API_ENDPOINT + (e ? \"/v\".concat(window.GLOBAL_ENV.API_VERSION) : \"\");"
+ )
+ .Replace(
+ "api_endpoint: \"\".concat(\"https:\").concat(window.GLOBAL_ENV.API_ENDPOINT)",
+ "api_endpoint: window.GLOBAL_ENV.API_PROTOCOL.concat(window.GLOBAL_ENV.API_ENDPOINT)"
+ )
+ .Replace(
+ "f = null != d ? \"https://\".concat(d) : location.protocol + window.GLOBAL_ENV.API_ENDPOINT,",
+ "f = null != d ? window.GLOBAL_ENV.API_PROTOCOL.concat(d) : location.protocol + window.GLOBAL_ENV.API_ENDPOINT,"
+ )
+ ;
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+}
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/BooleanPatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/BooleanPatch.cs
new file mode 100644
index 0000000..152a3f3
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/BooleanPatch.cs
@@ -0,0 +1,21 @@
+using System.Text;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public class BooleanPatch : IPatch {
+ public int GetOrder() => 0;
+
+ public string GetName() => "Use real booleans in JS files";
+ public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string _, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ stringContent = stringContent
+ .Replace("return!", "return !")
+ .Replace("!0", "true")
+ .Replace("!1", "false");
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+}
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/BooleanPropagationPatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/BooleanPropagationPatch.cs
new file mode 100644
index 0000000..fe73c8e
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/BooleanPropagationPatch.cs
@@ -0,0 +1,21 @@
+// using System.Text;
+// using System.Text.RegularExpressions;
+//
+// namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+//
+// public partial class BooleanPropagationPatch : IPatch {
+// public int GetOrder() => 3;
+//
+// public string GetName() => "Patch pointless boolean comparisons in JS";
+// public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+//
+// public async Task<byte[]> Execute(string relativePath, byte[] content) {
+// var stringContent = Encoding.UTF8.GetString(content);
+//
+// stringContent = stringContent
+// .Replace(" && true", "").Replace(" || false", "").Replace("false || ", "")
+// ;
+//
+// return Encoding.UTF8.GetBytes(stringContent);
+// }
+// }
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/DisableSciencePatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/DisableSciencePatch.cs
new file mode 100644
index 0000000..c44bf95
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/DisableSciencePatch.cs
@@ -0,0 +1,26 @@
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public partial class DisableSciencePatch : IPatch {
+ public int GetOrder() => 0;
+
+ public string GetName() => @"JS(web): Disable /science calls";
+ public bool Applies(string relativeName, byte[] content) => relativeName.StartsWith("assets/web.") && relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string _, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ var match = HandleTrackDefinitionRegex().Match(stringContent);
+ stringContent = stringContent.Insert(match.Index + match.Length, @"
+ return (new Promise(() => { }), false); // ReferenceClientProxyImplementation: Disable /science calls
+ ");
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+
+ [GeneratedRegex(@".\.handleTrack = function \(.\) \{", RegexOptions.Compiled)]
+ private static partial Regex HandleTrackDefinitionRegex();
+
+}
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/ExpandUnicodeEscapesPatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/ExpandUnicodeEscapesPatch.cs
new file mode 100644
index 0000000..9eed00c
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/ExpandUnicodeEscapesPatch.cs
@@ -0,0 +1,26 @@
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public partial class ExpandUnicodeEscapesPatch : IPatch {
+ public int GetOrder() => 0;
+
+ public string GetName() => @"JS: expand \x?? to \u00??";
+ public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string _, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ stringContent = XToURegex().Replace(
+ stringContent,
+ m => $"\\u00{m.Groups[1].Value}"
+ );
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+
+ [GeneratedRegex(@"\\x([0-9A-Fa-f]{2})", RegexOptions.Compiled)]
+ private static partial Regex XToURegex();
+
+}
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/IsStaffPatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/IsStaffPatch.cs
new file mode 100644
index 0000000..b741f56
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/IsStaffPatch.cs
@@ -0,0 +1,33 @@
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public partial class IsStaffPatch : IPatch {
+ public int GetOrder() => 2;
+
+ public string GetName() => "Patch isStaff/isStaffPersonal in JS";
+ public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string relativePath, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ stringContent = IsNullableStaffRegex().Replace(
+ stringContent,
+ m => $"{m.Groups[1].Value}!!{m.Groups[2].Value}"
+ );
+
+ stringContent = IsStaffRegex().Replace(
+ stringContent,
+ m => $"{m.Groups[1].Value}true"
+ );
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+
+ [GeneratedRegex(@"(\W)(\w|this|\w\.user)\.isStaff(Personal)?\(\)", RegexOptions.Compiled)]
+ private static partial Regex IsStaffRegex();
+
+ [GeneratedRegex(@"(\W)(\w|this|\w\.user)\?\.isStaff(Personal)?\(\)", RegexOptions.Compiled)]
+ private static partial Regex IsNullableStaffRegex();
+}
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/JsonParseMultilinePatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/JsonParseMultilinePatch.cs
new file mode 100644
index 0000000..b5e7d77
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/JsonParseMultilinePatch.cs
@@ -0,0 +1,79 @@
+using System.Diagnostics;
+using System.Text;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using ReferenceClientProxyImplementation.Configuration;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public partial class JsonParseMultilinePatch(ProxyConfiguration config) : IPatch {
+ public int GetOrder() => 1;
+
+ public string GetName() => "Patch null-coalescing expressions in JS";
+ public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string relativePath, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ var matches = JsonParseRegex().Matches(stringContent);
+ Console.WriteLine($"Found {matches.Count} JSON.parse calls in {relativePath}");
+
+
+
+ await Parallel.ForEachAsync(matches, async (match, ct) => {
+ string formattedJson = match.Groups[1].Value;
+ try {
+ var jsonElement = JsonSerializer.Deserialize<JsonElement>(formattedJson.Replace("\\", "\\\\") + "waef");
+ formattedJson = JsonSerializer.Serialize(jsonElement, new JsonSerializerOptions { WriteIndented = true });
+ } catch (JsonException je) {
+ // Console.WriteLine($"STJ: Failed to parse JSON in {relativePath} at index {match.Index}: {je.Message}"); // intentinally broken
+ try {
+ formattedJson = await formatJsonWithNodejs(relativePath, match, ct);
+ } catch (Exception e) {
+ Console.WriteLine($"Node.js: Failed to parse JSON in {relativePath} at index {match.Index}: {e.Message}");
+ return;
+ }
+ }
+
+ lock (matches) stringContent = stringContent.Replace(match.Value, $"JSON.parse(`{formattedJson.Replace("\\", "\\\\")}`);");
+ });
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+
+ private async Task<string> formatJsonWithNodejs(string relativePath, Match match, CancellationToken ct) {
+ // Extract the JSON string from the match
+ var id = "dcp_" + Path.GetFileName(relativePath).Replace('.', '_') + "_" + match.Index;
+ await File.WriteAllTextAsync($"{Environment.GetEnvironmentVariable("TMPDIR") ?? "/tmp"}/{id}.js", $"console.log(JSON.stringify(JSON.parse(`{match.Groups[1].Value.Replace("`", "\\\\\\`")}`), null, 2))");
+ var sw = Stopwatch.StartNew();
+
+ var psi = new ProcessStartInfo(config.AssetCache.NodePath, $"{Environment.GetEnvironmentVariable("TMPDIR") ?? "/tmp"}/{id}.js") { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true };
+
+ using var process = Process.Start(psi);
+ if (process == null) {
+ throw new InvalidOperationException("Failed to start the formatting process.");
+ }
+
+ var stdout = await process.StandardOutput.ReadToEndAsync();
+ var stderr = await process.StandardError.ReadToEndAsync();
+
+ await process.WaitForExitAsync();
+ // Console.WriteLine($"Formatted {relativeName} in {sw.ElapsedMilliseconds}ms: {process.ExitCode}");
+
+ if (process.ExitCode != 0) {
+ Console.WriteLine($"Failed to run {Environment.GetEnvironmentVariable("TMPDIR") ?? "/tmp"}/{id}.js in {sw.ElapsedMilliseconds}ms: {process.ExitCode}");
+ Console.WriteLine("Standard Output: " + stdout);
+ Console.WriteLine("Standard Error: " + stderr);
+ throw new Exception($"Failed to execute {Environment.GetEnvironmentVariable("TMPDIR") ?? "/tmp"}/{id}.js: {stderr}");
+ }
+
+ var formattedJson = stdout.Trim();
+ Console.WriteLine($"Parsed JSON({id}) in {sw.ElapsedMilliseconds}ms: {formattedJson.Length} bytes");
+ // stringContent = stringContent.Replace(match.Value, $"JSON.parse(`{formattedJson.Replace("\\n", "\\\\n")}`);");
+ await File.WriteAllTextAsync($"{config.TestClient.RevisionPath}/patched/assets/{Path.GetFileName(relativePath)}-{match.Index}.json", formattedJson);
+ return formattedJson;
+ }
+
+ [GeneratedRegex(@"JSON\.parse\(\n\s*'(.*?)',?\s*\);", RegexOptions.Compiled | RegexOptions.Multiline)]
+ private static partial Regex JsonParseRegex();
+}
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/KnownConstantsPatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/KnownConstantsPatch.cs
new file mode 100644
index 0000000..a94e312
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/KnownConstantsPatch.cs
@@ -0,0 +1,16 @@
+using System.Text;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public class KnownConstantsPatch : IPatch {
+ public int GetOrder() => 1;
+
+ public string GetName() => "Use named constants";
+ public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string _, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+}
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/LegacyJsPatches.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/LegacyJsPatches.cs
new file mode 100644
index 0000000..e7f78a0
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/LegacyJsPatches.cs
@@ -0,0 +1,32 @@
+// using System.Text;
+// using System.Text.RegularExpressions;
+//
+// namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+//
+// public partial class LegacyJsPathces : IPatch {
+// public int GetOrder() => 1;
+//
+// public string GetName() => "Patch deprecated JS constructs";
+// public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+//
+// public async Task<byte[]> Execute(string relativePath, byte[] content) {
+// var stringContent = Encoding.UTF8.GetString(content);
+//
+// while(MozInputSourceRegex().IsMatch(stringContent)) {
+// var match = MozInputSourceRegex().Match(stringContent);
+// var replacement = match.Groups[1].Value switch {
+// "0" => "",
+// "1" => "mouse",
+// "2" => "pen",
+// "3" => "pen",
+// "4" => "touch",
+// _ => throw new InvalidOperationException("Unreachable")
+// };
+// }
+//
+// return Encoding.UTF8.GetBytes(stringContent);
+// }
+//
+// [GeneratedRegex(@"([0-6]) === (\w).mozInputSource", RegexOptions.Compiled)]
+// private static partial Regex MozInputSourceRegex();
+// }
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/LogErrorContextPatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/LogErrorContextPatch.cs
new file mode 100644
index 0000000..2005c4c
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/LogErrorContextPatch.cs
@@ -0,0 +1,34 @@
+// using System.Text;
+// using System.Text.RegularExpressions;
+//
+// namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+//
+// public partial class LogErrorContextPatch : IPatch {
+// public int GetOrder() => 2;
+//
+// public string GetName() => "Patch assertions to log more context";
+// public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+//
+// public async Task<byte[]> Execute(string relativePath, byte[] content) {
+// var stringContent = Encoding.UTF8.GetString(content);
+//
+// stringContent = NotNullAssertionRegex().Replace(
+// stringContent,
+// m => {
+// var methodName = m.Groups[1].Value;
+// var objectName = m.Groups[2].Value;
+// var message = m.Groups[3].Value;
+// Console.WriteLine($"Patching not-null assertion in {relativePath}: {methodName} - {message}");
+//
+// return $@"{methodName}()(null != {objectName}, ""{message} - Context: "" + JSON.stringify({objectName}))";
+// }
+// );
+//
+// return Encoding.UTF8.GetBytes(stringContent);
+// }
+//
+// // null assertion: u()(null != o, "PrivateChannel.renderAvatar: Invalid prop configuration - no user or channel");
+// // capture: method name, object name, message
+// [GeneratedRegex(@"([a-zA-Z0-9_]+)\(\)\(\s*null != ([a-zA-Z0-9_]+),\s*""([^""]+)""\s*\)", RegexOptions.Compiled)]
+// private static partial Regex NotNullAssertionRegex();
+// }
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/NullCoalescingPatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/NullCoalescingPatch.cs
new file mode 100644
index 0000000..98156c8
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/NullCoalescingPatch.cs
@@ -0,0 +1,32 @@
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public partial class NullCoalescingPatch : IPatch {
+ public int GetOrder() => 1;
+
+ public string GetName() => "Patch null-coalescing expressions in JS";
+ public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string relativePath, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ stringContent = NullCoalescingRegex().Replace(
+ stringContent,
+ m => $"{m.Groups[1].Value}?.{m.Groups[2].Value}"
+ );
+ // stringContent = ParenNullCheckRegex().Replace(
+ // stringContent,
+ // m => $"{m.Groups[1].Value} == null"
+ // );
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+
+ [GeneratedRegex(@"null == ([a-zA-Z0-9_]+?) \? undefined : \1\.([a-zA-Z0-9_]+?)", RegexOptions.Compiled)]
+ private static partial Regex NullCoalescingRegex();
+
+ [GeneratedRegex(@"\(([^()]+?)\) == null", RegexOptions.Compiled)]
+ private static partial Regex ParenNullCheckRegex();
+}
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/PrefetchAssetsPatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/PrefetchAssetsPatch.cs
new file mode 100644
index 0000000..bab0756
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/PrefetchAssetsPatch.cs
@@ -0,0 +1,56 @@
+// using System.Text;
+// using System.Text.RegularExpressions;
+// using ReferenceClientProxyImplementation.Services;
+//
+// namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+//
+// public partial class PrefetchAssetsPatch(IServiceProvider sp) : IPatch {
+// public int GetOrder() => 1000000;
+//
+// public string GetName() => "Prefetch assets";
+// public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+//
+// private static SemaphoreSlim ss = new(2, 2);
+// private static HashSet<string> alreadyKnownAssets = new();
+//
+// public async Task<byte[]> Execute(string relativePath, byte[] content) {
+// // Can't inject service due to loop
+// var stringContent = Encoding.UTF8.GetString(content);
+// var matches = PrefetchAssetsRegex().Matches(stringContent);
+//
+// Console.WriteLine($"Found {matches.Count} prefetch assets in {relativePath}");
+// if (matches.Count == 0) {
+// return content; // No matches found, return original content
+// }
+//
+// var clientStore = sp.GetRequiredService<ClientStoreService>();
+//
+// var newAssets = matches
+// .Select(x => x.Groups[1].Value)
+// .Distinct()
+// .Where(x => !clientStore.HasRawAsset(x) && alreadyKnownAssets.Add(x));
+//
+// var tasks = newAssets
+// .Select(async match => {
+// await ss.WaitAsync();
+// Console.WriteLine($"Discovered prefetch asset in {relativePath}: {match}");
+// // var patches = sp.GetRequiredService<PatchSet>();
+// var res = await clientStore.GetOrDownloadRawAsset(match);
+// await res.DisposeAsync();
+// ss.Release();
+// Console.WriteLine($"Prefetched asset {match} in {relativePath}");
+// }).ToList();
+//
+// if (tasks.Count == 0) {
+// Console.WriteLine($"No new prefetch assets found in {relativePath}, returning original content.");
+// return content; // No new assets to prefetch, return original content
+// }
+//
+// await Task.WhenAny(tasks);
+//
+// return content;
+// }
+//
+// [GeneratedRegex(@".\.exports = ""((?:[a-z\d/\.]*?)\.\w{2,4})""", RegexOptions.Compiled)]
+// private static partial Regex PrefetchAssetsRegex();
+// }
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/Void0Patch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/Void0Patch.cs
new file mode 100644
index 0000000..9d819c7
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/Void0Patch.cs
@@ -0,0 +1,27 @@
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public partial class Void0Patch : IPatch {
+ public int GetOrder() => 0;
+
+ public string GetName() => "Use literal undefined instead of void 0";
+ public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string _, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ stringContent = stringContent
+ .Replace("void 0", "undefined");
+ stringContent = VoidFunctionRegex().Replace(
+ stringContent,
+ m => $"{m.Groups[1].Value}("
+ );
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+
+ [GeneratedRegex(@"\(0, ([a-zA-Z0-9_.$]+?)\)\(", RegexOptions.Compiled)]
+ private static partial Regex VoidFunctionRegex();
+}
\ No newline at end of file
diff --git a/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/WhileTruePatch.cs b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/WhileTruePatch.cs
new file mode 100644
index 0000000..277bf8a
--- /dev/null
+++ b/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/WhileTruePatch.cs
@@ -0,0 +1,19 @@
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace ReferenceClientProxyImplementation.Patches.Implementations.JSPatches;
+
+public partial class WhileTruePatch : IPatch {
+ public int GetOrder() => 1;
+
+ public string GetName() => "Patch while(true) expressions in JS";
+ public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".js");
+
+ public async Task<byte[]> Execute(string relativePath, byte[] content) {
+ var stringContent = Encoding.UTF8.GetString(content);
+
+ stringContent = stringContent.Replace("for (;;)", "while (true)");
+
+ return Encoding.UTF8.GetBytes(stringContent);
+ }
+}
\ No newline at end of file
|