CI / lint (pull_request) Successful in 3m39s
CI / format (pull_request) Successful in 3m24s
CI / install (pull_request) Successful in 9m4s
CI / typetest (pull_request) Successful in 3m5s
CI / typecheck (pull_request) Successful in 3m20s
CI / node-tests (pull_request) Successful in 3m31s
CI / browser-tests (pull_request) Successful in 5m16s
Give every workspace a README describing what it does, its entry points, and a short usage example, and link them from the root package table. Frame Tailshare in the root README as the application the libraries exist to be composed into, with the other apps as development and testing surfaces. Its own README records what works today and what the open issues intend for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@webnet/ftp
FTP/FTPS client and server built on @webnet/transport and @webnet/vfs.
FTPClient implements AsyncVFS, so it can be used anywhere an AsyncVFS is expected (stat, readdir, readFile/readFileRange, writeFile, delete, mkdir, move). It supports plaintext FTP, implicit FTPS (TLS from the first byte, via dialer.dialTls), and explicit FTPS (plaintext control connection upgraded with AUTH TLS/PROT P). FTPServer serves an AsyncVFS over FTP, with optional per-login authenticate to select a different VFS per user and optional TLS credentials for explicit AUTH TLS. Errors from both sides surface as FTPError, carrying the underlying Reply.
Entry points
| Entry point | Description |
|---|---|
@webnet/ftp |
FTPClient, FTPServer, FTPError, and their option/reply types. |
@webnet/ftp/client |
FTPClient and its option types only. |
@webnet/ftp/server |
FTPServer and its option types only. |
_internals entry points are unstable and are not part of the public API.
Usage
Client
import { FTPClient } from "@webnet/ftp/client"
import type { RawDialer } from "@webnet/transport"
declare const dialer: RawDialer
const client = new FTPClient({ dialer, host: "ftp.example.com", user: "anonymous" })
const entries = await client.readdir("/pub")
const stream = await client.readFile("/pub/readme.txt")
await client.close()
Server
import { FTPServer } from "@webnet/ftp/server"
import type { RawListener } from "@webnet/transport"
import type { AsyncVFS } from "@webnet/vfs"
declare const vfs: AsyncVFS
declare const controlListener: RawListener
declare const dataListen: (port: number) => Promise<RawListener>
const server = new FTPServer({ vfs, dataListen })
await server.listen(controlListener)
See also
@webnet/vfs— theAsyncVFSinterface this package implements and serves@webnet/transport— theRawDialer/RawListener/RawTransportprimitives this package is built on@webnet/sftp— an alternative file-transfer protocol over SSH instead of FTP