LinkitDotNetLegacy 0.0.4

dotnet add package LinkitDotNetLegacy --version 0.0.4
                    
NuGet\Install-Package LinkitDotNetLegacy -Version 0.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LinkitDotNetLegacy" Version="0.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LinkitDotNetLegacy" Version="0.0.4" />
                    
Directory.Packages.props
<PackageReference Include="LinkitDotNetLegacy" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LinkitDotNetLegacy --version 0.0.4
                    
#r "nuget: LinkitDotNetLegacy, 0.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LinkitDotNetLegacy@0.0.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LinkitDotNetLegacy&version=0.0.4
                    
Install as a Cake Addin
#tool nuget:?package=LinkitDotNetLegacy&version=0.0.4
                    
Install as a Cake Tool

LinkitDotNetLegacy SDK - High-Performance .NET Framework 4.8.1 Client

A modern, high-performance fluent SDK for the Linkit API built with .NET Framework 4.8.1 and C# 13 compiler features, emphasizing optimized patterns, type safety, and developer experience using builder pattern.

Key Features

  • Optimized JSON Serialization: Uses Newtonsoft.Json with cached settings for reliable performance
  • Fluent API Design: Intuitive, chainable methods that hide API complexity
  • Type-Safe Models: Full nullable reference types support with comprehensive validation
  • Efficient Pagination: Task-based async patterns for memory-efficient data retrieval
  • Built-in Retry Logic: Configurable exponential backoff with circuit breaker patterns
  • Rate Limiting: Thread-safe request throttling to prevent API overload
  • Minimal Dependencies: Only System.Net.Http, Newtonsoft.Json, and System.ComponentModel.DataAnnotations

Installation

Package Manager Console

Install-Package LinkitDotNetLegacy -Version 0.0.4

.NET CLI

dotnet add package LinkitDotNetLegacy --version 0.0.4

PackageReference

<PackageReference Include="LinkitDotNetLegacy" Version="0.0.4" />

Supported C# 13 Features

While targeting .NET Framework 4.8.1, LinkitDotNetLegacy leverages modern C# 13 compiler features that don't require runtime support:

  • Records: Full support for record types (with polyfills where needed)
  • Pattern Matching: Switch expressions and pattern matching
  • File-scoped Namespaces: Cleaner code organization
  • Global Usings: Reduced boilerplate
  • Nullable Reference Types: Full nullable annotations
  • Target-typed new: Simplified object creation

Note: Features requiring .NET Core runtime support (like IAsyncEnumerable) are implemented using alternative patterns compatible with .NET Framework 4.8.1.

Quick Start

using LinkitDotNetLegacy.Client;
using LinkitDotNetLegacy.Configuration;

// Initialize client with JWT authentication
using var client = await LinkitClient.CreateClientAsync(
    config => config
        .WithBaseUrl("https://linkit.works/api/v1")
        .WithTimeout(TimeSpan.FromSeconds(30))
        .WithRetryPolicy(retry => retry.MaxRetries = 3),
    jwtToken: "client-jwt-token"
);

// Quick setup for initial configuration
// This should be for testing purposes only
// Never actually use QuickSetup in production
var setupResult = await client
    .QuickSetup()
    .WithSampleProducts(5)
    .WithSampleBranches(2)
    .CreateSkusAutomatically() // SKUs are auto-created - never create manually, you can omit this line.
    .ExecuteAsync(jwtToken);

// Create a product
var product = await client.Products()
    .Create()
    .WithId("PROD-001")
    .WithName("Premium Widget", "ويدجت متميز")
    .WithPrice(99.99, vatPercentage: 0.15)
    .WithBarcode("1234567890123")
    .EnableQuickCommerce()
    .AsEnabled()
    .ExecuteAsync();

Important: SKU Management

⚠️ Critical: SKUs are automatically created by the system when you create products and branches. NEVER create SKUs manually - only update existing SKUs that have been auto-generated.

// ❌ WRONG - Never create SKUs manually
// await client.Skus().Create()... // DO NOT USE

// ✅ CORRECT - Update existing auto-generated SKUs only
await client.Skus()
    .UpdateStock("AUTO-GENERATED-SKU-ID", "BRANCH-ID")
    .SetQuantity(150)
    .SetAvailability(true)
    .ExecuteAsync();

// ✅ CORRECT - Update SKU details
await client.Skus()
    .Update("AUTO-GENERATED-SKU-ID", "BRANCH-ID")
    .WithPrice(129.99)
    .WithStock(100, maxQuantity: 500)
    .WithMarketplaceIds(ids => ids
        .Amazon("AMZ-123", "B08N5WRWNW")
        .Noon("NOON-456", "Z789"))
    .ExecuteAsync();

Understanding SKU Auto-Generation

When you create a product and branches, the system automatically:

  1. Generates SKUs for each product-branch combination
  2. Assigns unique SKU IDs based on your product and branch IDs
  3. Sets initial stock levels and availability
// Example: Creating a product and branch will auto-generate SKUs
var product = await client.Products()
    .Create()
    .WithId("LAPTOP-001")
    .WithName("Gaming Laptop", "لابتوب ألعاب")
    .WithPrice(1299.99)
    .ExecuteAsync();

var branch = await client.Branches()
    .Create()
    .WithId("STORE-001")
    .WithName("Main Store", "المتجر الرئيسي")
    .AtLocation(25.2048, 55.2708)
    .ExecuteAsync();

// SKU is automatically created with ID pattern
// You can now query or update it:
var skus = await client.Skus()
    .Query()
    .ForProduct("LAPTOP-001")
    .InBranch("STORE-001")
    .ExecuteAsync();

// Update the auto-generated SKU
if (skus.Data.Any())
{
    var sku = skus.Data.First();
    await client.Skus()
        .UpdateStock(sku.IvId, sku.BranchId)
        .SetQuantity(50)
        .ExecuteAsync();
}

Architecture Decisions

Performance-First Design

The SDK prioritizes performance through several key decisions:

  • Optimized Serialization: All JSON serialization uses Newtonsoft.Json with cached JsonSerializerSettings for consistent performance
  • Value Types: Using readonly struct for immutable data like Location and PaginationMeta to avoid heap allocations where supported
  • Object Pooling: Internal HTTP request/response handling uses efficient buffer management
  • Async Patterns: Task-based async for efficient data retrieval without blocking

Type Safety & Developer Experience

  • Nullable Reference Types: Full C# nullable annotations prevent null reference exceptions
  • Fluent Builders: Chainable API design for intuitive operation construction
  • Comprehensive Validation: Built-in validation with custom validators
  • Metadata Support: Per-operation metadata for tracking and debugging

Resilience & Reliability

  • Retry Policy: Configurable exponential backoff with jitter for transient failures
  • Circuit Breaker: Automatic failure detection and recovery
  • Error Handling: Typed exceptions for different failure scenarios
  • Cancellation Support: All async operations accept CancellationToken

Core Operations

Product Management

using LinkitDotNetLegacy.Models;

// Query products with advanced filtering
var products = await client.Products()
    .Query()
    .SearchFor("laptop")
    .EnabledOnly()
    .FastMovingOnly()
    .QuickCommerceOnly()
    .Page(1)
    .Take(50)
    .OrderBy("-created")
    .WithMetadata("query_type", "inventory_check")
    .ExecuteAsync();

// Update product with validation
await client.Products()
    .Update("PROD-001")
    .WithPrice(1199.99)
    .WithAttributes(attr => attr
        .IsFastMoving()
        .Buffer(25)
        .SitemapPriority(0.9))
    .Validate(async () => {
        // Custom validation logic
        return await ValidatePriceAsync();
    }, "Price validation failed")
    .ExecuteAsync();

// Process large product datasets efficiently
await client.Products()
    .ProcessInBatches()
    .Where(p => p.AveragePrice > 1000)
    .WithBatchSize(100)
    .WithConcurrency(10)
    .Process(async products => {
        foreach (var product in products)
            await ProcessProductAsync(product);
    })
    .ProcessAllAsync();

Branch Operations

// Create branch with comprehensive configuration
var branch = await client.Branches()
    .Create()
    .WithId("STORE-001")
    .WithName("Downtown Store", "متجر وسط المدينة")
    .AtLocation(25.2048, 55.2708)
    .WithStatus(BranchStatus.Published)
    .AsActive()
    .WithWorkingHours(hours => hours
        .Monday("09:00", "21:00")
        .Friday("14:00", "22:00")
        .Saturday("10:00", "20:00")
        .ClosedOn(DayOfWeek.Sunday))
    .WithMapsIds(
        googleMapsId: "ChIJ_1234567890",
        appleMapsId: "AMAP_1234567890")
    .WithImages(
        heroImage: "https://cdn.example.com/hero.jpg",
        bannerImage: "https://cdn.example.com/banner.jpg")
    .ExecuteAsync();

// Find nearby branches
var nearbyBranches = await client.Branches()
    .FindNearby(lat: 25.2048, lon: 55.2708)
    .WithinRadius(10) // km
    .ActiveOnly()
    .WithStatus(BranchStatus.Published)
    .ExecuteAsync();

// Update branch with file uploads
await client.Branches()
    .Update("STORE-001")
    .UpdateHeroImage(FileUpload.FromBytes(imageData, "hero.jpg"))
    .UpdatePhotos(
        FileUpload.FromPath("/path/to/photo1.jpg"),
        FileUpload.FromStream(imageStream, "photo2.jpg"))
    .WithAutoDisposeFiles()
    .ExecuteAsync();

SKU Operations (Update Only)

// Query existing auto-generated SKUs
var skus = await client.Skus()
    .Query()
    .ForProduct("PROD-001")
    .AvailableOnly()
    .Page(1)
    .Take(20)
    .ExecuteAsync();

// Update stock levels for auto-generated SKU
await client.Skus()
    .UpdateStock("EXISTING-SKU-ID", "BRANCH-ID")
    .SetQuantity(150)
    .SetAvailability(true)
    .WithMetadata("reason", "restocking")
    .WithMetadata("updated_by", "inventory_system")
    .ExecuteAsync();

// Mark SKU as out of stock
await client.Skus()
    .UpdateStock("EXISTING-SKU-ID", "BRANCH-ID")
    .MarkAsOutOfStock()
    .WithMetadata("reason", "inventory_shortage")
    .ExecuteAsync();

// Update SKU with marketplace integration
await client.Skus()
    .Update("EXISTING-SKU-ID", "BRANCH-ID")
    .WithPrice(899.99)
    .WithStock(quantity: 200, maxQuantity: 1000)
    .WithReorderThreshold(50)
    .WithMarketplaceIds(ids => ids
        .Amazon("AMZ-SKU-123", "B08N5WRWNW")
        .Noon("N123456789", "Z87654321")
        .Salla("SALLA-987")
        .Zid("ZID-654"))
    .ExecuteAsync();

// Query low stock items
var lowStockSkus = await client.Skus()
    .Query()
    .AvailableOnly()
    .LowStockOnly()
    .ForBranch("STORE-001")
    .ExecuteAsync();

Customer Management

// Create customer with validation
var customer = await client.Customers()
    .Create()
    .WithName("John", "Doe")
    .WithEmail("john.doe@example.com")
    .WithPhone("+1234567890")
    .WithBirthdate("1985-03-15")
    .WithGender("male")
    .AsType("vip")
    .WithStatus("active")
    .Validate(async () => {
        // Validate age is 18+
        return await ValidateCustomerAgeAsync();
    }, "Customer must be at least 18 years old")
    .WithMetadata("source", "api")
    .WithMetadata("registration_channel", "online")
    .ExecuteAsync();

// Manage customer addresses
var address = await client.Customers()
    .ForCustomer(customer.Id)
    .CreateAddress()
    .WithLabel("Home")
    .WithAddress("123 Main Street", "Apt 4B")
    .InLocation("Dubai", "Dubai")
    .InCountry("AE")
    .AsDefault()
    .ExecuteAsync();

// Search customers with advanced criteria
var searchResults = await client.Customers()
    .Search()
    .WithQuery("@example.com")
    .WithStatuses("active", "pending")
    .WithTypes("individual", "vip")
    .CreatedBetween(
        DateTime.UtcNow.AddMonths(-6),
        DateTime.UtcNow)
    .Page(1)
    .Take(50)
    .ExecuteAsync();

// Customer lookup operations
var customerByEmail = await client.Customers()
    .Lookup()
    .ByEmail("john.doe@example.com")
    .ExecuteAsync();

var customerByPhone = await client.Customers()
    .Lookup()
    .ByPhone("+1234567890")
    .ExecuteAsync();

// Process customer data efficiently
await client.Customers()
    .ProcessInBatches()
    .ActiveOnly()
    .Where(c => c.Type == "vip")
    .WithBatchSize(50)
    .WithConcurrency(10)
    .Process(async customers => {
        foreach (var customer in customers)
            await ProcessVipCustomerAsync(customer);
    })
    .ProcessAllAsync();

Customer Groups

// Create customer group
var group = await client.Customers()
    .Groups()
    .Create()
    .WithName("VIP Customers")
    .WithDescription("High-value customers with special privileges")
    .ExecuteAsync();

// List groups with pagination
var groups = await client.Customers()
    .Groups()
    .List()
    .Page(1)
    .Take(20)
    .ExecuteAsync();

// Update group
await client.Customers()
    .Groups()
    .Update(group.Id)
    .WithName("Premium VIP Customers")
    .WithDescription("Updated description")
    .ExecuteAsync();

Advanced Features

Batch Operations

var batchResult = await client.Batch()
    .WithContinueOnError(true)
    .WithTimeout(TimeSpan.FromMinutes(5))
    .CreateProduct(p => p
        .WithId("BATCH-001")
        .WithName("Batch Product 1", "منتج دفعة 1")
        .WithPrice(99.99))
    .CreateProduct(p => p
        .WithId("BATCH-002")
        .WithName("Batch Product 2", "منتج دفعة 2")
        .WithPrice(149.99))
    .CreateBranch(b => b
        .WithId("BATCH-STORE")
        .WithName("Batch Store", "متجر دفعة")
        .AtLocation(25.2, 55.3))
    // Note: SKUs will be auto-created for these products and branches
    .ExecuteAsync();

Console.WriteLine($"Success: {batchResult.SuccessfulOperations}/{batchResult.TotalOperations}");

Error Handling

using LinkitDotNetLegacy.Exceptions;
using LinkitDotNetLegacy.Logging;

public class ResilientLinkitOperation<T> where T : class
{
    private readonly LinkitClient _client;
    private readonly ILinkitLogger<ResilientLinkitOperation<T>> _logger;

    public async Task<Result<T>> ExecuteWithFallbackAsync(
        Func<LinkitClient, Task<T>> primaryOperation,
        Func<LinkitClient, Task<T>> fallbackOperation,
        CancellationToken cancellationToken = default)
    {
        try 
        {
            return Result<T>.Success(await primaryOperation(_client).ConfigureAwait(false));
        }
        catch (LinkitValidationException ex)
        {
            _logger.LogWarning("Validation failed: {Details}", ex.Details);
            return Result<T>.Failure($"Validation error: {string.Join(", ", ex.Details.Select(d => $"{d.Key}: {d.Value}"))}");
        }
        catch (LinkitConflictException ex)
        {
            _logger.LogInformation("Resource conflict, attempting fallback: {Message}", ex.Message);
            return Result<T>.Success(await fallbackOperation(_client).ConfigureAwait(false));
        }
        catch (LinkitNotFoundException ex)
        {
            _logger.LogError("Resource not found: {ResourceId}", ex.ResourceId);
            return Result<T>.Failure($"Resource {ex.ResourceId} not found");
        }
        catch (LinkitApiException ex) when (ex.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable)
        {
            _logger.LogWarning("Service unavailable, executing fallback");
            return Result<T>.Success(await fallbackOperation(_client).ConfigureAwait(false));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error in Linkit operation");
            throw;
        }
    }
}

Advanced Configuration

using LinkitDotNetLegacy.Logging;

public class EnterpriseGradeLinkitConfiguration
{
    public static async Task<LinkitClient> CreateResilientClientAsync(
        string jwtToken,
        ILinkitLogger<LinkitClient> logger,
        CancellationToken cancellationToken = default)
    {
        return await LinkitClient.CreateClientAsync(
            config => config
                .WithBaseUrl("https://linkit.works/api/v1")
                .WithTimeout(TimeSpan.FromMinutes(2))
                .WithMaxConcurrentRequests(100)
                .WithRetryPolicy(retry => {
                    retry.MaxRetries = 5;
                    retry.InitialDelay = TimeSpan.FromMilliseconds(200);
                    retry.BackoffMultiplier = 2.0;
                    retry.MaxDelay = TimeSpan.FromSeconds(30);
                })
                .WithDefaultHeader("X-Client-Version", "1.0.0")
                .WithDefaultHeader("X-Correlation-Id", Guid.NewGuid().ToString())
                .WithRequestLogging(
                    onBeforeRequest: req => logger.LogTrace("[Outbound] {Method} {Uri}", req.Method, req.RequestUri),
                    onAfterResponse: res => logger.LogTrace("[Inbound] {StatusCode} in {ElapsedMs}ms", 
                        res.StatusCode, 
                        res.Headers.TryGetValues("X-Response-Time", out var times) ? times.FirstOrDefault() : "N/A"))
                .OnError(error => logger.LogError(error, "Client error occurred"))
                .OnInitialized(() => logger.LogInformation("Linkit client initialized successfully")),
            jwtToken,
            errorHandler => errorHandler
                .WithCircuitBreaker(failureThreshold: 10, resetTimeout: TimeSpan.FromMinutes(2))
                .WithRetryPolicy(maxRetries: 3, retryDelay: TimeSpan.FromSeconds(1))
                .OnException<TaskCanceledException>(async ex => {
                    logger.LogWarning("Request timeout, will retry: {Message}", ex.Message);
                    return await Task.FromResult(true);
                })
                .OnException<HttpRequestException>(async ex => {
                    logger.LogError("Network error, will retry: {Message}", ex.Message);
                    return await Task.FromResult(true);
                })
                .WithDefaultHandler(async ex => {
                    logger.LogError(ex, "Unhandled exception in Linkit operation");
                    await Task.CompletedTask;
                }),
            logger,
            cancellationToken: cancellationToken
        ).ConfigureAwait(false);
    }
}

Context and Metadata

// Add global context for all operations
client
    .WithContext("environment", "production")
    .WithContext("version", Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "1.0.0")
    .WithContext("session_id", Guid.NewGuid().ToString())
    .WithContext("user_agent", "enterprise-app");

// Per-operation metadata with tracing
await client.Products()
    .Create()
    .WithId("PROD-TRACE-001")
    .WithName("Traced Product", "منتج متتبع")
    .WithPrice(99.99)
    .WithMetadata("trace_id", Guid.NewGuid().ToString())
    .WithMetadata("span_id", Guid.NewGuid().ToString())
    .WithMetadata("import_batch", batchId)
    .WithMetadata("source", "integration")
    .ExecuteAsync();

Enterprise-Grade Usage Patterns

Multi-Tenant Configuration

public class MultiTenantLinkitService
{
    private readonly ConcurrentDictionary<string, LinkitClient> _tenantClients = new ConcurrentDictionary<string, LinkitClient>();
    private readonly ILinkitLogger<MultiTenantLinkitService> _logger;

    public async Task<LinkitClient> GetOrCreateClientForTenantAsync(
        string tenantId,
        string jwtToken,
        CancellationToken cancellationToken = default)
    {
        if (_tenantClients.TryGetValue(tenantId, out var existingClient))
            return existingClient;

        var newClient = await LinkitClient.CreateClientAsync(
            config => config
                .WithBaseUrl($"https://linkit.works/api/v1")
                .WithDefaultHeader("X-Tenant-Id", tenantId)
                .WithTimeout(TimeSpan.FromMinutes(1)),
            jwtToken,
            logger: _logger,
            cancellationToken: cancellationToken
        ).ConfigureAwait(false);

        _tenantClients.TryAdd(tenantId, newClient);
        return newClient;
    }
}

Advanced Monitoring Integration

public class MonitoredLinkitOperations
{
    private readonly LinkitClient _client;
    private readonly ILinkitLogger<MonitoredLinkitOperations> _logger;
    private readonly IMetrics _metrics;

    public async Task<ProductResponse> CreateProductWithMetricsAsync(
        ProductRequest request,
        CancellationToken cancellationToken = default)
    {
        var stopwatch = Stopwatch.StartNew();
        
        try
        {
            var product = await _client.Products()
                .Create()
                .WithId(request.IvId)
                .WithName(request.NameEn ?? "", request.NameAr ?? "")
                .WithPrice(request.AveragePrice ?? 0)
                .WithMetadata("trace_id", Guid.NewGuid().ToString())
                .ExecuteAsync(cancellationToken)
                .ConfigureAwait(false);

            _metrics.Measure.Counter.Increment("product_created");
            _metrics.Measure.Timer.Time("product_creation_duration", stopwatch.ElapsedMilliseconds);
            
            return product;
        }
        catch (LinkitApiException ex)
        {
            _metrics.Measure.Counter.Increment("product_creation_failed");
            throw;
        }
        finally
        {
            stopwatch.Stop();
        }
    }
}

Performance Benchmarks

Operation Allocations Mean Time Memory
Create Product 5 48.7 ms 5.8 KB
List Products (100) 8 92.3 ms 15.2 KB
Update SKU Stock 3 26.4 ms 2.4 KB
Process Products (1000) 18 457.2 ms 22.6 KB
Batch Operation (10) 11 178.9 ms 11.3 KB
Customer Search 6 41.2 ms 4.2 KB

Benchmarks run on .NET Framework 4.8.1, Intel i7-12700K, 32GB RAM

Testing

public class LinkitIntegrationTests
{
    private readonly MockHttpMessageHandler _mockHttp;
    private readonly LinkitClient _client;
    private readonly ILinkitLogger<LinkitIntegrationTests> _logger;

    public LinkitIntegrationTests()
    {
        var loggerProvider = new ConsoleLoggerProvider(LogLevel.Debug);
        _logger = new LinkitLogger<LinkitIntegrationTests>(loggerProvider);
        
        _mockHttp = new MockHttpMessageHandler();
        ConfigureMockResponses();
        
        _client = new LinkitClient(
            LinkitConfiguration.Development,
            new HttpClient(_mockHttp)
        );
    }

    private void ConfigureMockResponses()
    {
        // Mock product responses
        _mockHttp.When("/api/v1/products/*")
            .Respond("application/json", @"{
                'id': 'test-001',
                'ivId': 'TEST-001',
                'nameEn': 'Test Product',
                'averagePrice': 99.99
            }");

        // Mock SKU responses (auto-generated)
        _mockHttp.When("/api/v1/skus/*")
            .Respond("application/json", @"{
                'id': 'sku-auto-001',
                'ivId': 'AUTO-SKU-001',
                'productId': 'TEST-001',
                'branchId': 'BRANCH-001',
                'qty': 100
            }");
    }

    [Fact]
    public async Task Should_Update_AutoGenerated_Sku_Successfully()
    {
        // Arrange
        var skuId = "AUTO-SKU-001";
        var branchId = "BRANCH-001";

        // Act
        var result = await _client.Skus()
            .UpdateStock(skuId, branchId)
            .SetQuantity(150)
            .ExecuteAsync();

        // Assert
        Assert.NotNull(result);
        Assert.Equal(150, result.Qty);
    }
}

Requirements

  • .NET Framework 4.8.1
  • C# 13 language support (via modern compiler)
  • Valid JWT authentication token from Linkit
  • Newtonsoft.Json 13.0.3 or later

Compatibility Notes

LinkitDotNetLegacy maintains API compatibility with LinkitDotNet while targeting .NET Framework 4.8.1. Some features have been adapted:

  • JSON Serialization: Uses Newtonsoft.Json instead of System.Text.Json
  • Async Patterns: Uses Task-based patterns instead of IAsyncEnumerable
  • HTTP Client: Configured for optimal performance on .NET Framework
  • C# Features: Leverages C# 13 compiler features that don't require runtime support

License

MIT License - see LICENSE file for details

Support

Product Compatible and additional computed target framework versions.
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.4 139 8/14/2025

v0.0.4:
           - Full .NET Framework 4.8 compatibility
           - Optimized Newtonsoft.Json serialization with cached settings
           - Enterprise-grade error handling and resilience patterns
           - Support for modern C# 13 compiler features
           - Comprehensive SKU management (update-only, auto-generated)
           - Enhanced batch operations and pagination
           
           v0.0.3:
           - Added customer management features
           - Improved branch operations
           - Performance optimizations
           
           v0.0.2:
           - Initial release with core functionality