Skip to content

Commit 7a9033a

Browse files
committed
feat: cloudflare workers support
1 parent d299934 commit 7a9033a

35 files changed

Lines changed: 2058 additions & 10 deletions

.github/workflows/ci.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
dotnet-version: |
1515
8.0.x
1616
9.0.x
17+
10.0.x
1718
- name: Install dependencies
1819
run: dotnet restore
1920
- name: Locate MSBuild with vswhere
@@ -36,6 +37,14 @@ jobs:
3637
& $env:MSBUILD AspNetCore.sln /p:Configuration=Debug /p:Platform="Any CPU"
3738
- name: Test Zapto.AspNetCore.NetFx
3839
run: dotnet test --no-restore --no-build --verbosity normal tests/Zapto.AspNetCore.NetFx.Tests/Zapto.AspNetCore.NetFx.Tests.csproj
40+
- name: Setup Node.js
41+
uses: actions/setup-node@v4
42+
with:
43+
node-version: '22'
44+
- name: Install wrangler
45+
run: npm install -g wrangler
46+
- name: Test Zapto.AspNetCore.Wasm
47+
run: dotnet test --verbosity normal tests/Zapto.AspNetCore.Wasm.Tests/Zapto.AspNetCore.Wasm.Tests.csproj
3948
- name: Publish
4049
uses: GerardSmit/publish-nuget@v4.0.2
4150
if: github.ref == 'refs/heads/main'

AspNetCore.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zapto.AspNetCore.NetFx.Test
1414
EndProject
1515
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zapto.AspNetCore.Polyfill", "src\Zapto.AspNetCore.Polyfill\Zapto.AspNetCore.Polyfill.csproj", "{507A2467-52E9-4181-8414-426AC45E01B3}"
1616
EndProject
17+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zapto.AspNetCore.Wasm", "src\Zapto.AspNetCore.Wasm\Zapto.AspNetCore.Wasm.csproj", "{A8E7D4F1-2B3C-4D5E-9F6A-1B2C3D4E5F6A}"
18+
EndProject
19+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SDK", "SDK", "{E1F2A3B4-C5D6-4E7F-8A9B-0C1D2E3F4A5B}"
20+
EndProject
21+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zapto.AspNetCore.CloudFlare.SDK", "sdk\Zapto.AspNetCore.CloudFlare.SDK\Zapto.AspNetCore.CloudFlare.SDK.csproj", "{B1C2D3E4-F5A6-4B7C-8D9E-0A1B2C3D4E5F}"
22+
EndProject
23+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WasmApp", "sandbox\WasmApp\WasmApp.csproj", "{C2D3E4F5-A6B7-4C8D-9E0F-1A2B3C4D5E6F}"
24+
EndProject
25+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zapto.AspNetCore.Wasm.Tests", "tests\Zapto.AspNetCore.Wasm.Tests\Zapto.AspNetCore.Wasm.Tests.csproj", "{D3E4F5A6-B7C8-4D9E-0F1A-2B3C4D5E6F7A}"
26+
EndProject
1727
Global
1828
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1929
Debug|Any CPU = Debug|Any CPU
@@ -24,6 +34,10 @@ Global
2434
{77FA9DA3-FB50-46EC-B9B8-905121594EA1} = {DD6845EA-4012-4E0A-8B78-8BAE5FF6967B}
2535
{305C7374-1A54-4409-B527-968D49B258DF} = {B3DA0F06-4511-4791-BB12-A84655F02BFA}
2636
{507A2467-52E9-4181-8414-426AC45E01B3} = {7F209FD2-1190-4721-9A08-9569AB4AA8D5}
37+
{A8E7D4F1-2B3C-4D5E-9F6A-1B2C3D4E5F6A} = {7F209FD2-1190-4721-9A08-9569AB4AA8D5}
38+
{B1C2D3E4-F5A6-4B7C-8D9E-0A1B2C3D4E5F} = {E1F2A3B4-C5D6-4E7F-8A9B-0C1D2E3F4A5B}
39+
{C2D3E4F5-A6B7-4C8D-9E0F-1A2B3C4D5E6F} = {DD6845EA-4012-4E0A-8B78-8BAE5FF6967B}
40+
{D3E4F5A6-B7C8-4D9E-0F1A-2B3C4D5E6F7A} = {B3DA0F06-4511-4791-BB12-A84655F02BFA}
2741
EndGlobalSection
2842
GlobalSection(ProjectConfigurationPlatforms) = postSolution
2943
{C3CEF02D-43F9-4C28-BC1D-18ED3D1FC0E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -42,5 +56,21 @@ Global
4256
{507A2467-52E9-4181-8414-426AC45E01B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
4357
{507A2467-52E9-4181-8414-426AC45E01B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
4458
{507A2467-52E9-4181-8414-426AC45E01B3}.Release|Any CPU.Build.0 = Release|Any CPU
59+
{A8E7D4F1-2B3C-4D5E-9F6A-1B2C3D4E5F6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
60+
{A8E7D4F1-2B3C-4D5E-9F6A-1B2C3D4E5F6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
61+
{A8E7D4F1-2B3C-4D5E-9F6A-1B2C3D4E5F6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
62+
{A8E7D4F1-2B3C-4D5E-9F6A-1B2C3D4E5F6A}.Release|Any CPU.Build.0 = Release|Any CPU
63+
{B1C2D3E4-F5A6-4B7C-8D9E-0A1B2C3D4E5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
64+
{B1C2D3E4-F5A6-4B7C-8D9E-0A1B2C3D4E5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
65+
{B1C2D3E4-F5A6-4B7C-8D9E-0A1B2C3D4E5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
66+
{B1C2D3E4-F5A6-4B7C-8D9E-0A1B2C3D4E5F}.Release|Any CPU.Build.0 = Release|Any CPU
67+
{C2D3E4F5-A6B7-4C8D-9E0F-1A2B3C4D5E6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68+
{C2D3E4F5-A6B7-4C8D-9E0F-1A2B3C4D5E6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
69+
{C2D3E4F5-A6B7-4C8D-9E0F-1A2B3C4D5E6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
70+
{C2D3E4F5-A6B7-4C8D-9E0F-1A2B3C4D5E6F}.Release|Any CPU.Build.0 = Release|Any CPU
71+
{D3E4F5A6-B7C8-4D9E-0F1A-2B3C4D5E6F7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
72+
{D3E4F5A6-B7C8-4D9E-0F1A-2B3C4D5E6F7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
73+
{D3E4F5A6-B7C8-4D9E-0F1A-2B3C4D5E6F7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
74+
{D3E4F5A6-B7C8-4D9E-0F1A-2B3C4D5E6F7A}.Release|Any CPU.Build.0 = Release|Any CPU
4575
EndGlobalSection
4676
EndGlobal

Directory.Build.props

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project>
2+
3+
<PropertyGroup>
4+
<Version>1.0.0-alpha.2</Version>
5+
<PackageVersion>1.0.0-alpha.2</PackageVersion>
6+
<Authors>Zapto</Authors>
7+
<RepositoryUrl>https://github.com/zapto-dev/AspNetCore</RepositoryUrl>
8+
<Copyright>Copyright © 2025 Zapto</Copyright>
9+
<PackageTags>zapto, aspnetcore</PackageTags>
10+
<PackageProjectUrl>https://github.com/zapto-dev/AspNetCore</PackageProjectUrl>
11+
<PackageLicenseUrl>https://github.com/zapto-dev/AspNetCore/blob/main/LICENSE</PackageLicenseUrl>
12+
</PropertyGroup>
13+
14+
</Project>

nuget.config

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<packageSources>
4+
<clear />
5+
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
6+
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
7+
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
8+
</packageSources>
9+
</configuration>

sandbox/WasmApp/Program.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System.Text.Json.Serialization;
2+
using Microsoft.AspNetCore.Builder;
3+
using Microsoft.AspNetCore.Hosting;
4+
using Microsoft.AspNetCore.Http;
5+
using Microsoft.Extensions.DependencyInjection;
6+
7+
var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions());
8+
9+
builder.Services.AddRouting();
10+
11+
builder.Services.ConfigureHttpJsonOptions(options =>
12+
{
13+
options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
14+
});
15+
16+
builder.WebHost.UseWasmServer(options =>
17+
{
18+
options.IncludeExceptionDetails = true;
19+
});
20+
21+
var app = builder.Build();
22+
23+
app.Use(async (context, next) =>
24+
{
25+
try
26+
{
27+
await next();
28+
}
29+
catch (Exception ex)
30+
{
31+
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
32+
context.Response.ContentType = "text/plain";
33+
await context.Response.WriteAsync(ex.ToString());
34+
}
35+
});
36+
37+
app.UseRouting();
38+
39+
app.MapGet("/", () => "Hello from ASP.NET Core on Cloudflare Workers!");
40+
41+
app.MapGet("/api/time", () =>
42+
{
43+
return Results.Json(new TimeResponse(DateTime.UtcNow.ToString("O")), AppJsonContext.Default.TimeResponse);
44+
});
45+
46+
app.MapGet("/api/greet/{name}", (string name) => $"Hello, {name}!");
47+
48+
app.MapPost("/api/echo", async context =>
49+
{
50+
using var reader = new StreamReader(context.Request.Body);
51+
var body = await reader.ReadToEndAsync(context.RequestAborted);
52+
await context.Response.WriteAsync($"Echo: {body}");
53+
});
54+
55+
await app.StartAsync();
56+
57+
internal record TimeResponse([property: JsonPropertyName("time")] string Time);
58+
59+
[JsonSerializable(typeof(TimeResponse))]
60+
internal partial class AppJsonContext : JsonSerializerContext;

sandbox/WasmApp/WasmApp.csproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<!-- Import SDK props -->
3+
<Import Project="..\..\sdk\Zapto.AspNetCore.CloudFlare.SDK\Sdk\Sdk.props" />
4+
5+
<PropertyGroup>
6+
<!-- For local development, reference the project directly -->
7+
<ZaptoWasmUseProjectReference>true</ZaptoWasmUseProjectReference>
8+
</PropertyGroup>
9+
10+
<!-- Import SDK targets -->
11+
<Import Project="..\..\sdk\Zapto.AspNetCore.CloudFlare.SDK\Sdk\Sdk.targets" />
12+
</Project>
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<Project>
2+
3+
<PropertyGroup>
4+
<!-- Target .NET 10 for WASM -->
5+
<TargetFramework>net10.0</TargetFramework>
6+
7+
<!-- Build as an executable to support top-level entry points -->
8+
<OutputType>Exe</OutputType>
9+
10+
<!-- Enable unsafe code for interop -->
11+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
12+
</PropertyGroup>
13+
14+
<!-- Cloudflare Wrangler configuration defaults -->
15+
<PropertyGroup>
16+
<!-- Worker name: defaults to assembly name converted to kebab-case (e.g., Hello.World -> hello-world) -->
17+
<_WranglerNameIsDefault Condition="'$(WranglerName)' == ''">true</_WranglerNameIsDefault>
18+
<WranglerName Condition="'$(WranglerName)' == ''">$(AssemblyName)</WranglerName>
19+
<!-- Main entry point -->
20+
<WranglerMain Condition="'$(WranglerMain)' == ''">index.js</WranglerMain>
21+
<!-- Compatibility date -->
22+
<WranglerCompatibilityDate Condition="'$(WranglerCompatibilityDate)' == ''">2026-01-01</WranglerCompatibilityDate>
23+
<!-- Compatibility flags (comma-separated, will be quoted in TOML) -->
24+
<WranglerCompatibilityFlags Condition="'$(WranglerCompatibilityFlags)' == ''">nodejs_compat</WranglerCompatibilityFlags>
25+
<!-- Development server port -->
26+
<WranglerDevPort Condition="'$(WranglerDevPort)' == ''">8787</WranglerDevPort>
27+
<!-- Development server protocol -->
28+
<WranglerDevProtocol Condition="'$(WranglerDevProtocol)' == ''">http</WranglerDevProtocol>
29+
</PropertyGroup>
30+
31+
<PropertyGroup>
32+
<UseAppHost>false</UseAppHost>
33+
<DotNetJsApi>true</DotNetJsApi>
34+
<UsingBrowserRuntimeWorkload>false</UsingBrowserRuntimeWorkload>
35+
<EmccFlags>$(EmccFlags) -O3</EmccFlags>
36+
37+
<!-- NativeAOT LLVM settings (applied during publish) -->
38+
<PublishTrimmed>true</PublishTrimmed>
39+
<SelfContained>true</SelfContained>
40+
<NativeLib Condition="'$(OutputType)' == 'Library'">Shared</NativeLib>
41+
42+
<!-- Enable workload resolver to pick up EmscriptenSdkToolsPath -->
43+
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
44+
<EmitLegacyAssetsFileItems>true</EmitLegacyAssetsFileItems>
45+
46+
<!-- Runtime identifier for browser-wasm -->
47+
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
48+
49+
<!-- Target architecture -->
50+
<TargetArchitecture>wasm</TargetArchitecture>
51+
<PlatformTarget>AnyCPU</PlatformTarget>
52+
53+
<!-- Globalization -->
54+
<InvariantGlobalization>true</InvariantGlobalization>
55+
<InvariantTimezone>true</InvariantTimezone>
56+
<PredefinedCulturesOnly>true</PredefinedCulturesOnly>
57+
58+
<!-- ILC (IL Compiler) settings for size optimization -->
59+
<IlcTrimMetadata>true</IlcTrimMetadata>
60+
<IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
61+
62+
<!-- Trimming and feature switches for minimal output -->
63+
<TrimMode>full</TrimMode>
64+
<TrimmerRemoveSymbols>true</TrimmerRemoveSymbols>
65+
<_AggressiveAttributeTrimming>true</_AggressiveAttributeTrimming>
66+
<AutoreleasePoolSupport>false</AutoreleasePoolSupport>
67+
<DebuggerSupport>false</DebuggerSupport>
68+
<EnableCppCLIHostActivation>false</EnableCppCLIHostActivation>
69+
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
70+
<EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization>false</EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization>
71+
<EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
72+
<_EnableConsumingManagedCodeFromNativeHosting>false</_EnableConsumingManagedCodeFromNativeHosting>
73+
<EventSourceSupport>false</EventSourceSupport>
74+
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
75+
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
76+
<UseNativeHttpHandler>true</UseNativeHttpHandler>
77+
<UseSystemResourceKeys>true</UseSystemResourceKeys>
78+
<StartupHookSupport>false</StartupHookSupport>
79+
<CustomResourceTypesSupport>false</CustomResourceTypesSupport>
80+
<BuiltInComInteropSupport>false</BuiltInComInteropSupport>
81+
<WasmEmitSourceMap>false</WasmEmitSourceMap>
82+
<WasmNativeDebugSymbols>false</WasmNativeDebugSymbols>
83+
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
84+
<MetricsSupport>false</MetricsSupport>
85+
<DisableDependencyInjectionDynamicEngine>true</DisableDependencyInjectionDynamicEngine>
86+
<NullabilityInfoContextSupport>false</NullabilityInfoContextSupport>
87+
<DynamicCodeSupport>false</DynamicCodeSupport>
88+
<XmlResolverIsNetworkingEnabledByDefault>false</XmlResolverIsNetworkingEnabledByDefault>
89+
<StackTraceSupport>false</StackTraceSupport>
90+
<DebugType>none</DebugType>
91+
92+
<!-- Emscripten arguments -->
93+
<EmccExtraArgs>-s EXPORTED_RUNTIME_METHODS=cwrap -s ENVIRONMENT=webview -s EXPORT_ES6=1 -s ASSERTIONS=0</EmccExtraArgs>
94+
95+
<!-- Implicit usings and nullable -->
96+
<ImplicitUsings>enable</ImplicitUsings>
97+
<Nullable>enable</Nullable>
98+
99+
<!-- Avoid requiring a repo-level nuget.config -->
100+
<RestoreAdditionalProjectSources>
101+
https://api.nuget.org/v3/index.json;
102+
https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json;
103+
</RestoreAdditionalProjectSources>
104+
</PropertyGroup>
105+
</Project>

0 commit comments

Comments
 (0)