/** * A typed attribute value, externally tagged exactly as `nidus` serde-encodes * `Value` on the wire: `{ Str }`, `{ Int }`, `{ Bool }`, `{ List }`, `{ Float }`, * `{ DateTime }` (epoch milliseconds, UTC), or the bare string `"Null"`. * * `Null` is distinct from an absent key: absence means "not set / not indexed", * `Null` means "set, and empty/none". */ type Value = { Str: string; } | { Int: number; } | { Bool: boolean; } | { List: string[]; } | { Float: number; } | { DateTime: number; } | "Null"; /** * What callers may pass anywhere a {@link Value} is expected: either an * explicitly-tagged `Value` (from the `v.*` helpers) or a plain JS scalar that * the SDK normalizes — `string → Str`, `boolean → Bool`, `string[] → List`, * `Date → DateTime`, `null → Null`, and a `number` to `Int` or `Float` by * `Number.isInteger` (JS has no int type, so the value has to decide; use * {@link v.float} to pin a whole-numbered field to `Float`). */ type AttrInput = Value | string | number | boolean | string[] | Date | null; /** A document: caller-supplied `id`, an optional embedding, and typed metadata. */ interface NidusRecord { id: string; /** Omit for a text-only doc (indexed by FTS/metadata only, never by vector search). */ vector?: number[]; /** * Named vectors beyond the reserved `"default"` vector above (nidus-85t), keyed by * name. Each name must be declared on the collection first with * {@link NidusClient.setVectorNames}, and every vector must be the store's dimension. */ vectors?: Record; attrs: Record; } /** Like {@link NidusRecord} but accepts plain JS values in `attrs` (auto-normalized). */ interface RecordInput { id: string; vector?: number[]; vectors?: Record; attrs: Record; } /** A record read back from the server, with `attrs` decoded to plain JS values. */ interface DecodedRecord { id: string; vector?: number[]; vectors?: Record; attrs: Record; } /** A single attribute predicate, externally tagged as `nidus` encodes `Predicate`. */ type Predicate = { Eq: [string, Value]; } | { Ne: [string, Value]; } | { Glob: [string, string]; } | { IGlob: [string, string]; } | { In: [string, Value[]]; } | { NotIn: [string, Value[]]; } | { Lt: [string, Value]; } | { Le: [string, Value]; } | { Gt: [string, Value]; } | { Ge: [string, Value]; } | { Contains: [string, Value]; } | { NotContains: [string, Value]; } | { ContainsAny: [string, Value[]]; } | { All: Predicate[]; } | { Any: Predicate[]; } | { Not: Predicate; } /** The one three-element leaf: key, text, and the edit budget. */ | { Fuzzy: [string, string, number]; } | { ContainsAllTokens: [string, string]; } | { ContainsAnyToken: [string, string]; } | { ContainsTokenSequence: [string, string]; } | { Regex: [string, string]; }; /** * A conjunction (AND) of predicates. On the wire `Filter` is a newtype over * `Vec`, so it serializes as a bare array — an empty array matches * everything. */ type Filter = Predicate[]; /** A search/list result row, decoded so `attrs` holds plain JS values. */ interface Hit { collection: string; id: string; score: number; attrs: Record; /** Why this hit matched — present only when the query asked to `explain` or highlight. */ annotations?: Annotations; /** The hit's chunk widened with its neighbours. Present only when the query asked to * `expand` (or, on recall, to `rollup`). */ context?: string; } /** One fusion leg's own view of a hit: its rank in that leg (0-based) and that leg's score. */ interface LegScore { rank: number; score: number; } /** One BM25 clause's contribution to a hit's text score. Only matched clauses appear. */ interface ClauseScore { field: string; score: number; /** Set only for a prefix clause. `matched > scored` means the cap truncated it. */ expansion?: Expansion; } /** * How far a prefix clause's final term expanded: how many indexed terms carried the * prefix, and how many were scored after the expansion cap. */ interface Expansion { matched: number; scored: number; } /** * An excerpt of a field's stored text plus the ranges a query term matched. The server * reports UTF-8 **byte** offsets; the SDK converts them to JS string indices (UTF-16 code * units), so `text.slice(...span)` is the matched term even when the excerpt is not ASCII. */ interface Fragment { text: string; spans: [number, number][]; } /** The fragments found in one full-text field. */ interface Highlight { field: string; fragments: Fragment[]; } /** * Why a hit matched. Every part is opt-in and absent when it carries nothing, so a hit * annotated by `explain` alone has no `highlights` key. */ interface Annotations { /** The vector leg's rank and score, on a hybrid hit that leg returned. */ vector?: LegScore; /** The BM25 leg's rank and combined text score, on a hybrid hit that leg returned. */ text?: LegScore; /** Each matched clause's own BM25 score, in query order. */ clauses?: ClauseScore[]; /** Highlighted fragments, one entry per clause field that had a match. */ highlights?: Highlight[]; } /** * The scan strategy a `*WithPlan` query actually took. An open string, not a closed * union: known values today are `"ann"`, `"ann_prefilter_fallback"`, `"segmented"`, * `"quantized"`, `"exact"`, but a newer server may report one this SDK predates. */ type QueryPath = string; /** How many candidates survived each stage of a `*WithPlan` query, per {@link QueryPlan}. */ interface PlanCandidates { surfaced: number; survived: number; droppedOutOfScope: number; droppedStale: number; droppedFiltered: number; droppedMinScore: number; } /** Whether a `*WithPlan` query's filter index narrowed the scan, per {@link QueryPlan}. */ interface PlanNarrowing { state: "inactive" | "declined" | "narrowed"; /** Present only when `state` is `"narrowed"`. */ candidates?: number; } /** Per-stage timings of a `*WithPlan` query, in integer microseconds. */ interface PlanTimings { narrowUs?: number; gatherUs?: number; walkUs?: number; resolveUs?: number; firstPassUs?: number; rescoreUs?: number; scoreUs?: number; totalUs: number; } /** * How the server answered a `*WithPlan` query, returned alongside its hits by * {@link NidusClient.searchWithPlan}, {@link NidusClient.searchSimilarWithPlan}, and * {@link NidusClient.hybridSearchWithPlan}. */ interface QueryPlan { path: QueryPath; /** Absent on the `ann`/`segmented` paths, where it does not apply. */ rowsScanned?: number; /** Absent when no filter-index walk ran. */ candidates?: PlanCandidates; narrowing: PlanNarrowing; timings: PlanTimings; } /** * A {@link Value} decoded back to a plain JS value. A `DateTime` comes back as a * `Date`, not a number, so a decoded `attrs` map re-encodes to what it came from. */ type DecodedValue = string | number | boolean | string[] | Date | null; /** On-disk footprint, mirroring `FootprintDto`. */ interface Footprint { rows: number; dead_rows: number; dimension: number; vector_bytes: number; doc_count: number; } /** Active ANN-index configuration, mirroring `AnnDto` (`null` when exact search). */ interface AnnInfo { kind: string; overscan: number; seed: number; m?: number; ef_construction?: number; ef_search?: number; n_lists?: number; n_probe?: number; } /** Store-wide introspection, mirroring the `/stats` response. */ interface Stats { dimension: number; distance: string; ann: AnnInfo | null; collections: string[]; footprint: Footprint; } /** * `/ready` readiness, mirroring the wire verbatim in snake_case. A `200` decodes * `role`/`staleness_secs`; a `503` carries only `reason` (see * {@link NidusClient.ready}, which never throws on either). */ interface Readiness { ready: boolean; role?: string; staleness_secs?: number; reason?: string; } /** * `/cluster` status, mirroring the wire verbatim in snake_case. `role` is the * server's own `{:?}` spelling, passed through as-is so a future role needs no * SDK change (same reasoning as {@link AnnInfo.kind}). */ interface ClusterStatus { role: string; cluster: boolean; holds_writer_handle: boolean; fenced: boolean; lease_owner: string | null; commit_version: number; staleness_secs: number; max_staleness_secs: number | null; } /** The readable commit points and this instance's pin (SPEC §14.2). */ interface StoreVersions { commit_version: number; oldest_readable: number | null; pinned: number | null; readable: number[]; } /** * Which attrs the returned hits carry. Omit both for every attr (the default). * Sending both is a `400` — the server refuses rather than picking one. */ interface ProjectionOptions { /** Return only these attrs. A named attr the record lacks is simply absent. */ includeAttributes?: string[]; /** Return every attr but these. */ excludeAttributes?: string[]; } /** * Recency decay over a timestamp attribute. The penalty is *subtracted* from the base * score — `score = base - lambda * (1 - decay ^ (age / scale))` — so it stays meaningful * for a metric whose scores are negative or unbounded (Euclidean, dot product, BM25). */ interface Decay { /** The timestamp attribute: a `DateTime`, or an `Int` of epoch milliseconds. */ field: string; /** * "Now". Ages are measured back from here rather than from the wall clock, so the same * query against an unchanged store ranks the same way twice. A `Date` or epoch ms. */ origin: Date | number; /** Age in milliseconds at which the factor equals `decay` (default: 7 days). */ scale?: number; /** Factor reached at exactly `scale` old, in `(0, 1)`; the default `0.5` makes it a half-life. */ decay?: number; /** Score a fully-decayed hit gives up (default `1`). */ lambda?: number; /** Factor for a record whose `field` is missing or not a timestamp. Defaults to `1` — no penalty. */ missing?: number; /** * An `Int` attribute counting how often a record was used (e.g. `nidus.access_count`). * The count term applies only when this is set; omit it to decay by recency alone. */ countField?: string; /** Count at which the count term reaches its full effect (default `10`). */ countScale?: number; /** Weight of the count term, subtracted like the recency penalty (default `1`). */ countLambda?: number; } /** * A ranking expression layered over the store's distance metric. Omitting it is the bare * metric — the ranking nidus has always returned. */ type RankBy = { decay: Decay; }; /** * Cap how many hits may carry any one value of an attribute — "at most 2 hits per file". * Records *missing* the attribute form one shared group, so an absent value cannot evade * the cap. Approximate: it thins the ranking rather than searching deeper to refill it. */ interface LimitPer { field: string; max: number; } /** * Widen each hit with the neighbouring chunks of its own document, returned as * {@link Hit.context}. Payload only: the ranking is exactly what it was without it. * Every field but `radius` defaults to the reserved attrs `nidus ingest` stamps. */ interface Expand { radius: number; parentField?: string; indexField?: string; textField?: string; } /** * Read a chunked corpus as documents rather than fragments: keep each document's best * `perParent` chunks, then widen each with `neighbours` chunks either side. */ interface Rollup { perParent?: number; neighbours?: number; } /** * Sort a {@link NidusClient.list} by an attribute instead of storage order. Values of * another type, unorderable ones (`null`/lists/`NaN`), and records missing the attribute * sort into one trailing bucket, which stays trailing in both directions. */ interface OrderBy { field: string; descending?: boolean; } /** * How several named-vector scores fold into one per-record score, before top-k * selection (nidus-85t). `"Max"` (the default) takes the best weighted per-name score; * an absent name then contributes nothing and carries no penalty. `"Sum"` adds every * weighted named score. Meaningless when {@link SearchOptions.names} is empty. */ type Pool = "Max" | "Sum"; /** Ranking knobs shared by {@link NidusClient.search} and {@link NidusClient.textSearch}. */ interface RankingOptions { rankBy?: RankBy; limitPer?: LimitPer; /** * Maximal Marginal Relevance lambda, spreading results apart in vector space so * near-duplicates stop filling a page. `1` is pure relevance, `0` pure variety. * Omitted leaves the ranking exactly as it is. */ diversity?: number; /** See {@link Expand}. Adds `context` to each hit and changes nothing else. */ expand?: Expand; } /** How several {@link TextClause}s fold into one text score. */ type FtsCombine = "Sum" | "Max"; /** One clause of a multi-field text query: an indexed field and the query text for it. */ interface TextClause { field: string; query: string; /** Expand the clause's final term as a prefix (typeahead). Omitted means `false`. */ prefix?: boolean; } /** How much text a highlight carries. `fragmentChars` is a character budget, not bytes. */ interface HighlightOptions { /** Most fragments returned per field (default `1`). */ maxFragments?: number; /** Characters per fragment (default `160`): leading context, then the match and its tail. */ fragmentChars?: number; } /** Annotation knobs shared by {@link NidusClient.textSearch} and {@link NidusClient.hybridSearch}. */ interface AnnotationOptions { /** Report each leg's and each matched clause's own score in a hit's `annotations`. */ explain?: boolean; /** * Return highlighted fragments; `true` takes the defaults. Highlighting reads the * stored text, so it still works on a field the projection dropped. */ highlight?: boolean | HighlightOptions; } /** * Rerank the candidate window with a hosted cross-encoder before returning it. The server * ranks `(offset + topK) * overscan` deep, scores each candidate's text against `query`, * and returns the caller's page of that. Requires a server started with * `--rerank-provider`; without one the request is a 400. */ interface RerankOptions { /** * Text scored against each candidate. Required on {@link NidusClient.search} and * {@link NidusClient.hybridSearch}; defaults to the request's own text on * {@link NidusClient.recall} and on the single-field spelling of * {@link NidusClient.textSearch}. */ query?: string; /** Candidate over-fetch multiple (default `10`). Higher finds more, costs more. */ overscan?: number; /** Attr holding each candidate's text (default `"nidus.text"`). */ textAttr?: string; } /** Options for {@link NidusClient.search}. An empty/omitted `scope` searches every collection. */ interface SearchOptions extends ProjectionOptions, RankingOptions { query: number[]; scope?: string[]; topK?: number; /** Skip this many top-ranked hits, for pagination. `offset + topK` may not exceed 10000. */ offset?: number; minScore?: number; filter?: Filter; /** * Force the exact scan for this query, bypassing any ANN index and the * quantized first pass. The index stays in place for every other query. */ exact?: boolean; /** See {@link RerankOptions}. `query` is required here. */ rerank?: RerankOptions; /** * Named vectors to score (nidus-85t), each declared on the collection first with * {@link NidusClient.setVectorNames}. Omitted or empty (the default, and the only * shape a pre-nidus-85t caller ever sends) searches only the reserved `"default"` * vector, so an existing call is unaffected. A record is scored on whichever of * these names it actually carries, reduced to one score by `pool`. */ names?: string[]; /** * Per-name weight multiplying that name's score before pooling. A name absent here * weights `1`. Meaningless when `names` is empty. */ nameWeights?: Record; /** How several named scores fold into one record score (default `"Max"`). See {@link Pool}. */ pool?: Pool; } /** * Options for {@link NidusClient.searchSimilar}. `collection`/`id` name the source * record; an empty/omitted `scope` searches the source's own collection (not every * collection — the one place this differs from {@link NidusClient.search}). The source * record is never in the results. */ interface SimilarSearchOptions extends ProjectionOptions, RankingOptions { collection: string; id: string; scope?: string[]; topK?: number; /** Skip this many top-ranked hits, for pagination. `offset + topK` may not exceed 10000. */ offset?: number; minScore?: number; filter?: Filter; /** * Force the exact scan for this query, bypassing any ANN index and the * quantized first pass. The index stays in place for every other query. */ exact?: boolean; } /** * The two accepted spellings of a text query: one `field` plus its `query`, or a list of * `clauses` each carrying its own text. Sending both, or an empty list, is a `400` — an * empty result would otherwise read as "no matches" rather than "no query". */ type TextQuerySpelling = { field: string; query: string; /** Expand the shorthand's final term as a prefix (typeahead). Omitted means `false`. */ prefix?: boolean; clauses?: never; combine?: never; } | { clauses: TextClause[]; combine?: FtsCombine; field?: never; query?: never; prefix?: never; }; /** The knobs of {@link TextSearchOptions} that do not name what to search. */ interface TextSearchBase extends ProjectionOptions, RankingOptions, AnnotationOptions { scope?: string[]; topK?: number; /** Skip this many top-ranked hits, for pagination. */ offset?: number; /** A raw BM25 score floor (not cosine). */ minScore?: number; filter?: Filter; /** See {@link RerankOptions}. Backfills `query` from the single-field spelling. */ rerank?: RerankOptions; } /** Options for {@link NidusClient.textSearch} (BM25). */ type TextSearchOptions = TextSearchBase & TextQuerySpelling; /** Options for {@link NidusClient.suggest}. */ interface SuggestOptions { /** Collections whose vocabulary to complete from. Omit or leave empty for all of them. */ scope?: string[]; /** The full-text-indexed field whose vocabulary to complete from. */ field: string; /** * The phrase typed so far. Its final token is completed; the words before it narrow the * completions to documents that also contain them, so send the whole phrase. */ prefix: string; /** How many completions to return. The server defaults to 10. */ limit?: number; /** * Each completion's `df` counts only documents matching this filter, so a completion no * matching document carries is not offered at all. */ filter?: Filter; /** Typo tolerance. Omitted means the server default, which is on; pass `false` to opt out. */ fuzzy?: boolean; } /** One completion: an indexed term and how many live documents contain it. */ interface Suggestion { term: string; df: number; } /** * {@link NidusClient.suggest}'s result. `matched` counts every term the prefix matched * before the server's 256-term cap, so `matched > suggestions.length` means it truncated. */ interface Suggestions { suggestions: Suggestion[]; matched: number; } /** * One entry of {@link NidusClient.setFtsSchema}'s `fields`: the attribute to index * plus any BM25/analyzer knobs to override. Every knob is optional — omit them all * (or pass the bare field name instead) for the server's defaults, `k1 = 1.2`, * `b = 0.75`, US English, no folding, no token-length cap. */ interface FtsField { /** The attribute to full-text index. */ field: string; /** BM25 term-frequency saturation (default `1.2`). */ k1?: number; /** BM25 length normalization, `0`–`1` (default `0.75`). */ b?: number; /** Analyzer language; `"english"` is the only one today. */ language?: string; /** Fold Latin diacritics to ASCII, so `café` and `cafe` share a term. */ asciiFolding?: boolean; /** Drop tokens longer than this many characters (default: no cap). */ maxTokenLen?: number; } /** * One entry of {@link NidusClient.setFilterIndex}'s `fields`: the attribute to index for * the text predicates (`Fuzzy`, `ContainsAllTokens`, `ContainsAnyToken`, * `ContainsTokenSequence`, `Regex`), plus which structures to build for it. Both default * to `true`, so pass the bare field name unless you want to leave one out. * * Declaring an index changes how fast those predicates run, never what they return. */ interface FilterIndexField { /** The attribute to index. */ field: string; /** Token postings, serving the three token predicates (default `true`). */ tokens?: boolean; /** Character trigrams, serving `Fuzzy` and `Regex` (default `true`). */ trigrams?: boolean; } /** {@link TextQuerySpelling} for hybrid search, whose single form spells the text `text`. */ type HybridQuerySpelling = { field: string; text: string; /** Expand the shorthand's final term as a prefix (typeahead). Omitted means `false`. */ prefix?: boolean; clauses?: never; combine?: never; } | { clauses: TextClause[]; combine?: FtsCombine; field?: never; text?: never; prefix?: never; }; /** The knobs of {@link HybridSearchOptions} that do not name what the text leg searches. */ interface HybridSearchBase extends AnnotationOptions { vector: number[]; scope?: string[]; topK?: number; /** Skip this many hits of the *fused* ranking, for pagination. */ offset?: number; filter?: Filter; rrfK?: number; candidates?: number; /** Weight on the vector leg's RRF contribution. Both weights at `1` is plain fusion. */ vectorWeight?: number; /** Weight on the BM25 leg's RRF contribution (default `1`). */ textWeight?: number; /** See {@link Expand}. Applied after fusion, so the RRF order is untouched. */ expand?: Expand; /** See {@link RerankOptions}. `query` is required here. */ rerank?: RerankOptions; /** * Cap the fused hits carrying any one value of an attribute (nidus-29ui). Applied on * the shared cap → MMR → page-cut tail, same as {@link NidusClient.search}. */ limitPer?: LimitPer; /** MMR lambda spreading the fused page in vector space (nidus-29ui). See {@link RankingOptions.diversity}. */ diversity?: number; } /** Options for {@link NidusClient.hybridSearch} (vector + BM25 fused via RRF). */ type HybridSearchOptions = HybridSearchBase & HybridQuerySpelling; /** Options for {@link NidusClient.list} (metadata-only, paginated). */ interface ListOptions extends ProjectionOptions { scope?: string[]; offset?: number; limit?: number; filter?: Filter; /** Sort by an attribute instead of storage order. */ orderBy?: OrderBy; } /** Options for {@link NidusClient.aggregate}. An empty/omitted `scope` covers every collection. */ interface AggregateOptions { scope?: string[]; filter?: Filter; /** Attributes to sum. A missing or non-numeric value is skipped, not counted as zero. */ sum?: string[]; /** * Report one {@link Group} per distinct value of this attribute, alongside the * whole-scope totals. An empty string is a `400`, not "no grouping" — omit it instead. */ groupBy?: string; } /** What {@link NidusClient.aggregate} answers: the match count plus one sum per named field. */ interface Aggregation { count: number; /** One entry per requested `sum` field, decoded from its tagged `Int`/`Float`. */ sums: Record; /** One row per distinct `groupBy` value, largest first. Absent when none was asked for. */ groups?: Group[]; /** Distinct values outran the server's cap and later ones were dropped. */ groupsTruncated?: boolean; } /** * One distinct `groupBy` value with the aggregates over just its records. `value` is `null` * for the records missing the attribute — a different group from those holding a `null`. */ interface Group { value: DecodedValue | null; count: number; sums: Record; } /** * Options for {@link NidusClient.batchSearch}: several vector queries answered in one * round-trip, capped at 16 by the server. Each entry is an ordinary {@link SearchOptions}. */ interface BatchSearchOptions { queries: SearchOptions[]; /** Merge the per-query rankings into ONE list instead of returning them side by side. */ fuse?: BatchFuse; } /** * Cross-query Reciprocal Rank Fusion — the same fusion `hybridSearch` runs, over N query * legs. `weights` must be empty or exactly as long as `queries`; the server refuses a short * list rather than silently re-weighting the wrong leg. */ interface BatchFuse { rrfK?: number; weights?: number[]; topK?: number; } /** * Options for {@link NidusClient.remember} (text-native ingest). The server * embeds the text and upserts; `mode: "summarize"` summarizes it first (and * requires the server to have been started with a summarizer). */ interface RememberOptions { /** * `"raw"` (embed the text as given, the default) or `"summarize"` (summarize * first, then embed the summary — stamps a `nidus.summary` attr). The raw text * is always stored under `nidus.text`. */ mode?: "raw" | "summarize"; /** Typed metadata to stamp on the stored record (plain JS values auto-normalized). */ attrs?: Record; /** Seconds until this memory expires, counted from the write. Omit to never expire. */ ttlSeconds?: number; /** * Cosine-similarity floor above which this write updates the nearest existing entry * instead of inserting a competing near-duplicate. Omit to disable (a plain upsert by * `id`). Needs a server with an embedder; an expired entry is never a candidate. */ dedupeThreshold?: number; } /** What {@link NidusClient.remember} resolves to. */ interface RememberResult { /** * The id actually written. Equal to the requested id unless `deduped` — a dedupe match * redirects the write onto the entry it matched. */ id: string; /** How many records the write touched. */ upserted: number; /** Whether `dedupeThreshold` matched an existing entry and redirected the write. */ deduped: boolean; } /** * Options for {@link NidusClient.codeSearch} (the `code` feature): a text query over the * metadata `nidus code ingest` stamps, grouped by file. */ interface CodeSearchOptions { /** Collection to search. */ collection: string; /** The search text: a symbol name, phrase, or snippet, depending on `vector`. */ query: string; /** How many hits to gather before grouping by file. The server defaults to 10. */ limit?: number; filter?: Filter; /** * `true` forces a vector search, `false` forces BM25. Omit to defer to the store: a * dimension-0 (fts-only) store answers BM25, any other store answers vector. */ vector?: boolean; } /** * One matched symbol within a file, from {@link NidusClient.codeSearch}: everything an * agent needs to go read the real source, never the source body itself. `symbol`, `kind`, * `startLine` and `endLine` are `null` for a hit whose file fell back to non-AST chunking. */ interface CodeSymbolHit { symbol: string | null; kind: string | null; /** 1-based first line of the symbol in its source file. */ startLine: number | null; /** 1-based last line of the symbol in its source file. */ endLine: number | null; score: number; } /** * Every hit that landed in one file, from {@link NidusClient.codeSearch}: its language * (present only when the file was AST-chunked) and its symbols, ranked by descending score. */ interface CodeFileHit { path: string; language: string | null; symbols: CodeSymbolHit[]; } /** Options for {@link NidusClient.recall} (embed the query text, then vector-search). */ interface RecallOptions { topK?: number; /** Cosine-similarity floor; hits below it are dropped. */ minScore?: number; filter?: Filter; /** * Maximal Marginal Relevance lambda, so one verbose document's near-identical chunks * stop filling the recalled window. `1` is pure relevance, `0` pure variety. */ diversity?: number; /** See {@link Rollup}. The text-native spelling of `limitPer` plus `expand`. */ rollup?: Rollup; /** See {@link RerankOptions}. Defaults `query` to the request's own text. */ rerank?: RerankOptions; /** * Record that these entries proved useful, stamping `nidus.access_count` and * `nidus.last_accessed`. This makes the recall a write: it takes the server's writer * lock and is refused on a read-only server. */ reinforce?: boolean; /** * Push an existing `nidus.expires_at` out to this many seconds from now. Only applies * with `reinforce`, and never gives an expiry to an entry that had none. */ extendTtlSeconds?: number; /** * Ranking expression layered over cosine: decay over `nidus.last_accessed`, a * reinforcement term over `nidus.access_count`, or both. See {@link Decay}. */ rankBy?: RankBy; } /** * One statement's answer from {@link NidusClient.query}: a ranked/listed statement decodes * to a bare {@link Hit} array, or to `{hits, plan}` when it asked `WITH (plan)`; a `GROUP BY` * statement decodes to an {@link Aggregation}. */ type QueryAnswer = Hit[] | { hits: Hit[]; plan: QueryPlan; } | Aggregation; /** * One compiled statement from {@link NidusClient.compile}: the typed value the SQL front end * would hand the matching `Store` method (`search`/`text_search`/`hybrid_search`/`list`/ * `aggregate`), rendered for introspection only. `opts` (and, for `text_search`/`hybrid`, * `query`/`text`) are the server's raw snake_case JSON — never executed, so never decoded * into this SDK's own camelCase option shapes. */ type Compiled = { kind: "search"; collections: string[]; vector: number[]; opts: Record; } | { kind: "text_search"; collections: string[]; query: unknown; opts: Record; } | { kind: "hybrid"; collections: string[]; vector: number[]; text: unknown; opts: Record; } | { kind: "list"; collections: string[]; opts: Record; } | { kind: "aggregate"; collections: string[]; opts: Record; }; /** Minimal `fetch` signature the client needs — satisfied by the platform global. */ type FetchLike = (input: string, init?: RequestInit) => Promise; /** Construction options for {@link NidusClient}. */ interface NidusClientOptions { /** Base URL of the server, e.g. `http://127.0.0.1:7700`. Trailing slash optional. */ baseUrl: string; /** Bearer token, when the server was started with `--token`. */ token?: string; /** Override the `fetch` implementation (defaults to `globalThis.fetch`). */ fetch?: FetchLike; /** Per-request timeout in milliseconds. Omit (or `0`) to disable. */ timeoutMs?: number; /** Extra headers sent on every request. */ headers?: Record; /** * Address a single namespace on a server started with `--namespaced` (nidus-pcpc.2): * every request is sent as `/ns/{namespace}/...` instead of flat. Omit against a * single-store server; every path is produced byte-for-byte as it was before this * option existed. */ namespace?: string; } declare class NidusClient { private readonly baseUrl; private readonly token?; private readonly doFetch; private readonly timeoutMs; private readonly extraHeaders; private readonly namespace?; constructor(options: NidusClientOptions); /** Liveness check. Returns `true` when the server answers `/health`. */ health(): Promise; /** Store-wide introspection: dimension, distance, ANN config, collections, footprint. */ stats(): Promise; /** * Readiness: whether this instance can serve. A `503` is the negative answer, not an * error, so a poll loop branches on `ready` instead of catching. Other failures throw. */ ready(): Promise; /** Cluster role, writer-handle state, fencing token, commit counter, staleness. */ cluster(): Promise; /** The readable commit points and this instance's pin, if any. */ versions(): Promise; /** List every collection name. */ collections(): Promise; /** Create a collection. Idempotent on the server side. */ createCollection(name: string): Promise; /** Drop a collection and all its records. */ dropCollection(name: string): Promise; /** Every alias and the concrete collection it resolves to. */ aliases(): Promise>; /** Create or repoint an alias. The target must already exist; aliases never chain. */ setAlias(name: string, target: string): Promise; /** Remove an alias. Deletes no records. */ dropAlias(name: string): Promise; /** Read a collection's free-form string metadata. */ getMeta(name: string): Promise>; /** Replace a collection's free-form string metadata. */ setMeta(name: string, meta: Record): Promise; /** * Insert or replace records (idempotent on `id` within the collection). * `attrs` accept plain JS values or `v.*` helpers; they are normalized for you. * Returns the number of records upserted. */ upsert(name: string, records: RecordInput[]): Promise; /** Delete records by id. Returns the number deleted. */ delete(name: string, opts: { ids: string[]; }): Promise; /** Delete every record matching `filter`. Returns the number deleted. */ deleteWhere(name: string, filter: Filter): Promise; /** Fetch every record in a collection (attrs decoded to plain JS values). */ records(name: string): Promise; /** * Declare the full-text-indexed attribute fields for a collection. A bare string * takes the server's BM25/analyzer defaults; an {@link FtsField} object tunes `k1`, * `b`, and the analyzer for that field alone. */ setFtsSchema(name: string, fields: (string | FtsField)[]): Promise; /** * Declare which attribute fields are indexed for the text predicates (`Fuzzy`, * `ContainsAllTokens`, `ContainsAnyToken`, `ContainsTokenSequence`, `Regex`). Fields * already written are indexed as part of applying the declaration. * * This changes how fast those predicates run, never what they return: the index * proposes candidate documents and the predicate itself still decides. The cost is * paid at write time and in memory. Pass an empty array to drop the declaration. */ setFilterIndex(name: string, fields: (string | FilterIndexField)[]): Promise; /** * Declare the named-vector fields a collection accepts on upsert and search, beyond * the reserved `"default"` vector (nidus-85t). Upserting an undeclared name is a * `400` naming it; mirrors {@link NidusClient.setFtsSchema}'s declare-then-use shape. */ setVectorNames(name: string, names: string[]): Promise; /** Vector (cosine) nearest-neighbour search. Empty `scope` searches all collections. */ search(opts: SearchOptions): Promise; /** Like {@link NidusClient.search}, but also reports the scan strategy the server took. */ searchWithPlan(opts: SearchOptions): Promise<{ hits: Hit[]; plan: QueryPlan; }>; /** Records most like an existing one. The source record itself is never returned. */ searchSimilar(opts: SimilarSearchOptions): Promise; /** Like {@link NidusClient.searchSimilar}, but also reports the scan strategy taken. */ searchSimilarWithPlan(opts: SimilarSearchOptions): Promise<{ hits: Hit[]; plan: QueryPlan; }>; /** * BM25 full-text search over one indexed field, or over a `clauses` list folded by * `combine` (`"Sum"` unless said otherwise). Naming the fields both ways is a `400`. */ textSearch(opts: TextSearchOptions): Promise; /** * AST-aware source and docs search over one corpus (the `code` feature, `POST * /code-search`): a text query grouped by file, each with the symbols it matched. * `vector` picks vector-vs-BM25; omitted, it defers to the store the same way * {@link NidusClient.textSearch} never has to (that route is BM25-only). */ codeSearch(opts: CodeSearchOptions): Promise; /** * Ranked term completions for a partial word, for an autocomplete dropdown. Ranked by * document frequency (commonest first), which is the opposite of how a prefix clause * ranks documents. Completions are real spellings: the prefix is folded, not stemmed. * * The `df` counts only documents passing `filter` and carrying every word already typed * before the final token, so a permission-scoped dropdown is expressible and "quick br" * completes against the documents that also say "quick". */ suggest(opts: SuggestOptions): Promise; /** * Hybrid search: fuse a vector query and a BM25 text query via RRF. The text leg takes * the same single-field / `clauses` choice as {@link NidusClient.textSearch}. */ hybridSearch(opts: HybridSearchOptions): Promise; /** Like {@link NidusClient.hybridSearch}, but also reports the scan strategy taken. */ hybridSearchWithPlan(opts: HybridSearchOptions): Promise<{ hits: Hit[]; plan: QueryPlan; }>; /** Metadata-only listing (no vector), paginated by `offset`/`limit`. */ list(opts?: ListOptions): Promise; /** * Count the records matching a filter and sum the named attributes. Answered from the * in-RAM index alone — no record is built and no vector is read. */ aggregate(opts?: AggregateOptions): Promise; /** * Compile and run a `SELECT ...` script against `POST /query` (SPEC §7.12). A single * statement decodes to its own answer (hits, `{hits, plan}`, or an {@link Aggregation}); * a `;`-separated script answers one {@link QueryAnswer} per statement, in request order. * A parse error rejects with the server's message verbatim (byte offset and §7 section). */ query(sql: string): Promise; /** * Compile a `SELECT ...` script without running it: the typed value(s) the matching * `Store` method would receive. Always an array, one entry per `;`-separated statement. */ compile(sql: string): Promise; /** A wire hit's shape, distinguishing a bare hits-answer from a batch of answers. */ private isHitShaped; /** Decode one statement's `/query` answer: hits (bare or `{hits, plan}`), or an aggregation. */ private decodeQueryAnswer; /** * Answer several vector queries in one round-trip (16 max). Returns one ranking per * query in request order, or — with `opts.fuse` — a single array holding the one fused * ranking, so the return shape is uniform either way. * * The server validates the whole batch before running any leg, so a malformed query * fails the call rather than returning a partial answer that cannot be told apart. */ batchSearch(opts: BatchSearchOptions): Promise; /** * Embed `text` and upsert it under `id` in `collection` (idempotent on `id`). * With `opts.mode === "summarize"` the server summarizes first, embeds the * summary, and stamps a `nidus.summary` attr (requires the server to have a * summarizer). The raw text is always stored under `nidus.text`. `opts.attrs` accept plain JS values or `v.*` * helpers; they are normalized for you. * * Read `id` off the result rather than assuming the one you passed: * `opts.dedupeThreshold` can redirect the write onto a near-duplicate. */ remember(collection: string, id: string, text: string, opts?: RememberOptions): Promise; /** * Embed `query` and vector-search `collection`, best-first (attrs decoded to * plain JS values). Refused with a cross-model guard if the collection was * written with a different embedder than the server's. */ recall(collection: string, query: string, opts?: RecallOptions): Promise; /** Force a durability flush. */ flush(): Promise; /** Compact the store (reclaim space from deleted/overwritten rows). */ compact(): Promise; /** Adopt a writer's newer committed state. Returns whether anything was adopted. */ refresh(): Promise; /** Run a search-family request and decode the resulting hits' attrs. */ private searchRequest; /** Like {@link NidusClient.searchRequest}, asking for `plan: true` and decoding it too. */ private searchRequestWithPlan; /** One wire hit into a {@link Hit}. Shared so every search surface decodes identically. */ private decodeHit; /** Issue a request and parse a JSON body, mapping a non-2xx to {@link NidusError}. */ private request; /** * Prefix `path` with `/ns/{namespace}` when the client was constructed with one * (nidus-pcpc.2), so every call builds its path the same way it always did and the * namespace is applied in this one place. No namespace configured means no prefix: * today's flat paths, byte for byte. */ private namespaced; /** The bare transport: headers, auth, timeout, and transport-error mapping. */ private raw; } /** An error returned by a `nidus` server, or a transport failure reaching it. */ declare class NidusError extends Error { /** The HTTP status code, or `0` for a transport/timeout failure (no response). */ readonly status: number; constructor(message: string, status: number); /** A malformed request the server rejected (HTTP 400). */ get isBadRequest(): boolean; /** The store is read-only (HTTP 403). */ get isReadOnly(): boolean; /** The writer lock is held by another process (HTTP 409). */ get isLocked(): boolean; /** Out of capacity: `max_vector_bytes` exceeded or OOM (HTTP 507). */ get isOutOfCapacity(): boolean; } /** * Predicate constructors. Each accepts a plain JS value (auto-normalized) or an * explicit `v.*` {@link Value}. Combine results into a {@link Filter} array, or * use {@link f.and} for readability. */ declare const f: { /** `attrs[key] === value`. */ readonly eq: (key: string, value: AttrInput) => Predicate; /** `attrs[key]` is present and `!== value`. */ readonly ne: (key: string, value: AttrInput) => Predicate; /** `attrs[key]` is a `Str` matching the glob pattern (`*`, `?`, `[..]`). */ readonly glob: (key: string, pattern: string) => Predicate; /** * {@link f.glob}, ignoring **ASCII** case on both sides — `"Src/*"` matches * `"src/main.rs"`. Non-ASCII is not folded (`É` does not match `é`). */ readonly iglob: (key: string, pattern: string) => Predicate; /** `attrs[key]` equals one of `values`. */ readonly in: (key: string, values: AttrInput[]) => Predicate; /** `attrs[key]` is present and equals none of `values`. */ readonly notIn: (key: string, values: AttrInput[]) => Predicate; /** `attrs[key] < value` (same-type, orderable). */ readonly lt: (key: string, value: AttrInput) => Predicate; /** `attrs[key] <= value` (same-type, orderable). */ readonly le: (key: string, value: AttrInput) => Predicate; /** `attrs[key] > value` (same-type, orderable). */ readonly gt: (key: string, value: AttrInput) => Predicate; /** `attrs[key] >= value` (same-type, orderable). */ readonly ge: (key: string, value: AttrInput) => Predicate; /** `attrs[key]` is a `List` containing `value` (whole-element, not substring). */ readonly contains: (key: string, value: AttrInput) => Predicate; /** `attrs[key]` is a present `List` not containing `value`. */ readonly notContains: (key: string, value: AttrInput) => Predicate; /** `attrs[key]` is a `List` sharing at least one element with `values`. */ readonly containsAny: (key: string, values: AttrInput[]) => Predicate; /** Every sub-predicate holds. `all()` is `true`. */ readonly all: (...preds: Predicate[]) => Predicate; /** At least one sub-predicate holds. `any()` is `false`. */ readonly any: (...preds: Predicate[]) => Predicate; /** * The sub-predicate does not hold. Differs from {@link f.ne} on an absent key: * `not(eq(k, v))` matches a record with no `k`, `ne(k, v)` does not. */ readonly not: (pred: Predicate) => Predicate; /** * `attrs[key]` is within `maxEdits` Levenshtein edits of `text`, ASCII-case-folded on * both sides; a `List` matches if any element does. The only three-element predicate. * A `maxEdits` above 8 is refused by the server, not clamped. */ readonly fuzzy: (key: string, text: string, maxEdits: number) => Predicate; /** * Every token of `text` appears among `attrs[key]`'s tokens, in any order. Tokens are * ASCII-case-folded runs of alphanumerics; a `List` matches if any single element does. */ readonly containsAllTokens: (key: string, text: string) => Predicate; /** At least one token of `text` appears among `attrs[key]`'s tokens. Empty never matches. */ readonly containsAnyToken: (key: string, text: string) => Predicate; /** `text`'s tokens appear consecutively and in order — a phrase match. */ readonly containsTokenSequence: (key: string, text: string) => Predicate; /** * `attrs[key]` matches the regular expression, **anchored at both ends** like * {@link f.glob} — `.*` opts back into a substring search, and `(?i)` into case folding. * The syntax is Rust's `regex`, not JS's: no backreferences and no lookaround. */ readonly regex: (key: string, pattern: string) => Predicate; /** Collect predicates into a {@link Filter} (purely sugar — they already AND). */ readonly and: (...preds: Predicate[]) => Filter; }; /** * Value constructors mirroring the `Value` variants. `v.int` requires a safe * integer and `v.float` a finite number — `NaN`/`Infinity` have no JSON spelling, * and `JSON.stringify` would quietly write `null`. */ declare const v: { readonly str: (s: string) => Value; readonly int: (n: number) => Value; readonly float: (n: number) => Value; readonly bool: (b: boolean) => Value; readonly list: (items: string[]) => Value; /** * A UTC instant, from a `Date` or a raw epoch-millisecond count. Milliseconds is * the wire type, so there is no sub-millisecond precision and no timezone. */ readonly datetime: (when: Date | number) => Value; /** The explicit `Null` value — set-but-empty, distinct from an absent key. */ readonly nil: () => Value; }; /** * Normalize a caller-supplied {@link AttrInput} into the wire {@link Value} shape. * Plain scalars map by type; an already-tagged `Value` passes through unchanged. * Throws on a non-finite number, an invalid `Date`, or a non-string list element. */ declare function encodeValue(input: AttrInput): Value; /** Normalize a whole `attrs` map of {@link AttrInput} into wire {@link Value}s. */ declare function encodeAttrs(attrs: Record): Record; /** Decode a wire {@link Value} back to a plain JS value. */ declare function decodeValue(value: Value): DecodedValue; /** Decode a whole wire `attrs` map back to plain JS values. */ declare function decodeAttrs(attrs: Record): Record; export { type AggregateOptions, type Aggregation, type AnnInfo, type AnnotationOptions, type Annotations, type AttrInput, type ClauseScore, type ClusterStatus, type CodeFileHit, type CodeSearchOptions, type CodeSymbolHit, type Compiled, type Decay, type DecodedRecord, type DecodedValue, type Expand, type Expansion, type FetchLike, type Filter, type FilterIndexField, type Footprint, type Fragment, type FtsCombine, type FtsField, type Highlight, type HighlightOptions, type Hit, type HybridQuerySpelling, type HybridSearchBase, type HybridSearchOptions, type LegScore, type LimitPer, type ListOptions, NidusClient, type NidusClientOptions, NidusError, type NidusRecord, type OrderBy, type PlanCandidates, type PlanNarrowing, type PlanTimings, type Predicate, type ProjectionOptions, type QueryAnswer, type QueryPath, type QueryPlan, type RankBy, type RankingOptions, type Readiness, type RecallOptions, type RecordInput, type RememberOptions, type RememberResult, type RerankOptions, type Rollup, type SearchOptions, type SimilarSearchOptions, type Stats, type StoreVersions, type SuggestOptions, type Suggestion, type Suggestions, type TextClause, type TextQuerySpelling, type TextSearchBase, type TextSearchOptions, type Value, decodeAttrs, decodeValue, encodeAttrs, encodeValue, f, v };