@headband/react 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,13 +1,152 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ComponentType } from 'react';
3
3
 
4
+ /**
5
+ * Runtime helpers for Headband-managed retrieval experiments.
6
+ *
7
+ * Headband stores experiment rules, but applications own runtime assignment.
8
+ * These helpers fetch the saved rules and deterministically assign a subject
9
+ * to a variant without adding another dependency.
10
+ */
11
+ type ExperimentConfigStatus = "draft" | "active" | "paused" | "archived";
12
+ interface ExperimentVariantConfig {
13
+ key: string;
14
+ indexUid: string;
15
+ weight: number;
16
+ }
17
+ interface ExperimentConfig {
18
+ id?: string;
19
+ experimentKey: string;
20
+ name: string;
21
+ status: ExperimentConfigStatus;
22
+ trafficPercent: number;
23
+ killSwitch: boolean;
24
+ primaryMetric: string;
25
+ guardrailMetric: string | null;
26
+ assignmentSalt: string;
27
+ variants: ExperimentVariantConfig[];
28
+ updatedAt?: string;
29
+ }
30
+ interface ExperimentSearchMetadata {
31
+ experimentKey: string;
32
+ variant: string;
33
+ sessionId?: string;
34
+ }
35
+ type ExperimentEventType = "experiment_view" | "search_result_click" | "conversion" | "custom";
36
+ interface ExperimentEventInput extends ExperimentSearchMetadata {
37
+ eventType: ExperimentEventType;
38
+ indexUid: string;
39
+ query?: string;
40
+ filters?: string;
41
+ conversionValue?: number;
42
+ metadata?: Record<string, unknown>;
43
+ }
44
+ interface ExperimentEventResponse {
45
+ accepted: boolean;
46
+ eventType: ExperimentEventType;
47
+ experimentKey: string;
48
+ variant: string;
49
+ indexUid: string;
50
+ }
51
+ interface ExperimentAssignment extends ExperimentSearchMetadata {
52
+ indexUid: string;
53
+ config: ExperimentConfig;
54
+ }
55
+ interface HostedExperimentAssignmentQuery {
56
+ projectId?: string;
57
+ indexUid?: string;
58
+ experimentKey: string;
59
+ subjectId: string;
60
+ sessionId?: string;
61
+ }
62
+ interface HostedExperimentAssignmentResponse {
63
+ assigned: boolean;
64
+ reason?: "not_runnable" | "outside_rollout" | "no_weight";
65
+ experimentKey: string;
66
+ variant?: string;
67
+ indexUid?: string;
68
+ sessionId: string;
69
+ config: {
70
+ status: ExperimentConfigStatus;
71
+ trafficPercent: number;
72
+ killSwitch: boolean;
73
+ updatedAt?: string;
74
+ };
75
+ }
76
+ interface ExperimentConfigQuery {
77
+ projectId?: string;
78
+ indexUid?: string;
79
+ experimentKey?: string;
80
+ }
81
+ interface ExperimentConfigResponse {
82
+ data?: {
83
+ experiments?: ExperimentConfig[];
84
+ };
85
+ }
86
+ declare function buildExperimentConfigPath(query?: ExperimentConfigQuery): string;
87
+ declare function isRunnableExperiment(config: ExperimentConfig): boolean;
88
+ declare function chooseExperimentVariant(config: ExperimentConfig, subjectId: string): ExperimentVariantConfig | null;
89
+ declare function assignExperiment(config: ExperimentConfig, subjectId: string, options?: {
90
+ sessionId?: string;
91
+ }): ExperimentAssignment | null;
92
+ declare function experimentSearchMetadata(assignment: ExperimentAssignment | null | undefined): ExperimentSearchMetadata | undefined;
93
+
94
+ /**
95
+ * Search events for click feedback: `search_result_click` and `conversion`.
96
+ *
97
+ * A search response carries a `queryId` when click feedback is on for your organization. Send it
98
+ * with a click so Headband knows which search showed the result. Clicks and conversions are
99
+ * counted per query; when an index has "Use click feedback in ranking" on, results people pick
100
+ * for a query move up for that query. No experiment is needed.
101
+ */
102
+ type SearchEventType = "search_result_click" | "conversion";
103
+ interface SearchEvent {
104
+ type: SearchEventType;
105
+ /** The index the result came from. */
106
+ index: string;
107
+ /** The clicked or converted document's id (its primary key). */
108
+ docId: string | number;
109
+ /** The `queryId` of the search that showed the result. */
110
+ queryId?: string;
111
+ /** 1-based position of the result in the whole result list, not just the page. */
112
+ position?: number;
113
+ /** A stable id for the visitor's session. Strongly recommended: events without one count half. */
114
+ sessionId?: string;
115
+ /** Optional, non-negative: an order value for a conversion. */
116
+ value?: number;
117
+ }
118
+ type SearchEventRejectReason = "invalid_event" | "invalid_type" | "invalid_index" | "invalid_doc_id" | "invalid_query_id" | "invalid_position" | "invalid_session_id" | "invalid_value" | "index_not_found" | "bot";
119
+ interface SendEventsResponse {
120
+ accepted: number;
121
+ /** `index` is the event's position in the array you sent. */
122
+ rejected: Array<{
123
+ index: number;
124
+ reason: SearchEventRejectReason;
125
+ }>;
126
+ }
127
+ /**
128
+ * A session id for this browser tab, kept in sessionStorage when it is available and in memory
129
+ * otherwise. It identifies nothing about the visitor; Headband stores it only as a keyed hash.
130
+ */
131
+ declare function browserSessionId(): string;
132
+
4
133
  /**
5
134
  * Headband search client.
6
135
  *
7
- * Makes requests to the Headband `/v1/search` and `/v1/multi-search` endpoints.
136
+ * Makes requests to the Headband `/v1/search`, `/v1/multi-search`, `/v1/warm` and
137
+ * `/v1/relevance/events` endpoints.
138
+ */
139
+
140
+ /**
141
+ * Visor's ranking mode: `instant` (the default, typo-tolerant, as-you-type) or `retrieval` (BM25F,
142
+ * for longer queries), on the same index. Indexes on the other engines ignore it.
8
143
  */
144
+ type SearchMode = "instant" | "retrieval";
9
145
  interface SearchParams {
10
- filter?: string;
146
+ experimentKey?: string;
147
+ variant?: string;
148
+ sessionId?: string;
149
+ filter?: string | string[];
11
150
  facets?: string[];
12
151
  sort?: string[];
13
152
  limit?: number;
@@ -15,12 +154,21 @@ interface SearchParams {
15
154
  attributesToHighlight?: string[];
16
155
  attributesToRetrieve?: string[];
17
156
  attributesToCrop?: string[];
157
+ /** Visor crops to 20 words and refuses any other value with 400 feature_not_supported. */
18
158
  cropLength?: number;
159
+ /** Visor returns no match positions and refuses `true` with 400 feature_not_supported. */
19
160
  showMatchesPosition?: boolean;
161
+ /** Visor matches as `last` does and refuses `all` and `frequency` with 400 feature_not_supported. */
20
162
  matchingStrategy?: "all" | "last" | "frequency";
163
+ mode?: SearchMode;
21
164
  }
22
165
  interface SearchResponse {
23
166
  hits: Record<string, unknown>[];
167
+ /**
168
+ * The id of this search, `q_` and 16 characters, when click feedback is on for your
169
+ * organization. Send it with clicks (`trackClick`) so Headband knows which search showed them.
170
+ */
171
+ queryId?: string;
24
172
  estimatedTotalHits?: number;
25
173
  totalHits?: number;
26
174
  processingTimeMs: number;
@@ -34,22 +182,60 @@ interface SearchResponse {
34
182
  interface MultiSearchQuery {
35
183
  indexUid: string;
36
184
  q: string;
37
- filter?: string;
185
+ filter?: string | string[];
38
186
  facets?: string[];
39
187
  sort?: string[];
40
188
  limit?: number;
41
189
  offset?: number;
42
190
  attributesToHighlight?: string[];
43
191
  attributesToRetrieve?: string[];
192
+ mode?: SearchMode;
44
193
  }
45
194
  interface MultiSearchResponse {
46
195
  results: Array<SearchResponse & {
47
196
  indexUid: string;
48
197
  }>;
49
198
  }
199
+ interface ExperimentConfigsResponse {
200
+ experiments: ExperimentConfig[];
201
+ }
202
+ /** `auto` starts an engine used in the last 24 hours and stages an older one. */
203
+ type WarmLevel = "auto" | "stage" | "start";
204
+ /** Indexes by name, or every index of a project. */
205
+ type WarmInput = {
206
+ indexes: string[];
207
+ } | {
208
+ project: string;
209
+ branch?: string;
210
+ };
211
+ interface WarmResponse {
212
+ level: WarmLevel;
213
+ indexes: Array<{
214
+ index: string;
215
+ engine: string;
216
+ state: "awake" | "staging" | "staged" | "starting" | "refused";
217
+ reason?: string;
218
+ }>;
219
+ }
50
220
  interface HeadbandClient {
51
221
  search(index: string, query: string, params?: SearchParams): Promise<SearchResponse>;
52
222
  multiSearch(queries: MultiSearchQuery[]): Promise<MultiSearchResponse>;
223
+ getExperimentConfigs(query?: ExperimentConfigQuery): Promise<ExperimentConfigsResponse>;
224
+ assignExperiment(query: HostedExperimentAssignmentQuery): Promise<HostedExperimentAssignmentResponse>;
225
+ recordExperimentEvent(event: ExperimentEventInput): Promise<ExperimentEventResponse>;
226
+ /** Pre-wakes indexes, or a project's, so the next search does not wait for a sleeping engine. Free. */
227
+ warm(target: WarmInput, opts?: {
228
+ level?: WarmLevel;
229
+ }): Promise<WarmResponse>;
230
+ /** Sends up to 100 click and conversion events (POST /v1/relevance/events). */
231
+ sendEvents(events: SearchEvent[]): Promise<SendEventsResponse>;
232
+ /**
233
+ * Records a click on a search result. Pass the search's `queryId`; the client remembers it for
234
+ * the document so a later `trackConversion` names the same search.
235
+ */
236
+ trackClick(event: Omit<SearchEvent, "type">): Promise<SendEventsResponse>;
237
+ /** Records a conversion (a purchase, a sign-up) of a document found by search. */
238
+ trackConversion(event: Omit<SearchEvent, "type" | "position">): Promise<SendEventsResponse>;
53
239
  }
54
240
  declare class HeadbandError extends Error {
55
241
  readonly status: number;
@@ -58,6 +244,25 @@ declare class HeadbandError extends Error {
58
244
  }
59
245
  declare function headband(host: string, apiKey: string): HeadbandClient;
60
246
 
247
+ type FilterValue = string | number | boolean;
248
+ type FilterInput = string | string[] | null | undefined;
249
+ declare function quoteFilterValue(value: FilterValue): string;
250
+ declare function filterEq(attribute: string, value: FilterValue): string;
251
+ declare function filterNotEq(attribute: string, value: FilterValue): string;
252
+ declare function filterIn(attribute: string, values: FilterValue[]): string;
253
+ declare function filterExists(attribute: string): string;
254
+ declare function filterRange(attribute: string, range: {
255
+ gt?: number;
256
+ gte?: number;
257
+ lt?: number;
258
+ lte?: number;
259
+ min?: number;
260
+ max?: number;
261
+ }): string | undefined;
262
+ declare function andFilters(...filters: FilterInput[]): string | undefined;
263
+ declare function orFilters(...filters: FilterInput[]): string | undefined;
264
+ declare function combineFilters(baseFilter: FilterInput, generatedFilter: FilterInput): string | string[] | undefined;
265
+
61
266
  /** Map of attribute -> selected values for facet refinements. */
62
267
  type RefinementMap = Map<string, Set<string>>;
63
268
  /** Numeric range filter: attribute -> { min?, max? }. */
@@ -92,20 +297,56 @@ interface SearchActions {
92
297
  unregisterFacet: (attribute: string) => void;
93
298
  refresh: () => void;
94
299
  }
300
+ /** How useClickTracking finds a hit's id and the visitor's session. */
301
+ interface ClickTrackingConfig {
302
+ /** The field hits carry the document id in. Default `id`. */
303
+ idField: string;
304
+ /** The session id events carry; a per-tab id when absent. */
305
+ sessionId?: string;
306
+ }
95
307
  interface HeadbandContextValue {
96
308
  client: HeadbandClient;
97
309
  index: string;
98
310
  state: SearchState;
99
311
  actions: SearchActions;
312
+ tracking: ClickTrackingConfig;
100
313
  }
101
314
  declare function useHeadbandContext(): HeadbandContextValue;
102
315
  interface HeadbandProviderProps {
103
316
  client: HeadbandClient;
104
317
  index: string;
105
318
  initialQuery?: string;
319
+ experiment?: ExperimentSearchMetadata | null;
320
+ /**
321
+ * Optional app-owned filter expression(s) ANDed with UI refinements/ranges.
322
+ * Use this for tenant, permission, availability, or custom OR/AND groups that
323
+ * should apply to every search request.
324
+ */
325
+ baseFilter?: FilterInput;
326
+ /**
327
+ * Visor's ranking mode for every search: `instant` (the engine's default) or `retrieval` (BM25F,
328
+ * for longer queries). Indexes on the other engines ignore it.
329
+ */
330
+ mode?: SearchMode;
331
+ /**
332
+ * Store-style facet counts (default true). With a value ticked in a facet list, that list still
333
+ * counts its other values, so a shopper can tick a second one. Each facet with a ticked value
334
+ * costs one more search per request. Set false for one search per request, and lists that narrow
335
+ * to the ticked values.
336
+ */
337
+ disjunctiveFacets?: boolean;
338
+ /**
339
+ * The field your documents' ids are in, for click feedback (`useClickTracking`). Default `id`.
340
+ */
341
+ idField?: string;
342
+ /**
343
+ * A stable id for the visitor's session, sent with click and conversion events. Without it the
344
+ * experiment's session id is used, else an id kept for this browser tab.
345
+ */
346
+ sessionId?: string;
106
347
  children: ReactNode;
107
348
  }
108
- declare function HeadbandProvider({ client, index, initialQuery, children, }: HeadbandProviderProps): react_jsx_runtime.JSX.Element;
349
+ declare function HeadbandProvider({ client, index, initialQuery, experiment, baseFilter, mode, disjunctiveFacets, idField, sessionId, children, }: HeadbandProviderProps): react_jsx_runtime.JSX.Element;
109
350
 
110
351
  /**
111
352
  * Core hook that exposes the full search state and actions.
@@ -273,6 +514,31 @@ interface UseHighlightReturn {
273
514
  */
274
515
  declare function useHighlight({ hit, attribute, }: UseHighlightProps): UseHighlightReturn;
275
516
 
517
+ interface UseClickTrackingReturn {
518
+ /** The current search's id, when click feedback is on for your organization. */
519
+ queryId: string | undefined;
520
+ /**
521
+ * Records a click on a hit. `positionOnPage` is the hit's 0-based place in the current page;
522
+ * the absolute position is worked out from the page. Never throws: tracking must not break a
523
+ * results page, so a failed send resolves to null.
524
+ */
525
+ trackClick: (hit: Record<string, unknown>, positionOnPage?: number) => Promise<SendEventsResponse | null>;
526
+ /**
527
+ * Records a conversion of a document. It names the search the document was last clicked from
528
+ * in this tab, unless you pass a `queryId`. Never throws.
529
+ */
530
+ trackConversion: (docId: string | number, opts?: {
531
+ value?: number;
532
+ queryId?: string;
533
+ }) => Promise<SendEventsResponse | null>;
534
+ }
535
+ /**
536
+ * Click feedback events for the provider's index: `search_result_click` and `conversion`. When the
537
+ * index has "Use click feedback in ranking" on, results people pick for a query move up for that
538
+ * query.
539
+ */
540
+ declare function useClickTracking(): UseClickTrackingReturn;
541
+
276
542
  interface SearchBoxProps {
277
543
  /** Placeholder text. */
278
544
  placeholder?: string;
@@ -413,4 +679,4 @@ interface PoweredByProps {
413
679
  */
414
680
  declare function PoweredBy({ className, styled, href, }: PoweredByProps): react_jsx_runtime.JSX.Element;
415
681
 
416
- export { type HeadbandClient, type HeadbandContextValue, HeadbandError, HeadbandProvider, type HeadbandProviderProps, Highlight, type HighlightProps, type Hit, Hits, type HitsProps, type MultiSearchQuery, type MultiSearchResponse, Pagination, type PaginationProps, PoweredBy, type PoweredByProps, RangeInput, type RangeInputProps, type RangeMap, RefinementList, type RefinementListItem, type RefinementListProps, type RefinementMap, type SearchActions, SearchBox, type SearchBoxProps, type SearchParams, type SearchResponse, type SearchState, SortBy, type SortByItem, type SortByProps, Stats, type StatsProps, type UseHighlightProps, type UseHighlightReturn, type UseHitsReturn, type UsePaginationReturn, type UseRangeProps, type UseRangeReturn, type UseRefinementListProps, type UseRefinementListReturn, type UseSearchBoxReturn, type UseSortByProps, type UseSortByReturn, type UseStatsReturn, headband, useHeadbandContext, useHighlight, useHits, usePagination, useRange, useRefinementList, useSearch, useSearchBox, useSortBy, useStats };
682
+ export { type ClickTrackingConfig, type ExperimentAssignment, type ExperimentConfig, type ExperimentConfigQuery, type ExperimentConfigResponse, type ExperimentConfigStatus, type ExperimentConfigsResponse, type ExperimentEventInput, type ExperimentEventResponse, type ExperimentEventType, type ExperimentSearchMetadata, type ExperimentVariantConfig, type FilterInput, type FilterValue, type HeadbandClient, type HeadbandContextValue, HeadbandError, HeadbandProvider, type HeadbandProviderProps, Highlight, type HighlightProps, type Hit, Hits, type HitsProps, type HostedExperimentAssignmentQuery, type HostedExperimentAssignmentResponse, type MultiSearchQuery, type MultiSearchResponse, Pagination, type PaginationProps, PoweredBy, type PoweredByProps, RangeInput, type RangeInputProps, type RangeMap, RefinementList, type RefinementListItem, type RefinementListProps, type RefinementMap, type SearchActions, SearchBox, type SearchBoxProps, type SearchEvent, type SearchEventRejectReason, type SearchEventType, type SearchMode, type SearchParams, type SearchResponse, type SearchState, type SendEventsResponse, SortBy, type SortByItem, type SortByProps, Stats, type StatsProps, type UseClickTrackingReturn, type UseHighlightProps, type UseHighlightReturn, type UseHitsReturn, type UsePaginationReturn, type UseRangeProps, type UseRangeReturn, type UseRefinementListProps, type UseRefinementListReturn, type UseSearchBoxReturn, type UseSortByProps, type UseSortByReturn, type UseStatsReturn, type WarmInput, type WarmLevel, type WarmResponse, andFilters, assignExperiment, browserSessionId, buildExperimentConfigPath, chooseExperimentVariant, combineFilters, experimentSearchMetadata, filterEq, filterExists, filterIn, filterNotEq, filterRange, headband, isRunnableExperiment, orFilters, quoteFilterValue, useClickTracking, useHeadbandContext, useHighlight, useHits, usePagination, useRange, useRefinementList, useSearch, useSearchBox, useSortBy, useStats };