QwkSync

QWKSync.NET — Public API Overview

Scope

This document describes the entire intended public API surface of QWKSync.NET.


Design Constraints

Namespace - QwkSync

The project name is referred to as QWKSync.NET; the namespace is QwkSync.


Core Types

IQwkSyncClient

The single entry point.

public interface IQwkSyncClient
{
    Task<QwkSyncResult> SyncAsync(
        QwkSyncProfile profile,
        QwkSyncPlan plan,
        CancellationToken cancellationToken = default);
}

QwkSyncProfile

Defines where and how to connect.

public sealed record QwkSyncProfile
{
    public required Uri Endpoint { get; init; }
    public required string TransportId { get; init; }

    public CredentialSource Credentials { get; init; } = CredentialSource.None;

    public IReadOnlyDictionary<string, string> Settings { get; init; }
        = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}

QwkSyncPlan

Defines what to do.

public sealed record QwkSyncPlan
{
    public required string LocalInboxDirectory { get; init; }
    public required string LocalOutboxDirectory { get; init; }

    public PacketDiscovery Discovery { get; init; } = PacketDiscovery.Default;
    public RemoteLifecycle Remote { get; init; } = RemoteLifecycle.SafeDefaults;
    public TransferPolicy Transfer { get; init; } = TransferPolicy.Default;

    public IProgress<QwkSyncProgress>? Progress { get; init; }
    public ISyncLogSink? Log { get; init; }
}

PacketDiscovery

Controls file discovery and ordering.

public sealed record PacketDiscovery
{
    public string QwkSearchPattern { get; init; } = "*.qwk";
    public string RepSearchPattern { get; init; } = "*.rep";

    public PacketPickStrategy PickStrategy { get; init; }
        = PacketPickStrategy.Newest;

    public string? RemoteInboxPath { get; init; }
    public string? RemoteOutboxPath { get; init; }

    public static PacketDiscovery Default => new();
}

TransferPolicy

Controls atomicity and retries. Timeout applies per transfer operation (each list, download, upload, move, delete call).

public sealed record TransferPolicy
{
    public int MaxRetries { get; init; } = 0;
    public TimeSpan Timeout { get; init; } = TimeSpan.FromMinutes(2);

    public bool UseAtomicDownloads { get; init; } = true;

    public static TransferPolicy Default => new();
}

Results and Progress

Progress and logging are informational only and must not affect behaviour. Progress/log sinks must never gate control flow, ordering, retries, or error classification.

public sealed record QwkSyncResult
{
    public QwkSyncOutcome Outcome { get; init; }
    public IReadOnlyList<QwkSyncIssue> Issues { get; init; }
}

Transport Extension Contracts

public interface ITransportFactory
{
    string TransportId { get; }
    ITransport Create(QwkSyncProfile profile, TransferPolicy policy);
}
public interface ITransport : IAsyncDisposable
{
    Task<IReadOnlyList<RemoteItem>> ListAsync(
        string? remotePath,
        string searchPattern,
        CancellationToken ct);

    Task DownloadAsync(
        RemoteItem item,
        string localTempPath,
        IProgress<QwkSyncProgress>? progress,
        CancellationToken ct);

    Task UploadAsync(
        string localFilePath,
        string? remotePath,
        string remoteFileName,
        IProgress<QwkSyncProgress>? progress,
        CancellationToken ct);

    Task DeleteAsync(RemoteItem item, CancellationToken ct);
    Task MoveAsync(RemoteItem item, string destinationPath, CancellationToken ct);
}

QWKSync.NET must never call DeleteAsync or MoveAsync unless RemoteLifecycle explicitly requires it.


Guarantees

Anything outside these guarantees belongs elsewhere.