- name
- search-documents
- description
- Domain knowledge for @azure/search-documents. Covers architecture, type conversions, data flow, and common pitfalls. WHEN: regenerate search-documents; modify search-documents; fix search-documents bug; add search-documents feature; search-documents tsp-client update.
# @azure/search-documents — Package Skill
## Common Pitfalls
- **Regeneration must pin the preview api-version** — the spec's `tspconfig.yaml` does not set an `api-version` for `@azure-tools/typespec-ts`, so `tsp-client update` without an explicit `api-version=` emitter option can regenerate a different surface and delete preview-only operations and models. The `generate:client` script selects the targeted preview; keep that override in sync when moving to a newer preview.
- **Check the API reports for `ae-forgotten-export` after regenerating** — new spec types are frequently referenced by public members without being exported from `src/index.ts`, which leaves them unnameable by consumers. Grep `review/*.api.md` for `ae-forgotten-export` and diff against `main` to see which are newly introduced, then export them. Exporting one type often surfaces more transitively, so re-run the build until the list stops growing.
- **`nextLinkName` in `src/searchIndex/api/operations.ts` and `src/searchIndexer/api/operations.ts` is deliberately corrected.** The emitter writes the *wire* property name (`"NextLink"` or `"@odata.nextLink"`), but `buildPagedAsyncIterator` reads the link off the **deserialized** model, where those properties are named `nextLink` and `odataNextLink`. With the generated values, `getNextLink` returns `undefined` and every paged operation silently stops after the first page. All eleven generated paged operations are corrected in `src/`; re-check them after any regeneration, and keep `test/internal/node/paging.spec.ts` green — it fails if the generated values come back.
- **Generated types migrate between `src/models/models.ts` and the subpath barrels.** The emitter moves a type into `src/models/azure/search/documents/{indexes,knowledgeBases}/` as soon as the spec references it from that namespace, which breaks hand-authored `import ... from "./models/index.js"` statements with `TS2305: has no exported member`. The fix is to repoint the import at the subpath barrel, not to re-add the export. Check `src/index.ts`, `src/serviceModels.ts`, and `src/knowledgeBaseModels.ts` after every regeneration.
- **When the spec catches up to a hand-authored type, delete the hand-authored copy.** Types stubbed in the convenience layer ahead of the spec (e.g. the SSE event payloads in `knowledgeBaseModels.ts`) become duplicate declarations once the emitter starts producing them, which api-extractor surfaces as a `_2` suffix. Re-export the generated type from the hand-authored module so `src/index.ts` and consumers keep the same import path, and diff the two shapes first — the generated version is often more precise (the generated SSE `reasoningEffort` is a discriminated union where the hand-authored one was the open base type).
- **`BlobIndexerParsingMode` is deliberately narrowed in `src/`** — the generated alias in `src/models/azure/search/documents/indexes/models.ts` is changed from `string` to `` `${KnownBlobIndexerParsingMode}` `` and `serviceModels.ts` imports it instead of declaring its own copy. Without this, two distinct types share the name and api-extractor emits `BlobIndexerParsingMode_2`. This is the only intentional drift in that generated mirror file; preserve it through the customization merge.
- **Streaming retrieval requires a `src` customization plus `@azure/core-sse`** — the emitter still generates a buffered `Uint8Array` response and `get-binary-response` helpers under `generated/`. The customized `src/knowledgeBaseRetrieval/api/operations.ts` instead uses `getBinaryStreamResponse`, and the hand-authored convenience client parses the platform stream through `#platform/sseHelper`. Keep `@azure/core-sse` at `^2.4.0` or newer so browser and React Native builds resolve platform-specific stream declarations. The `src` mirror does not need the generated `get-binary-response` helpers.
- **Generated sub-client ≠ convenience client** — `src/search/searchClient.ts` is the GENERATED sub-client (overwritten on regeneration). `src/searchClient.ts` is the HAND-AUTHORED convenience client. Adding methods to the generated file does nothing for consumers. Always edit the hand-authored file at the `src/` root. See the "Generated vs Hand-Authored Files" table in Architecture.
- **New operations must be wired through the convenience layer** — after regeneration, new operations appearing in generated files are NOT available to consumers until explicitly exposed via the hand-authored convenience clients, model files, and `src/index.ts`. Do not stop at regeneration. See "Exposing New Operations and Models" below.
- **New models must be wired through the convenience layer** — likewise, new generated `interface` / `type` / `enum` declarations (including new discriminated-union members like additional `*KnowledgeSource`, `*Skill`, `*Vectorizer`, or `*Activity` kinds) are invisible to consumers until mirrored in `src/serviceModels.ts` / `src/indexModels.ts` / `src/knowledgeBaseModels.ts`, plumbed through any `serviceUtils.ts` dispatch tables that switch on `kind`, and re-exported from `src/index.ts`.
- **Never hand-edit files in `generated/`** — these are overwritten on every `tsp-client update`. Edit `src/` files directly; the 3-way merge preserves your changes.
- **Never hand-edit generated-mirrored files** — files under `src/search/`, `src/searchIndex/`, `src/searchIndexer/`, `src/knowledgeBaseRetrieval/`, and `src/models/` are mirrored from `generated/`. Edits will be overwritten on next regeneration.
- **Check for merge conflicts FIRST after regeneration** — `dev-tool customization apply` can produce conflict markers in `src/`. Run `grep -r "<<<<<<" src/ --include="*.ts"`.
- **`src/index.ts` is skipped during customization apply** — new exports must be manually added. The `ae-forgotten-export` lint error catches missing exports.
- **`test/snippets.spec.ts` is documentation, not tests** — it contains source code for README and doc-comment snippets. Never delete or replace its contents when updating real tests.
- **Uses `@azure-rest/core-client`** not `@azure/core-client` — this TypeSpec package uses the REST variant for `OperationOptions`, `ClientOptions`, etc.
- **`knownSkills` whitelist** — `convertSkillsToPublic()` in `src/serviceUtils.ts` silently drops unknown skill types. New service skill types require updating this hardcoded record.
- **No reverse conversion for some types** — `vectorSearch` and individual skills are passed through directly without dedicated public-to-generated converters.
- **`search()` uses custom POST pagination** — not standard `nextLink` GET paging. Uses `nextPageParameters` POST body with opaque Base64 continuation tokens.
- **Date deserialization is strict** — only matches UTC ISO strings with 0-3 fractional seconds and `Z` suffix. Non-UTC strings are not auto-converted.
- **`indexDocuments` 207 behavior** — partial success (HTTP 207) is thrown as an exception when `throwOnAnyFailure` is set. This is intentional.
- **Generated client argument order ≠ public client argument order** — when wrapping new operations in `src/searchIndexClient.ts` / `src/searchClient.ts` etc., open the matching generated `searchIndexClient.ts` (under `src/searchIndex/` etc.) and verify the parameter order on the call. Subtle swaps silently target the wrong resource (e.g. `deleteKnowledgeSourceFile(fileId, name)` vs `(name, fileId)`), producing no error and a silently no-op'd request.
## Architecture
Two-directory layout with customization merge:
- `generated/` — auto-generated from TypeSpec. Never hand-edit.
- `src/` — mirrors `generated/` structure plus hand-authored convenience layer. Files with counterparts in `generated/` are merged; files without counterparts are preserved as-is.
Four convenience clients wrap generated sub-clients:
| Client | Constructor | Purpose |
|---|---|---|
| `SearchClient<TModel>` | `(endpoint, indexName, credential, options?)` | Search, suggest, autocomplete, document CRUD |
| `SearchIndexClient` | `(endpoint, credential, options?)` | Index CRUD, synonym maps, aliases, knowledge bases |
| `SearchIndexerClient` | `(endpoint, credential, options?)` | Indexer, data source, skillset CRUD |
| `KnowledgeRetrievalClient` | `(endpoint, knowledgeBaseName, credential, options?)` | Knowledge base retrieval |
Each client: creates generated client → replaces auth with search-specific auth (API key header or bearer token) → adds OData metadata policy → wraps every method in `tracingClient.withSpan()` with type conversion. `SearchClient` uses OData metadata `"none"`; others use `"minimal"`.
### Generated vs Hand-Authored Files — Know the Difference
The file paths look deceptively similar. Confusing them is the single most common mistake:
| File | Type | Edit? |
|---|---|---|
| `src/search/searchClient.ts` | **Generated** sub-client (mirrored from `generated/`) | **NEVER** — overwritten on regeneration |
| `src/searchClient.ts` | **Hand-authored** convenience client (`SearchClient<TModel>`) | ✅ Add operations here |
| `src/searchIndex/searchIndexClient.ts` | **Generated** sub-client | **NEVER** |
| `src/searchIndexClient.ts` | **Hand-authored** convenience client (`SearchIndexClient`) | ✅ Add operations here |
| `src/searchIndexer/searchIndexerClient.ts` | **Generated** sub-client | **NEVER** |
| `src/searchIndexerClient.ts` | **Hand-authored** convenience client (`SearchIndexerClient`) | ✅ Add operations here |
| `src/knowledgeBaseRetrieval/knowledgeBaseRetrievalClient.ts` | **Generated** sub-client | **NEVER** |
| `src/knowledgeRetrievalClient.ts` | **Hand-authored** convenience client (`KnowledgeRetrievalClient`) | ✅ Add operations here |
| `src/search/api/operations.ts` | **Generated** operations | **NEVER** |
| `src/models/models.ts` | **Generated** models | **NEVER** |
**Rule:** Any file under `src/{subclient}/` (i.e. `src/search/`, `src/searchIndex/`, `src/searchIndexer/`, `src/knowledgeBaseRetrieval/`) or `src/models/` is generated-mirrored code. Never hand-edit these files.
The convenience layer converts between generated and public types via `src/serviceUtils.ts` (200+ conversion functions). Key patterns: `additionalProperties` unwrapping, field visibility inversion (`hidden` ↔ `!retrievable`), encryption key renames (`vaultUrl` ↔ `vaultUri`), and custom serialization for NaN/Infinity/Date/GeographyPoint.
## Regeneration
Generation command (from `package.json`):
```
tsp-client update -d --emitter-options="api-version=2026-08-01-preview;ignore-nullable-on-optional=true;wrap-non-model-return=false" && npm run format && npx dev-tool customization apply --skip index.ts
```
Run via: `npm run generate:client`. **All changes must be committed first** — the 3-way merge requires committed state.
**Important flags:**
- `api-version=2026-08-01-preview` — **required**. The spec's `tspconfig.yaml` does not select an API version for `@azure-tools/typespec-ts`; this override ensures the emitter generates the intended preview surface instead of another default version. Bump this value when moving to a newer preview.
- `ignore-nullable-on-optional=true` — mitigates TypeSpec's `T | null` on optional properties
- `wrap-non-model-return=false` — prevents wrapping non-model return types
- `--skip index.ts` — barrel export is manually maintained
### Error Categorization
| Error Location | Root Cause | Fix In |
|---|---|---|
| `generated/**/*.ts` | TypeSpec spec change | Update `tsp-location.yaml`, re-run `npm run generate:client` |
| `src/<sub-client>/api/*.ts` (merged) | Merge conflict from spec change | Resolve conflict in `src/`, preserving hand-authored intent |
| `src/serviceUtils.ts` | Type conversion mismatch | Update conversion functions to match new generated types |
| `src/serviceModels.ts` or `src/indexModels.ts` | Public type doesn't match generated shape | Update public type definitions and their conversions |
| `src/index.ts` | Missing export after regeneration | Manually add new exports |
| `src/searchClient.ts` | Wire format encoding changed | Update conversion/encoding helpers in searchClient.ts |
### Breaking Change Detection
After spec changes, check for:
- New/removed/renamed properties in generated models → update conversions in `serviceUtils.ts`
- New/removed operations → add/remove convenience wrappers and exports
- New sub-clients → create convenience wrapper, add exports
- Changed discriminator values → update union handling in conversion functions
For 3-way merge details and conflict resolution, see [references/customization.md](references/customization.md).
### Exposing New Operations and Models
New operations and models appearing after regeneration are **not available to consumers** until explicitly wired through the hand-authored layer. Regeneration only changes `generated/` and the generated-mirrored files under `src/{subclient}/` and `src/models/` — none of those are part of the public package API. **Do not stop at regeneration.**
Before declaring a regeneration done, do a post-regeneration audit:
1. **Diff the generated client wrappers** — `git diff generated/<subclient>/<subclient>Client.ts src/<subclient>/<subclient>Client.ts` — to enumerate every operation method added, removed, or with a changed signature. Every newly added operation needs a hand-authored convenience method; every signature change needs a matching update.
2. **Diff `generated/models/`** for new exported `interface` / `type` / `enum` declarations, especially new discriminated-union members (e.g. new `*KnowledgeSource`, `*Skill`, `*Vectorizer`, `*Authentication`, `*Activity` kinds). Each new union member that consumers should be able to construct or pattern-match needs a public mirror in `src/serviceModels.ts` / `src/indexModels.ts` / `src/knowledgeBaseModels.ts`, and a re-export from `src/index.ts`.
3. **Diff the generated barrel** (`generated/index.ts`) for new `Known*` enums and union aliases (`*Union`). These typically need to be re-exported from `src/index.ts` so consumers can name them.
4. **Update `knownSkills` and any other whitelists** — `convertSkillsToPublic()` in `src/serviceUtils.ts` (and similar discriminator switches) silently drop unknown kinds. New skill / KS / vectorizer / authentication kinds must be added to every dispatch table that switches on `kind`.
5. **Add conversion functions** in `src/serviceUtils.ts` for every new request/response type that needs public ↔ generated translation. Simple pass-throughs can stay inline in the convenience client, but anything with field renames, enum widening, or polymorphic discrimination belongs in `serviceUtils.ts`.
6. **Export every new public symbol** from `src/index.ts`. This file is intentionally skipped during the customization merge, so new exports are never added automatically. The `ae-forgotten-export` lint error catches the most obvious omissions; it does not catch missing operations or types that are never referenced internally.
7. **Run `api-extractor`** (via `pnpm run extract-api` or the package build) and inspect `review/search-documents-node.api.md`. Cross-check that every new operation and every new model from steps 1–3 appears. **If a symbol isn't in the API report, it is not part of the public package, even if it compiles.**
8. **Update the changelog** under `CHANGELOG.md` → `Features Added` (and `Breaking Changes` if applicable). Every new public symbol or method should be mentioned. When preparing a release, replace the `(Unreleased)` placeholder next to the version heading with today's date in `YYYY-MM-DD` form (e.g. `## 13.1.0-beta.1 (2026-05-28)`); leave the placeholder in place for non-release commits.
Then add a public method per new operation:
- Place the method on the correct hand-authored convenience client (`src/searchClient.ts`, `src/searchIndexClient.ts`, `src/searchIndexerClient.ts`, or `src/knowledgeRetrievalClient.ts`) — **never** in the generated-mirrored sub-client under `src/<subclient>/`.
- Wrap the body in `tracingClient.withSpan("ClientName-methodName", options, async (updatedOptions) => { ... })`.
- Convert public input types → generated types, call `this.client.<operation>(...)`, then convert the generated response → public types.
- **Mirror the generated argument order exactly** when calling `this.client.*` (see the pitfall about arg-order swaps). Open the matching generated wrapper and verify each parameter position.
- Add a TSDoc comment with `@param` entries and an `@example` snippet when the operation has non-trivial inputs.
The task is not complete until:
- Every new generated operation has a corresponding convenience method.
- Every new generated model / union member that consumers need to construct or pattern-match is mirrored, converted, and re-exported.
- `review/*.api.md` contains the new surface, the changelog lists it, and the public surface compiles with no `ae-forgotten-export` or `ae-internal-mixed-release` warnings.
For a detailed example (wrong vs right), see [references/architecture.md](references/architecture.md).
## Where to Make Changes
| Goal | Where to edit |
|---|---|
| Add/modify type conversions (generated ↔ public) | `src/serviceUtils.ts` |
| Add/modify public model types for indexes, indexers, skills, data sources | `src/serviceModels.ts` |
| Add/modify public types for search/suggest/autocomplete operations | `src/indexModels.ts` |
| Add/modify public types for knowledge base operations | `src/knowledgeBaseModels.ts` |
| Change how search/suggest/autocomplete/document operations work | `src/searchClient.ts` |
| Change how index management works | `src/searchIndexClient.ts` |
| Change how indexer/data source/skillset management works | `src/searchIndexerClient.ts` |
| Change how knowledge retrieval works | `src/knowledgeRetrievalClient.ts` |
Auf GitHub ansehen