@pgxsinkit/pgwasm
Version:
163 lines • 8.56 kB
TypeScript
/**
* `RepackedSyncClient` — the backend side of the synchronous broker.
*
* Every method blocks the calling thread in `Atomics.wait` until the coordinator answers, which is the
* whole point: a thread running wasm parks in futexes and can never observe a promise, so the file
* layer under it has to be synchronous. Nothing here is async, and nothing here knows about wasm.
*
* ## What throws and what does not
*
* A FILE rejection is data: every method returns `{ errno }` (0 on success) with whatever else the
* operation produced. Callers map the errno straight into their own ABI — the values are WASI preview1
* errno numbers already.
*
* A TRANSPORT failure throws, because there is no answer to return: the server detached this client,
* the client sent something the server refused, the server never replied inside the timeout, or the
* store itself failed. Those are `RepackedBrokerTransportError` and `RepackedBrokerStoreError`.
*
* ## Mapping onto WASI preview1
*
* The method set is shaped so a preview1 adapter is a straight translation, with no state of its own
* beyond the preopen table:
*
* path_open → open(path, flags, mode) (flags are the POSIX bits, see protocol)
* fd_close → close(fd)
* fd_read → read(fd, length) (no position: the store's own cursor)
* fd_pread → read(fd, length, position)
* fd_write → write(fd, bytes)
* fd_pwrite → write(fd, bytes, position)
* fd_seek → resolved by the caller; the store's cursor moves on cursor reads/writes
* fd_sync / fd_datasync → fsync(fd) (store-wide; see the server's note)
* fd_filestat_get → fstat(fd)
* path_filestat_get → stat(path) / lstat(path)
* fd_readdir → readdir(path) or readdirPage(path, cursor) for the cookie form
* path_create_directory → mkdir(path)
* path_remove_directory → rmdir(path)
* path_unlink_file → unlink(path)
* path_rename → rename(oldPath, newPath)
* path_symlink → symlink(target, path) (targets are ABSOLUTE; see the core)
* path_readlink → readlink(path)
* fd_filestat_set_size → truncate(path, size) — the core resizes by PATH only, so the adapter
* keeps the path it opened each fd with and resolves it here
*
* Reads and writes larger than the channel's payload region are split transparently; a caller never
* has to know the channel size.
*/
import type { BrokerStat, RepackedChannel } from "./protocol";
/** The transport failed: there is no answer, and this client can no longer make progress. */
export declare class RepackedBrokerTransportError extends Error {
readonly brokerCode: "detached" | "protocol" | "timeout";
constructor(brokerCode: "detached" | "protocol" | "timeout", message: string);
}
/** The store behind the broker failed. Not a file rejection, and not this client's mistake. */
export declare class RepackedBrokerStoreError extends Error {
readonly brokerCode = "store";
constructor(message: string);
}
/** Every result carries an errno; 0 means the operation succeeded. */
export interface BrokerResult {
readonly errno: number;
}
export interface BrokerOpenResult extends BrokerResult {
readonly fd: number;
}
export interface BrokerCountResult extends BrokerResult {
/** Bytes actually transferred. Short of the request means end-of-file (read) or a store limit (write). */
readonly count: number;
}
export interface BrokerReadResult extends BrokerCountResult {
readonly bytes: Uint8Array;
}
export interface BrokerStatResult extends BrokerResult {
readonly stat: BrokerStat | undefined;
}
export interface BrokerSizeResult extends BrokerResult {
readonly size: bigint;
}
export interface BrokerReaddirResult extends BrokerResult {
readonly entries: readonly string[];
}
export interface BrokerReadlinkResult extends BrokerResult {
/** The link's target, or `undefined` when the call was rejected. */
readonly target: string | undefined;
}
export interface BrokerReaddirPageResult extends BrokerReaddirResult {
/** The cursor to resume from, or `undefined` when the listing is complete. */
readonly nextCursor: number | undefined;
}
export interface RepackedSyncClientOptions {
/**
* How long one request may go unanswered before the client gives up and throws. A dead coordinator
* would otherwise park this thread forever, which is worse than a loud failure. Pass `Infinity` for
* a host that genuinely prefers to wait.
*/
readonly requestTimeoutMs?: number;
}
export declare class RepackedSyncClient {
#private;
readonly channel: RepackedChannel;
constructor(channel: RepackedChannel, options?: RepackedSyncClientOptions);
/** The largest single read or write that fits one request. Reads/writes above it are chunked. */
get maxTransferBytes(): number;
open(path: string, flags: number, mode?: number): BrokerOpenResult;
close(fd: number): BrokerResult;
/**
* Read up to `length` bytes. With no `position` the store's own descriptor cursor is used and
* advances; with a `position` the cursor is untouched (`fd_pread`). A result shorter than `length`
* means end-of-file, never a partial transport.
*/
read(fd: number, length: number, position?: bigint): BrokerReadResult;
/**
* Write `bytes`. With no `position` the store's own descriptor cursor is used and advances; with a
* `position` the cursor is untouched (`fd_pwrite`). A descriptor opened with `O_APPEND` always
* writes at end-of-file and ignores `position`, exactly as POSIX requires.
*/
write(fd: number, bytes: Uint8Array, position?: bigint): BrokerCountResult;
/**
* Flush the store. Durability is STORE-WIDE, not per-descriptor: on success every byte written
* through this broker by any client before the call returned is recoverable. `fd` is validated so
* the call still rejects a descriptor this client does not own.
*/
fsync(fd?: number): BrokerResult;
fstat(fd: number): BrokerStatResult;
stat(path: string): BrokerStatResult;
/** Reports the LINK itself when the final component is one; `stat` follows it instead. */
lstat(path: string): BrokerStatResult;
/**
* Create a symbolic link at `path` pointing at `target`. Targets are ABSOLUTE — the store refuses
* a relative one with `EINVAL` rather than reinterpreting it against the link's directory.
*/
symlink(target: string, path: string): BrokerResult;
/** The target of the symbolic link at `path`. `EINVAL` when the path is not a link. */
readlink(path: string): BrokerReadlinkResult;
/** The complete listing, paged transparently over as many requests as the channel needs. */
readdir(path: string): BrokerReaddirResult;
/**
* One page of a listing, for a caller that owns its own cookie (WASI `fd_readdir`). The cursor is an
* index into the store's sorted listing, recomputed per call — a paged listing is therefore not an
* atomic snapshot, which is exactly what POSIX `readdir` allows.
*/
readdirPage(path: string, cursor?: number): BrokerReaddirPageResult;
mkdir(path: string, options?: {
recursive?: boolean;
mode?: number;
}): BrokerResult;
rmdir(path: string): BrokerResult;
unlink(path: string): BrokerResult;
rename(oldPath: string, newPath: string): BrokerResult;
/**
* Resize by PATH. The core store has no resize-by-descriptor, so a WASI `fd_filestat_set_size`
* adapter keeps the path each fd was opened with and calls this. Nothing is lost by that: the store
* has no hard links, and a descriptor whose path was unlinked meanwhile is an orphan the core keeps
* readable but no longer resizes.
*/
truncate(path: string, size: bigint): BrokerResult;
/** The size of one file, without the rest of a stat. `EISDIR` for a directory. */
size(path: string): BrokerSizeResult;
}
/**
* Turn a broker result into the throw the core store would have produced, for a caller that prefers
* exceptions to errnos. The rebuilt `FsError` carries the same numeric `code` the broker reported.
*/
export declare function throwOnErrno(result: BrokerResult, operation: string, path?: string): void;
//# sourceMappingURL=client.d.ts.map