/** * HTTP QUERY read-leg — the mutation channel's read side (#119). * * Transport-agnostic graph read: the host's {@link GraphStore.loadGraph} is the * authority; LiteShip validates the wire graph and exposes conditional reads via * the sha256 `integrity_digest` (never fnv1a — silent-stale / cache-poisoning). * * client: sendGraphQuery(url, \{ ifNoneMatch \}) * server: handleGraphQuery(request, store) * * `@czap/astro` wraps `handleGraphQuery` into `graphQueryRoute`; the host mounts * it on `export const QUERY` (POST + `X-Czap-Query` fallback when QUERY is absent). * * @module */ import type { DocumentGraph } from './document-graph.js'; import type { GraphStore } from './graph-mutation.js'; /** Optional conditional-read validator carried on the wire or from `If-None-Match`. */ export interface GraphQueryRequest { /** Client's cached etag — MUST be the sha256 `integrity_digest`, never fnv1a. */ readonly ifNoneMatch?: string; } /** * Read-leg response — one shape for callers: * - `ok` — the verified server graph + its etag; * - `not_modified` — conditional hit (digest unchanged); * - `refused` — malformed validator or store graph failed verification; * - `error` — server-side load failure (retryable). */ export type GraphQueryResponse = { readonly status: 'ok'; readonly graph: DocumentGraph; readonly etag: string; } | { readonly status: 'not_modified'; readonly etag: string; } | { readonly status: 'refused'; readonly errors: readonly string[]; } | { readonly status: 'error'; readonly message: string; }; /** HTTP fallback header when the host cannot dispatch `QUERY` (loud ladder, not silent). */ export declare const GRAPH_QUERY_FALLBACK_HEADER = "X-Czap-Query"; /** * The cache validator for conditional reads — sha256 `integrity_digest`, NOT the * fnv1a display `id`. The digest excludes mutable `meta` by construction, so a * meta-only advance (display/version bookkeeping) intentionally does NOT * invalidate a cached graph: CAS correctness keys on `base.id`, and `meta` is * display-layer data that never participates in patch application. Documented * contract, not an oversight. */ export declare function graphQueryEtag(graph: DocumentGraph): string; /** Parsed multi-member `If-None-Match`: sha256 candidates plus the `*` wildcard. */ export interface GraphQueryEtagCandidates { readonly candidates: readonly string[]; /** RFC 9110: `If-None-Match: *` matches any current representation. */ readonly matchAny: boolean; } /** * Parse a full `If-None-Match` header into ALL comma-separated members * (RFC 9110 §13.1.2 — a compliant cache may list several stored validators; * evaluating only the first would 422 or full-200 requests that should 304). * Any fnv1a member refuses the whole request — a client that cached the * display id is the silent-stale bug this channel exists to prevent. */ export declare function parseGraphQueryEtagList(value: string | undefined): GraphQueryEtagCandidates | { readonly errors: readonly string[]; }; /** Normalize a SINGLE HTTP etag value (e.g. a response `ETag` header) to bare sha256, or refuse fnv1a. */ export declare function normalizeGraphQueryEtag(value: string | undefined): string | { readonly errors: readonly string[]; }; /** * Process one graph read against the host store. Pure of transport: load → verify → * conditional etag compare. NEVER throws — failures map to the response shape. */ export declare function handleGraphQuery(request: GraphQueryRequest, store: Pick): Promise; /** Options for the retrying QUERY read sender. */ export interface SendGraphQueryOptions { /** Injectable fetch for tests / non-browser hosts. Defaults to global `fetch`. */ readonly fetchImpl?: typeof fetch; /** Conditional validator — sha256 integrity_digest only. */ readonly ifNoneMatch?: string; /** Bounded retries on transport / server `error` outcomes (reads are idempotent). Default: 2. */ readonly maxRetries?: number; /** * Base delay between retry attempts in ms (doubles each attempt — 150, 300, * 600, …). Default: 150. Pass 0 for immediate retries (tests). */ readonly retryDelayMs?: number; } /** * Client-side sender: QUERY the host's graph read endpoint with optional conditional * etag and bounded retries. Tries `QUERY` first; on 405/501/404 falls back to POST with * {@link GRAPH_QUERY_FALLBACK_HEADER} (loud — not a silent downgrade). NEVER rejects. */ export declare function sendGraphQuery(url: string, options?: SendGraphQueryOptions): Promise; /** Build a host-owned `refreshBase` for {@link createGraphMutationClient} over the read leg. */ export declare function createGraphQueryRefreshBase(url: string, options?: Pick & { readonly currentEtag?: () => string | undefined; /** * The caller's CURRENT base graph. A conditional read that returns * `not_modified` (F-REC-4) is a NORMAL outcome, not an error — the caller * already holds the current graph (it computed the `If-None-Match` etag from * it). Supplying it lets `refreshBase` resolve to that graph instead of * throwing, so a `not_modified` no longer aborts recovery. */ readonly currentBase?: () => DocumentGraph | undefined; }): () => Promise; //# sourceMappingURL=graph-query.d.ts.map