diff --git a/DevProxy.Abstractions/DevProxy.Abstractions.csproj b/DevProxy.Abstractions/DevProxy.Abstractions.csproj
index d1d1c557c..d02c78db3 100644
--- a/DevProxy.Abstractions/DevProxy.Abstractions.csproj
+++ b/DevProxy.Abstractions/DevProxy.Abstractions.csproj
@@ -5,7 +5,7 @@
DevProxy.Abstractions
enable
enable
- 4.0.0
+ 4.1.0
false
true
true
diff --git a/DevProxy.Abstractions/Models/MockResponse.cs b/DevProxy.Abstractions/Models/MockResponse.cs
index fd0c0e5e0..bae12703c 100644
--- a/DevProxy.Abstractions/Models/MockResponse.cs
+++ b/DevProxy.Abstractions/Models/MockResponse.cs
@@ -12,6 +12,12 @@ public class MockResponse : ICloneable
{
public MockResponseRequest? Request { get; set; }
public MockResponseResponse? Response { get; set; }
+ ///
+ /// Probability (0-100) that the mock is applied when it matches a request.
+ /// When not set, the mock is always applied. When the mock isn't applied,
+ /// the request falls through to the next matching mock or the real API.
+ ///
+ public int? Rate { get; set; }
public object Clone()
{
diff --git a/DevProxy.Integration.Tests/ManipulationAndMockingIntegrationTests.cs b/DevProxy.Integration.Tests/ManipulationAndMockingIntegrationTests.cs
index d2740434a..12a90184e 100644
--- a/DevProxy.Integration.Tests/ManipulationAndMockingIntegrationTests.cs
+++ b/DevProxy.Integration.Tests/ManipulationAndMockingIntegrationTests.cs
@@ -452,6 +452,171 @@ public async Task MockResponse_Nth_DistinctMatchersHaveIndependentCounts(
Assert.Equal(HttpStatusCode.Accepted, third.StatusCode);
}
+ [Theory]
+ [InlineData(null)]
+ [InlineData("100")]
+ [InlineData("-5")]
+ [InlineData("150")]
+ public async Task MockResponse_Rate_AlwaysAppliesWhenAbsentFullOrInvalid(string? rate)
+ {
+ await using var origin = await FakeOrigin.StartAsync();
+ var urls = KestrelProxyHarness.BuildUrlsToWatch(origin.Host);
+ var config = PluginConfig.FromJson($$"""
+ {
+ "mocks": [
+ {
+ "request": { "url": "http://{{origin.Host}}/get", "method": "GET" },
+ "response": { "statusCode": 201 },
+ "rate": {{rate ?? "null"}}
+ }
+ ]
+ }
+ """);
+ using var plugin = new MockResponsePlugin(
+ SharedHttpClient, NullLogger.Instance, urls, ProxyConfig, config);
+ await using var proxy = await KestrelProxyHarness.StartAsync(origin.Host, [plugin]);
+ using var client = proxy.CreateHttpClient();
+ var uri = new Uri($"http://{origin.Host}/get");
+
+ for (var i = 0; i < 10; i++)
+ {
+ using var response = await client.GetAsync(uri);
+ Assert.Equal(HttpStatusCode.Created, response.StatusCode);
+ }
+ }
+
+ [Fact]
+ public async Task MockResponse_Rate0_PassesThroughToOrigin()
+ {
+ await using var origin = await FakeOrigin.StartAsync();
+ var urls = KestrelProxyHarness.BuildUrlsToWatch(origin.Host);
+ var config = PluginConfig.FromJson($$"""
+ {
+ "mocks": [
+ {
+ "request": { "url": "http://{{origin.Host}}/get", "method": "GET" },
+ "response": { "statusCode": 503 },
+ "rate": 0
+ }
+ ]
+ }
+ """);
+ using var plugin = new MockResponsePlugin(
+ SharedHttpClient, NullLogger.Instance, urls, ProxyConfig, config);
+ await using var proxy = await KestrelProxyHarness.StartAsync(origin.Host, [plugin]);
+ using var client = proxy.CreateHttpClient();
+ var uri = new Uri($"http://{origin.Host}/get");
+
+ for (var i = 0; i < 10; i++)
+ {
+ using var response = await client.GetAsync(uri);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+ }
+
+ [Fact]
+ public async Task MockResponse_Rate0_FallsThroughToNextMatchingMock()
+ {
+ await using var origin = await FakeOrigin.StartAsync();
+ var urls = KestrelProxyHarness.BuildUrlsToWatch(origin.Host);
+ var config = PluginConfig.FromJson($$"""
+ {
+ "mocks": [
+ {
+ "request": { "url": "http://{{origin.Host}}/get", "method": "GET" },
+ "response": { "statusCode": 503 },
+ "rate": 0
+ },
+ {
+ "request": { "url": "http://{{origin.Host}}/get", "method": "GET" },
+ "response": { "statusCode": 202 }
+ }
+ ]
+ }
+ """);
+ using var plugin = new MockResponsePlugin(
+ SharedHttpClient, NullLogger.Instance, urls, ProxyConfig, config);
+ await using var proxy = await KestrelProxyHarness.StartAsync(origin.Host, [plugin]);
+ using var client = proxy.CreateHttpClient();
+ var uri = new Uri($"http://{origin.Host}/get");
+
+ for (var i = 0; i < 10; i++)
+ {
+ using var response = await client.GetAsync(uri);
+ Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
+ }
+ }
+
+ [Fact]
+ public async Task MockResponse_Rate0_WithNth_IsNeverApplied()
+ {
+ await using var origin = await FakeOrigin.StartAsync();
+ var urls = KestrelProxyHarness.BuildUrlsToWatch(origin.Host);
+ var config = PluginConfig.FromJson($$"""
+ {
+ "mocks": [
+ {
+ "request": { "url": "http://{{origin.Host}}/get", "method": "GET", "nth": 2 },
+ "response": { "statusCode": 201 },
+ "rate": 0
+ },
+ {
+ "request": { "url": "http://{{origin.Host}}/get", "method": "GET" },
+ "response": { "statusCode": 202 }
+ }
+ ]
+ }
+ """);
+ using var plugin = new MockResponsePlugin(
+ SharedHttpClient, NullLogger.Instance, urls, ProxyConfig, config);
+ await using var proxy = await KestrelProxyHarness.StartAsync(origin.Host, [plugin]);
+ using var client = proxy.CreateHttpClient();
+ var uri = new Uri($"http://{origin.Host}/get");
+
+ for (var i = 0; i < 3; i++)
+ {
+ using var response = await client.GetAsync(uri);
+ Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
+ }
+ }
+
+ [Fact]
+ public async Task MockResponse_PartialRate_AppliesSomeOfTheTime()
+ {
+ await using var origin = await FakeOrigin.StartAsync();
+ var urls = KestrelProxyHarness.BuildUrlsToWatch(origin.Host);
+ var config = PluginConfig.FromJson($$"""
+ {
+ "mocks": [
+ {
+ "request": { "url": "http://{{origin.Host}}/get", "method": "GET" },
+ "response": { "statusCode": 503 },
+ "rate": 50
+ },
+ {
+ "request": { "url": "http://{{origin.Host}}/get", "method": "GET" },
+ "response": { "statusCode": 202 }
+ }
+ ]
+ }
+ """);
+ using var plugin = new MockResponsePlugin(
+ SharedHttpClient, NullLogger.Instance, urls, ProxyConfig, config);
+ await using var proxy = await KestrelProxyHarness.StartAsync(origin.Host, [plugin]);
+ using var client = proxy.CreateHttpClient();
+ var uri = new Uri($"http://{origin.Host}/get");
+
+ var statusCodes = new HashSet();
+ // the chance of 100 rolls at 50% landing on the same side is ~2^-99
+ for (var i = 0; i < 100; i++)
+ {
+ using var response = await client.GetAsync(uri);
+ _ = statusCodes.Add(response.StatusCode);
+ }
+
+ Assert.Equal([HttpStatusCode.Accepted, HttpStatusCode.ServiceUnavailable], statusCodes.Order());
+ }
+
[Theory]
[InlineData("/Get")]
[InlineData("/Ge*")]
diff --git a/DevProxy.Plugins/DevProxy.Plugins.csproj b/DevProxy.Plugins/DevProxy.Plugins.csproj
index 5fde4dd2f..70048f737 100644
--- a/DevProxy.Plugins/DevProxy.Plugins.csproj
+++ b/DevProxy.Plugins/DevProxy.Plugins.csproj
@@ -6,7 +6,7 @@
enable
true
OnOutputUpdated
- 4.0.0
+ 4.1.0
false
true
true
diff --git a/DevProxy.Plugins/Mocking/MockResponsePlugin.cs b/DevProxy.Plugins/Mocking/MockResponsePlugin.cs
index 80f9fa853..22e36678a 100644
--- a/DevProxy.Plugins/Mocking/MockResponsePlugin.cs
+++ b/DevProxy.Plugins/Mocking/MockResponsePlugin.cs
@@ -35,7 +35,7 @@ public sealed class MockResponseConfiguration
[JsonIgnore]
public bool NoMocks { get; set; }
[JsonPropertyName("$schema")]
- public string Schema { get; set; } = "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/mockresponseplugin.mocksfile.schema.json";
+ public string Schema { get; set; } = "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/mockresponseplugin.mocksfile.schema.json";
}
public class MockResponsePlugin(
@@ -308,8 +308,32 @@ Configuration.Mocks is null ||
}
return matchingMocks.FirstOrDefault(mockResponse =>
- mockResponse.Request!.Nth is null ||
- mockResponse.Request.Nth == requestCounts[GetMockKey(mockResponse)]);
+ (mockResponse.Request!.Nth is null ||
+ mockResponse.Request.Nth == requestCounts[GetMockKey(mockResponse)]) &&
+ ShouldApplyMock(mockResponse));
+ }
+
+ internal static bool IsValidRate(int rate) => rate is >= 0 and <= 100;
+
+ private bool ShouldApplyMock(MockResponse mockResponse)
+ {
+ if (mockResponse.Rate is not { } rate || !IsValidRate(rate))
+ {
+ return true;
+ }
+
+ var roll = Random.Shared.Next(1, 101);
+ if (roll <= rate)
+ {
+ return true;
+ }
+
+ Logger.LogDebug(
+ "Skipping mock for {Method} {Url} based on its rate of {Rate}%",
+ mockResponse.Request!.Method,
+ mockResponse.Request.Url,
+ rate);
+ return false;
}
private static MockRequestKey GetMockKey(MockResponse mockResponse)
diff --git a/DevProxy.Plugins/Mocking/MockResponsesLoader.cs b/DevProxy.Plugins/Mocking/MockResponsesLoader.cs
index 8a9d31fa7..bb56e1167 100644
--- a/DevProxy.Plugins/Mocking/MockResponsesLoader.cs
+++ b/DevProxy.Plugins/Mocking/MockResponsesLoader.cs
@@ -34,6 +34,15 @@ protected override void LoadData(string fileContents)
{
Logger.LogInformation("Mock responses for {ConfigResponseCount} url patterns loaded from {MockFile}", configResponses.Count(), _configuration.MocksFile);
}
+
+ foreach (var mock in configResponses.Where(m => m.Rate is not null && !MockResponsePlugin.IsValidRate(m.Rate.Value)))
+ {
+ Logger.LogWarning(
+ "Mock for {Method} {Url} has an invalid rate {Rate}. Rate must be between 0 and 100. Ignoring rate and always applying the mock",
+ mock.Request?.Method,
+ mock.Request?.Url,
+ mock.Rate);
+ }
}
}
catch (Exception ex)
diff --git a/DevProxy.Plugins/Mocking/MockStdioResponsePlugin.cs b/DevProxy.Plugins/Mocking/MockStdioResponsePlugin.cs
index 7dc4b4a43..d1455ea6f 100644
--- a/DevProxy.Plugins/Mocking/MockStdioResponsePlugin.cs
+++ b/DevProxy.Plugins/Mocking/MockStdioResponsePlugin.cs
@@ -32,7 +32,7 @@ public sealed class MockStdioResponseConfiguration
public bool NoMocks { get; set; }
[JsonPropertyName("$schema")]
- public string Schema { get; set; } = "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/mockstdioresponseplugin.mocksfile.schema.json";
+ public string Schema { get; set; } = "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/mockstdioresponseplugin.mocksfile.schema.json";
}
public class MockStdioResponsePlugin(
diff --git a/DevProxy.Plugins/Mocking/WebSocketMockResponsePlugin.cs b/DevProxy.Plugins/Mocking/WebSocketMockResponsePlugin.cs
index 833484303..d068a7581 100644
--- a/DevProxy.Plugins/Mocking/WebSocketMockResponsePlugin.cs
+++ b/DevProxy.Plugins/Mocking/WebSocketMockResponsePlugin.cs
@@ -28,7 +28,7 @@ public sealed class WebSocketMockResponseConfiguration
public bool NoMocks { get; set; }
[JsonPropertyName("$schema")]
- public string Schema { get; set; } = "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/websocketmockresponseplugin.mocksfile.schema.json";
+ public string Schema { get; set; } = "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/websocketmockresponseplugin.mocksfile.schema.json";
}
///
diff --git a/DevProxy.Proxy.Kestrel/DevProxy.Proxy.Kestrel.csproj b/DevProxy.Proxy.Kestrel/DevProxy.Proxy.Kestrel.csproj
index a323735bf..8ff151f71 100644
--- a/DevProxy.Proxy.Kestrel/DevProxy.Proxy.Kestrel.csproj
+++ b/DevProxy.Proxy.Kestrel/DevProxy.Proxy.Kestrel.csproj
@@ -5,7 +5,7 @@
DevProxy.Proxy.Kestrel
enable
enable
- 4.0.0
+ 4.1.0
false
true
true
diff --git a/DevProxy/DevProxy.csproj b/DevProxy/DevProxy.csproj
index 61603a35a..14b13524c 100644
--- a/DevProxy/DevProxy.csproj
+++ b/DevProxy/DevProxy.csproj
@@ -8,7 +8,7 @@
enable
LICENSE
Dev Proxy
- 4.0.0
+ 4.1.0
.NET Foundation
Dev Proxy
devproxy
diff --git a/DevProxy/config-templates/devproxyrc.json b/DevProxy/config-templates/devproxyrc.json
index 1bcebe333..b2160fa96 100644
--- a/DevProxy/config-templates/devproxyrc.json
+++ b/DevProxy/config-templates/devproxyrc.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/rc.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/rc.schema.json",
"plugins": [
],
"urlsToWatch": [
diff --git a/DevProxy/config-templates/devproxyrc.yaml b/DevProxy/config-templates/devproxyrc.yaml
index e16fc8af1..08068e5ee 100644
--- a/DevProxy/config-templates/devproxyrc.yaml
+++ b/DevProxy/config-templates/devproxyrc.yaml
@@ -1,4 +1,4 @@
-# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/rc.schema.json
+# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/rc.schema.json
plugins:
-
urlsToWatch:
diff --git a/DevProxy/config/m365-mocks.json b/DevProxy/config/m365-mocks.json
index b252737e4..d94950165 100644
--- a/DevProxy/config/m365-mocks.json
+++ b/DevProxy/config/m365-mocks.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/mockresponseplugin.mocksfile.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/mockresponseplugin.mocksfile.schema.json",
"mocks": [
{
"request": {
diff --git a/DevProxy/config/m365.json b/DevProxy/config/m365.json
index 3f96a2228..a81144a0b 100644
--- a/DevProxy/config/m365.json
+++ b/DevProxy/config/m365.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/rc.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/rc.schema.json",
"plugins": [
{
"name": "DevToolsPlugin",
@@ -173,11 +173,11 @@
"https://*.sharepoint-df.*/*_vti_bin/*"
],
"mocksPlugin": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/mockresponseplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/mockresponseplugin.schema.json",
"mocksFile": "m365-mocks.json"
},
"graphRandomErrorsPlugin": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/graphrandomerrorplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/graphrandomerrorplugin.schema.json",
"allowedErrors": [
429,
500,
@@ -189,28 +189,28 @@
"rate": 50
},
"executionSummaryPlugin": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/executionsummaryplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/executionsummaryplugin.schema.json",
"groupBy": "url"
},
"graphMinimalPermissionsPlugin": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/graphminimalpermissionsplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/graphminimalpermissionsplugin.schema.json",
"type": "delegated"
},
"cachingGuidance": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/cachingguidanceplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/cachingguidanceplugin.schema.json",
"cacheThresholdSeconds": 5
},
"latencyPlugin": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/latencyplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/latencyplugin.schema.json",
"minMs": 200,
"maxMs": 10000
},
"devTools": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/devtoolsplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/devtoolsplugin.schema.json",
"preferredBrowser": "Edge"
},
"rateLimiting": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/ratelimitingplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/ratelimitingplugin.schema.json",
"costPerRequest": 2,
"rateLimit": 120,
"resetTimeWindowSeconds": 5
diff --git a/DevProxy/config/microsoft-graph-rate-limiting.json b/DevProxy/config/microsoft-graph-rate-limiting.json
index f2b4fe51c..a78eb41ff 100644
--- a/DevProxy/config/microsoft-graph-rate-limiting.json
+++ b/DevProxy/config/microsoft-graph-rate-limiting.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/rc.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/rc.schema.json",
"plugins": [
{
"name": "RateLimitingPlugin",
diff --git a/DevProxy/config/microsoft-graph.json b/DevProxy/config/microsoft-graph.json
index ccad5b7c3..bd8c9c82c 100644
--- a/DevProxy/config/microsoft-graph.json
+++ b/DevProxy/config/microsoft-graph.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/rc.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/rc.schema.json",
"plugins": [
{
"name": "GraphSelectGuidancePlugin",
@@ -67,7 +67,7 @@
"https://microsoftgraph.chinacloudapi.cn/beta/*"
],
"graphRandomErrorsPlugin": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/graphrandomerrorplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/graphrandomerrorplugin.schema.json",
"allowedErrors": [
429,
500,
@@ -79,7 +79,7 @@
"rate": 50
},
"executionSummaryPlugin": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/executionsummaryplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/executionsummaryplugin.schema.json",
"groupBy": "url"
},
"labelMode": "text",
diff --git a/DevProxy/config/spo-csom-types.json b/DevProxy/config/spo-csom-types.json
index 528502d16..455fc3a86 100644
--- a/DevProxy/config/spo-csom-types.json
+++ b/DevProxy/config/spo-csom-types.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/minimalcsompermissions.types.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/minimalcsompermissions.types.schema.json",
"types": {
"268004ae-ef6b-4e9b-8425-127220d84719": "Microsoft.Online.SharePoint.TenantAdministration.Tenant",
"3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a": "Microsoft.SharePoint.Client.RequestContext"
diff --git a/DevProxy/devproxy-errors.json b/DevProxy/devproxy-errors.json
index 6200efe12..40fdb285a 100644
--- a/DevProxy/devproxy-errors.json
+++ b/DevProxy/devproxy-errors.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/genericrandomerrorplugin.errorsfile.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/genericrandomerrorplugin.errorsfile.schema.json",
"errors": [
{
"request": {
diff --git a/DevProxy/devproxy-errors.yaml b/DevProxy/devproxy-errors.yaml
index 0a50f9932..ee8ab02b6 100644
--- a/DevProxy/devproxy-errors.yaml
+++ b/DevProxy/devproxy-errors.yaml
@@ -1,4 +1,4 @@
-# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/genericrandomerrorplugin.errorsfile.schema.json
+# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/genericrandomerrorplugin.errorsfile.schema.json
# Dev Proxy error responses configuration
# This YAML version serves as a reference example alongside the default JSON configuration.
# It demonstrates YAML features like comments, multiline strings, and anchors.
diff --git a/DevProxy/devproxyrc.json b/DevProxy/devproxyrc.json
index dcc528f07..f9a75243b 100644
--- a/DevProxy/devproxyrc.json
+++ b/DevProxy/devproxyrc.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/rc.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/rc.schema.json",
"plugins": [
{
"name": "RetryAfterPlugin",
@@ -17,7 +17,7 @@
"https://jsonplaceholder.typicode.com/*"
],
"genericRandomErrorPlugin": {
- "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/genericrandomerrorplugin.schema.json",
+ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/genericrandomerrorplugin.schema.json",
"errorsFile": "devproxy-errors.json",
"rate": 50
},
diff --git a/DevProxy/devproxyrc.yaml b/DevProxy/devproxyrc.yaml
index c93076bda..0b0b57033 100644
--- a/DevProxy/devproxyrc.yaml
+++ b/DevProxy/devproxyrc.yaml
@@ -1,4 +1,4 @@
-# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/rc.schema.json
+# yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/rc.schema.json
# Dev Proxy configuration file
# This YAML version serves as a reference example alongside the default JSON configuration.
@@ -16,7 +16,7 @@ urlsToWatch:
- "https://jsonplaceholder.typicode.com/*"
genericRandomErrorPlugin:
- # yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.0.0/genericrandomerrorplugin.schema.json
+ # yaml-language-server: $schema=https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/genericrandomerrorplugin.schema.json
errorsFile: devproxy-errors.yaml
rate: 50
diff --git a/DevProxy/packages.lock.json b/DevProxy/packages.lock.json
index 4fe7fca45..6104bd1f5 100644
--- a/DevProxy/packages.lock.json
+++ b/DevProxy/packages.lock.json
@@ -346,7 +346,7 @@
"devproxy.proxy.kestrel": {
"type": "Project",
"dependencies": {
- "DevProxy.Abstractions": "[4.0.0, )"
+ "DevProxy.Abstractions": "[4.1.0, )"
}
}
}
diff --git a/Dockerfile b/Dockerfile
index 1aeaa6acc..b01a19ffa 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
FROM ubuntu:26.04
-ARG DEVPROXY_VERSION=4.0.0
+ARG DEVPROXY_VERSION=4.1.0
ARG USERNAME=devproxy
ENV DEVPROXY_VERSION=${DEVPROXY_VERSION}
diff --git a/Dockerfile_beta b/Dockerfile_beta
index ba16404ce..eb2af71ef 100644
--- a/Dockerfile_beta
+++ b/Dockerfile_beta
@@ -1,6 +1,6 @@
FROM ubuntu:26.04
-ARG DEVPROXY_VERSION=4.0.0
+ARG DEVPROXY_VERSION=4.1.0
ARG USERNAME=devproxy
ENV DEVPROXY_VERSION=${DEVPROXY_VERSION}
diff --git a/install-beta.iss b/install-beta.iss
index 4cb02da6b..f606cbf63 100644
--- a/install-beta.iss
+++ b/install-beta.iss
@@ -3,8 +3,8 @@
#define MyAppName "Dev Proxy Beta"
; for local use only. In production replaced by a command line arg
-#define MyAppSetupExeName "dev-proxy-installer-win-x64-4.0.0-beta.1"
-#define MyAppVersion "4.0.0-beta.1"
+#define MyAppSetupExeName "dev-proxy-installer-win-x64-4.1.0-beta.1"
+#define MyAppVersion "4.1.0-beta.1"
#define MyAppPublisher ".NET Foundation"
#define MyAppURL "https://aka.ms/devproxy"
#define DevProxyExecutable "devproxy-beta.exe"
diff --git a/install.iss b/install.iss
index 14f8143b6..4845c0167 100644
--- a/install.iss
+++ b/install.iss
@@ -3,8 +3,8 @@
#define MyAppName "Dev Proxy"
; for local use only. In production replaced by a command line arg
-#define MyAppSetupExeName "dev-proxy-installer-win-x64-4.0.0"
-#define MyAppVersion "4.0.0"
+#define MyAppSetupExeName "dev-proxy-installer-win-x64-4.1.0"
+#define MyAppVersion "4.1.0"
#define MyAppPublisher ".NET Foundation"
#define MyAppURL "https://aka.ms/devproxy"
#define DevProxyExecutable "devproxy.exe"
diff --git a/schemas/v4.1.0/apicenterminimalpermissionsplugin.schema.json b/schemas/v4.1.0/apicenterminimalpermissionsplugin.schema.json
new file mode 100644
index 000000000..2ff95ae65
--- /dev/null
+++ b/schemas/v4.1.0/apicenterminimalpermissionsplugin.schema.json
@@ -0,0 +1,46 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy ApiCenterMinimalPermissionsPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "permissionsToExclude": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "The scopes to ignore and not include in the report. Default: ['profile', 'openid', 'offline_access', 'email'].",
+ "default": ["profile", "openid", "offline_access", "email"]
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "description": "Name of the resource group where the Azure API Center is located."
+ },
+ "serviceName": {
+ "type": "string",
+ "description": "Name of the Azure API Center instance that Dev Proxy should use to check if the APIs used in the app are registered."
+ },
+ "subscriptionId": {
+ "type": "string",
+ "description": "ID of the Azure subscription where the Azure API Center instance is located."
+ },
+ "workspaceName": {
+ "type": "string",
+ "description": "Name of the Azure API Center workspace to use. Default is 'default'.",
+ "default": "default"
+ },
+ "schemeName": {
+ "type": "string",
+ "description": "The name of the security scheme definition. Used to determine minimal permissions required for API calls."
+ }
+ },
+ "required": [
+ "resourceGroupName",
+ "serviceName",
+ "subscriptionId"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/apicenteronboardingplugin.schema.json b/schemas/v4.1.0/apicenteronboardingplugin.schema.json
new file mode 100644
index 000000000..35ce03d26
--- /dev/null
+++ b/schemas/v4.1.0/apicenteronboardingplugin.schema.json
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy ApiCenterOnboardingPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "createApicEntryForNewApis": {
+ "type": "boolean",
+ "description": "Set to true to have Dev Proxy create new API entries for APIs detected but not yet registered in API Center. When false, Dev Proxy only lists unregistered APIs. Default is true."
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "description": "Name of the resource group where the Azure API Center is located."
+ },
+ "serviceName": {
+ "type": "string",
+ "description": "Name of the Azure API Center instance that Dev Proxy should use to check if the APIs used in the app are registered."
+ },
+ "subscriptionId": {
+ "type": "string",
+ "description": "ID of the Azure subscription where the Azure API Center instance is located."
+ },
+ "workspaceName": {
+ "type": "string",
+ "description": "Name of the Azure API Center workspace to use. Default is 'default'."
+ }
+ },
+ "required": [
+ "resourceGroupName",
+ "serviceName",
+ "subscriptionId"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/apicenterproductionversionplugin.schema.json b/schemas/v4.1.0/apicenterproductionversionplugin.schema.json
new file mode 100644
index 000000000..5caacc503
--- /dev/null
+++ b/schemas/v4.1.0/apicenterproductionversionplugin.schema.json
@@ -0,0 +1,33 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy ApiCenterProductionVersionPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "description": "Name of the resource group where the Azure API Center is located."
+ },
+ "serviceName": {
+ "type": "string",
+ "description": "Name of the Azure API Center instance that Dev Proxy should use to check if the APIs used in the app are registered."
+ },
+ "subscriptionId": {
+ "type": "string",
+ "description": "ID of the Azure subscription where the Azure API Center instance is located."
+ },
+ "workspaceName": {
+ "type": "string",
+ "description": "Name of the Azure API Center workspace to use. Default is 'default'."
+ }
+ },
+ "required": [
+ "resourceGroupName",
+ "serviceName",
+ "subscriptionId"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/authplugin.schema.json b/schemas/v4.1.0/authplugin.schema.json
new file mode 100644
index 000000000..900d07aeb
--- /dev/null
+++ b/schemas/v4.1.0/authplugin.schema.json
@@ -0,0 +1,133 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy AuthPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "apiKey": {
+ "type": "object",
+ "description": "Configuration for API key authentication and authorization.",
+ "properties": {
+ "allowedKeys": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of allowed API keys."
+ },
+ "parameters": {
+ "type": "array",
+ "description": "List of parameters that contain the API key.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "in": {
+ "type": "string",
+ "enum": [
+ "header",
+ "query",
+ "cookie"
+ ],
+ "description": "Where the parameter is expected to be found. Allowed values: header, query, cookie."
+ },
+ "name": {
+ "type": "string",
+ "description": "Name of the parameter."
+ }
+ },
+ "required": [
+ "in",
+ "name"
+ ]
+ }
+ }
+ },
+ "required": [
+ "allowedKeys",
+ "parameters"
+ ]
+ },
+ "oauth2": {
+ "type": "object",
+ "description": "Configuration for OAuth2 authentication and authorization.",
+ "properties": {
+ "metadataUrl": {
+ "type": "string",
+ "description": "URL to the OpenID Connect metadata document."
+ },
+ "allowedApplications": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of allowed application IDs. Leave empty to not validate the application (appid or azp claim) for which the token is issued."
+ },
+ "allowedAudiences": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of allowed audiences. Leave empty to not validate the audience (aud claim) for which the token is issued."
+ },
+ "allowedPrincipals": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of allowed principals. Leave empty to not validate the principal (oid claim) for which the token is issued."
+ },
+ "allowedTenants": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of allowed tenants. Leave empty to not validate the tenant (tid claim) for which the token is issued."
+ },
+ "issuer": {
+ "type": "string",
+ "description": "Allowed token issuer. Leave empty to not validate the token issuer."
+ },
+ "roles": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of allowed roles. Leave empty to not validate the roles (roles claim) on the token."
+ },
+ "scopes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of allowed scopes. Leave empty to not validate the scopes (scp claim) on the token."
+ },
+ "validateLifetime": {
+ "type": "boolean",
+ "description": "Set to false to disable validating the token lifetime. Default is true."
+ },
+ "validateSigningKey": {
+ "type": "boolean",
+ "description": "Set to false to disable validating the token signature. Default is true."
+ }
+ },
+ "required": [
+ "metadataUrl"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "apiKey",
+ "oauth2"
+ ],
+ "description": "Type of authentication and authorization that Dev Proxy should use. Allowed values: apiKey, oauth2."
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/cachingguidanceplugin.schema.json b/schemas/v4.1.0/cachingguidanceplugin.schema.json
new file mode 100644
index 000000000..18116180a
--- /dev/null
+++ b/schemas/v4.1.0/cachingguidanceplugin.schema.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy CachingGuidancePlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "cacheThresholdSeconds": {
+ "type": "integer",
+ "description": "The number of seconds between the same request that triggers the guidance warning. Default is 5."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/crudapiplugin.apifile.schema.json b/schemas/v4.1.0/crudapiplugin.apifile.schema.json
new file mode 100644
index 000000000..35f6fc7a9
--- /dev/null
+++ b/schemas/v4.1.0/crudapiplugin.apifile.schema.json
@@ -0,0 +1,178 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "CRUD API plugin API definition",
+ "description": "API definition for use with the CRUD API Dev Proxy plugin",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "baseUrl": {
+ "type": "string",
+ "description": "Base URL where Dev Proxy exposes the API. Dev Proxy prepends this base URL to the URLs defined in actions."
+ },
+ "enableCors": {
+ "type": "boolean",
+ "description": "Set to true to enable CORS for the API. Default is true."
+ },
+ "dataFile": {
+ "type": "string",
+ "description": "Path to the file that contains the data for the API. The file must define a JSON array."
+ },
+ "actions": {
+ "type": "array",
+ "description": "List of actions that the API supports. Each action defines how Dev Proxy interacts with the data.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "action": {
+ "type": "string",
+ "enum": [
+ "create",
+ "getAll",
+ "getOne",
+ "getMany",
+ "merge",
+ "update",
+ "delete"
+ ],
+ "description": "Defines the type of action. Possible values: getAll, getOne, getMany, create, merge, update, delete."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL where Dev Proxy exposes the action. Appended to the baseUrl. Can contain parameters in curly braces."
+ },
+ "query": {
+ "type": "string",
+ "description": "JSONPath query (using Newtonsoft.Json) that Dev Proxy uses to find the data in the data file. Parameters can be referenced using curly braces."
+ },
+ "method": {
+ "type": "string",
+ "enum": [
+ "GET",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE",
+ "QUERY"
+ ],
+ "description": "HTTP method that Dev Proxy uses to expose the action. Defaults depend on the action type."
+ },
+ "auth": {
+ "type": "string",
+ "enum": [
+ "none",
+ "entra"
+ ],
+ "description": "Determines if the action is secured. Allowed values: none, entra. Default is none."
+ },
+ "entraAuthConfig": {
+ "type": "object",
+ "description": "Configuration for Microsoft Entra authentication for this action. Overrides the root entraAuthConfig if specified.",
+ "properties": {
+ "audience": {
+ "type": "string",
+ "description": "Valid audience for the token. If specified, the token's audience must match."
+ },
+ "issuer": {
+ "type": "string",
+ "description": "Valid token issuer. If specified, the token's issuer must match."
+ },
+ "scopes": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "Array of valid scopes. At least one must be present in the token."
+ },
+ "roles": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "Array of valid roles. At least one must be present in the token."
+ },
+ "validateLifetime": {
+ "type": "boolean",
+ "description": "Set to true to validate that the token hasn't expired."
+ },
+ "validateSigningKey": {
+ "type": "boolean",
+ "description": "Set to true to validate the token's signature."
+ }
+ }
+ }
+ },
+ "required": [
+ "action"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "auth": {
+ "type": "string",
+ "enum": [
+ "none",
+ "entra",
+ "apiKey"
+ ],
+ "description": "Determines if the API is secured. Allowed values: none, entra, apiKey. Default is none."
+ },
+ "apiKeyAuthConfig": {
+ "type": "object",
+ "description": "Configuration for API Key authentication. Applies to all actions unless overridden at the action level.",
+ "properties": {
+ "apiKey": {
+ "type": "string",
+ "description": "The valid API key that must be present in the request."
+ },
+ "headerName": {
+ "type": "string",
+ "description": "The HTTP header name to read the API key from."
+ },
+ "queryParameterName": {
+ "type": "string",
+ "description": "The name of the query-string parameter to read the API key from."
+ }
+ },
+ "required": [
+ "apiKey"
+ ]
+ },
+ "entraAuthConfig": {
+ "type": "object",
+ "description": "Configuration for Microsoft Entra authentication. Applies to all actions unless overridden at the action level.",
+ "properties": {
+ "audience": {
+ "type": "string",
+ "description": "Valid audience for the token. If specified, the token's audience must match."
+ },
+ "issuer": {
+ "type": "string",
+ "description": "Valid token issuer. If specified, the token's issuer must match."
+ },
+ "scopes": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "Array of valid scopes. At least one must be present in the token."
+ },
+ "roles": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "Array of valid roles. At least one must be present in the token."
+ },
+ "validateLifetime": {
+ "type": "boolean",
+ "description": "Set to true to validate that the token hasn't expired. Default is false."
+ },
+ "validateSigningKey": {
+ "type": "boolean",
+ "description": "Set to true to validate the token's signature. Default is false."
+ }
+ }
+ }
+ },
+ "required": [
+ "baseUrl",
+ "dataFile",
+ "actions"
+ ],
+ "additionalProperties": true
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/crudapiplugin.schema.json b/schemas/v4.1.0/crudapiplugin.schema.json
new file mode 100644
index 000000000..8c6d499ca
--- /dev/null
+++ b/schemas/v4.1.0/crudapiplugin.schema.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy CrudApiPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "apiFile": {
+ "type": "string",
+ "description": "Path to the file that contains the definition of the CRUD API."
+ }
+ },
+ "required": [
+ "apiFile"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/devtoolsplugin.schema.json b/schemas/v4.1.0/devtoolsplugin.schema.json
new file mode 100644
index 000000000..4bbdfec64
--- /dev/null
+++ b/schemas/v4.1.0/devtoolsplugin.schema.json
@@ -0,0 +1,36 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy DevToolsPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "preferredBrowser": {
+ "type": "string",
+ "enum": [
+ "Edge",
+ "EdgeDev",
+ "Chrome",
+ "EdgeBeta"
+ ],
+ "description": "Which browser to use to launch Dev Tools. Supported values: Edge, EdgeDev, Chrome, EdgeBeta. Default: Edge."
+ },
+ "preferredBrowserPath": {
+ "type": "string",
+ "description": "Path to the browser executable. When specified, overrides preferredBrowser."
+ },
+ "port": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 65535,
+ "description": "Port on which the plugin listens for DevTools connections. Use a fixed port when you need to publish it, eg. when running Dev Proxy in a container. Default: 0 (random available port)."
+ },
+ "launchBrowser": {
+ "type": "boolean",
+ "description": "Whether to launch the browser with DevTools. When false, the plugin logs the DevTools URL that you can open in Microsoft Edge or Google Chrome yourself, eg. when running Dev Proxy in a container. Default: true."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/executionsummaryplugin.schema.json b/schemas/v4.1.0/executionsummaryplugin.schema.json
new file mode 100644
index 000000000..e370b0523
--- /dev/null
+++ b/schemas/v4.1.0/executionsummaryplugin.schema.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy ExecutionSummaryPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "groupBy": {
+ "type": "string",
+ "enum": [
+ "url",
+ "messageType"
+ ],
+ "description": "How proxy should group the information in the summary. Available options: url, messageType. Default: url."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/genericrandomerrorplugin.errorsfile.schema.json b/schemas/v4.1.0/genericrandomerrorplugin.errorsfile.schema.json
new file mode 100644
index 000000000..f3c4491cf
--- /dev/null
+++ b/schemas/v4.1.0/genericrandomerrorplugin.errorsfile.schema.json
@@ -0,0 +1,106 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy GenericRandomErrorPlugin responses",
+ "description": "Error responses for the Dev Proxy GenericRandomErrorPlugin",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "errors": {
+ "type": "array",
+ "description": "List of error response definitions to simulate. Each entry defines a request pattern and possible error responses.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "request": {
+ "type": "object",
+ "description": "Request pattern to match for simulating an error.",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "URL pattern to match for the request. Supports wildcards."
+ },
+ "method": {
+ "type": "string",
+ "enum": [
+ "GET",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE",
+ "HEAD",
+ "OPTIONS",
+ "CONNECT",
+ "TRACE",
+ "QUERY"
+ ],
+ "description": "HTTP method to match for the request. Optional."
+ },
+ "bodyFragment": {
+ "type": "string",
+ "description": "Fragment of the request body to match. Optional."
+ }
+ },
+ "required": [
+ "url"
+ ]
+ },
+ "responses": {
+ "type": "array",
+ "description": "Possible error responses to return for the matched request.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "body": {
+ "type": [
+ "object",
+ "array",
+ "string",
+ "number",
+ "boolean"
+ ],
+ "description": "Response body to return. Can be an object, array, string, number, or boolean."
+ },
+ "statusCode": {
+ "type": "integer",
+ "description": "HTTP status code to return."
+ },
+ "headers": {
+ "type": "array",
+ "description": "List of headers to include in the response.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Header name."
+ },
+ "value": {
+ "type": "string",
+ "description": "Header value."
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "required": [
+ "request",
+ "responses"
+ ]
+ }
+ }
+ },
+ "required": [
+ "errors"
+ ],
+ "additionalProperties": true
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/genericrandomerrorplugin.schema.json b/schemas/v4.1.0/genericrandomerrorplugin.schema.json
new file mode 100644
index 000000000..fde0213e5
--- /dev/null
+++ b/schemas/v4.1.0/genericrandomerrorplugin.schema.json
@@ -0,0 +1,30 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy GenericRandomErrorPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "errorsFile": {
+ "type": "string",
+ "description": "Path to the file that contains error responses."
+ },
+ "rate": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100,
+ "description": "The percentage of requests to fail with a random error. Value between 0 and 100."
+ },
+ "retryAfterInSeconds": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The number of seconds to wait before retrying the request. Included on the Retry-After response header for dynamic throttling. Default: 5."
+ }
+ },
+ "required": [
+ "errorsFile"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/graphminimalpermissionsguidanceplugin.schema.json b/schemas/v4.1.0/graphminimalpermissionsguidanceplugin.schema.json
new file mode 100644
index 000000000..721994513
--- /dev/null
+++ b/schemas/v4.1.0/graphminimalpermissionsguidanceplugin.schema.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy GraphMinimalPermissionsGuidancePlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "Reference to the JSON schema definition."
+ },
+ "permissionsToExclude": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "The scopes to ignore and not include in the report. Default: ['profile', 'openid', 'offline_access', 'email'].",
+ "default": ["profile", "openid", "offline_access", "email"]
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/graphminimalpermissionsplugin.schema.json b/schemas/v4.1.0/graphminimalpermissionsplugin.schema.json
new file mode 100644
index 000000000..d72dd68fa
--- /dev/null
+++ b/schemas/v4.1.0/graphminimalpermissionsplugin.schema.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy GraphMinimalPermissionsPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "Reference to the JSON schema definition."
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "delegated",
+ "application"
+ ],
+ "description": "Determines which type of permission scopes to return. Can be 'delegated' or 'application'. Default: 'delegated'."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/graphrandomerrorplugin.schema.json b/schemas/v4.1.0/graphrandomerrorplugin.schema.json
new file mode 100644
index 000000000..79db8a233
--- /dev/null
+++ b/schemas/v4.1.0/graphrandomerrorplugin.schema.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy GraphRandomErrorPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "Reference to the JSON schema definition."
+ },
+ "allowedErrors": {
+ "type": "array",
+ "description": "Array of HTTP status codes (integers between 400 and 599) that the plugin can use to simulate errors. For example, [429] to simulate throttling.",
+ "items": {
+ "type": "integer",
+ "minimum": 400,
+ "maximum": 599
+ }
+ },
+ "rate": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100,
+ "description": "The percentage (0-100) of requests that should be failed with a random error."
+ },
+ "retryAfterInSeconds": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The number of seconds to set in the Retry-After header for throttling responses."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/hargeneratorplugin.schema.json b/schemas/v4.1.0/hargeneratorplugin.schema.json
new file mode 100644
index 000000000..a7aa5b5e9
--- /dev/null
+++ b/schemas/v4.1.0/hargeneratorplugin.schema.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy HarGeneratorPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "Reference to the JSON schema definition."
+ },
+ "includeSensitiveInformation": {
+ "type": "boolean",
+ "description": "Determines whether to include sensitive information (such as authentication headers, and cookies) in the generated HAR file. When set to false, sensitive information will be redacted. Default: false."
+ },
+ "includeResponse": {
+ "type": "boolean",
+ "description": "Determines whether to include HTTP response body in the generated HAR file. When set to false, only request information will be included. Default: false."
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/schemas/v4.1.0/httpfilegeneratorplugin.schema.json b/schemas/v4.1.0/httpfilegeneratorplugin.schema.json
new file mode 100644
index 000000000..f25784f1c
--- /dev/null
+++ b/schemas/v4.1.0/httpfilegeneratorplugin.schema.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy HttpFileGeneratorPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "Reference to the JSON schema definition."
+ },
+ "includeOptionsRequests": {
+ "type": "boolean",
+ "description": "Determines whether to include OPTIONS requests in the generated HTTP file. Default: false."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/languagemodelfailureplugin.schema.json b/schemas/v4.1.0/languagemodelfailureplugin.schema.json
new file mode 100644
index 000000000..db6dc115f
--- /dev/null
+++ b/schemas/v4.1.0/languagemodelfailureplugin.schema.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy LanguageModelFailurePlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "failures": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Array of failure types to simulate in language model responses. If not specified, uses default failure types including AmbiguityVagueness, BiasStereotyping, CircularReasoning, ContradictoryInformation, FailureDisclaimHedge, FailureFollowInstructions, Hallucination, IncorrectFormatStyle, Misinterpretation, OutdatedInformation, OverSpecification, OverconfidenceUncertainty, Overgeneralization, OverreliancePriorConversation, and PlausibleIncorrect."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/languagemodelratelimitingplugin.customresponsefile.schema.json b/schemas/v4.1.0/languagemodelratelimitingplugin.customresponsefile.schema.json
new file mode 100644
index 000000000..248f2dbe1
--- /dev/null
+++ b/schemas/v4.1.0/languagemodelratelimitingplugin.customresponsefile.schema.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy LanguageModelRateLimitingPlugin response",
+ "description": "Mock for the Dev Proxy LanguageModelRateLimitingPlugin",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The URL of the JSON schema used to validate this custom response file."
+ },
+ "body": {
+ "type": [
+ "object",
+ "array",
+ "string",
+ "number",
+ "boolean"
+ ],
+ "description": "The body of the custom response returned when the token limit is exceeded. Can be an object, array, string, number, or boolean."
+ },
+ "statusCode": {
+ "type": "integer",
+ "description": "HTTP status code to return when the token limit is exceeded (e.g., 429)."
+ },
+ "headers": {
+ "type": "array",
+ "description": "List of headers to include in the custom response.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Header name."
+ },
+ "value": {
+ "type": "string",
+ "description": "Header value. Use '@dynamic' for the Retry-After header to automatically calculate seconds until reset."
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ]
+ }
+ }
+ },
+ "additionalProperties": true
+}
diff --git a/schemas/v4.1.0/languagemodelratelimitingplugin.schema.json b/schemas/v4.1.0/languagemodelratelimitingplugin.schema.json
new file mode 100644
index 000000000..1cc836bb4
--- /dev/null
+++ b/schemas/v4.1.0/languagemodelratelimitingplugin.schema.json
@@ -0,0 +1,43 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy LanguageModelRateLimitingPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The URL of the JSON schema used to validate this configuration file."
+ },
+ "headerRetryAfter": {
+ "type": "string",
+ "description": "Name of the response header that communicates the retry-after period (e.g., 'Retry-After')."
+ },
+ "resetTimeWindowSeconds": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "How long in seconds until the next token limit reset."
+ },
+ "promptTokenLimit": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Number of prompt tokens allowed per time window."
+ },
+ "completionTokenLimit": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Number of completion tokens allowed per time window."
+ },
+ "whenLimitExceeded": {
+ "type": "string",
+ "enum": [
+ "Throttle",
+ "Custom"
+ ],
+ "description": "Behavior when the token limit is exceeded: 'Throttle' (default throttling) or 'Custom' (custom response)."
+ },
+ "customResponseFile": {
+ "type": "string",
+ "description": "Path to a file containing a custom error response to use when the token limit is exceeded."
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/schemas/v4.1.0/latencyplugin.schema.json b/schemas/v4.1.0/latencyplugin.schema.json
new file mode 100644
index 000000000..82fdd5c92
--- /dev/null
+++ b/schemas/v4.1.0/latencyplugin.schema.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy LatencyPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "Reference to the JSON schema definition."
+ },
+ "minMs": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The minimum amount of delay (in milliseconds) added to a request. Default: 0."
+ },
+ "maxMs": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The maximum amount of delay (in milliseconds) added to a request. Default: 5000."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/minimalcsompermissions.types.schema.json b/schemas/v4.1.0/minimalcsompermissions.types.schema.json
new file mode 100644
index 000000000..c212cdb5c
--- /dev/null
+++ b/schemas/v4.1.0/minimalcsompermissions.types.schema.json
@@ -0,0 +1,58 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "SharePoint CSOM Types and Permissions Schema",
+ "description": "Schema for defining SharePoint CSOM types, return types, and their required permissions",
+ "type": "object",
+ "required": ["types", "returnTypes", "actions"],
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "Reference to the JSON schema definition."
+ },
+ "types": {
+ "type": "object",
+ "description": "Mapping of GUIDs to their corresponding SharePoint CSOM types. Used for readability and easier mapping.",
+ "patternProperties": {
+ "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$": {
+ "type": "string",
+ "description": "Fully qualified name of a SharePoint CSOM type."
+ }
+ }
+ },
+ "returnTypes": {
+ "type": "object",
+ "description": "Mapping of method signatures to their return types. Used to traverse the CSOM API hierarchy.",
+ "patternProperties": {
+ "^[A-Za-z0-9.]+\\.[A-Za-z0-9.]+$": {
+ "type": "string",
+ "description": "Fully qualified name of the return type."
+ }
+ }
+ },
+ "actions": {
+ "type": "object",
+ "description": "Mapping of method signatures to their required permissions. Each action lists the delegated and application permissions needed, sorted by least privilege first.",
+ "patternProperties": {
+ "^[A-Za-z0-9.]+\\.[A-Za-z0-9.]+$": {
+ "type": "object",
+ "properties": {
+ "delegated": {
+ "type": "array",
+ "description": "Required delegated permissions.",
+ "items": {
+ "type": "string"
+ }
+ },
+ "application": {
+ "type": "array",
+ "description": "Required application permissions.",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/schemas/v4.1.0/minimalcsompermissionsplugin.schema.json b/schemas/v4.1.0/minimalcsompermissionsplugin.schema.json
new file mode 100644
index 000000000..f4b3eba17
--- /dev/null
+++ b/schemas/v4.1.0/minimalcsompermissionsplugin.schema.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy MinimalCsomPermissionsPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "Reference to the JSON schema definition."
+ },
+ "typesFilePath": {
+ "type": "string",
+ "description": "Path to the file that lists permissions required to call SharePoint CSOM APIs. Default: ~appFolder/config/spo-csom-types.json."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/minimalpermissionsguidanceplugin.schema.json b/schemas/v4.1.0/minimalpermissionsguidanceplugin.schema.json
new file mode 100644
index 000000000..967657c77
--- /dev/null
+++ b/schemas/v4.1.0/minimalpermissionsguidanceplugin.schema.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy MinimalPermissionsGuidancePlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "apiSpecsFolderPath": {
+ "type": "string",
+ "description": "Relative or absolute path to the folder with API specs. Used to compare JWT token permissions against minimal required scopes."
+ },
+ "permissionsToExclude": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "The scopes to ignore and not include in the report. Default: ['profile', 'openid', 'offline_access', 'email'].",
+ "default": ["profile", "openid", "offline_access", "email"]
+ },
+ "schemeName": {
+ "type": "string",
+ "description": "The name of the security scheme definition. Used to determine minimal permissions required for API calls."
+ }
+ },
+ "required": [
+ "apiSpecsFolderPath"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/minimalpermissionsplugin.schema.json b/schemas/v4.1.0/minimalpermissionsplugin.schema.json
new file mode 100644
index 000000000..84d1d24d1
--- /dev/null
+++ b/schemas/v4.1.0/minimalpermissionsplugin.schema.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy MinimalPermissionsPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "apiSpecsFolderPath": {
+ "type": "string",
+ "description": "Relative or absolute path to the folder with API specs. Used to determine minimal permissions required for API calls."
+ },
+ "schemeName": {
+ "type": "string",
+ "description": "The name of the security scheme definition. Used to determine minimal permissions required for API calls."
+ }
+ },
+ "required": [
+ "apiSpecsFolderPath"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/mockrequestplugin.mockfile.schema.json b/schemas/v4.1.0/mockrequestplugin.mockfile.schema.json
new file mode 100644
index 000000000..e339900e1
--- /dev/null
+++ b/schemas/v4.1.0/mockrequestplugin.mockfile.schema.json
@@ -0,0 +1,77 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy MockRequestPlugin mocks",
+ "description": "Mock request for the Dev Proxy MockRequestPlugin",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "request": {
+ "type": "object",
+ "description": "The request to issue.",
+ "properties": {
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "description": "URL to call."
+ },
+ "method": {
+ "type": "string",
+ "enum": [
+ "GET",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE",
+ "HEAD",
+ "OPTIONS",
+ "CONNECT",
+ "TRACE",
+ "QUERY"
+ ],
+ "description": "HTTP method to use (default: POST)."
+ },
+ "body": {
+ "type": [
+ "object",
+ "array",
+ "string",
+ "number",
+ "boolean"
+ ],
+ "description": "Body of the request (object, array, string, number, or boolean)."
+ },
+ "headers": {
+ "type": "array",
+ "description": "Array of request headers (name/value pairs).",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Request header name."
+ },
+ "value": {
+ "type": "string",
+ "description": "Request header value."
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ]
+ }
+ }
+ },
+ "required": [
+ "url"
+ ]
+ }
+ },
+ "required": [
+ "request"
+ ],
+ "additionalProperties": true
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/mockrequestplugin.schema.json b/schemas/v4.1.0/mockrequestplugin.schema.json
new file mode 100644
index 000000000..08355e537
--- /dev/null
+++ b/schemas/v4.1.0/mockrequestplugin.schema.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy MockRequestPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "mockFile": {
+ "type": "string",
+ "description": "Path to the file containing the mock request."
+ }
+ },
+ "required": [
+ "mockFile"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/mockresponseplugin.mocksfile.schema.json b/schemas/v4.1.0/mockresponseplugin.mocksfile.schema.json
new file mode 100644
index 000000000..0a990ff20
--- /dev/null
+++ b/schemas/v4.1.0/mockresponseplugin.mocksfile.schema.json
@@ -0,0 +1,114 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy MockResponsePlugin mocks",
+ "description": "Mocks for the Dev Proxy MockResponsePlugin",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "mocks": {
+ "type": "array",
+ "description": "Array of mock definitions.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "request": {
+ "type": "object",
+ "description": "The request to match.",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The URL to match. Supports wildcards."
+ },
+ "method": {
+ "type": "string",
+ "enum": [
+ "GET",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE",
+ "HEAD",
+ "OPTIONS",
+ "CONNECT",
+ "TRACE",
+ "QUERY"
+ ],
+ "description": "HTTP method to match."
+ },
+ "nth": {
+ "type": "integer",
+ "description": "(Optional) Match the nth occurrence of the request."
+ },
+ "bodyFragment": {
+ "type": "string",
+ "description": "(Optional) A fragment of the request body to match."
+ }
+ },
+ "required": [
+ "url"
+ ]
+ },
+ "response": {
+ "type": "object",
+ "description": "The response to return.",
+ "properties": {
+ "body": {
+ "type": [
+ "object",
+ "array",
+ "string",
+ "number",
+ "boolean"
+ ],
+ "description": "The response body (object, array, string, number, or boolean; strings can reference a file with '@filename')."
+ },
+ "statusCode": {
+ "type": "integer",
+ "description": "HTTP status code to return."
+ },
+ "headers": {
+ "type": "array",
+ "description": "Array of response headers (name/value pairs).",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Header name."
+ },
+ "value": {
+ "type": "string",
+ "description": "Header value."
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ]
+ }
+ }
+ }
+ },
+ "rate": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100,
+ "default": 100,
+ "description": "(Optional) Probability (0-100) that the mock is applied when it matches a request. When the mock isn't applied, the request falls through to the next matching mock, plugin, or the real API. Default: 100."
+ }
+ },
+ "required": [
+ "request",
+ "response"
+ ]
+ }
+ }
+ },
+ "required": [
+ "mocks"
+ ],
+ "additionalProperties": true
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/mockresponseplugin.schema.json b/schemas/v4.1.0/mockresponseplugin.schema.json
new file mode 100644
index 000000000..5343240c4
--- /dev/null
+++ b/schemas/v4.1.0/mockresponseplugin.schema.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy MockResponsePlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "mocksFile": {
+ "type": "string",
+ "description": "Path to the file containing the mock responses."
+ },
+ "blockUnmockedRequests": {
+ "type": "boolean",
+ "description": "Set to true to return 502 Bad Gateway response for requests that aren't mocked. Default is false."
+ }
+ },
+ "required": [
+ "mocksFile"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/mockstdioresponseplugin.mocksfile.schema.json b/schemas/v4.1.0/mockstdioresponseplugin.mocksfile.schema.json
new file mode 100644
index 000000000..cfeea3c3f
--- /dev/null
+++ b/schemas/v4.1.0/mockstdioresponseplugin.mocksfile.schema.json
@@ -0,0 +1,69 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy MockStdioResponsePlugin mocks",
+ "description": "Mocks for the Dev Proxy MockStdioResponsePlugin",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "mocks": {
+ "type": "array",
+ "description": "Array of stdio mock definitions.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "request": {
+ "type": "object",
+ "description": "The request pattern to match against stdin.",
+ "properties": {
+ "bodyFragment": {
+ "type": "string",
+ "description": "(Optional) A fragment of the stdin body to match (case-insensitive contains). You can specify bodyFragment, bodyRegex, or both; if both are specified, bodyRegex takes precedence. If neither is specified, the mock matches any stdin or is applied immediately on startup."
+ },
+ "bodyRegex": {
+ "type": "string",
+ "description": "(Optional) A regular expression pattern to match against the stdin body (case-insensitive). You can specify bodyRegex, bodyFragment, or both; if both are specified, bodyRegex takes precedence. If neither is specified, the mock matches any stdin or is applied immediately on startup."
+ },
+ "nth": {
+ "type": "integer",
+ "description": "(Optional) Match the nth occurrence. If not specified, matches every occurrence."
+ }
+ }
+ },
+ "response": {
+ "type": "object",
+ "description": "The mock response to return.",
+ "properties": {
+ "stdout": {
+ "type": [
+ "object",
+ "array",
+ "string",
+ "number",
+ "boolean"
+ ],
+ "description": "The stdout content to return. Can be an object, array, string, number, or boolean. If a string value starts with @, it's treated as a file path."
+ },
+ "stderr": {
+ "type": [
+ "object",
+ "array",
+ "string",
+ "number",
+ "boolean"
+ ],
+ "description": "The stderr content to return. Can be an object, array, string, number, or boolean. If a string value starts with @, it's treated as a file path."
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "required": [
+ "mocks"
+ ],
+ "additionalProperties": true
+}
diff --git a/schemas/v4.1.0/mockstdioresponseplugin.schema.json b/schemas/v4.1.0/mockstdioresponseplugin.schema.json
new file mode 100644
index 000000000..42e5ca0e7
--- /dev/null
+++ b/schemas/v4.1.0/mockstdioresponseplugin.schema.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy MockStdioResponsePlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "mocksFile": {
+ "type": "string",
+ "description": "Path to the file containing the stdio mock responses."
+ },
+ "blockUnmockedRequests": {
+ "type": "boolean",
+ "description": "Set to true to block stdin requests that aren't mocked. Default is false."
+ }
+ },
+ "required": [
+ "mocksFile"
+ ],
+ "additionalProperties": false
+}
diff --git a/schemas/v4.1.0/openaitelemetryplugin.pricesfile.schema.json b/schemas/v4.1.0/openaitelemetryplugin.pricesfile.schema.json
new file mode 100644
index 000000000..c03e0c1ba
--- /dev/null
+++ b/schemas/v4.1.0/openaitelemetryplugin.pricesfile.schema.json
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "OpenAI Telemetry Plugin language model prices file schema",
+ "description": "Schema for the language model prices file used by the OpenAI Telemetry plugin.",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "prices": {
+ "type": "object",
+ "description": "Map of model names to their pricing information.",
+ "additionalProperties": {
+ "type": "object",
+ "properties": {
+ "input": {
+ "type": "number",
+ "description": "The price per million tokens for input/prompt tokens."
+ },
+ "output": {
+ "type": "number",
+ "description": "The price per million tokens for output/completion tokens."
+ }
+ },
+ "required": [
+ "input",
+ "output"
+ ]
+ }
+ }
+ },
+ "required": [
+ "prices"
+ ],
+ "additionalProperties": true
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/openaitelemetryplugin.schema.json b/schemas/v4.1.0/openaitelemetryplugin.schema.json
new file mode 100644
index 000000000..13d16e85e
--- /dev/null
+++ b/schemas/v4.1.0/openaitelemetryplugin.schema.json
@@ -0,0 +1,52 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "OpenAI Telemetry Plugin",
+ "description": "Settings for the OpenAI Telemetry plugin which captures OpenAI API calls and emits OpenTelemetry information.",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "application": {
+ "type": "string",
+ "description": "The name of the application using the OpenTelemetry plugin.",
+ "default": "default"
+ },
+ "currency": {
+ "type": "string",
+ "description": "The currency used for cost calculations.",
+ "default": "USD"
+ },
+ "environment": {
+ "type": "string",
+ "description": "The environment in which the application is running (e.g., production, staging, development).",
+ "default": "development"
+ },
+ "exporterEndpoint": {
+ "type": "string",
+ "description": "The endpoint of the OpenTelemetry collector to send information to.",
+ "default": "http://localhost:4318"
+ },
+ "includeCompletion": {
+ "type": "boolean",
+ "description": "Whether to include the completion in the OpenTelemetry span. Disable for privacy or security concerns.",
+ "default": true
+ },
+ "includeCosts": {
+ "type": "boolean",
+ "description": "Whether to calculate and include cost information in the spans. Requires prices data.",
+ "default": false
+ },
+ "includePrompt": {
+ "type": "boolean",
+ "description": "Whether to include the prompt in the OpenTelemetry span. Disable for privacy or security concerns.",
+ "default": true
+ },
+ "pricesFile": {
+ "type": "string",
+ "description": "Path to the JSON file containing prices data for language models."
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/schemas/v4.1.0/openapispecgeneratorplugin.schema.json b/schemas/v4.1.0/openapispecgeneratorplugin.schema.json
new file mode 100644
index 000000000..60417b04f
--- /dev/null
+++ b/schemas/v4.1.0/openapispecgeneratorplugin.schema.json
@@ -0,0 +1,46 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy OpenApiSpecGeneratorPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "includeOptionsRequests": {
+ "type": "boolean",
+ "description": "Determines whether to include OPTIONS requests in the generated OpenAPI spec. Default: false."
+ },
+ "ignoreResponseTypes": {
+ "type": "boolean",
+ "description": "Determines whether to ignore response types in the generated OpenAPI spec. Default: false."
+ },
+ "specVersion": {
+ "type": "string",
+ "enum": [
+ "v2_0",
+ "v3_0",
+ "v3_1",
+ "v3_2"
+ ],
+ "description": "Specifies the OpenAPI spec version to generate. Allowed values: 'v2_0', 'v3_0', 'v3_1' or 'v3_2'. Default: 'v3_0'."
+ },
+ "specFormat": {
+ "type": "string",
+ "enum": [
+ "Json",
+ "Yaml"
+ ],
+ "description": "Specifies the format of the generated OpenAPI spec. Allowed values: 'Json' or 'Yaml'. Default: 'Json'."
+ },
+ "includeParameters": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Defines the list of query string parameters to include in the generated OpenAPI spec, along with their default values. Default: []",
+ "default": []
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/ratelimitingplugin.customresponsefile.schema.json b/schemas/v4.1.0/ratelimitingplugin.customresponsefile.schema.json
new file mode 100644
index 000000000..5f2c88200
--- /dev/null
+++ b/schemas/v4.1.0/ratelimitingplugin.customresponsefile.schema.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy RateLimitingPlugin response",
+ "description": "Mock for the Dev Proxy RateLimitingPlugin",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The URL of the JSON schema used to validate this custom response file."
+ },
+ "body": {
+ "type": [
+ "object",
+ "array",
+ "string",
+ "number",
+ "boolean"
+ ],
+ "description": "The body of the custom response returned when the rate limit is exceeded. Can be an object, array, string, number, or boolean."
+ },
+ "statusCode": {
+ "type": "integer",
+ "description": "HTTP status code to return when the rate limit is exceeded (e.g., 403)."
+ },
+ "headers": {
+ "type": "array",
+ "description": "List of headers to include in the custom response.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Header name."
+ },
+ "value": {
+ "type": "string",
+ "description": "Header value."
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ]
+ }
+ }
+ },
+ "additionalProperties": true
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/ratelimitingplugin.schema.json b/schemas/v4.1.0/ratelimitingplugin.schema.json
new file mode 100644
index 000000000..62b1422ab
--- /dev/null
+++ b/schemas/v4.1.0/ratelimitingplugin.schema.json
@@ -0,0 +1,69 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy RateLimitingPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The URL of the JSON schema used to validate this configuration file."
+ },
+ "headerLimit": {
+ "type": "string",
+ "description": "Name of the response header that communicates the rate-limiting limit (e.g., 'RateLimit-Limit')."
+ },
+ "headerRemaining": {
+ "type": "string",
+ "description": "Name of the response header that communicates the remaining number of resources before the reset (e.g., 'RateLimit-Remaining')."
+ },
+ "headerReset": {
+ "type": "string",
+ "description": "Name of the response header that communicates the time remaining until the reset (e.g., 'RateLimit-Reset')."
+ },
+ "headerRetryAfter": {
+ "type": "string",
+ "description": "Name of the response header that communicates the retry-after period (e.g., 'Retry-After')."
+ },
+ "costPerRequest": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "How many resources a single request costs."
+ },
+ "resetTimeWindowSeconds": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "How long in seconds until the next rate limit reset."
+ },
+ "warningThresholdPercent": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100,
+ "description": "The percentage of the rate limit after which warning headers are returned."
+ },
+ "rateLimit": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Number of resources allowed per time window."
+ },
+ "whenLimitExceeded": {
+ "type": "string",
+ "enum": [
+ "Throttle",
+ "Custom"
+ ],
+ "description": "Behavior when the rate limit is exceeded: 'Throttle' (default throttling) or 'Custom' (custom response)."
+ },
+ "resetFormat": {
+ "type": "string",
+ "enum": [
+ "SecondsLeft",
+ "UtcEpochSeconds"
+ ],
+ "description": "Format for the reset header: 'SecondsLeft' (seconds until reset) or 'UtcEpochSeconds' (UTC epoch seconds)."
+ },
+ "customResponseFile": {
+ "type": "string",
+ "description": "Path to a file containing a custom error response to use when the rate limit is exceeded."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/rc.schema.json b/schemas/v4.1.0/rc.schema.json
new file mode 100644
index 000000000..4db88db85
--- /dev/null
+++ b/schemas/v4.1.0/rc.schema.json
@@ -0,0 +1,233 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy config",
+ "description": "Configuration for Dev Proxy",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The URL of the JSON schema used to validate this configuration file. Should match the Dev Proxy version."
+ },
+ "apiAllowedOrigins": {
+ "type": "array",
+ "default": [],
+ "uniqueItems": true,
+ "description": "Exact browser origins allowed to call the control API, for example http://127.0.0.1:3000. No wildcards, paths, or trailing slashes. All requests still require the instance bearer token.",
+ "items": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https?://[^/*?#@]+$"
+ }
+ },
+ "apiIpAddress": {
+ "type": "string",
+ "default": "127.0.0.1",
+ "description": "API bind IP address, independent of ipAddress. Defaults to 127.0.0.1; use ::1 for IPv6 loopback. Non-loopback bindings require a trusted network; HTTP bearer tokens are not encrypted.",
+ "anyOf": [{ "format": "ipv4" }, { "format": "ipv6" }]
+ },
+ "apiPort": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 65535,
+ "description": "Port for the authenticated Dev Proxy API on apiIpAddress, independent of the traffic proxy. Set 0 to let the OS assign an available port."
+ },
+ "asSystemProxy": {
+ "type": "boolean",
+ "description": "Whether to set Dev Proxy as the system proxy."
+ },
+ "env": {
+ "type": "object",
+ "description": "Environment variables to set for the Dev Proxy process. Values are also used to replace {name} placeholders in API spec files used by the MinimalPermissions plugins. Values passed with --env replace these. Changes to HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, or NO_PROXY (including lowercase variants) require a full stop/start; hot reload cannot refresh cached HTTP proxy settings.",
+ "propertyNames": {
+ "minLength": 1,
+ "pattern": "^[^=]+$"
+ },
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "filterByHeaders": {
+ "type": "array",
+ "description": "List of headers to filter requests by. Each object specifies a header name and value.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Header name to filter by."
+ },
+ "value": {
+ "type": "string",
+ "description": "Header value to filter by."
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ]
+ }
+ },
+ "ipAddress": {
+ "type": "string",
+ "anyOf": [
+ {
+ "format": "ipv4"
+ },
+ {
+ "format": "ipv6"
+ }
+ ],
+ "description": "IP address for Dev Proxy to listen on."
+ },
+ "languageModel": {
+ "type": "object",
+ "description": "Configuration for using a local language model with Dev Proxy.",
+ "properties": {
+ "cacheResponses": {
+ "type": "boolean",
+ "description": "Whether to cache responses from the language model."
+ },
+ "client": {
+ "type": "string",
+ "enum": [
+ "Ollama",
+ "OpenAI"
+ ],
+ "description": "The client to use for the local language model."
+ },
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether the language model integration is enabled."
+ },
+ "model": {
+ "type": "string",
+ "description": "The name of the language model to use."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the local language model server."
+ }
+ }
+ },
+ "logLevel": {
+ "type": "string",
+ "enum": [
+ "debug",
+ "information",
+ "warning",
+ "error",
+ "trace"
+ ],
+ "description": "The minimum log level for Dev Proxy output."
+ },
+ "output": {
+ "type": "string",
+ "enum": [
+ "text",
+ "json"
+ ],
+ "description": "Output format. Use 'text' for readable console output (default), or 'json' for structured JSON Lines output suitable for LLMs and agents."
+ },
+ "newVersionNotification": {
+ "type": "string",
+ "enum": [
+ "none",
+ "stable",
+ "beta"
+ ],
+ "description": "Controls notifications about new Dev Proxy versions."
+ },
+ "plugins": {
+ "type": "array",
+ "description": "List of plugins to load. Each object defines a plugin instance.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the plugin."
+ },
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether the plugin is enabled."
+ },
+ "pluginPath": {
+ "type": "string",
+ "description": "Path to the plugin DLL."
+ },
+ "configSection": {
+ "type": "string",
+ "description": "Name of the configuration section for this plugin instance."
+ },
+ "urlsToWatch": {
+ "type": "array",
+ "description": "List of URL patterns for the plugin to watch.",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "required": [
+ "name",
+ "pluginPath"
+ ]
+ }
+ },
+ "port": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 65535,
+ "description": "Port for Dev Proxy to listen on."
+ },
+ "record": {
+ "type": "boolean",
+ "description": "Whether to record requests and responses."
+ },
+ "showSkipMessages": {
+ "type": "boolean",
+ "description": "Show messages for skipped requests."
+ },
+ "urlsToWatch": {
+ "type": "array",
+ "description": "List of URL patterns for Dev Proxy to intercept.",
+ "items": {
+ "type": "string"
+ }
+ },
+ "validateSchemas": {
+ "type": "boolean",
+ "description": "Whether to validate configuration files against their schemas. Only applies to JSON-based configuration files."
+ },
+ "watchPids": {
+ "type": "array",
+ "description": "List of process IDs to watch for network traffic.",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "watchProcessNames": {
+ "type": "array",
+ "description": "List of process names to watch for network traffic.",
+ "items": {
+ "type": "string"
+ }
+ },
+ "watchProcessTree": {
+ "type": "boolean",
+ "description": "Whether to also watch descendants of processes selected by watchPids or watchProcessNames."
+ },
+ "showTimestamps": {
+ "type": "boolean",
+ "description": "Show request timestamps in interactive log output. Detached log files always include full timestamps, including on warnings and errors, so logs can be filtered by date and time."
+ },
+ "timeoutSeconds": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Time in seconds after which Dev Proxy exits. Resets when Dev Proxy intercepts a request."
+ }
+ },
+ "required": [
+ "plugins"
+ ],
+ "additionalProperties": true
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/rewriteplugin.rewritesfile.schema.json b/schemas/v4.1.0/rewriteplugin.rewritesfile.schema.json
new file mode 100644
index 000000000..44f55f3da
--- /dev/null
+++ b/schemas/v4.1.0/rewriteplugin.rewritesfile.schema.json
@@ -0,0 +1,72 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy RewritePlugin rewrite rules",
+ "description": "Rewrite rules for the Dev Proxy RewritePlugin",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The URL of the JSON schema used to validate this rewrite rules file."
+ },
+ "rewrites": {
+ "type": "array",
+ "description": "Array of rewrite rule objects that define the list of rewrite rules the RewritePlugin applies.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "in": {
+ "type": "object",
+ "description": "Pattern to match the incoming request.",
+ "properties": {
+ "url": {
+ "type": "string",
+ "pattern": "^.+$",
+ "description": "Regular expression to match the incoming request URL."
+ }
+ },
+ "required": ["url"]
+ },
+ "out": {
+ "type": "object",
+ "description": "Pattern to rewrite the request.",
+ "properties": {
+ "url": {
+ "type": "string",
+ "pattern": "^.*$",
+ "description": "URL to rewrite the request to. Can use capture groups from the 'in' pattern and reference environment variables using ${env:VAR_NAME}. Environment variable values are inserted as-is, so they can also reference capture groups. If a referenced environment variable isn't set, the rule is skipped. If omitted, the request URL isn't changed."
+ },
+ "headers": {
+ "type": "array",
+ "description": "Request headers to set on requests matching the 'in' URL. Replaces the value of an existing header (matched case-insensitively) or adds the header if it's missing.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "pattern": "^.+$",
+ "description": "Name of the request header."
+ },
+ "value": {
+ "type": "string",
+ "description": "Value to set for the request header. Can reference environment variables using ${env:VAR_NAME}. If a referenced environment variable isn't set, the rule is skipped."
+ }
+ },
+ "required": ["name", "value"]
+ }
+ }
+ },
+ "anyOf": [
+ { "required": ["url"] },
+ { "required": ["headers"] }
+ ]
+ }
+ },
+ "required": ["in", "out"]
+ }
+ }
+ },
+ "required": [
+ "rewrites"
+ ],
+ "additionalProperties": true
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/rewriteplugin.schema.json b/schemas/v4.1.0/rewriteplugin.schema.json
new file mode 100644
index 000000000..6cf593e96
--- /dev/null
+++ b/schemas/v4.1.0/rewriteplugin.schema.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy RewritePlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The URL of the JSON schema used to validate this configuration file."
+ },
+ "rewritesFile": {
+ "type": "string",
+ "description": "Path to the file containing rewrite definitions (e.g., 'rewrites.json')."
+ }
+ },
+ "required": [
+ "rewritesFile"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/typespecgeneratorplugin.schema.json b/schemas/v4.1.0/typespecgeneratorplugin.schema.json
new file mode 100644
index 000000000..db770ca4f
--- /dev/null
+++ b/schemas/v4.1.0/typespecgeneratorplugin.schema.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy TypeSpecGeneratorPlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The URL of the JSON schema used to validate this configuration file."
+ },
+ "ignoreResponseTypes": {
+ "type": "boolean",
+ "description": "Determines whether to generate types for API responses (false) or to set them to 'string' (true)."
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/schemas/v4.1.0/websocketmockresponseplugin.mocksfile.schema.json b/schemas/v4.1.0/websocketmockresponseplugin.mocksfile.schema.json
new file mode 100644
index 000000000..b3768b602
--- /dev/null
+++ b/schemas/v4.1.0/websocketmockresponseplugin.mocksfile.schema.json
@@ -0,0 +1,113 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy WebSocketMockResponsePlugin mocks",
+ "description": "Mocks for the Dev Proxy WebSocketMockResponsePlugin",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "mocks": {
+ "type": "array",
+ "description": "Array of WebSocket mock definitions.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "description": "The WebSocket URL to match. Accepts ws://, wss://, http:// or https:// (schemes are normalized when matching) and supports * wildcards."
+ },
+ "onConnect": {
+ "type": "array",
+ "description": "Messages sent to the client immediately after the handshake, in order.",
+ "items": {
+ "$ref": "#/$defs/message"
+ }
+ },
+ "rules": {
+ "type": "array",
+ "description": "Reactive rules evaluated in order against each inbound client message.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "match": {
+ "type": "object",
+ "description": "How to match an inbound client message. Omit (or leave empty) to match any message. When more than one is set, precedence is bodyJson > bodyRegex > bodyFragment.",
+ "properties": {
+ "bodyFragment": {
+ "type": "string",
+ "description": "A case-insensitive substring matched against the inbound message text."
+ },
+ "bodyRegex": {
+ "type": "string",
+ "description": "A regular expression matched against the inbound message text."
+ },
+ "bodyJson": {
+ "type": [
+ "object",
+ "array",
+ "string",
+ "number",
+ "boolean"
+ ],
+ "description": "A JSON value compared structurally (key-order-independent) against the inbound message parsed as JSON."
+ }
+ },
+ "additionalProperties": false
+ },
+ "responses": {
+ "type": "array",
+ "description": "Messages to send to the client when this rule matches.",
+ "items": {
+ "$ref": "#/$defs/message"
+ }
+ },
+ "closeAfter": {
+ "type": "boolean",
+ "description": "When true, the mock server closes the connection after replying. Default is false."
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "closeOnUnmatched": {
+ "type": "boolean",
+ "description": "When true, the mock server closes if an inbound message matches no rule. Default is false."
+ }
+ },
+ "required": [
+ "url"
+ ],
+ "additionalProperties": false
+ }
+ }
+ },
+ "required": [
+ "mocks"
+ ],
+ "additionalProperties": true,
+ "$defs": {
+ "message": {
+ "type": "object",
+ "properties": {
+ "body": {
+ "type": "string",
+ "description": "The message payload. Sent verbatim for text, base64-decoded for binary."
+ },
+ "messageType": {
+ "type": "string",
+ "enum": [
+ "text",
+ "binary"
+ ],
+ "description": "Whether body is text or base64 binary. Default is text."
+ }
+ },
+ "required": [
+ "body"
+ ],
+ "additionalProperties": false
+ }
+ }
+}
diff --git a/schemas/v4.1.0/websocketmockresponseplugin.schema.json b/schemas/v4.1.0/websocketmockresponseplugin.schema.json
new file mode 100644
index 000000000..fbe9c1d45
--- /dev/null
+++ b/schemas/v4.1.0/websocketmockresponseplugin.schema.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Dev Proxy WebSocketMockResponsePlugin config schema",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "The JSON schema reference for validation."
+ },
+ "mocksFile": {
+ "type": "string",
+ "description": "Path to the file containing the WebSocket mock responses."
+ }
+ },
+ "required": [
+ "mocksFile"
+ ],
+ "additionalProperties": false
+}
diff --git a/scripts/Dockerfile_local b/scripts/Dockerfile_local
index 3bab86120..d211016f6 100644
--- a/scripts/Dockerfile_local
+++ b/scripts/Dockerfile_local
@@ -1,6 +1,6 @@
FROM ubuntu:24.04
-ARG DEVPROXY_VERSION=4.0.0-beta.1
+ARG DEVPROXY_VERSION=4.1.0-beta.1
ARG USERNAME=devproxy
ENV DEVPROXY_VERSION=${DEVPROXY_VERSION}
diff --git a/scripts/local-setup.ps1 b/scripts/local-setup.ps1
index 11b4d4791..2ac879481 100644
--- a/scripts/local-setup.ps1
+++ b/scripts/local-setup.ps1
@@ -2,7 +2,7 @@
# The .NET Foundation licenses this file to you under the MIT license.
# See the LICENSE file in the project root for more information.
-$versionString = "v4.0.0-beta.1"
+$versionString = "v4.1.0-beta.1"
$version = $versionString.Substring(1)
$isBeta = $version.Contains("-beta")
diff --git a/scripts/version.ps1 b/scripts/version.ps1
index 780f41c3e..8fbc54c7d 100644
--- a/scripts/version.ps1
+++ b/scripts/version.ps1
@@ -2,4 +2,4 @@
# The .NET Foundation licenses this file to you under the MIT license.
# See the LICENSE file in the project root for more information.
-$script:versionString = "v4.0.0-beta.1"
+$script:versionString = "v4.1.0-beta.1"