From 8ef462c6f30e45946643e98fa74d98fdaa90c513 Mon Sep 17 00:00:00 2001 From: tr00d Date: Tue, 25 Aug 2026 07:56:46 +0200 Subject: [PATCH] feat(dependency-injection): add Supabase.Extensions.DependencyInjection package --- Directory.Packages.props | 2 + Supabase.sln | 39 ++++++ .../DependencyInjection.Tests.csproj | 28 +++++ ...upabaseServiceCollectionExtensionsTests.cs | 118 ++++++++++++++++++ .../DependencyInjection.csproj | 50 ++++++++ .../DependencyInjection/PublicAPI.Shipped.txt | 1 + .../PublicAPI.Unshipped.txt | 3 + .../SupabaseServiceCollectionExtensions.cs | 94 ++++++++++++++ .../extensions/DependencyInjection/README.md | 49 ++++++++ release-please-config.json | 3 +- 10 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 packages/extensions/DependencyInjection/DependencyInjection.Tests/DependencyInjection.Tests.csproj create mode 100644 packages/extensions/DependencyInjection/DependencyInjection.Tests/SupabaseServiceCollectionExtensionsTests.cs create mode 100644 packages/extensions/DependencyInjection/DependencyInjection/DependencyInjection.csproj create mode 100644 packages/extensions/DependencyInjection/DependencyInjection/PublicAPI.Shipped.txt create mode 100644 packages/extensions/DependencyInjection/DependencyInjection/PublicAPI.Unshipped.txt create mode 100644 packages/extensions/DependencyInjection/DependencyInjection/SupabaseServiceCollectionExtensions.cs create mode 100644 packages/extensions/DependencyInjection/README.md diff --git a/Directory.Packages.props b/Directory.Packages.props index 549e29a2..db2ca2fd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,6 +18,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Supabase.sln b/Supabase.sln index 25ee67f6..c544af25 100644 --- a/Supabase.sln +++ b/Supabase.sln @@ -174,6 +174,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "red-test", "red-test", "{61 scripts\quality-gate\fixtures\red-test\overlay\Sample.Tests\WidgetTests.cs = scripts\quality-gate\fixtures\red-test\overlay\Sample.Tests\WidgetTests.cs EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{996F8526-0051-4327-A551-1175C455CC5F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DependencyInjection", "DependencyInjection", "{9FD9C523-BB83-D457-CA1C-9430D9580376}" + ProjectSection(SolutionItems) = preProject + packages\extensions\DependencyInjection\README.md = packages\extensions\DependencyInjection\README.md + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjection", "packages\extensions\DependencyInjection\DependencyInjection\DependencyInjection.csproj", "{481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjection.Tests", "packages\extensions\DependencyInjection\DependencyInjection.Tests\DependencyInjection.Tests.csproj", "{709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -352,6 +363,30 @@ Global {ACE6D9C0-1F55-4A95-8250-C074D5EBA913}.Release|x64.Build.0 = Release|Any CPU {ACE6D9C0-1F55-4A95-8250-C074D5EBA913}.Release|x86.ActiveCfg = Release|Any CPU {ACE6D9C0-1F55-4A95-8250-C074D5EBA913}.Release|x86.Build.0 = Release|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Debug|x64.ActiveCfg = Debug|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Debug|x64.Build.0 = Debug|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Debug|x86.ActiveCfg = Debug|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Debug|x86.Build.0 = Debug|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Release|Any CPU.Build.0 = Release|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Release|x64.ActiveCfg = Release|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Release|x64.Build.0 = Release|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Release|x86.ActiveCfg = Release|Any CPU + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6}.Release|x86.Build.0 = Release|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Debug|Any CPU.Build.0 = Debug|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Debug|x64.ActiveCfg = Debug|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Debug|x64.Build.0 = Debug|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Debug|x86.ActiveCfg = Debug|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Debug|x86.Build.0 = Debug|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Release|Any CPU.ActiveCfg = Release|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Release|Any CPU.Build.0 = Release|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Release|x64.ActiveCfg = Release|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Release|x64.Build.0 = Release|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Release|x86.ActiveCfg = Release|Any CPU + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -389,6 +424,10 @@ Global {1E2B584D-174B-4820-967B-FDB389FC03B4} = {3CC0DE6E-563A-4EA3-AF1A-0AF14FC57FE5} {097CDFF6-9B52-46C9-88BF-62ED8FE62957} = {3CC0DE6E-563A-4EA3-AF1A-0AF14FC57FE5} {612A3F59-5D51-4E02-80C1-4E687887B73D} = {3CC0DE6E-563A-4EA3-AF1A-0AF14FC57FE5} + {996F8526-0051-4327-A551-1175C455CC5F} = {809F86A1-1C4C-B159-0CD4-DF9D33D876CE} + {9FD9C523-BB83-D457-CA1C-9430D9580376} = {996F8526-0051-4327-A551-1175C455CC5F} + {481455AC-C8A3-4F61-A0C2-5D1CBF3D76F6} = {9FD9C523-BB83-D457-CA1C-9430D9580376} + {709EBF63-BDED-465F-9CEA-D1C7C9E2AB03} = {9FD9C523-BB83-D457-CA1C-9430D9580376} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {832DE89D-7252-4B03-9301-BB8D36B40992} diff --git a/packages/extensions/DependencyInjection/DependencyInjection.Tests/DependencyInjection.Tests.csproj b/packages/extensions/DependencyInjection/DependencyInjection.Tests/DependencyInjection.Tests.csproj new file mode 100644 index 00000000..e9795cad --- /dev/null +++ b/packages/extensions/DependencyInjection/DependencyInjection.Tests/DependencyInjection.Tests.csproj @@ -0,0 +1,28 @@ + + + + DependencyInjection.Tests + DependencyInjection.Tests + false + enable + true + true + latest + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/packages/extensions/DependencyInjection/DependencyInjection.Tests/SupabaseServiceCollectionExtensionsTests.cs b/packages/extensions/DependencyInjection/DependencyInjection.Tests/SupabaseServiceCollectionExtensionsTests.cs new file mode 100644 index 00000000..3ab6c484 --- /dev/null +++ b/packages/extensions/DependencyInjection/DependencyInjection.Tests/SupabaseServiceCollectionExtensionsTests.cs @@ -0,0 +1,118 @@ +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Supabase; +using Supabase.Core.Http; +using Supabase.Extensions.DependencyInjection; + +namespace DependencyInjection.Tests; + +/// +/// adds no new injection seams of its own — +/// every knob it touches (ClientOptions.HttpClient per package, Storage's three named clients, +/// ' passthrough) already exists and is covered elsewhere. These tests instead +/// pin the two things that are actually new: that the wiring reaches every sub-client correctly, and — the +/// entire point of this package — that genuinely pools the underlying +/// transport across resolutions instead of the SDK building a fresh one per Client. +/// +[TestClass] +[TestCategory("Unit")] +public class SupabaseServiceCollectionExtensionsTests +{ + private const string SupabaseUrl = "http://localhost:54321"; + private const string SupabaseKey = "test-key"; + + [TestMethod] + public void AddSupabase_ShouldResolveAClientAndEverySubClient() + { + var services = new ServiceCollection(); + services.AddSupabase(SupabaseUrl, SupabaseKey); + using var provider = services.BuildServiceProvider(); + using var scope = provider.CreateScope(); + var sp = scope.ServiceProvider; + + sp.GetRequiredService().Should().NotBeNull(); + sp.GetRequiredService().Auth.Should().NotBeNull(); + sp.GetRequiredService().Postgrest.Should().NotBeNull(); + sp.GetRequiredService().Storage.Should().NotBeNull(); + sp.GetRequiredService().Functions.Should().NotBeNull(); + sp.GetRequiredService().Realtime.Should().NotBeNull(); + } + + [TestMethod] + public void AddSupabase_ShouldWireFactoryCreatedHttpClients_IntoAuthPostgrestFunctionsAndStorage() + { + var services = new ServiceCollection(); + services.AddSupabase(SupabaseUrl, SupabaseKey); + using var provider = services.BuildServiceProvider(); + using var scope = provider.CreateScope(); + var client = scope.ServiceProvider.GetRequiredService(); + + client.Auth.Options.HttpClient.Should().NotBeNull("Auth must send through the factory-pooled client, not build its own"); + client.Postgrest.Options.HttpClient.Should().NotBeNull("Postgrest must send through the factory-pooled client, not build its own"); + ((Supabase.Functions.Client) client.Functions).Options.HttpClient.Should().NotBeNull("Functions must send through the factory-pooled client, not build its own"); + client.Auth.Options.HttpClient.Should().BeSameAs(client.Postgrest.Options.HttpClient, + "Auth, Postgrest and Functions share one named client, not one each"); + + client.Storage.Options.HttpRequestClient.Should().NotBeNull(); + client.Storage.Options.HttpUploadClient.Should().NotBeNull(); + client.Storage.Options.HttpDownloadClient.Should().NotBeNull(); + client.Storage.Options.HttpRequestClient.Should().NotBeSameAs(client.Storage.Options.HttpUploadClient, + "Storage's request/upload/download clients are independently named — a shared instance would collapse their independent timeout profiles"); + } + + [TestMethod] + public void AddSupabase_ShouldApplyConfigureOptions_ButOverwriteAnyHttpClientItSets() + { + var callerSuppliedClient = new HttpClient(); + var retry = new RetryOptions { MaxRetries = 4 }; + var services = new ServiceCollection(); + services.AddSupabase(SupabaseUrl, SupabaseKey, options => + { + options.PostgrestRetry = retry; + options.HttpClient = callerSuppliedClient; + }); + using var provider = services.BuildServiceProvider(); + using var scope = provider.CreateScope(); + var client = scope.ServiceProvider.GetRequiredService(); + + client.Postgrest.Options.Retry.Should().BeSameAs(retry, "plain configuration must reach the sub-clients unchanged"); + client.Postgrest.Options.HttpClient.Should().NotBeSameAs(callerSuppliedClient, + "AddSupabase must supply the factory-pooled client even if configureOptions set one directly — that overwrite is documented on the method"); + } + + [TestMethod] + public void AddSupabase_ShouldReuseThePooledHandler_AcrossMultipleScopes() + { + CountingHandler.ConstructedCount = 0; + var services = new ServiceCollection(); + services.AddSupabase(SupabaseUrl, SupabaseKey); + services.AddHttpClient("Supabase").AddHttpMessageHandler(() => new CountingHandler()); + + using var provider = services.BuildServiceProvider(); + + using (var scope1 = provider.CreateScope()) + _ = scope1.ServiceProvider.GetRequiredService(); + + using (var scope2 = provider.CreateScope()) + _ = scope2.ServiceProvider.GetRequiredService(); + + CountingHandler.ConstructedCount.Should().Be(1, + "the handler pipeline must be pooled across scope disposal, not rebuilt per Client resolution — " + + "that pooling is the actual socket-exhaustion fix this package exists for"); + } + + private sealed class CountingHandler : DelegatingHandler + { + public static int ConstructedCount; + + public CountingHandler() => Interlocked.Increment(ref ConstructedCount); + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => + Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)); + } +} diff --git a/packages/extensions/DependencyInjection/DependencyInjection/DependencyInjection.csproj b/packages/extensions/DependencyInjection/DependencyInjection/DependencyInjection.csproj new file mode 100644 index 00000000..369eb15e --- /dev/null +++ b/packages/extensions/DependencyInjection/DependencyInjection/DependencyInjection.csproj @@ -0,0 +1,50 @@ + + + true + Supabase.Extensions.DependencyInjection + https://github.com/supabase-community/supabase-csharp/tree/master/packages/extensions/DependencyInjection + Supabase.Extensions.DependencyInjection + Supabase.Extensions.DependencyInjection + en + Joseph Schultz <joseph@acupofjose.com> + ASP.NET Core / Microsoft.Extensions.DependencyInjection integration for the Supabase C# SDK + Supabase Dependency Injection + Registers a Supabase.Client (and each of its sub-clients) against IHttpClientFactory-managed, pooled HttpClients, for safe use under sustained ASP.NET Core load. + supabase, dependency-injection, aspnetcore + + 7.4.0 + 7.4.0 + + true + README.md + true + snupkg + true + enable + CS8600;CS8602;CS8603 + + + + 7.4.0 + + $(VersionPrefix)-$(VersionSuffix) + $(VersionPrefix) + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + diff --git a/packages/extensions/DependencyInjection/DependencyInjection/PublicAPI.Shipped.txt b/packages/extensions/DependencyInjection/DependencyInjection/PublicAPI.Shipped.txt new file mode 100644 index 00000000..7dc5c581 --- /dev/null +++ b/packages/extensions/DependencyInjection/DependencyInjection/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/packages/extensions/DependencyInjection/DependencyInjection/PublicAPI.Unshipped.txt b/packages/extensions/DependencyInjection/DependencyInjection/PublicAPI.Unshipped.txt new file mode 100644 index 00000000..f02643fe --- /dev/null +++ b/packages/extensions/DependencyInjection/DependencyInjection/PublicAPI.Unshipped.txt @@ -0,0 +1,3 @@ +#nullable enable +static Supabase.Extensions.DependencyInjection.SupabaseServiceCollectionExtensions.AddSupabase(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, string! supabaseUrl, string! supabaseKey, System.Action? configureOptions = null) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +Supabase.Extensions.DependencyInjection.SupabaseServiceCollectionExtensions diff --git a/packages/extensions/DependencyInjection/DependencyInjection/SupabaseServiceCollectionExtensions.cs b/packages/extensions/DependencyInjection/DependencyInjection/SupabaseServiceCollectionExtensions.cs new file mode 100644 index 00000000..1e52089c --- /dev/null +++ b/packages/extensions/DependencyInjection/DependencyInjection/SupabaseServiceCollectionExtensions.cs @@ -0,0 +1,94 @@ +using System; +using System.Net; +using System.Net.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Supabase.Extensions.DependencyInjection; + +/// +/// Registers a — and each of its sub-clients individually — against +/// -managed, pooled s. This is the fix for the +/// socket/connection-pool exhaustion that an SDK building its own per instance +/// causes under sustained ASP.NET Core load. +/// +/// This package adds no new injection seams of its own: , +/// , the per-package retry options, and Storage's three named +/// clients on already exist. only wires +/// -created clients into those existing seams and calls the +/// already-public constructor. +/// +public static class SupabaseServiceCollectionExtensions +{ + private const string HttpClientName = "Supabase"; + private const string StorageRequestHttpClientName = "Supabase.Storage.Request"; + private const string StorageUploadHttpClientName = "Supabase.Storage.Upload"; + private const string StorageDownloadHttpClientName = "Supabase.Storage.Download"; + + /// + /// Registers a scoped , plus each of its sub-clients individually + /// (IGotrueClient<User, Session>, IPostgrestClient, IStorageClient<Bucket, FileObject>, + /// IFunctionsClient, IRealtimeClient<RealtimeSocket, RealtimeChannel>) so a handler can inject + /// just the one it needs instead of the whole umbrella client. + /// + /// + /// The project's Supabase URL, e.g. "https://xyz.supabase.co". + /// The project's anon or service-role API key. + /// + /// Configures the passed to every sub-client. Any + /// or HttpXClient + /// properties set here are overwritten — supplies those from + /// so they come from the pooled, DI-managed handlers instead. + /// + public static IServiceCollection AddSupabase( + this IServiceCollection services, + string supabaseUrl, + string supabaseKey, + Action? configureOptions = null) + { + // Read once, purely for the static (non-per-request) proxy/timeout config below — never handed + // to a Client instance. The Client actually constructed per scope gets its own fresh + // SupabaseOptions further down, so concurrent scopes never race over shared mutable options. + var seed = new SupabaseOptions(); + configureOptions?.Invoke(seed); + + services.AddHttpClient(HttpClientName) + .ConfigurePrimaryHttpMessageHandler(() => CreateHandler(seed.Proxy)); + + services.AddHttpClient(StorageRequestHttpClientName) + .ConfigurePrimaryHttpMessageHandler(() => CreateHandler(seed.StorageClientOptions.Proxy)) + .ConfigureHttpClient(c => c.Timeout = seed.StorageClientOptions.HttpRequestTimeout); + + services.AddHttpClient(StorageUploadHttpClientName) + .ConfigurePrimaryHttpMessageHandler(() => CreateHandler(seed.StorageClientOptions.Proxy)) + .ConfigureHttpClient(c => c.Timeout = seed.StorageClientOptions.HttpUploadTimeout); + + services.AddHttpClient(StorageDownloadHttpClientName) + .ConfigurePrimaryHttpMessageHandler(() => CreateHandler(seed.StorageClientOptions.Proxy)) + .ConfigureHttpClient(c => c.Timeout = seed.StorageClientOptions.HttpDownloadTimeout); + + services.AddScoped(sp => + { + var factory = sp.GetRequiredService(); + var options = new SupabaseOptions(); + configureOptions?.Invoke(options); + + options.HttpClient = factory.CreateClient(HttpClientName); + options.StorageClientOptions.HttpRequestClient = factory.CreateClient(StorageRequestHttpClientName); + options.StorageClientOptions.HttpUploadClient = factory.CreateClient(StorageUploadHttpClientName); + options.StorageClientOptions.HttpDownloadClient = factory.CreateClient(StorageDownloadHttpClientName); + + return new Client(supabaseUrl, supabaseKey, options); + }); + + services.AddScoped(sp => sp.GetRequiredService().Auth); + services.AddScoped(sp => sp.GetRequiredService().Postgrest); + services.AddScoped(sp => sp.GetRequiredService().Storage); + services.AddScoped(sp => sp.GetRequiredService().Functions); + services.AddScoped(sp => sp.GetRequiredService().Realtime); + + return services; + } + + private static HttpClientHandler CreateHandler(IWebProxy? proxy) => + new() { Proxy = proxy, UseProxy = proxy != null }; +} diff --git a/packages/extensions/DependencyInjection/README.md b/packages/extensions/DependencyInjection/README.md new file mode 100644 index 00000000..bc9c0863 --- /dev/null +++ b/packages/extensions/DependencyInjection/README.md @@ -0,0 +1,49 @@ +# Supabase.Extensions.DependencyInjection + +[![Build and Test](https://github.com/supabase-community/supabase-csharp/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/supabase-community/supabase-csharp/actions/workflows/build-and-test.yml) +[![NuGet](https://img.shields.io/nuget/vpre/Supabase.Extensions.DependencyInjection)](https://www.nuget.org/packages/Supabase.Extensions.DependencyInjection/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](../../../LICENSE) + +ASP.NET Core / `Microsoft.Extensions.DependencyInjection` integration for the +[Supabase C# SDK](https://github.com/supabase-community/supabase-csharp). + +Registers a `Supabase.Client` — and each of its sub-clients individually — against +`IHttpClientFactory`-managed, pooled `HttpClient`s. Without this, an app that resolves a +new `Supabase.Client` per request builds a new `HttpClient` (and socket) per request too; +under sustained load that exhausts the connection pool. `IHttpClientFactory` fixes that by +pooling the underlying `SocketsHttpHandler` across resolutions. + +## Installation + +```sh +dotnet add package Supabase.Extensions.DependencyInjection +``` + +Targets .NET Standard 2.1. + +## Usage + +```csharp +builder.Services.AddSupabase( + supabaseUrl: "https://xyz.supabase.co", + supabaseKey: builder.Configuration["Supabase:Key"]!, + configureOptions: options => + { + options.AutoConnectRealtime = true; + options.PostgrestRetry = new RetryOptions { MaxRetries = 3 }; + }); +``` + +This registers a scoped `Supabase.Client`, plus each sub-client individually +(`IGotrueClient`, `IPostgrestClient`, `IStorageClient`, +`IFunctionsClient`, `IRealtimeClient`), so a handler can +inject just the one it needs: + +```csharp +app.MapGet("/todos", async (IPostgrestClient postgrest) => + await postgrest.Table().Get()); +``` + +Any `HttpClient`/proxy set directly via `configureOptions` is overwritten — this package +supplies those from `IHttpClientFactory` so every client's traffic goes through the pooled, +DI-managed handlers instead of a handler the SDK builds and owns itself. diff --git a/release-please-config.json b/release-please-config.json index 4da92c8e..f4bac11d 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -15,7 +15,8 @@ "packages/Postgrest/Postgrest/Postgrest.csproj", "packages/Realtime/Realtime/Realtime.csproj", "packages/Storage/Storage/Storage.csproj", - "packages/Supabase/Supabase/Supabase.csproj" + "packages/Supabase/Supabase/Supabase.csproj", + "packages/extensions/DependencyInjection/DependencyInjection/DependencyInjection.csproj" ] } },