This document describes the entire intended public API surface of QWKSync.NET.
The project name is referred to as QWKSync.NET; the namespace is QwkSync.
The single entry point.
public interface IQwkSyncClient
{
Task<QwkSyncResult> SyncAsync(
QwkSyncProfile profile,
QwkSyncPlan plan,
CancellationToken cancellationToken = default);
}
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);
}
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; }
}
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();
}
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();
}
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; }
}
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.
RemoteLifecycleAnything outside these guarantees belongs elsewhere.