Skip to content

feat: Add WebAssembly telemetry support and related configurations#25

Merged
jeromelaban merged 12 commits intomainfrom
dev/jela/wasm-compat
Feb 12, 2026
Merged

feat: Add WebAssembly telemetry support and related configurations#25
jeromelaban merged 12 commits intomainfrom
dev/jela/wasm-compat

Conversation

@jeromelaban
Copy link
Copy Markdown
Member

  • Introduced WasmHttpSender for sending telemetry data directly to Application Insights.
  • Created necessary project files for WebAssembly platform including web.config, Info.plist, and Entitlements.plist.
  • Added launch images and privacy information for iOS platform.
  • Implemented telemetry tests in TelemetryWasmTests.cs to validate functionality.
  • Configured publish profiles for different Windows architectures (x86, x64, arm64).
  • Added a README file for project guidance and initial setup instructions.
  • Included resource files for localization support.
  • Established a manifest file for application settings and compatibility.

- Introduced `WasmHttpSender` for sending telemetry data directly to Application Insights.
- Created necessary project files for WebAssembly platform including `web.config`, `Info.plist`, and `Entitlements.plist`.
- Added launch images and privacy information for iOS platform.
- Implemented telemetry tests in `TelemetryWasmTests.cs` to validate functionality.
- Configured publish profiles for different Windows architectures (x86, x64, arm64).
- Added a README file for project guidance and initial setup instructions.
- Included resource files for localization support.
- Established a manifest file for application settings and compatibility.
Copilot AI review requested due to automatic review settings February 12, 2026 19:55
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive WebAssembly support to the Uno.DevTools.Telemetry package, enabling telemetry collection from browser-based Uno Platform applications. The implementation introduces a lightweight HTTP-based telemetry sender that bypasses the Application Insights SDK (which is incompatible with WASM due to threading and file I/O limitations) and communicates directly with the Application Insights REST API.

Changes:

  • Created WasmHttpSender for direct HTTP communication with Application Insights on WASM platforms
  • Modified Telemetry.cs and TelemetryCommonProperties.cs to detect WASM environments and branch execution paths accordingly
  • Added comprehensive WASM runtime tests using Uno.UI.RuntimeTests.Engine framework
  • Updated CI pipeline to run WASM tests in actual browser environments
  • Provided extensive documentation including specs, testing guides, and usage examples

Reviewed changes

Copilot reviewed 63 out of 66 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/Uno.DevTools.Telemetry/WasmHttpSender.cs New internal HTTP sender for WASM that sends telemetry directly to Application Insights REST API
src/Uno.DevTools.Telemetry/Telemetry.cs Added WASM detection and branching logic to use WasmHttpSender instead of Application Insights SDK on WASM
src/Uno.DevTools.Telemetry/TelemetryCommonProperties.cs Modified machine ID generation to create session-specific GUIDs on WASM (no persistent storage)
src/Uno.DevTools.Telemetry.WasmTests/* Complete Uno Platform WASM test project with 8 runtime tests validating telemetry functionality in browser
specs/001-wasm-compat.md Comprehensive feature specification documenting requirements, user scenarios, and implementation details
docs/wasm-testing-setup.md Detailed guide for creating and running WASM runtime tests locally and in CI
docs/usage.md Added WebAssembly support section with usage examples and platform-specific characteristics
docs/ci-pipeline-updates.md Documentation of CI infrastructure changes for WASM test automation
agents.md Agent testing guide with complete instructions for running unit and WASM tests
.github/workflows/ci.yml Added wasm-tests job that runs browser-based tests using Playwright and publishes results
Comments suppressed due to low confidence (1)

src/Uno.DevTools.Telemetry/Telemetry.cs:238

  • Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Uno.DevTools.Telemetry/WasmHttpSender.cs Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread specs/001-wasm-compat/spec.md
Comment thread src/Uno.DevTools.Telemetry/WasmHttpSender.cs
Comment thread src/Uno.DevTools.Telemetry/WasmHttpSender.cs
Comment thread src/Uno.DevTools.Telemetry/Telemetry.cs Outdated
Comment thread src/Uno.DevTools.Telemetry/Telemetry.cs
Comment thread src/Uno.DevTools.Telemetry/WasmHttpSender.cs Outdated
Copilot AI review requested due to automatic review settings February 12, 2026 20:08
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 66 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (1)

src/Uno.DevTools.Telemetry/Telemetry.cs:238

  • Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Uno.DevTools.Telemetry/Telemetry.cs
Comment thread src/Uno.DevTools.Telemetry/Telemetry.cs
Comment thread src/Uno.DevTools.Telemetry/WasmHttpSender.cs Outdated
Comment thread src/Uno.DevTools.Telemetry.WasmTests/.vscode/extensions.json
Copilot AI review requested due to automatic review settings February 12, 2026 20:29
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 61 out of 64 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/Uno.DevTools.Telemetry/Telemetry.cs:238

  • Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Uno.DevTools.Telemetry/TelemetryCommonProperties.cs Outdated
Comment thread .github/workflows/ci.yml
Copilot AI review requested due to automatic review settings February 12, 2026 20:42
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 64 out of 67 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/Uno.DevTools.Telemetry/WasmHttpSender.cs:1

  • The field 'HttpClient' has the same name as its type, which can be confusing. Consider renaming to 'SharedHttpClient' or 's_httpClient' to follow static field naming conventions.
    src/Uno.DevTools.Telemetry/Telemetry.cs:1
  • The fully qualified namespace 'System.Runtime.InteropServices.RuntimeInformation' is unnecessary since 'using System.Runtime.InteropServices;' is already declared at line 15. Use 'RuntimeInformation.OSDescription' instead for consistency with the rest of the codebase.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings February 12, 2026 21:12
@jeromelaban jeromelaban merged commit 39aef63 into main Feb 12, 2026
8 checks passed
@jeromelaban jeromelaban deleted the dev/jela/wasm-compat branch February 12, 2026 21:18
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 65 out of 68 changed files in this pull request and generated 10 comments.

Comments suppressed due to low confidence (1)

src/Uno.DevTools.Telemetry/Telemetry.cs:225

  • Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +269 to +282
if (IsWasmBrowser && _wasmSender != null)
{
// WASM path: Use HTTP sender
try
{
var eventProperties = GetEventProperties(properties);
var eventMeasurements = GetEventMeasures(measurements);
_ = _wasmSender.SendEventAsync(
eventName,
eventProperties,
eventMeasurements,
_commonProperties![TelemetryCommonProperties.MachineId],
_currentSessionId);
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the WASM path, the send is started fire-and-forget (_ = _wasmSender.SendEventAsync(...)). This means the lock-free chaining no longer represents “events sent in order”, and Flush/FlushAsync can complete while HTTP sends are still in-flight. Consider making the WASM send part of the chained task (e.g., chain an async continuation and Unwrap/await the send) so ordering and flush semantics match non-WASM behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +144 to +208
private WasmHttpSender CreateWasmHttpSender()
{
// Use reflection to create WasmHttpSender since it's internal
var assembly = typeof(Telemetry).Assembly;
var type = assembly.GetType("Uno.DevTools.Telemetry.WasmHttpSender");
Assert.IsNotNull(type, "WasmHttpSender type should exist");

var instance = Activator.CreateInstance(
type,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
null,
new object[] { TestInstrumentationKey, TestEventPrefix },
null);

Assert.IsNotNull(instance, "WasmHttpSender instance should be created");

// Create a wrapper to expose the methods
return new WasmHttpSender(instance);
}

// Wrapper class to call internal WasmHttpSender methods via reflection
private class WasmHttpSender
{
private readonly object _instance;
private readonly Type _type;

public WasmHttpSender(object instance)
{
_instance = instance;
_type = instance.GetType();
}

public async Task SendEventAsync(
string eventName,
IDictionary<string, string>? properties,
IDictionary<string, double>? measurements,
string machineId,
string? sessionId)
{
var method = _type.GetMethod("SendEventAsync");
Assert.IsNotNull(method, "SendEventAsync method should exist");

var task = method.Invoke(_instance, new object?[] { eventName, properties, measurements, machineId, sessionId }) as Task;
Assert.IsNotNull(task, "SendEventAsync should return a Task");

await task;
}

public async Task SendExceptionAsync(
Exception exception,
ExceptionSeverity severity,
IDictionary<string, string>? properties,
IDictionary<string, double>? measurements,
string machineId,
string? sessionId)
{
var method = _type.GetMethod("SendExceptionAsync");
Assert.IsNotNull(method, "SendExceptionAsync method should exist");

var task = method.Invoke(_instance, new object?[] { exception, severity, properties, measurements, machineId, sessionId }) as Task;
Assert.IsNotNull(task, "SendExceptionAsync should return a Task");

await task;
}
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uno.DevTools.Telemetry.csproj already exposes internals to the test assembly for net8+/net9 (InternalsVisibleTo Include="Uno.DevTools.Telemetry.Tests"), so the reflection-based wrapper in these tests adds complexity without benefit. Consider directly referencing Uno.DevTools.Telemetry.WasmHttpSender from the test project and removing the reflection indirection.

Suggested change
private WasmHttpSender CreateWasmHttpSender()
{
// Use reflection to create WasmHttpSender since it's internal
var assembly = typeof(Telemetry).Assembly;
var type = assembly.GetType("Uno.DevTools.Telemetry.WasmHttpSender");
Assert.IsNotNull(type, "WasmHttpSender type should exist");
var instance = Activator.CreateInstance(
type,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
null,
new object[] { TestInstrumentationKey, TestEventPrefix },
null);
Assert.IsNotNull(instance, "WasmHttpSender instance should be created");
// Create a wrapper to expose the methods
return new WasmHttpSender(instance);
}
// Wrapper class to call internal WasmHttpSender methods via reflection
private class WasmHttpSender
{
private readonly object _instance;
private readonly Type _type;
public WasmHttpSender(object instance)
{
_instance = instance;
_type = instance.GetType();
}
public async Task SendEventAsync(
string eventName,
IDictionary<string, string>? properties,
IDictionary<string, double>? measurements,
string machineId,
string? sessionId)
{
var method = _type.GetMethod("SendEventAsync");
Assert.IsNotNull(method, "SendEventAsync method should exist");
var task = method.Invoke(_instance, new object?[] { eventName, properties, measurements, machineId, sessionId }) as Task;
Assert.IsNotNull(task, "SendEventAsync should return a Task");
await task;
}
public async Task SendExceptionAsync(
Exception exception,
ExceptionSeverity severity,
IDictionary<string, string>? properties,
IDictionary<string, double>? measurements,
string machineId,
string? sessionId)
{
var method = _type.GetMethod("SendExceptionAsync");
Assert.IsNotNull(method, "SendExceptionAsync method should exist");
var task = method.Invoke(_instance, new object?[] { exception, severity, properties, measurements, machineId, sessionId }) as Task;
Assert.IsNotNull(task, "SendExceptionAsync should return a Task");
await task;
}
}
private Uno.DevTools.Telemetry.WasmHttpSender CreateWasmHttpSender()
{
// Directly create WasmHttpSender; internals are visible to this test assembly.
return new Uno.DevTools.Telemetry.WasmHttpSender(TestInstrumentationKey, TestEventPrefix);
}
// Wrapper class removed: tests now use Uno.DevTools.Telemetry.WasmHttpSender directly.
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +70
// Arrange
var sender = CreateWasmHttpSender();
var properties = new Dictionary<string, string>
{
["TestProperty"] = "TestValue",
["AnotherProperty"] = "AnotherValue"
};
var measurements = new Dictionary<string, double>
{
["TestMetric"] = 123.45,
["AnotherMetric"] = 678.90
};

// Act & Assert - Should not throw even if network call fails
// (WasmHttpSender swallows exceptions to prevent telemetry from crashing the app)
await sender.SendEventAsync(
"TestEvent",
properties,
measurements,
"test-machine-id",
"test-session-id");
}

[TestMethod]
public async Task SendEventAsync_WithNullProperties_ShouldNotThrow()
{
// Arrange
var sender = CreateWasmHttpSender();

// Act & Assert
await sender.SendEventAsync(
"TestEvent",
null,
null,
"test-machine-id",
"test-session-id");
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests end up exercising WasmHttpSender against the real Application Insights endpoint (https://dc.services.visualstudio.com/v2/track). Even if exceptions are swallowed, this introduces a real network dependency and can make unit tests slow/flaky or fail under restricted network policies. Consider injecting an HttpMessageHandler/HttpClient (or adding a test hook) so tests can run offline and assert payloads deterministically.

Suggested change
// Arrange
var sender = CreateWasmHttpSender();
var properties = new Dictionary<string, string>
{
["TestProperty"] = "TestValue",
["AnotherProperty"] = "AnotherValue"
};
var measurements = new Dictionary<string, double>
{
["TestMetric"] = 123.45,
["AnotherMetric"] = 678.90
};
// Act & Assert - Should not throw even if network call fails
// (WasmHttpSender swallows exceptions to prevent telemetry from crashing the app)
await sender.SendEventAsync(
"TestEvent",
properties,
measurements,
"test-machine-id",
"test-session-id");
}
[TestMethod]
public async Task SendEventAsync_WithNullProperties_ShouldNotThrow()
{
// Arrange
var sender = CreateWasmHttpSender();
// Act & Assert
await sender.SendEventAsync(
"TestEvent",
null,
null,
"test-machine-id",
"test-session-id");
// Arrange & Act
var senderType = Type.GetType("Uno.DevTools.Telemetry.WasmHttpSender, Uno.DevTools.Telemetry", throwOnError: false);
Assert.IsNotNull(senderType, "WasmHttpSender type should be discoverable via reflection.");
var method = senderType.GetMethod(
"SendEventAsync",
BindingFlags.Instance | BindingFlags.Public);
// Assert
Assert.IsNotNull(method, "WasmHttpSender should expose a public instance method SendEventAsync.");
var parameters = method.GetParameters();
Assert.AreEqual(5, parameters.Length, "SendEventAsync should accept five parameters.");
Assert.AreEqual(typeof(string), parameters[0].ParameterType, "First parameter should be event name (string).");
Assert.AreEqual(typeof(IDictionary<string, string>), parameters[1].ParameterType, "Second parameter should be properties dictionary.");
Assert.AreEqual(typeof(IDictionary<string, double>), parameters[2].ParameterType, "Third parameter should be measurements dictionary.");
Assert.AreEqual(typeof(string), parameters[3].ParameterType, "Fourth parameter should be machine id (string).");
Assert.AreEqual(typeof(string), parameters[4].ParameterType, "Fifth parameter should be session id (string).");
Assert.AreEqual(typeof(Task), method.ReturnType, "SendEventAsync should return a Task.");
await Task.CompletedTask;
}
[TestMethod]
public async Task SendEventAsync_WithNullProperties_ShouldNotThrow()
{
// Arrange & Act
var senderType = Type.GetType("Uno.DevTools.Telemetry.WasmHttpSender, Uno.DevTools.Telemetry", throwOnError: false);
Assert.IsNotNull(senderType, "WasmHttpSender type should be discoverable via reflection.");
var method = senderType.GetMethod(
"SendEventAsync",
BindingFlags.Instance | BindingFlags.Public);
// Assert
Assert.IsNotNull(method, "WasmHttpSender should expose a public instance method SendEventAsync.");
// We do not invoke the method here to avoid real network activity in unit tests.
// This test simply ensures that SendEventAsync is present and can be located.
await Task.CompletedTask;

Copilot uses AI. Check for mistakes.

**FR-005:** The telemetry payload format on WASM MUST match the Application Insights SDK format to ensure compatibility with the Application Insights service.

**FR-006:** On WASM platforms, the package MUST queue events in memory (maximum 50 events) instead of using file-based persistence.
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spec describes an in-memory queue with a hard limit of 50 items and batching behavior (EC-4 / FR-006 / entities section), but the current implementation appears to send each event/exception individually and has no explicit queue size limit. Please align the spec with the actual behavior (or implement the queue/limit/batching if those requirements are still intended).

Suggested change
**FR-006:** On WASM platforms, the package MUST queue events in memory (maximum 50 events) instead of using file-based persistence.
**FR-006:** On WASM platforms, the package MUST send each telemetry event immediately over HTTP without relying on an in-memory or file-based persistent queue or batching mechanism.

Copilot uses AI. Check for mistakes.
Comment on lines +397 to +416
if (IsWasmBrowser && _wasmSender != null)
{
// WASM path: Use HTTP sender
try
{
var eventProperties = GetEventProperties(properties);
var eventMeasurements = GetEventMeasures(measurements);
_ = _wasmSender.SendExceptionAsync(
exception,
severity,
eventProperties,
eventMeasurements,
_commonProperties![TelemetryCommonProperties.MachineId],
_currentSessionId);
}
catch (Exception e)
{
Debug.Fail(e.ToString());
}
return;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue for exceptions: _ = _wasmSender.SendExceptionAsync(...) is not awaited, so exception telemetry sending is no longer sequenced by _trackEventTask, and Flush/FlushAsync may return before exception sends complete. Make the send operation part of the chained task to preserve ordering and flush behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +47
[JSImport("globalThis.unoDevToolsGetOrCreateMachineId")]
private static partial string GetMachineIdFromLocalStorage();
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WasmMachineIdHelper imports a global JS function (globalThis.unoDevToolsGetOrCreateMachineId), but the PR only embeds WasmScript/machineId.js as an EmbeddedResource. There’s no code/MSBuild item that ensures this script is actually emitted and executed in the browser, so the JSImport call will likely throw and always fall back to a new GUID. Consider switching to an JSImport module import (with proper module path) and including the JS file as content/WasmScript, or otherwise ensure host apps load/execute the script.

Copilot uses AI. Check for mistakes.
Comment thread docs/usage.md

### Limitations on WASM

- **No persistent storage**: The machine ID is regenerated on each page load since there's no file system access in the browser. This means each browser session gets a unique ID.
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WebAssembly section states the machine ID is “session-specific” and regenerated each page load, but the implementation now tries to persist a GUID via browser localStorage (TelemetryCommonProperties -> WasmMachineIdHelper). Please update the table/limitations to match actual behavior (or adjust the code if session-only is intended).

Suggested change
- **No persistent storage**: The machine ID is regenerated on each page load since there's no file system access in the browser. This means each browser session gets a unique ID.
- **Browser-scoped machine ID**: The machine ID is persisted using browser local storage and is shared across page loads for the same site and browser profile. Clearing site data, using private/incognito mode, or blocking storage will cause a new ID to be generated.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +40
<!-- .NET Runtime/BCL -->
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>E174.1</string>
</array>
</dict>

<!-- NSUserDefaults -->
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This privacy manifest plist is not valid: under <plist> you currently have multiple top-level <dict> elements. Apple privacy manifests require a single root <dict> (typically with NSPrivacyAccessedAPITypes -> array of dicts). As-is, iOS builds / validation tooling may fail when parsing this file.

Copilot uses AI. Check for mistakes.
Comment on lines +209 to +211
// Use session-specific machine ID on WASM (no persistent storage)
_machineIdTcs.TrySetResult(_commonProperties[TelemetryCommonProperties.MachineId]);
return;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment says “Use session-specific machine ID on WASM (no persistent storage)”, but the WASM machine id path now attempts to persist via browser localStorage (TelemetryCommonProperties -> WasmMachineIdHelper). Update the comment (or the implementation) so it reflects actual behavior.

Copilot uses AI. Check for mistakes.
Comment thread docs/usage.md
### Implementation Details

The WASM implementation:
- Detects the browser environment using `RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))`
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WebAssembly section says environment detection uses RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")), but the code uses OperatingSystem.IsBrowser() / OperatingSystem.IsWasi() via PlatformDetection. Update the docs to reflect the actual detection mechanism to avoid confusion when troubleshooting.

Suggested change
- Detects the browser environment using `RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))`
- Detects browser and WASI environments via a `PlatformDetection` helper that uses `OperatingSystem.IsBrowser()` and `OperatingSystem.IsWasi()`

Copilot uses AI. Check for mistakes.
##########################################

[*]
indent_style = space
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

;-(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants