summary refs log tree commit diff
path: root/ReferenceClientProxyImplementation/Patches/Implementations/JSPatches
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2026-02-23 02:03:20 +0100
committerRory& <root@rory.gay>2026-02-23 02:03:20 +0100
commit77a609758bb80bac9497d2e3988550f8be578407 (patch)
tree991a9d258ca4fece1132a1a344d0fe11e3b03d51 /ReferenceClientProxyImplementation/Patches/Implementations/JSPatches
downloadReferenceClientProxyImplementation-77a609758bb80bac9497d2e3988550f8be578407.tar.xz
Initial commit HEAD master
Diffstat (limited to 'ReferenceClientProxyImplementation/Patches/Implementations/JSPatches')
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/ApiProtocolPatch.cs32
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/BooleanPatch.cs21
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/BooleanPropagationPatch.cs21
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/DisableSciencePatch.cs26
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/ExpandUnicodeEscapesPatch.cs26
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/IsStaffPatch.cs33
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/JsonParseMultilinePatch.cs79
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/KnownConstantsPatch.cs16
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/LegacyJsPatches.cs32
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/LogErrorContextPatch.cs34
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/NullCoalescingPatch.cs32
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/PrefetchAssetsPatch.cs56
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/Void0Patch.cs27
-rw-r--r--ReferenceClientProxyImplementation/Patches/Implementations/JSPatches/WhileTruePatch.cs19
14 files changed, 454 insertions, 0 deletions
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