using AyCode.Core.Loggers; using AyCode.Core.Serializers.Binaries; using AyCode.Services.SignalRs; using FruitBank.Common; using FruitBank.Common.Models; using FruitBank.Common.Services; using FruitBank.Common.Server.Services.Loggers; using FruitBank.Common.Server.Services.SignalRs; using FruitBankHybrid.Shared.Databases; using FruitBankHybrid.Shared.Services; using FruitBankHybrid.Shared.Services.Loggers; using FruitBankHybrid.Shared.Services.SignalRs; using FruitBankHybrid.Web.Components; using FruitBankHybrid.Web.Services; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.Options; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorComponents().AddInteractiveWebAssemblyComponents(); builder.Services.AddDevExpressBlazor(configure => configure.SizeMode = DevExpress.Blazor.SizeMode.Medium); builder.Services.AddMvc(); builder.Services.AddSignalR(options => options.MaximumReceiveMessageSize = 256 * 1024); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); // Logger factory — thin Logger, DI-singleton writers. Each call yields a fresh LoggerClient // with the caller's categoryName. Mirrors Microsoft's ILoggerFactory / ILogger pattern. builder.Services.AddSingleton>(sp => categoryName => new LoggerClient(categoryName, sp.GetServices().ToArray())); builder.Services.AddSingleton(sp => new LoggedInModel(sp.GetRequiredService())); // Bind SignalR options from appsettings.json — single Configure call per options type. // The lambda runs the appsettings Bind first, then any runtime overrides (e.g. the WASM safety net). builder.Services.Configure(opts => builder.Configuration.GetSection("AcHubConnection").Bind(opts)); builder.Services.Configure(opts => { builder.Configuration.GetSection("AcBinaryHubProtocol").Bind(opts); // Platform safety net: on WebAssembly the AsyncSegment send-path is unsupported // (Validate() would throw). No-op on this server host, but matches the contract. if (OperatingSystem.IsBrowser() && opts.ProtocolMode == BinaryProtocolMode.AsyncSegment) opts.ProtocolMode = BinaryProtocolMode.Segment; }); // HubConnectionBuilder — transient so each consumer gets a fresh builder to Build(). // All connection and protocol configuration flows from appsettings.json via IOptions; // AddFruitBankDefaults only bridges the provided logger into SignalR's internal pipeline. builder.Services.AddTransient(sp => { var loggerFactory = sp.GetRequiredService>(); var connectionOpts = sp.GetRequiredService>().Value; var logger = loggerFactory(nameof(FruitBankSignalRClient)); var hubBuilder = new HubConnectionBuilder().AddFruitBankDefaults(logger, connectionOpts); hubBuilder.AddAcBinaryProtocol(); // IOptions from DI return hubBuilder; }); builder.Services.AddSingleton(); builder.Services.AddSingleton(); //builder.Services.AddScoped(); //builder.Services.AddSingleton(); //builder.Services.AddScoped(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseWebAssemblyDebugging(); } else { app.UseExceptionHandler("/Error", createScopeForErrors: true); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.MapHub($"/{FruitBankConstClient.LoggerHubName}"); app.MapHub($"/{FruitBankConstClient.DefaultHubName}"); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAntiforgery(); app.MapStaticAssets(); app.MapRazorComponents() //.AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(FruitBankHybrid.Shared._Imports).Assembly, typeof(FruitBankHybrid.Web.Client._Imports).Assembly); app.Run();