using System.Text;
namespace XFESystem.Browser;
/// Explicit packaged application resources. This interface never reads host paths on behalf of a URL.
public interface IBrowserResourceSource
{
bool TryGet(Uri address, out WebResponse response);
}
public sealed class PackagedBrowserResources : IBrowserResourceSource
{
public const string Origin = "http://xfesystem.local";
public const string MonacoCoreRoot = Origin + "/Browser/Monaco/0.52.2/Core/";
public const string MonacoPageAddress = Origin + "/Browser/Monaco/Index.html";
public const string WebsiteCodeRoot = Origin + "/Browser/WebsiteCode/";
private sealed record CachedResource(string ContentType, string Body);
// Only this finite assembly manifest is cached. The completed dictionary
// is never mutated, and script execution still uses a fresh page/runtime.
private static readonly IReadOnlyDictionary resources = LoadEmbeddedResources();
/// Reserve large immutable script strings while the early boot
/// heap still has contiguous space, before browsers allocate transient DOM/JS state.
public static int PrewarmTextResources() => resources.Count;
private static IReadOnlyDictionary LoadEmbeddedResources()
{
var loaded = new Dictionary(StringComparer.Ordinal);
foreach (string name in typeof(PackagedBrowserResources).Assembly.GetManifestResourceNames())
{
var normalized = name.Replace('\\', '/');
string? path = normalized.StartsWith("MonacoCore/", StringComparison.Ordinal) ? "/Browser/Monaco/0.52.2/Core/" + normalized[11..] :
normalized.StartsWith("WebsiteCode/", StringComparison.Ordinal) ? "/Browser/WebsiteCode/" + normalized[12..] : null;
if (path is null) continue;
string extension = Path.GetExtension(path).ToLowerInvariant();
if (extension is not (".js" or ".css" or ".json")) continue;
using var stream = typeof(PackagedBrowserResources).Assembly.GetManifestResourceStream(name)!;
using var reader = new StreamReader(stream, Encoding.UTF8, true);
loaded.Add(path, new(extension == ".css" ? "text/css" : extension == ".json" ? "application/json" : "application/javascript", reader.ReadToEnd()));
}
return new System.Collections.ObjectModel.ReadOnlyDictionary(loaded);
}
public bool TryGet(Uri address, out WebResponse response)
{
response = null!;
if (address.GetLeftPart(UriPartial.Authority) != Origin) return false;
if (address.AbsolutePath == "/Browser/Monaco/Index.html")
{ response = new(address, 200, "text/html", MonacoPage); return true; }
if (XFESystem.Documents.WebsiteAssets.TryReadWebText(address.AbsolutePath, out var website, out var mime))
{
response = new(address, 200, mime, website); return true;
}
if (!resources.TryGetValue(address.AbsolutePath, out var cached)) return false;
response = new(address, 200, cached.ContentType, cached.Body);
return true;
}
public const string MonacoPage = """
XFESystem Monaco