# @coda/search-api

ConnectRPC client and proto definitions for the hybrid search service (ows-coda-search). Provides natural-language search over GraphQL and Snowflake schemas using BM25 + HNSW hybrid ranking.

## Installation

```jsonc
// package.json
{ "dependencies": { "@coda/search-api": "workspace:*" } }
```

## Usage

```ts
import { SearchClient } from "@coda/search-api";

const search = new SearchClient({ baseUrl: "http://localhost:8090" });

// Search GraphQL fields
const result = await search.searchGraphQL({ query: "revenue", limit: 10 });
if (result.ok) {
  for (const field of result.data.fields) {
    console.log(field.name, field.score);
  }
}

// Search Snowflake tables
const sfResult = await search.searchSnowflake({
  query: "account balance",
  database: "ANALYTICS",
  limit: 5,
});

// Fetch full schema (conditional — skips transfer if hash matches)
const schema = await search.getGraphQLSchema({ knownHash: cachedHash });
```

## API Reference

### SearchClient

| Method                    | Description                     | Default Timeout | Retries |
| ------------------------- | ------------------------------- | --------------- | ------- |
| `searchGraphQL(req)`      | Search GraphQL fields by query  | 500 ms          | Yes (3) |
| `searchSnowflake(req)`    | Search Snowflake tables/columns | 500 ms          | Yes (3) |
| `getGraphQLSchema(req)`   | Retrieve full GraphQL schema    | 10 s            | Yes (3) |
| `getSnowflakeSchema(req)` | Retrieve full Snowflake schema  | 10 s            | Yes (3) |

### buildClient(config)

Create a raw ConnectRPC client for integration tests:

```ts
import { buildClient } from "@coda/search-api";

const raw = buildClient({ baseUrl: "http://localhost:8090" });
const response = await raw.searchGraphQL({ query: "test" });
```

## Configuration

All clients accept `BaseClientConfig` from `@coda/api-common`:

```ts
interface BaseClientConfig {
  baseUrl: string;
  transport?: "connect" | "grpc";
  interceptors?: Interceptor[];
  defaultTimeoutMs?: number;
  onError?: (method: string, err: unknown) => void;
}
```

All methods return `RpcResult<T>` — a discriminated union that never throws. See `@coda/api-common` for details.

## Exported Types

| Type                         | Description                                            |
| ---------------------------- | ------------------------------------------------------ |
| `SearchClientConfig`         | Client constructor config (extends `BaseClientConfig`) |
| `SearchGraphQLResponse`      | GraphQL search result with ranked fields               |
| `SearchSnowflakeResponse`    | Snowflake search result with ranked tables/columns     |
| `GetGraphQLSchemaResponse`   | Full GraphQL schema payload                            |
| `GetSnowflakeSchemaResponse` | Full Snowflake schema payload                          |
| `QueryFieldResult`           | Single GraphQL field match                             |
| `TypeResult`                 | GraphQL type metadata                                  |
| `GraphNeighborhood`          | Graph context around a matched field                   |
| `TableResult`                | Single Snowflake table match                           |
| `ColumnResult`               | Single Snowflake column match                          |
| `JoinPath`                   | Detected join path between tables                      |

## Proto sources

`proto/search/` — `.proto` files defining the RPC service and message types.

## Generated code

`gen/` — auto-generated TypeScript. Do not edit manually.

## Regenerate

```bash
pnpm buf:generate
```

Requires [Buf CLI](https://buf.build/). Generated output is committed to the repo.
