Skip to content
Runtime
Framework

Environment packages

function createBrowserAdapter(options?: BrowserAdapterOptions): BrowserQueryAdapter;

BrowserAdapterOptions is { target?: Window }. The target is resolved when the adapter is created; without one, and without an ambient window, it throws with a message naming target.

BrowserQueryAdapter is a QueryAdapter plus dispose(): void. After disposal, push, replace, and subscribe throw; read continues to work.

Writes go through history.pushState and history.replaceState. The URL is rebuilt from location.href with only the query replaced, so a pathname that starts with // and the hash are kept. replace keeps the entry’s existing history.state; push starts the new entry with null state, so another library’s state is not copied onto an entry it did not create. Subscribers are notified directly after a write — the History API does not fire popstate for programmatic navigation — and on a popstate whose location.search differs from the last announced one; a hash-only or path-only movement stays quiet.

A write performed by other code through pushState or replaceState fires no event and is not observed. Share one adapter per window rather than creating several, and prefer @queryweave/vue-router when a router owns the history.

function readUrlQuery<TDefs>(url: string | URL, model: QueryModel<TDefs>): DecodeResult<Values>;
function readUrlQueryAsync<TDefs>(
url: string | URL,
model: QueryModel<TDefs>,
): Promise<DecodeResult<Values>>;
function readRequestQuery<TDefs>(request: Request, model: QueryModel<TDefs>): DecodeResult<Values>;
function readRequestQueryAsync<TDefs>(
request: Request,
model: QueryModel<TDefs>,
): Promise<DecodeResult<Values>>;

Only the query is read, and it is read from the string itself: the part between the first ? and any #. A string does not need to be a well-formed URL, so an unusual host or path never throws.

function createRequestQuerySource(request: Request): WebRequestQuerySource;

WebRequestQuerySource is a QuerySource plus readonly request: Request.

function encodeQuery<TDefs>(model: QueryModel<TDefs>, value: Values): string;
function createQueryUrl<TDefs>(base: string | URL, model: QueryModel<TDefs>, value: Values): URL;

encodeQuery returns the canonical query string with no leading ?. createQueryUrl returns a new URL — the base is copied, not mutated — with managed keys first and unmanaged keys preserved afterwards in their original order. Unlike the readers, it needs a well-formed base; a string that does not parse as absolute is resolved against relativeUrlBase.

const relativeUrlBase = "http://queryweave.invalid";
function resolveNodeRequestUrl(
request: IncomingMessage,
options?: ResolveNodeRequestUrlOptions,
): URL;

ResolveNodeRequestUrlOptions is { host?, protocol?, trustForwardedHeaders? }.

Precedence for the authority: explicit host, then x-forwarded-host only if trustForwardedHeaders is true, then the host header, then the HTTP/2 :authority pseudo-header, then queryweave.invalid. The scheme follows the same pattern with x-forwarded-proto and :scheme, then https when the request’s socket is encrypted, then http. A comma-separated header uses its first entry, trimmed; an empty value is ignored.

The request target is originalUrl when a framework set it (Express and others strip a mounted router’s prefix from url), otherwise url. An origin-form target is composed onto the authority path-first, so GET //evil.example/p stays a path on the request’s own host; an absolute-form target is used as given, with explicit host and protocol overrides applied. The function never throws: an authority that cannot form a URL falls back to queryweave.invalid.

function readNodeQuery<TDefs>(request, model, options?): DecodeResult<Values>;
function readNodeQueryAsync<TDefs>(request, model, options?): Promise<DecodeResult<Values>>;
function createNodeQuerySource(request, options?): NodeRequestQuerySource;

NodeRequestQuerySource is a QuerySource plus readonly request: IncomingMessage. All three delegate decoding to @queryweave/server.

function createMemoryQueryAdapter(options?: MemoryQueryAdapterOptions): MemoryQueryAdapter;

MemoryQueryAdapterOptions is { initial?: QueryInput; guard? }. guard is (next: QueryOutput, mode: QueryNavigationMode) => QueryNavigationResult | undefined, called before every write: return nothing to let it through, or a refused or redirected result to leave the stack untouched and report that outcome, the way a router guard would.

MemoryQueryAdapter is a QueryAdapter plus:

Member Returns Notes
current() string The stored query, formatted, without a leading ?
entries() QueryOutput The same entries read() returns
back() void No-op at the start of the stack
forward() void No-op at the end of the stack
canGoBack() boolean
canGoForward() boolean
dispose() void Mutating calls throw afterwards

push truncates any forward entries, exactly like a browser. replace rewrites the current entry without changing the stack length. Entries are copied on write. Navigation is synchronous, which real adapters are not; a test that depends on asynchronous ordering should wrap the adapter or use the runtime’s serialization guarantees rather than assume the memory adapter’s timing.

The adapter’s index is intentionally not exposed; canGoBack and canGoForward are the supported way to reason about position.