VwV.Documents.Client 1.0.0-preview.7

This is a prerelease version of VwV.Documents.Client.
dotnet add package VwV.Documents.Client --version 1.0.0-preview.7
                    
NuGet\Install-Package VwV.Documents.Client -Version 1.0.0-preview.7
                    
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="VwV.Documents.Client" Version="1.0.0-preview.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="VwV.Documents.Client" Version="1.0.0-preview.7" />
                    
Directory.Packages.props
<PackageReference Include="VwV.Documents.Client" />
                    
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 VwV.Documents.Client --version 1.0.0-preview.7
                    
#r "nuget: VwV.Documents.Client, 1.0.0-preview.7"
                    
#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 VwV.Documents.Client@1.0.0-preview.7
                    
#: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=VwV.Documents.Client&version=1.0.0-preview.7&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=VwV.Documents.Client&version=1.0.0-preview.7&prerelease
                    
Install as a Cake Tool

VwV.Documents

Reusable .NET 10 document contracts, configurable storage adapters and Bit Blazor UI, licensed under MIT. The host owns catalog persistence, tenancy, authorization and HTTP transport. No EF Core or Identity dependency is required.

Built by Vira Web Vorna (VwV) — virawebvorna.ir · arvindev1400@gmail.com.

Package on NuGet.org Published version Install in Contents
VwV.Documents.Contracts 1.0.0-preview.7 Shared contracts/server/client Folder, file and revision DTOs; policy, queries, concurrency/idempotency contracts
VwV.Documents.Storage 1.0.0-preview.7 Server only Disk, private S3/Liara, FTP/FTPS, named destinations and custom providers
VwV.Documents.Client 1.0.0-preview.7 Blazor UI projects Browser, picker, uploads, preview/history, download and policy controls

Requirements and version

Use .NET 10 and keep all three VwV.Documents package versions aligned. The current release is 1.0.0-preview.7, which fixes disabled/busy controls and interop readiness reported in issue #7. The first public release was preview.5. It uses 11.0.0-pre-14 of Bit.BlazorUI, Icons, Extras and Butil, the latest listed Bit release when this version was prepared. It remains a preview because these dependencies are previews. Earlier preview numbers belong to the private release history.

Install from NuGet.org

Version 1.0.0-preview.7 is published and listed on NuGet.org. Install directly from the public feed; no GitHub token or private feed is required:

dotnet add YourApp.Shared package VwV.Documents.Contracts --version 1.0.0-preview.7
dotnet add YourApp.Server package VwV.Documents.Storage --version 1.0.0-preview.7
dotnet add YourApp.Client package VwV.Documents.Client --version 1.0.0-preview.7

Use your actual project paths. Storage and Client already depend on Contracts, so an explicit Contracts reference is only needed when your shared project uses its DTOs directly. Put the UI package in every project that compiles the Razor components, including a server/prerender project when applicable. Never place storage credentials in a browser project.

For central package management, add the versions to Directory.Packages.props and use versionless PackageReference items:

<ItemGroup>
  <PackageVersion Include="VwV.Documents.Contracts" Version="1.0.0-preview.7" />
  <PackageVersion Include="VwV.Documents.Storage" Version="1.0.0-preview.7" />
  <PackageVersion Include="VwV.Documents.Client" Version="1.0.0-preview.7" />
</ItemGroup>

The repository's NuGet.Config uses only NuGet.org. When migrating from the private GitHub feed, remove any VwV.Documents.*/VwV.* source mapping that forces these packages to GitHub, or map this family to nuget.org.

The preview.6 GitHub release records the exact source, dependencies and tested package bytes. Its consumer-proof.json includes a fresh package-only restore, disabled/interop/busy component regressions and real English/LTR and Persian/RTL pointer/keyboard browser checks. NuGet.org publication is verified separately by signed-payload readback and the publication and installation report.

Server registration and profiles

using VwV.Documents.Storage;
builder.Services.AddDocumentStorage(builder.Configuration.GetSection("Documents"));
// Resolve DocumentStorageRegistry through constructor injection.

A complete credential-free template is in examples/documents.example.json. Set a unique project Prefix in every destination that uses a shared bucket. Supply secrets from server-only environment variables or a secret store. For example Documents__Profiles__liara__SecretKey overrides the corresponding configuration property. Do not serialize DocumentStorageProfile into client DTOs or logs.

{
  "Documents": {
    "ActiveProfile": "liara",
    "Profiles": {
      "liara": {
        "Provider": "s3", "Endpoint": "https://storage.example.invalid",
        "Bucket": "YOUR_PRIVATE_BUCKET", "Prefix": "my-project",
        "AccessKey": "FROM_SERVER_SECRET_STORE", "SecretKey": "FROM_SERVER_SECRET_STORE",
        "SigningRegion": "us-east-1", "ForcePathStyle": true,
        "ConditionalWrites": false, "TimeoutSeconds": 60
      },
      "local": { "Provider": "disk", "Root": "D:/ApplicationData/Documents", "Prefix": "my-project" },
      "ftps": {
        "Provider": "ftp", "FtpHost": "files.example.invalid", "FtpPort": 21,
        "FtpUser": "FROM_SECRET_STORE", "FtpPassword": "FROM_SECRET_STORE",
        "FtpNamespace": "stable-chroot-name", "Root": "/documents", "Prefix": "my-project",
        "FtpUseTls": true, "AllowUnencryptedFtp": false, "TimeoutSeconds": 60
      }
    }
  }
}

Policy properties on Documents include MaximumFileBytes, WorkspaceQuotaBytes, ActorQuotaBytes, MaximumConcurrentUploads, MaximumFolderDepth, PageSize and AllowedExtensions. Registration validates coherent limits; the host's catalog/reservation transaction must enforce actual quotas and permissions. A storage adapter alone does not implement a catalog, quotas or account authorization.

Select ActiveProfile to change the destination for new objects. Each immutable revision must retain DestinationId plus exact object key. Resolve retained revisions through registry.Resolve(revision.DestinationId), not registry.Active. Keep earlier named destinations configured while their objects are retained. Credential rotation does not change destination identity; bucket/prefix/root/namespace changes do. Moving existing bytes or purging storage requires an explicit operator plan and verified backup. Do not re-create or overwrite historical revisions.

Storage use and folder structure

var store = registry.Active;
var key = DocumentObjectKeys.Create("academy-north", "learning", documentId, revisionId);
// Physical location: my-project/tenants/academy-north/learning/{documentId}/{revisionId}
using var payload = new MemoryStream(bytes);
await store.PutAsync(new StoredObject(key, bytes.Length, contentType, sha256), payload, cancellationToken);
await using var read = await registry.Resolve(savedDestinationId).OpenReadAsync(savedKey, cancellationToken: cancellationToken);
await read.Stream.CopyToAsync(destinationStream, cancellationToken);

Document/revision IDs are GUIDs to avoid collisions and preserve immutable replacement history; the catalog stores the display filename. The immutable workspace folder name uses lowercase letters, digits and hyphens, up to 63 characters. Validate it, require uniqueness in the host and keep it stable after creation. Modules use a validated registered name, not free user input. The project prefix separates applications; tenants and platform keys are separate roots below it. For platform-owned media a host can generate a validated platform/profiles/{userId}/{revisionId} key using StorageKeys.Validate. Never present physical keys or infrastructure folders as user-created logical folders.

For a non-tenant application, use one explicit host workspace (for example fixed folder main and a nonempty stable WorkspaceId). For multi-tenant applications derive the workspace from authenticated context. A null workspace filter must never mean unrestricted access. Reauthorize all endpoints and media reads; tenant users cannot select other tenants or alter global storage configuration. Platform operators require a separate explicit policy. Display logical catalog folders instead of enumerating the bucket to tenant users.

IDocumentStorage provides StatAsync, PutAsync, OpenReadAsync, ListAsync and DeleteAsync with cancellation. Dispose the StorageRead to release its stream and provider connection. Inspect Capabilities rather than assuming atomic create, byte ranges or inventory on every provider. S3 conditional writes are enabled only when the provider supports them. FTP uses verified explicit TLS by default; plaintext requires explicit opt-in and is intended for controlled tests. Do not expose DeleteAsync to ordinary archive operations: archive is a catalog change, physical deletion is an operator/retention decision.

For another backend, implement IDocumentStorageFactory and IDocumentStorage, register the factory in DI before AddDocumentStorage, and use its Provider name in a profile. Keep destination identity stable and return accurate capabilities. The module's HostCanRegisterAnotherProviderWithoutClubDependencies test is a compiled example. Settings supplies additional provider configuration.

Blazor integration

Configure the pinned Bit UI/Icons/Extras and Butil dependencies and services in both server/prerender and WASM containers, following their setup guides. Include Bit UI/Icons/Extras styles, Bit UI/Extras scripts and the host's generated MyApp.styles.css. The package supplies its own static JavaScript and compiled scoped CSS; consumers need no Node or Sass runtime. Library developers regenerate paired SCSS outputs after edits.

using VwV.Documents.Client;
services.AddScoped<IDocumentBrowserInterop, DocumentBrowserInterop>();
services.AddScoped<IDocumentFolderTransport, DocumentFolderTransport>();
services.AddScoped<IDocumentDownloadTransport, DocumentDownloadTransport>();
// Register your own authenticated IDocumentClient implementation.
@using VwV.Documents.Client
<DocumentBrowser Client="documentClient" Browser="browserInterop" Read="ReadAuthorizedAsync"
    WorkspaceId="workspaceId" Module="learning" ScopeKey="@scopeKey"
    CanUpload="canUpload" CanCreateFolders="canOrganize" CanManageFolders="canOrganize"
    Text="Translate" OnError="HandleError" />

Client implements IDocumentClient by calling your authenticated API. DocumentBrowserInterop implements IDocumentBrowserInterop, whose CreateUrl/RevokeUrl methods manage bounded object-URL previews; inspect DocumentViewFile.cs and DocumentContentReader.cs for signatures. ReadAuthorizedAsync(DocumentViewFile, CancellationToken) returns authorized bounded preview bytes; the shared preview limit is 50 MiB. A host must enforce the limit before reading and honor exact revision and cancellation.

Use UploadContent and ReplacementContent slots for your authorized streaming uploader. DocumentUploadBox handles an ordinary file input; DocumentFolderUpload uses DocumentFolderTransport to keep file bytes in the browser. Do not send large file bytes over a Blazor Server SignalR circuit. Your upload API should reserve, stream, complete and reconcile operations with idempotency/concurrency checks. DocumentDownloadTransport streams authorized browser downloads and aborts on cancellation. The included JavaScript is loaded from _content/VwV.Documents.Client/ by these helpers.

Picker/InlineDetails permit shared selection inside a host editor. Change ScopeKey whenever tenant/resource/context changes; that cancels stale work and clears the prior view. DocumentPageDto can explicitly disable upload/folder creation and explain a write restriction. DocumentActions and CanUpload flags shape the UI but never grant backend permission. Supply Text, GridStrings and OnError consistently for localization and error display.

Server operations must validate RequestId, long Version, ownership, exact revision, allowed module, target folder, quotas and capabilities again. IDocumentAccessPolicy describes domain-specific actions but does not automatically protect an endpoint. Persist audit/idempotency/reservation records in your host; this package deliberately supplies no EF migrations.

Standalone consumer and tests

dotnet run --project VwV.Documents.Consumer
dotnet run --project VwV.Documents.Consumer -p:UsePublishedPackages=true -p:DocumentsPackageVersion="$VERSION"
dotnet run --project VwV.Documents.Tests -c Release -- --filter "TestCategory!=ExternalProvider"

The consumer uses only a unique temporary disk root and removes its exact synthetic object in finally. It verifies bytes and references the UI assembly without Club. It creates no database and reads no appsettings or real bucket credentials. Empty owned temporary folders may remain. Tests exercise disk, scope/key validation, provider registration and a loopback FTP server. The optional Liara test is skipped unless CLUB_DOCUMENT_LIARA_TEST_CONFIG explicitly points to a disposable private provider configuration; it creates/deletes only exact synthetic keys below an owned GUID prefix. Never set this variable to production for CI.

Build, release and publish

See the release guide, release notes and changelog. CI runs storage/playground tests on Windows and Linux, audits package metadata and static assets, and restores an isolated consumer using only the built packages. Optional real-provider tests require separate disposable credentials.

public-release.yml builds a clean immutable tag once and creates a GitHub release containing three packages, checksums and the consumer proof. A separate explicit publish dispatch uploads those existing assets to NuGet.org and verifies their payloads after repository signing. Public publishing uses NuGet Trusted Publishing with NUGET_USER; a scoped NUGET_API_KEY remains an optional fallback. Symbol archives stay in internal workflow artifacts. The historical private packages.yml workflow and ECO-003 remain available for earlier private releases.

License and support

Copyright (c) 2026 Vira Web Vorna (VwV). Distributed under the MIT license. Website: virawebvorna.ir. Contact: arvindev1400@gmail.com. Third-party package licenses are recorded in the release consumer proof; those dependencies are referenced rather than embedded in the packages.

Preview 2 storage additions

AllowLoopbackHttp permits HTTP only for explicitly enabled local S3 emulators; remote HTTP remains rejected. Production hosts must leave it false. IReplaceableDocumentStorage.PutOrReplaceAsync explicitly supports mutable host objects such as existing profile-image keys on disk, S3 and FTP. Immutable document revisions must continue using PutAsync; its create-only semantics are unchanged. Destination identity and existing object keys are unchanged.

Original upload names (preview 3)

DocumentUploadNames.Create(revisionId, extension) keeps opaque GUID names by default. Pass originalFileName and useOriginalFileName=true to retain a sanitized Unicode basename under a unique revision directory. Persian letters, combining marks and ordinary spaces are supported; paths/control characters are rejected, unsafe characters are replaced, Windows device names are prefixed and stems are bounded. Private or identifying filenames should remain opaque according to host policy. StorageKeys.Validate applies portable new-write/filesystem rules. S3 retained reads additionally support the explicitly bounded object-key compatibility contract described below. Two original files with the same name have distinct immutable revision paths.

Local storage playground

Run dotnet run --project VwV.Documents.Playground and open http://127.0.0.1:5292. The isolated developer sample previews settings and uploads/downloads synthetic documents through VwV.Documents disk storage without production credentials. See VwV.Documents.Playground/README.md for limits and session-local metadata.

Operator playground

The Persian/RTL playground at http://127.0.0.1:5292 provides initial settings forms for disk, Liara/S3 and TLS FTP, write-only server-memory keys, real browser progress, explicit retry with hash reconciliation, previews and storage reports/CSV/JSON. All IO uses these adapters and isolated prefixes. Run dotnet run --project VwV.Documents.Playground; validate with dotnet run --project VwV.Documents.Playground.Tests. This is a loopback-only developer sample with an ephemeral catalog, not a production multiuser portal.

Portable creation and retained object keys (source candidate preview 4)

DocumentObjectKeys.Create validates every generated key. Logical workspace/module names matching Windows device names (con, prn, aux, nul, com1–com9, lpt1–lpt9) receive an underscore in their physical key segment. The accepted logical grammar excludes underscores, so the encoding is collision-free; ordinary keys do not change. This does not rename tenants, change registered module names, rewrite stored keys or move bytes. Every revision still stores its exact original key and destination.

StorageKeys.Validate remains strict for new keys, Disk, FTP and filesystem compatibility bridges. ValidateRetainedObjectKey is a stable bounded object-store read grammar: Windows device names and edge dots/spaces are literal object names, while traversal, absolute/empty segments, backslashes, controls, encoded separators and oversized keys remain invalid. S3 Stat/OpenRead/List use it, including retained configured prefixes; destination identity is unchanged. S3 new writes, explicit replacement and Delete still require portable keys/prefixes. A retained nonportable prefix is readable, not a new-write destination. No operation trims or rewrites the stored key. Unsupported filesystem aliases fail closed.

Synthetic tests exercise actual adapters against owned loopback servers; no customer object, database or external provider is involved. The package-only Consumer links only the synthetic HTTP fixture, never Contracts/Storage/Client production source. It verifies packaged key/JSON/byte/retained-S3 contracts and loads the UI assembly. Package asset inspection checks scoped CSS and scripts; actual Club render-mode/static-asset/browser acceptance remains a separate consumer gate.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows 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
1.0.0-preview.7 38 10/27/2026
1.0.0-preview.6 42 10/27/2026
1.0.0-preview.5 44 10/27/2026

Fix disabled, busy and interop readiness states (issue #7), including accepted folder batches when a host locks inputs through UploadingChanged. See CHANGELOG.md.