@seekora-ai/search-sdk 0.2.12 → 0.2.14

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/client.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Seekora SDK Client
3
3
  * High-level wrapper around generated OpenAPI client
4
4
  */
5
- import type { DataTypesIndexConfig, DataTypesEventPayload } from './generated';
5
+ import type { DataTypesIndexConfig, DataTypesEventPayload, DataTypesFiltersResponse, DataTypesFacetValuesSearchResponse, DataTypesFiltersSchemaResponse, DataTypesFilterField, DataTypesFilterValue, DataTypesFilterStats, DataTypesFilterSchemaField, DataTypesExperimentAssignmentResponse } from './generated';
6
6
  import { type SeekoraEnvironment } from './config';
7
7
  import { Logger, type LogLevel, type LoggerConfig } from './logger';
8
8
  import { type ContextCollectorConfig, type BrowserContext } from './context-collector';
@@ -46,8 +46,9 @@ export interface SeekoraClientConfig {
46
46
  */
47
47
  contextCollector?: ContextCollectorConfig;
48
48
  /**
49
- * Enable event queue for offline buffering and retry
50
- * @default false
49
+ * Enable event queue for batching, offline buffering, and retry
50
+ * Batches events automatically (default: 10 events or 5s interval) for better performance
51
+ * @default true
51
52
  */
52
53
  enableEventQueue?: boolean;
53
54
  /**
@@ -78,6 +79,14 @@ export interface SeekoraClientConfig {
78
79
  * @default 0
79
80
  */
80
81
  impressionTrackingDelay?: number;
82
+ /**
83
+ * A/B test experiment ID to include in all analytics events
84
+ */
85
+ abTestId?: string;
86
+ /**
87
+ * A/B test variant to include in all analytics events
88
+ */
89
+ abVariant?: string;
81
90
  /**
82
91
  * Enable event deduplication (industry standard: Segment, Amplitude pattern)
83
92
  * When enabled, generates insert_id for backend deduplication
@@ -147,6 +156,10 @@ export interface ExtendedEventPayload extends DataTypesEventPayload {
147
156
  insert_id?: string;
148
157
  /** Conversion type for conversion events (add_to_cart, wishlist, purchase, etc.) */
149
158
  conversion_type?: string;
159
+ /** A/B test experiment ID */
160
+ ab_test_id?: string;
161
+ /** A/B test variant */
162
+ ab_variant?: string;
150
163
  }
151
164
  export interface SearchOptions {
152
165
  q: string;
@@ -314,6 +327,8 @@ export declare class SeekoraClient {
314
327
  private storesApi;
315
328
  private documentsApi;
316
329
  private schemaApi;
330
+ private filtersApi;
331
+ private experimentsApi;
317
332
  private storeId;
318
333
  private readSecret;
319
334
  private writeSecret?;
@@ -330,6 +345,8 @@ export declare class SeekoraClient {
330
345
  private clientConfig;
331
346
  private enableDeduplication;
332
347
  private deduplicationWindow;
348
+ private abTestId?;
349
+ private abVariant?;
333
350
  constructor(config?: SeekoraClientConfig);
334
351
  /**
335
352
  * Search for documents
@@ -394,6 +411,15 @@ export declare class SeekoraClient {
394
411
  * Returns store search configuration and onboarding status
395
412
  */
396
413
  getConfig(): Promise<DataTypesIndexConfig>;
414
+ /**
415
+ * Get experiment assignments for the current user
416
+ *
417
+ * Returns variant assignments for all running experiments.
418
+ * Uses hash-based consistent assignment on the backend.
419
+ *
420
+ * @returns ExperimentAssignmentResponse with assignments array
421
+ */
422
+ getExperimentAssignment(): Promise<DataTypesExperimentAssignmentResponse>;
397
423
  /**
398
424
  * Get store information
399
425
  * Returns store metadata including name, status, and configuration details
@@ -743,6 +769,18 @@ export declare class SeekoraClient {
743
769
  anonId: string;
744
770
  sessionId: string;
745
771
  };
772
+ /**
773
+ * Set A/B test experiment ID and variant for all subsequent analytics events.
774
+ * Call this when experiment assignments are fetched.
775
+ */
776
+ setAbTest(abTestId?: string, abVariant?: string): void;
777
+ /**
778
+ * Get current A/B test fields
779
+ */
780
+ getAbTest(): {
781
+ abTestId?: string;
782
+ abVariant?: string;
783
+ };
746
784
  /**
747
785
  * Get the event queue instance (if enabled)
748
786
  */
@@ -787,5 +825,62 @@ export declare class SeekoraClient {
787
825
  * Get the logger instance
788
826
  */
789
827
  getLogger(): Logger;
828
+ /**
829
+ * Get filter values and counts for configured facets.
830
+ * Use this to build faceted navigation UIs without performing a full search.
831
+ * Supports disjunctive faceting for OR-based filter combinations.
832
+ *
833
+ * @param options - Filter options (query, filters, facet_by, max_facet_values, disjunctive_facets)
834
+ * @returns Filter values with counts grouped by field
835
+ */
836
+ getFilters(options?: FilterOptions): Promise<DataTypesFiltersResponse>;
837
+ /**
838
+ * Search within a single facet's values.
839
+ * Useful for facets with many values (e.g., brands) where you need typeahead/autocomplete.
840
+ *
841
+ * @param facetName - The facet field name to search within (e.g., "brand", "category")
842
+ * @param options - Search options including facetQuery (required), optional q, filter, maxValues
843
+ * @returns Matching facet values with counts
844
+ */
845
+ searchFacetValues(facetName: string, options: FacetSearchOptions): Promise<DataTypesFacetValuesSearchResponse>;
846
+ /**
847
+ * Get available filter field metadata (schema).
848
+ * Returns information about all facetable fields including types, sortability, and configured ranges.
849
+ * Useful for dynamically building filter UIs.
850
+ *
851
+ * @returns Filter field metadata with types, default facets, and max values
852
+ */
853
+ getFiltersSchema(): Promise<DataTypesFiltersSchemaResponse>;
854
+ }
855
+ /** Options for getFilters() — high-level wrapper with camelCase fields */
856
+ export interface FilterOptions {
857
+ /** Search query to scope filters */
858
+ q?: string;
859
+ /** Active filter string (Typesense filter_by syntax) */
860
+ filter?: string;
861
+ /** Comma-separated facet fields to return (defaults to store config) */
862
+ facetBy?: string;
863
+ /** Maximum number of values per facet */
864
+ maxFacetValues?: number;
865
+ /** Fields that should use disjunctive (OR) faceting */
866
+ disjunctiveFacets?: string[];
867
+ }
868
+ /** Options for searchFacetValues() — high-level wrapper with camelCase fields */
869
+ export interface FacetSearchOptions {
870
+ /** Search query to scope the facet values */
871
+ q?: string;
872
+ /** Active filter string */
873
+ filter?: string;
874
+ /** Query to search within facet values (required) */
875
+ facetQuery: string;
876
+ /** Maximum values to return */
877
+ maxValues?: number;
790
878
  }
879
+ export type FilterValue = DataTypesFilterValue;
880
+ export type FilterStats = DataTypesFilterStats;
881
+ export type FilterField = DataTypesFilterField;
882
+ export type FiltersResponse = DataTypesFiltersResponse;
883
+ export type FacetValuesSearchResponse = DataTypesFacetValuesSearchResponse;
884
+ export type FilterSchemaField = DataTypesFilterSchemaField;
885
+ export type FiltersSchemaResponse = DataTypesFiltersSchemaResponse;
791
886
  export default SeekoraClient;
package/dist/client.js CHANGED
@@ -42,6 +42,8 @@ class SeekoraClient {
42
42
  this.anonId = config.anonId || (0, utils_1.getOrCreateAnonId)();
43
43
  this.sessionId = config.sessionId || (0, utils_1.getOrCreateSessionId)();
44
44
  this.autoTrackSearch = config.autoTrackSearch || false;
45
+ this.abTestId = config.abTestId;
46
+ this.abVariant = config.abVariant;
45
47
  // Initialize context collection
46
48
  this.enableContextCollection = config.enableContextCollection !== false; // Default to true
47
49
  this.contextCollector = new context_collector_1.ContextCollector(config.contextCollector);
@@ -49,8 +51,8 @@ class SeekoraClient {
49
51
  if (this.enableContextCollection) {
50
52
  this.collectContextAsync();
51
53
  }
52
- // Initialize event queue if enabled
53
- this.enableEventQueue = config.enableEventQueue || false;
54
+ // Initialize event queue (enabled by default for batching and offline support)
55
+ this.enableEventQueue = config.enableEventQueue ?? true;
54
56
  if (this.enableEventQueue) {
55
57
  this.eventQueue = new event_queue_1.EventQueue(config.eventQueue);
56
58
  this.eventQueue.setLogger(this.logger);
@@ -97,6 +99,8 @@ class SeekoraClient {
97
99
  this.storesApi = new generated_1.SDKStoreConfigApi(this.config);
98
100
  this.documentsApi = new generated_1.SDKDocumentsApi(this.config);
99
101
  this.schemaApi = new generated_1.SDKSchemaApi(this.config);
102
+ this.filtersApi = new generated_1.FiltersApi(this.config);
103
+ this.experimentsApi = new generated_1.SDKExperimentsApi(this.config);
100
104
  this.logger.info('SeekoraClient initialized successfully');
101
105
  }
102
106
  /**
@@ -540,6 +544,37 @@ class SeekoraClient {
540
544
  throw this.handleError(error, 'getConfig');
541
545
  }
542
546
  }
547
+ /**
548
+ * Get experiment assignments for the current user
549
+ *
550
+ * Returns variant assignments for all running experiments.
551
+ * Uses hash-based consistent assignment on the backend.
552
+ *
553
+ * @returns ExperimentAssignmentResponse with assignments array
554
+ */
555
+ async getExperimentAssignment() {
556
+ this.logger.verbose('Getting experiment assignments');
557
+ try {
558
+ const headers = {
559
+ 'x-storeid': this.storeId,
560
+ 'x-storesecret': this.readSecret,
561
+ };
562
+ if (this.userId)
563
+ headers['x-user-id'] = this.userId;
564
+ if (this.anonId)
565
+ headers['x-anon-id'] = this.anonId;
566
+ const response = await this.experimentsApi.v1ExperimentsAssignmentGet(this.storeId, this.readSecret, this.userId, this.anonId, { headers });
567
+ const data = response.data;
568
+ this.logger.info('Experiment assignments retrieved', {
569
+ count: data?.assignments?.length ?? 0,
570
+ });
571
+ return data;
572
+ }
573
+ catch (error) {
574
+ this.logger.error('Failed to get experiment assignments', { error: error.message });
575
+ throw this.handleError(error, 'getExperimentAssignment');
576
+ }
577
+ }
543
578
  /**
544
579
  * Get store information
545
580
  * Returns store metadata including name, status, and configuration details
@@ -1235,6 +1270,13 @@ class SeekoraClient {
1235
1270
  payload.is_tablet = ctx.is_tablet ? 1 : 0;
1236
1271
  payload.is_touch_device = ctx.is_touch_device;
1237
1272
  }
1273
+ // Inject A/B test fields from client config (event-level values take precedence)
1274
+ if (this.abTestId && !payload.ab_test_id) {
1275
+ payload.ab_test_id = this.abTestId;
1276
+ }
1277
+ if (this.abVariant && !payload.ab_variant) {
1278
+ payload.ab_variant = this.abVariant;
1279
+ }
1238
1280
  // Generate insert_id for deduplication (industry standard: Segment/Amplitude pattern)
1239
1281
  // Always enable for purchase events (revenue must be accurate)
1240
1282
  // Optional for other events based on config
@@ -1631,6 +1673,21 @@ class SeekoraClient {
1631
1673
  sessionId: this.sessionId,
1632
1674
  };
1633
1675
  }
1676
+ /**
1677
+ * Set A/B test experiment ID and variant for all subsequent analytics events.
1678
+ * Call this when experiment assignments are fetched.
1679
+ */
1680
+ setAbTest(abTestId, abVariant) {
1681
+ this.abTestId = abTestId;
1682
+ this.abVariant = abVariant;
1683
+ this.logger.verbose('A/B test fields updated', { abTestId, abVariant });
1684
+ }
1685
+ /**
1686
+ * Get current A/B test fields
1687
+ */
1688
+ getAbTest() {
1689
+ return { abTestId: this.abTestId, abVariant: this.abVariant };
1690
+ }
1634
1691
  /**
1635
1692
  * Get the event queue instance (if enabled)
1636
1693
  */
@@ -1764,6 +1821,96 @@ class SeekoraClient {
1764
1821
  getLogger() {
1765
1822
  return this.logger;
1766
1823
  }
1824
+ // =========================================================================
1825
+ // Filters API
1826
+ // =========================================================================
1827
+ /**
1828
+ * Get filter values and counts for configured facets.
1829
+ * Use this to build faceted navigation UIs without performing a full search.
1830
+ * Supports disjunctive faceting for OR-based filter combinations.
1831
+ *
1832
+ * @param options - Filter options (query, filters, facet_by, max_facet_values, disjunctive_facets)
1833
+ * @returns Filter values with counts grouped by field
1834
+ */
1835
+ async getFilters(options) {
1836
+ this.logger.verbose('Getting filters', { options });
1837
+ try {
1838
+ const req = {
1839
+ q: options?.q,
1840
+ filter: options?.filter,
1841
+ facet_by: options?.facetBy,
1842
+ max_facet_values: options?.maxFacetValues,
1843
+ disjunctive_facets: options?.disjunctiveFacets,
1844
+ };
1845
+ const response = await this.filtersApi.v1FiltersPost(this.storeId, this.readSecret, req);
1846
+ const wrapper = response.data;
1847
+ const data = wrapper?.data || response.data;
1848
+ this.logger.info('Filters retrieved successfully', {
1849
+ filterCount: data?.filters?.length,
1850
+ totalResults: data?.total_results,
1851
+ });
1852
+ return data;
1853
+ }
1854
+ catch (error) {
1855
+ this.logger.error('Failed to get filters', { error: error.message });
1856
+ throw this.handleError(error, 'getFilters');
1857
+ }
1858
+ }
1859
+ /**
1860
+ * Search within a single facet's values.
1861
+ * Useful for facets with many values (e.g., brands) where you need typeahead/autocomplete.
1862
+ *
1863
+ * @param facetName - The facet field name to search within (e.g., "brand", "category")
1864
+ * @param options - Search options including facetQuery (required), optional q, filter, maxValues
1865
+ * @returns Matching facet values with counts
1866
+ */
1867
+ async searchFacetValues(facetName, options) {
1868
+ this.logger.verbose('Searching facet values', { facetName, options });
1869
+ try {
1870
+ const req = {
1871
+ facet_query: options.facetQuery,
1872
+ q: options?.q,
1873
+ filter: options?.filter,
1874
+ max_values: options?.maxValues,
1875
+ };
1876
+ const response = await this.filtersApi.v1FiltersFacetNameValuesPost(this.storeId, this.readSecret, facetName, req);
1877
+ const wrapper = response.data;
1878
+ const data = wrapper?.data || response.data;
1879
+ this.logger.info('Facet values retrieved', {
1880
+ facetName,
1881
+ valueCount: data?.values?.length,
1882
+ });
1883
+ return data;
1884
+ }
1885
+ catch (error) {
1886
+ this.logger.error('Failed to search facet values', { facetName, error: error.message });
1887
+ throw this.handleError(error, 'searchFacetValues');
1888
+ }
1889
+ }
1890
+ /**
1891
+ * Get available filter field metadata (schema).
1892
+ * Returns information about all facetable fields including types, sortability, and configured ranges.
1893
+ * Useful for dynamically building filter UIs.
1894
+ *
1895
+ * @returns Filter field metadata with types, default facets, and max values
1896
+ */
1897
+ async getFiltersSchema() {
1898
+ this.logger.verbose('Getting filters schema');
1899
+ try {
1900
+ const response = await this.filtersApi.v1FiltersSchemaGet(this.storeId, this.readSecret);
1901
+ const wrapper = response.data;
1902
+ const data = wrapper?.data || response.data;
1903
+ this.logger.info('Filters schema retrieved', {
1904
+ fieldCount: data?.fields?.length,
1905
+ defaultFacets: data?.default_facets?.length,
1906
+ });
1907
+ return data;
1908
+ }
1909
+ catch (error) {
1910
+ this.logger.error('Failed to get filters schema', { error: error.message });
1911
+ throw this.handleError(error, 'getFiltersSchema');
1912
+ }
1913
+ }
1767
1914
  }
1768
1915
  exports.SeekoraClient = SeekoraClient;
1769
1916
  // Export default