Skip to content

Commit 71ca232

Browse files
authored
Merge branch 'bitfoundation:develop' into perf/12753-coalesce-wasm-startup-renders
2 parents f780f84 + 87106c6 commit 71ca232

16 files changed

Lines changed: 365 additions & 76 deletions

File tree

.github/agents/contribute-to-bitplatform.agent.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@ git push origin <branch-name>
101101
```
102102

103103
### PR Title
104-
Use conventional-commit format and include the Issue number with a `#` prefix:
104+
Include the Issue number with a `#` prefix:
105105

106106
```
107-
<prefix>(<scope>): <short description> #<issue-number>
107+
<short description> #<issue-number>
108108
```
109109

110110
Example:
111111
```
112-
fix(BlazorUI): BitButton disabled state ignores pointer-events in Safari #1234
112+
Fix BitButton disabled state ignores pointer-events in Safari #1234
113113
```
114114

115115
### PR Description Template

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,6 @@ ModelManifest.xml
225225
#kiro
226226
.kiro
227227

228-
#cursor
229-
.cursor
230228

231229
profile.arm.json
232230

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
description: Project coding conventions, structure and behavioral directives
3+
alwaysApply: true
4+
---
5+
6+
# Cursor Rules
7+
8+
This project uses @AGENTS.md (at the repository root) as the single source of truth for all coding conventions, project structure, technology stack, and behavioral directives.
9+
10+
**Before performing any task, read the full content of `/AGENTS.md`.**

src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@
543543
"src/Tests/Features/Tenants/TenantInvitationUITests.cs",
544544
"src/Tests/Features/MinimalApiSample/IntegrationTests.cs",
545545
"src/Tests/Infrastructure/Configuration/AppConfigurationBuilderTests.cs",
546+
"src/Tests/Features/PubSub/PubSubServiceTests.cs",
546547
"src/Tests/Features/Diagnostics/HealthCheckIntegrationTests.cs",
547548
"src/Tests/Features/Attachments/UserProfilePictureWebPTests.cs",
548549
"src/Tests/Features/WellKnown/AppleAppSiteAssociationTests.cs",

src/Templates/Boilerplate/Bit.Boilerplate/AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,13 @@ Before implementing any changes, you **MUST** complete the following:
6767

6868
## 4. Critical Command Reference
6969

70+
<!--#if (aspire == true)-->
71+
- **Build the project**: Run `dotnet build` in Boilerplate.Server.AppHost project root directory.
72+
- **Run the project**: Run `dotnet watch` in Boilerplate.Server.AppHost project root directory. If needed, you may use the Playwright MCP tools to interact with the running UI to validate things (navigate, click, fill forms, take screenshots), and use `browser_evaluate` to run in-page JavaScript to accelerate the process (e.g. quickly locating elements, extracting data, or asserting state).
73+
<!--#else-->
7074
- **Build the project**: Run `dotnet build` in Boilerplate.Server.Web project root directory.
7175
- **Run the project**: Run `dotnet watch` in Boilerplate.Server.Web project root directory. If needed, you may use the Playwright MCP tools to interact with the running UI to validate things (navigate, click, fill forms, take screenshots), and use `browser_evaluate` to run in-page JavaScript to accelerate the process (e.g. quickly locating elements, extracting data, or asserting state).
76+
<!--#endif-->
7277
- **Run tests**: Run `dotnet test` in Boilerplate.Tests project root directory.
7378
- **Add new migrations**: Run `dotnet ef migrations add <MigrationName> --output-dir Data/Migrations --verbose` in Boilerplate.Server.Api project root directory.
7479
- **Generate Resx C# code**: Run `dotnet build -t:PrepareResources` in Boilerplate.Shared project root directory.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppClientCoordinator.cs

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -53,61 +53,64 @@ protected override async Task OnInitAsync()
5353

5454
if (InPrerenderSession is false)
5555
{
56-
unsubscribes.Add(PubSubService.Subscribe(ClientAppMessages.NAVIGATE_TO, async (uri) =>
56+
await Task.Run(async () =>
5757
{
58-
var uriValue = uri?.ToString()!;
59-
var replace = uriValue.Contains("replace=true", StringComparison.InvariantCultureIgnoreCase);
60-
var forceLoad = uriValue.Contains("forceLoad=true", StringComparison.InvariantCultureIgnoreCase);
61-
NavigationManager.NavigateTo(uriValue.Replace("replace=true", "", StringComparison.InvariantCultureIgnoreCase).Replace("forceLoad=true", "", StringComparison.InvariantCultureIgnoreCase).TrimEnd('&'), forceLoad, replace);
62-
}));
63-
//#if (signalR == true)
64-
unsubscribes.Add(PubSubService.Subscribe(SharedAppMessages.EXCEPTION_THROWN, async (payload) =>
65-
{
66-
if (payload is null) return;
58+
unsubscribes.Add(PubSubService.Subscribe(ClientAppMessages.NAVIGATE_TO, async (uri) =>
59+
{
60+
var uriValue = uri?.ToString()!;
61+
var replace = uriValue.Contains("replace=true", StringComparison.InvariantCultureIgnoreCase);
62+
var forceLoad = uriValue.Contains("forceLoad=true", StringComparison.InvariantCultureIgnoreCase);
63+
NavigationManager.NavigateTo(uriValue.Replace("replace=true", "", StringComparison.InvariantCultureIgnoreCase).Replace("forceLoad=true", "", StringComparison.InvariantCultureIgnoreCase).TrimEnd('&'), forceLoad, replace);
64+
}));
65+
//#if (signalR == true)
66+
unsubscribes.Add(PubSubService.Subscribe(SharedAppMessages.EXCEPTION_THROWN, async (payload) =>
67+
{
68+
if (payload is null) return;
6769

68-
var appProblemDetails = payload is JsonElement jsonDocument
69-
? jsonDocument.Deserialize(JsonSerializerOptions.GetTypeInfo<AppProblemDetails>())! /* Message gets published from server through SignalR */
70-
: (AppProblemDetails)payload;
70+
var appProblemDetails = payload is JsonElement jsonDocument
71+
? jsonDocument.Deserialize(JsonSerializerOptions.GetTypeInfo<AppProblemDetails>())! /* Message gets published from server through SignalR */
72+
: (AppProblemDetails)payload;
7173

72-
ExceptionHandler.Handle(appProblemDetails, displayKind: ExceptionDisplayKind.NonInterrupting);
73-
}));
74-
//#endif
74+
ExceptionHandler.Handle(appProblemDetails, displayKind: ExceptionDisplayKind.NonInterrupting);
75+
}));
76+
//#endif
7577

76-
if (AppPlatform.IsBlazorHybrid is false)
77-
{
78-
try
79-
{
80-
BitButil.UseFastInvoke(); // Ensures that `TelemetryContext.Platform` is available to components using this value in their `OnInitAsync` method, such as `SignInPage.razor.cs`.
81-
var userAgentData = await userAgent.Extract();
82-
TelemetryContext.Platform = string.Join(' ', [userAgentData.Manufacturer, userAgentData.OsName, userAgentData.Name, "browser"]);
83-
}
84-
finally
78+
if (AppPlatform.IsBlazorHybrid is false)
8579
{
86-
BitButil.UseNormalInvoke();
80+
try
81+
{
82+
BitButil.UseFastInvoke(); // Ensures that `TelemetryContext.Platform` is available to components using this value in their `OnInitAsync` method, such as `SignInPage.razor.cs`.
83+
var userAgentData = await userAgent.Extract();
84+
TelemetryContext.Platform = string.Join(' ', [userAgentData.Manufacturer, userAgentData.OsName, userAgentData.Name, "browser"]);
85+
}
86+
finally
87+
{
88+
BitButil.UseNormalInvoke();
89+
}
8790
}
88-
}
89-
TelemetryContext.TimeZone = await jsRuntime.GetTimeZone();
90-
TelemetryContext.Culture = CultureInfo.CurrentCulture.Name;
91-
TelemetryContext.PageUrl = HttpUtility.UrlDecode(NavigationManager.Uri);
91+
TelemetryContext.TimeZone = await jsRuntime.GetTimeZone();
92+
TelemetryContext.Culture = CultureInfo.CurrentCulture.Name;
93+
TelemetryContext.PageUrl = HttpUtility.UrlDecode(NavigationManager.Uri);
9294

93-
//#if (appInsights == true)
94-
_ = appInsights.AddTelemetryInitializer(new()
95-
{
96-
Data = new()
95+
//#if (appInsights == true)
96+
_ = appInsights.AddTelemetryInitializer(new()
9797
{
98-
["ai.application.ver"] = TelemetryContext.AppVersion,
99-
["ai.session.id"] = TelemetryContext.AppSessionId,
100-
["ai.device.locale"] = TelemetryContext.Culture
101-
}
98+
Data = new()
99+
{
100+
["ai.application.ver"] = TelemetryContext.AppVersion,
101+
["ai.session.id"] = TelemetryContext.AppSessionId,
102+
["ai.device.locale"] = TelemetryContext.Culture
103+
}
104+
});
105+
//#endif
106+
107+
NavigationManager.LocationChanged += NavigationManager_LocationChanged;
108+
AuthManager.AuthenticationStateChanged += AuthenticationStateChanged;
109+
//#if (signalR == true)
110+
SubscribeToSignalRSharedAppMessages();
111+
//#endif
112+
await PropagateAuthState(firstRun: true, AuthenticationStateTask);
102113
});
103-
//#endif
104-
105-
NavigationManager.LocationChanged += NavigationManager_LocationChanged;
106-
AuthManager.AuthenticationStateChanged += AuthenticationStateChanged;
107-
//#if (signalR == true)
108-
SubscribeToSignalRSharedAppMessages();
109-
//#endif
110-
await PropagateAuthState(firstRun: true, AuthenticationStateTask);
111114
}
112115
}
113116

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ private async Task HideTfaAndOtpPanels(LocationChangingContext args)
9191

9292
webAuthnAssertion = null;
9393

94+
isNewUser = false;
95+
9496
isOtpSent = false;
9597
model.Otp = null;
9698

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Infrastructure/Extensions/IConfigurationBuilderExtensions.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
using System.Reflection;
2+
using System.Runtime.CompilerServices;
23
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
34

45
namespace Microsoft.Extensions.Configuration;
56

67
public static partial class IConfigurationBuilderExtensions
78
{
9+
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_providers")]
10+
private static extern ref List<IConfigurationProvider> GetProviders(WebAssemblyHostConfiguration configuration);
11+
812
extension(IConfigurationBuilder builder)
913
{
1014
/// <summary>
@@ -68,8 +72,8 @@ public IConfigurationBuilder AddClientConfigurations(string clientEntryAssemblyN
6872

6973
if (AppPlatform.IsBrowser)
7074
{
71-
var providersField = builder.GetType().GetField("_providers", BindingFlags.NonPublic | BindingFlags.Instance)!;
72-
providersField.SetValue(builder, (((IConfigurationRoot)configBuilder).Providers).Union(((IConfigurationRoot)builder).Providers).ToList());
75+
GetProviders((WebAssemblyHostConfiguration)builder) =
76+
[.. ((IConfigurationRoot)configBuilder).Providers.Union(((IConfigurationRoot)builder).Providers)];
7377
}
7478
else if (AppPlatform.IsBlazorHybrid)
7579
{

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Infrastructure/Services/AuthManager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ async Task RefreshTokenImplementation()
144144

145145
/// <summary>
146146
/// Handles the process of determining the user's authentication state based on the availability of access and refresh tokens.
147-
///
147+
///
148148
/// - If no access / refresh token exists, an anonymous user object is returned to Blazor.
149149
/// - If an access token exists, a ClaimsPrincipal is created from it regardless of its expiration status. This ensures:
150150
/// - Users can access anonymous-allowed pages without unnecessary delays caused by token refresh attempts **during app startup**.
@@ -219,7 +219,7 @@ public async Task<bool> SwitchTenant(Guid tenantId, CancellationToken cancellati
219219
if (string.IsNullOrEmpty(accessToken))
220220
return null;
221221

222-
var isValid = IAuthTokenProvider.ParseAccessToken(accessToken, validateExpiry: true).IsAuthenticated();
222+
var isValid = await Task.Run(() => IAuthTokenProvider.ParseAccessToken(accessToken, validateExpiry: true).IsAuthenticated());
223223

224224
if (isValid) return accessToken;
225225

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Infrastructure/Services/HttpMessageHandlers/LoggingDelegatingHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
2222
var stopwatch = Stopwatch.StartNew();
2323
try
2424
{
25-
var response = await base.SendAsync(request, cancellationToken);
25+
var response = await Task.Run(() => base.SendAsync(request, cancellationToken), cancellationToken);
2626
if (AppPlatform.IsBrowser is false)
2727
{
2828
logScopeData["HttpVersion"] = response.Version;

0 commit comments

Comments
 (0)