/** * Native Typesense IndexerAdapter โ€” the v1 default for catalog-plane search. * * Uses an injected `TypesenseClient` interface (mirroring the storage R2Bucket * binding pattern) so the package doesn't take a hard dep on the Typesense * HTTP SDK. Templates wire in the actual client. * * See `docs/architecture/catalog-architecture.md` ยง5.4.1 for design. */ import type { FieldPolicyRegistry } from "../contract.js"; import type { DocumentEmitter, IndexerAdapter, IndexerSlice } from "./contract.js"; import { type TypesenseSearchQuery } from "./typesense-search-query.js"; export { buildDefaultTypesenseQueryBy, buildDefaultTypesenseSearchFields, buildSearchQuery, type TypesenseSearchQuery, } from "./typesense-search-query.js"; /** * Minimal interface the Typesense client must satisfy. Templates pass in * the real `typesense` SDK client (or a custom HTTP wrapper) and the adapter * uses only these methods. */ export interface TypesenseClient { collections(name?: string): { create(schema: TypesenseCollectionSchema): Promise; update(schema: Partial): Promise; delete(): Promise; retrieve(): Promise; documents(): { import(documents: unknown[], options?: { action?: "upsert" | "create"; }): Promise; delete(query: { filter_by: string; }): Promise; search(query: TypesenseSearchQuery): Promise; }; }; } export interface TypesenseFieldSchema { name: string; type: "string" | "string[]" | "int32" | "int64" | "float" | "bool" | "object" | "float[]"; facet?: boolean; index?: boolean; optional?: boolean; sort?: boolean; num_dim?: number; vec_dist?: "cosine" | "ip"; } export interface TypesenseCollectionSchema { name: string; fields: TypesenseFieldSchema[]; default_sorting_field?: string; enable_nested_fields?: boolean; metadata?: Record; } export interface TypesenseSearchHit { document: Record; text_match: number; } export interface TypesenseSearchResponse { hits: TypesenseSearchHit[]; found: number; facet_counts?: Array<{ field_name: string; counts: Array<{ value: string | number; count: number; }>; }>; } export interface TypesenseIndexerOptions { client: TypesenseClient; /** Embedding dimension shipped by the configured EmbeddingProvider. */ vectorDimensions?: number | null; /** Optional collection-name prefix (useful for multi-tenant single-cluster setups). */ collectionPrefix?: string; /** * Field-policy registries keyed by vertical. Seeds the per-vertical registry * cache so a search-only process (the worker, which never runs * `ensureCollection`) builds queries against the REAL policy โ€” including * numeric sort/filter fields. Without this, search falls back to * `inferRegistryFromCollection`, which only knows string fields, so numeric * sorts (e.g. `price-asc` โ†’ `priceFromAmountCents`) silently no-op. */ registries?: ReadonlyMap; } /** * Returns the Typesense collection name for one variant slice. Stable across * runs so existing collections survive deployments. */ export declare function collectionName(slice: IndexerSlice, prefix?: string): string; /** * Builds a Typesense collection schema from the field-policy registry. Maps * field-policy types onto Typesense field types using `query` + `class` from * the policy. */ export declare function buildCollectionSchema(slice: IndexerSlice, registry: FieldPolicyRegistry, options?: { vectorDimensions?: number | null; collectionPrefix?: string; }): TypesenseCollectionSchema; export declare function createTypesenseIndexer(options: TypesenseIndexerOptions): IndexerAdapter; /** * Helper for verticals that want to register a `DocumentEmitter` against * this adapter. Currently a thin pass-through; reserved for future emitter * registry extensions. */ export declare function attachEmitter(emitter: DocumentEmitter): DocumentEmitter; //# sourceMappingURL=typesense.d.ts.map