@findlane/server 0.24.0 → 0.25.0

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
@@ -394,6 +394,25 @@ export type SearchRequest = {
394
394
  /** Checks rules as at this time, in milliseconds since 1970, such as a campaign's start, instead of now
395
395
  * (0.20.0). Browser search doesn't take it. */
396
396
  rulesAt?: number;
397
+ /** The index's subindexes to search too, such as a store's categories and pages beside its products, by
398
+ * name: each with its own search, which takes this one's `q` and `highlight` unless it says, and 5 results
399
+ * unless it says, or `true` for that. Up to 5. Their results come back in `subindexes`; the search and its
400
+ * subindexes count as one search (0.25.0). */
401
+ subindexes?: Record<string, Omit<SearchRequest, "subindexes"> | true>;
402
+ };
403
+ /** A subindex whose search failed, such as one the index doesn't have (404). */
404
+ export type SubindexError = {
405
+ error: string;
406
+ status: number;
407
+ };
408
+ /** One of an index's subindexes: its full name (products.categories), its own name, and its records. */
409
+ export type Subindex = {
410
+ id: string;
411
+ name: string;
412
+ subindex: string;
413
+ kind: string | null;
414
+ status: string;
415
+ records: number;
397
416
  };
398
417
  /** A point in degrees: lat from -90 to 90, lng from -180 to 180. */
399
418
  export type GeoPoint = {
@@ -541,6 +560,8 @@ export type SearchResponse<T> = {
541
560
  }>;
542
561
  total: number;
543
562
  };
563
+ /** With `subindexes`: each subindex's results by name, or its error (0.25.0). */
564
+ subindexes?: Record<string, SearchResponse<JsonRecord> | SubindexError>;
544
565
  };
545
566
  /** How a product's recommendations are served (the recommendations add-on). */
546
567
  /** One of an index's lists (docs: Lists): a row of products, such as Popular or New arrivals, from a filter and
@@ -1366,6 +1387,2173 @@ export declare class SearchServerClient {
1366
1387
  intervalMs?: number;
1367
1388
  signal?: AbortSignal;
1368
1389
  }) => Promise<CurrentSettings>;
1390
+ /** A subindex of this index (0.25.0), such as `index("products").subindex("categories")`: an index of its own,
1391
+ * named products.categories, searched with this one (`subindexes` in a search) and opened by its keys. */
1392
+ subindex: <S extends {
1393
+ objectID: string;
1394
+ } = JsonRecord>(subindex: string) => {
1395
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
1396
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
1397
+ search: <R = S>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
1398
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
1399
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
1400
+ * a search. */
1401
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
1402
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
1403
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
1404
+ * 10, 5 by default. */
1405
+ querySuggestions: (query: {
1406
+ q: string;
1407
+ limit?: number;
1408
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
1409
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
1410
+ * empty until the index has enough searches. The list changes daily. */
1411
+ popularSearches: (options?: {
1412
+ limit?: number;
1413
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
1414
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
1415
+ * The API key needs the `search` scope; not counted as a search. */
1416
+ recommendations: <R = S>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
1417
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
1418
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
1419
+ * setting them up needs `write`. */
1420
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
1421
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
1422
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
1423
+ merchandising: {
1424
+ /** Every arranged category page and search. */
1425
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
1426
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
1427
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
1428
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
1429
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
1430
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
1431
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
1432
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
1433
+ };
1434
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
1435
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
1436
+ * later. Uploading and deleting need the `write` scope. */
1437
+ images: {
1438
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
1439
+ /** Uploads an image's bytes; the same image again gives the same URL. */
1440
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
1441
+ contentType?: string;
1442
+ signal?: AbortSignal;
1443
+ }) => Promise<UploadedImage & {
1444
+ error?: string;
1445
+ }>;
1446
+ /** Uploads the image at an https address, which Findlane fetches. */
1447
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
1448
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
1449
+ delete: (key: string, signal?: AbortSignal) => Promise<{
1450
+ deleted: true;
1451
+ }>;
1452
+ };
1453
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
1454
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
1455
+ * needs the `search` or `write` scope; the rest, `write`. */
1456
+ feed: {
1457
+ /** The feed and its last run, or null. */
1458
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
1459
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
1460
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
1461
+ /** Disconnects it; the index keeps its records. */
1462
+ remove: (signal?: AbortSignal) => Promise<void>;
1463
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
1464
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
1465
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
1466
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
1467
+ /** The last 20 runs, newest first. */
1468
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
1469
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
1470
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
1471
+ };
1472
+ lists: {
1473
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
1474
+ get: <R = S>(id: string, options?: {
1475
+ limit?: number;
1476
+ signal?: AbortSignal;
1477
+ }) => Promise<List<R>>;
1478
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
1479
+ getMany: <R = S>(ids?: string[], options?: {
1480
+ limit?: number;
1481
+ signal?: AbortSignal;
1482
+ }) => Promise<List<R>[]>;
1483
+ /** How the index's lists are set up, and their last build. */
1484
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
1485
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
1486
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
1487
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
1488
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
1489
+ /** Puts the lists in this order: every list's ID once. */
1490
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
1491
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
1492
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
1493
+ /** What a list would hold, built without saving it. */
1494
+ preview: <R = S>(list: ListDefinition, signal?: AbortSignal) => Promise<{
1495
+ list: List<R>;
1496
+ tookMs: number;
1497
+ }>;
1498
+ };
1499
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
1500
+ * scope. Orders count once each, however often they're sent. */
1501
+ events: {
1502
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
1503
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
1504
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
1505
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
1506
+ * cursor), 200 a request. */
1507
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
1508
+ /** Takes a cancelled or refunded order out of Bought together. */
1509
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
1510
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
1511
+ * within a week. 1,000 IDs a request. */
1512
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
1513
+ forgotten: number;
1514
+ }>;
1515
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
1516
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
1517
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
1518
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
1519
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
1520
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
1521
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
1522
+ * and its visitor. Answers with the API's response. */
1523
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
1524
+ };
1525
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
1526
+ * requests from web pages are refused. */
1527
+ analytics: {
1528
+ /** Searches, searches without results and response times, beside the same span before, and a series per
1529
+ * hour (up to 7 days, by default) or day. */
1530
+ searches: (options?: AnalyticsOptions & {
1531
+ interval?: "hour" | "day";
1532
+ }) => Promise<SearchesReport>;
1533
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
1534
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
1535
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
1536
+ list?: L | undefined;
1537
+ limit?: number;
1538
+ }) => Promise<QueriesReport<L>>;
1539
+ /** The filter fields and sorts searches used. */
1540
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
1541
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
1542
+ events: (options?: AnalyticsOptions & {
1543
+ interval?: "hour" | "day";
1544
+ }) => Promise<EventsReport>;
1545
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
1546
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
1547
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
1548
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
1549
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
1550
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
1551
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
1552
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
1553
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
1554
+ product: (objectID: string, options?: AnalyticsOptions & {
1555
+ interval?: "hour" | "day";
1556
+ }) => Promise<ProductReport>;
1557
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
1558
+ * product (0.20.0). */
1559
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
1560
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
1561
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
1562
+ banners: (options?: {
1563
+ range?: "7d" | "30d" | "90d";
1564
+ signal?: AbortSignal;
1565
+ }) => Promise<BannerStatsReport>;
1566
+ };
1567
+ upsert: (records: S[], signal?: AbortSignal) => Promise<WriteReceipt>;
1568
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S>>;
1569
+ upsertOne: (record: S, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
1570
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
1571
+ replaceAll: (records: S[], signal?: AbortSignal) => Promise<WriteReceipt>;
1572
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
1573
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S[], signal?: AbortSignal) => Promise<ReplacementStage>;
1574
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
1575
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
1576
+ replaceAllStaged: (records: Iterable<S> | AsyncIterable<S>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
1577
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
1578
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
1579
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
1580
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
1581
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
1582
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
1583
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
1584
+ * or normalization) throws a 409: save it first. Not counted as searches. */
1585
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
1586
+ /** The index's search tests. */
1587
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
1588
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
1589
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
1590
+ /** Waits until no settings rebuild is running; throws if it failed. */
1591
+ waitForSettings: (options?: {
1592
+ timeoutMs?: number;
1593
+ intervalMs?: number;
1594
+ signal?: AbortSignal;
1595
+ }) => Promise<CurrentSettings>;
1596
+ subindex: <S_1 extends {
1597
+ objectID: string;
1598
+ } = JsonRecord>(subindex: string) => {
1599
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
1600
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
1601
+ search: <R = S_1>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
1602
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
1603
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
1604
+ * a search. */
1605
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
1606
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
1607
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
1608
+ * 10, 5 by default. */
1609
+ querySuggestions: (query: {
1610
+ q: string;
1611
+ limit?: number;
1612
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
1613
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
1614
+ * empty until the index has enough searches. The list changes daily. */
1615
+ popularSearches: (options?: {
1616
+ limit?: number;
1617
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
1618
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
1619
+ * The API key needs the `search` scope; not counted as a search. */
1620
+ recommendations: <R = S_1>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
1621
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
1622
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
1623
+ * setting them up needs `write`. */
1624
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
1625
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
1626
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
1627
+ merchandising: {
1628
+ /** Every arranged category page and search. */
1629
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
1630
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
1631
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
1632
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
1633
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
1634
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
1635
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
1636
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
1637
+ };
1638
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
1639
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
1640
+ * later. Uploading and deleting need the `write` scope. */
1641
+ images: {
1642
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
1643
+ /** Uploads an image's bytes; the same image again gives the same URL. */
1644
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
1645
+ contentType?: string;
1646
+ signal?: AbortSignal;
1647
+ }) => Promise<UploadedImage & {
1648
+ error?: string;
1649
+ }>;
1650
+ /** Uploads the image at an https address, which Findlane fetches. */
1651
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
1652
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
1653
+ delete: (key: string, signal?: AbortSignal) => Promise<{
1654
+ deleted: true;
1655
+ }>;
1656
+ };
1657
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
1658
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
1659
+ * needs the `search` or `write` scope; the rest, `write`. */
1660
+ feed: {
1661
+ /** The feed and its last run, or null. */
1662
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
1663
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
1664
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
1665
+ /** Disconnects it; the index keeps its records. */
1666
+ remove: (signal?: AbortSignal) => Promise<void>;
1667
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
1668
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
1669
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
1670
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
1671
+ /** The last 20 runs, newest first. */
1672
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
1673
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
1674
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
1675
+ };
1676
+ lists: {
1677
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
1678
+ get: <R = S_1>(id: string, options?: {
1679
+ limit?: number;
1680
+ signal?: AbortSignal;
1681
+ }) => Promise<List<R>>;
1682
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
1683
+ getMany: <R = S_1>(ids?: string[], options?: {
1684
+ limit?: number;
1685
+ signal?: AbortSignal;
1686
+ }) => Promise<List<R>[]>;
1687
+ /** How the index's lists are set up, and their last build. */
1688
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
1689
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
1690
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
1691
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
1692
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
1693
+ /** Puts the lists in this order: every list's ID once. */
1694
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
1695
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
1696
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
1697
+ /** What a list would hold, built without saving it. */
1698
+ preview: <R = S_1>(list: ListDefinition, signal?: AbortSignal) => Promise<{
1699
+ list: List<R>;
1700
+ tookMs: number;
1701
+ }>;
1702
+ };
1703
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
1704
+ * scope. Orders count once each, however often they're sent. */
1705
+ events: {
1706
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
1707
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
1708
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
1709
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
1710
+ * cursor), 200 a request. */
1711
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
1712
+ /** Takes a cancelled or refunded order out of Bought together. */
1713
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
1714
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
1715
+ * within a week. 1,000 IDs a request. */
1716
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
1717
+ forgotten: number;
1718
+ }>;
1719
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
1720
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
1721
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
1722
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
1723
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
1724
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
1725
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
1726
+ * and its visitor. Answers with the API's response. */
1727
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
1728
+ };
1729
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
1730
+ * requests from web pages are refused. */
1731
+ analytics: {
1732
+ /** Searches, searches without results and response times, beside the same span before, and a series per
1733
+ * hour (up to 7 days, by default) or day. */
1734
+ searches: (options?: AnalyticsOptions & {
1735
+ interval?: "hour" | "day";
1736
+ }) => Promise<SearchesReport>;
1737
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
1738
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
1739
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
1740
+ list?: L | undefined;
1741
+ limit?: number;
1742
+ }) => Promise<QueriesReport<L>>;
1743
+ /** The filter fields and sorts searches used. */
1744
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
1745
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
1746
+ events: (options?: AnalyticsOptions & {
1747
+ interval?: "hour" | "day";
1748
+ }) => Promise<EventsReport>;
1749
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
1750
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
1751
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
1752
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
1753
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
1754
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
1755
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
1756
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
1757
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
1758
+ product: (objectID: string, options?: AnalyticsOptions & {
1759
+ interval?: "hour" | "day";
1760
+ }) => Promise<ProductReport>;
1761
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
1762
+ * product (0.20.0). */
1763
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
1764
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
1765
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
1766
+ banners: (options?: {
1767
+ range?: "7d" | "30d" | "90d";
1768
+ signal?: AbortSignal;
1769
+ }) => Promise<BannerStatsReport>;
1770
+ };
1771
+ upsert: (records: S_1[], signal?: AbortSignal) => Promise<WriteReceipt>;
1772
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S_1>>;
1773
+ upsertOne: (record: S_1, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
1774
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
1775
+ replaceAll: (records: S_1[], signal?: AbortSignal) => Promise<WriteReceipt>;
1776
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
1777
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S_1[], signal?: AbortSignal) => Promise<ReplacementStage>;
1778
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
1779
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
1780
+ replaceAllStaged: (records: Iterable<S_1> | AsyncIterable<S_1>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
1781
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
1782
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
1783
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
1784
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
1785
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
1786
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
1787
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
1788
+ * or normalization) throws a 409: save it first. Not counted as searches. */
1789
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
1790
+ /** The index's search tests. */
1791
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
1792
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
1793
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
1794
+ /** Waits until no settings rebuild is running; throws if it failed. */
1795
+ waitForSettings: (options?: {
1796
+ timeoutMs?: number;
1797
+ intervalMs?: number;
1798
+ signal?: AbortSignal;
1799
+ }) => Promise<CurrentSettings>;
1800
+ subindex: <S_2 extends {
1801
+ objectID: string;
1802
+ } = JsonRecord>(subindex: string) => {
1803
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
1804
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
1805
+ search: <R = S_2>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
1806
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
1807
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
1808
+ * a search. */
1809
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
1810
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
1811
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
1812
+ * 10, 5 by default. */
1813
+ querySuggestions: (query: {
1814
+ q: string;
1815
+ limit?: number;
1816
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
1817
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
1818
+ * empty until the index has enough searches. The list changes daily. */
1819
+ popularSearches: (options?: {
1820
+ limit?: number;
1821
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
1822
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
1823
+ * The API key needs the `search` scope; not counted as a search. */
1824
+ recommendations: <R = S_2>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
1825
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
1826
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
1827
+ * setting them up needs `write`. */
1828
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
1829
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
1830
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
1831
+ merchandising: {
1832
+ /** Every arranged category page and search. */
1833
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
1834
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
1835
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
1836
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
1837
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
1838
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
1839
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
1840
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
1841
+ };
1842
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
1843
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
1844
+ * later. Uploading and deleting need the `write` scope. */
1845
+ images: {
1846
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
1847
+ /** Uploads an image's bytes; the same image again gives the same URL. */
1848
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
1849
+ contentType?: string;
1850
+ signal?: AbortSignal;
1851
+ }) => Promise<UploadedImage & {
1852
+ error?: string;
1853
+ }>;
1854
+ /** Uploads the image at an https address, which Findlane fetches. */
1855
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
1856
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
1857
+ delete: (key: string, signal?: AbortSignal) => Promise<{
1858
+ deleted: true;
1859
+ }>;
1860
+ };
1861
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
1862
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
1863
+ * needs the `search` or `write` scope; the rest, `write`. */
1864
+ feed: {
1865
+ /** The feed and its last run, or null. */
1866
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
1867
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
1868
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
1869
+ /** Disconnects it; the index keeps its records. */
1870
+ remove: (signal?: AbortSignal) => Promise<void>;
1871
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
1872
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
1873
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
1874
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
1875
+ /** The last 20 runs, newest first. */
1876
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
1877
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
1878
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
1879
+ };
1880
+ lists: {
1881
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
1882
+ get: <R = S_2>(id: string, options?: {
1883
+ limit?: number;
1884
+ signal?: AbortSignal;
1885
+ }) => Promise<List<R>>;
1886
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
1887
+ getMany: <R = S_2>(ids?: string[], options?: {
1888
+ limit?: number;
1889
+ signal?: AbortSignal;
1890
+ }) => Promise<List<R>[]>;
1891
+ /** How the index's lists are set up, and their last build. */
1892
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
1893
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
1894
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
1895
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
1896
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
1897
+ /** Puts the lists in this order: every list's ID once. */
1898
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
1899
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
1900
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
1901
+ /** What a list would hold, built without saving it. */
1902
+ preview: <R = S_2>(list: ListDefinition, signal?: AbortSignal) => Promise<{
1903
+ list: List<R>;
1904
+ tookMs: number;
1905
+ }>;
1906
+ };
1907
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
1908
+ * scope. Orders count once each, however often they're sent. */
1909
+ events: {
1910
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
1911
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
1912
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
1913
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
1914
+ * cursor), 200 a request. */
1915
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
1916
+ /** Takes a cancelled or refunded order out of Bought together. */
1917
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
1918
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
1919
+ * within a week. 1,000 IDs a request. */
1920
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
1921
+ forgotten: number;
1922
+ }>;
1923
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
1924
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
1925
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
1926
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
1927
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
1928
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
1929
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
1930
+ * and its visitor. Answers with the API's response. */
1931
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
1932
+ };
1933
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
1934
+ * requests from web pages are refused. */
1935
+ analytics: {
1936
+ /** Searches, searches without results and response times, beside the same span before, and a series per
1937
+ * hour (up to 7 days, by default) or day. */
1938
+ searches: (options?: AnalyticsOptions & {
1939
+ interval?: "hour" | "day";
1940
+ }) => Promise<SearchesReport>;
1941
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
1942
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
1943
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
1944
+ list?: L | undefined;
1945
+ limit?: number;
1946
+ }) => Promise<QueriesReport<L>>;
1947
+ /** The filter fields and sorts searches used. */
1948
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
1949
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
1950
+ events: (options?: AnalyticsOptions & {
1951
+ interval?: "hour" | "day";
1952
+ }) => Promise<EventsReport>;
1953
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
1954
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
1955
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
1956
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
1957
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
1958
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
1959
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
1960
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
1961
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
1962
+ product: (objectID: string, options?: AnalyticsOptions & {
1963
+ interval?: "hour" | "day";
1964
+ }) => Promise<ProductReport>;
1965
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
1966
+ * product (0.20.0). */
1967
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
1968
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
1969
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
1970
+ banners: (options?: {
1971
+ range?: "7d" | "30d" | "90d";
1972
+ signal?: AbortSignal;
1973
+ }) => Promise<BannerStatsReport>;
1974
+ };
1975
+ upsert: (records: S_2[], signal?: AbortSignal) => Promise<WriteReceipt>;
1976
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S_2>>;
1977
+ upsertOne: (record: S_2, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
1978
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
1979
+ replaceAll: (records: S_2[], signal?: AbortSignal) => Promise<WriteReceipt>;
1980
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
1981
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S_2[], signal?: AbortSignal) => Promise<ReplacementStage>;
1982
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
1983
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
1984
+ replaceAllStaged: (records: Iterable<S_2> | AsyncIterable<S_2>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
1985
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
1986
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
1987
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
1988
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
1989
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
1990
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
1991
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
1992
+ * or normalization) throws a 409: save it first. Not counted as searches. */
1993
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
1994
+ /** The index's search tests. */
1995
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
1996
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
1997
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
1998
+ /** Waits until no settings rebuild is running; throws if it failed. */
1999
+ waitForSettings: (options?: {
2000
+ timeoutMs?: number;
2001
+ intervalMs?: number;
2002
+ signal?: AbortSignal;
2003
+ }) => Promise<CurrentSettings>;
2004
+ subindex: <S_3 extends {
2005
+ objectID: string;
2006
+ } = JsonRecord>(subindex: string) => {
2007
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
2008
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
2009
+ search: <R = S_3>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
2010
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
2011
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
2012
+ * a search. */
2013
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
2014
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
2015
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
2016
+ * 10, 5 by default. */
2017
+ querySuggestions: (query: {
2018
+ q: string;
2019
+ limit?: number;
2020
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2021
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
2022
+ * empty until the index has enough searches. The list changes daily. */
2023
+ popularSearches: (options?: {
2024
+ limit?: number;
2025
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2026
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
2027
+ * The API key needs the `search` scope; not counted as a search. */
2028
+ recommendations: <R = S_3>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
2029
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
2030
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
2031
+ * setting them up needs `write`. */
2032
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
2033
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
2034
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
2035
+ merchandising: {
2036
+ /** Every arranged category page and search. */
2037
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
2038
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
2039
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2040
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
2041
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
2042
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
2043
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
2044
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2045
+ };
2046
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
2047
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
2048
+ * later. Uploading and deleting need the `write` scope. */
2049
+ images: {
2050
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
2051
+ /** Uploads an image's bytes; the same image again gives the same URL. */
2052
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
2053
+ contentType?: string;
2054
+ signal?: AbortSignal;
2055
+ }) => Promise<UploadedImage & {
2056
+ error?: string;
2057
+ }>;
2058
+ /** Uploads the image at an https address, which Findlane fetches. */
2059
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
2060
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
2061
+ delete: (key: string, signal?: AbortSignal) => Promise<{
2062
+ deleted: true;
2063
+ }>;
2064
+ };
2065
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
2066
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
2067
+ * needs the `search` or `write` scope; the rest, `write`. */
2068
+ feed: {
2069
+ /** The feed and its last run, or null. */
2070
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
2071
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
2072
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
2073
+ /** Disconnects it; the index keeps its records. */
2074
+ remove: (signal?: AbortSignal) => Promise<void>;
2075
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
2076
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
2077
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
2078
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
2079
+ /** The last 20 runs, newest first. */
2080
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
2081
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
2082
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
2083
+ };
2084
+ lists: {
2085
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
2086
+ get: <R = S_3>(id: string, options?: {
2087
+ limit?: number;
2088
+ signal?: AbortSignal;
2089
+ }) => Promise<List<R>>;
2090
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
2091
+ getMany: <R = S_3>(ids?: string[], options?: {
2092
+ limit?: number;
2093
+ signal?: AbortSignal;
2094
+ }) => Promise<List<R>[]>;
2095
+ /** How the index's lists are set up, and their last build. */
2096
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
2097
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
2098
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
2099
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
2100
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
2101
+ /** Puts the lists in this order: every list's ID once. */
2102
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
2103
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
2104
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
2105
+ /** What a list would hold, built without saving it. */
2106
+ preview: <R = S_3>(list: ListDefinition, signal?: AbortSignal) => Promise<{
2107
+ list: List<R>;
2108
+ tookMs: number;
2109
+ }>;
2110
+ };
2111
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
2112
+ * scope. Orders count once each, however often they're sent. */
2113
+ events: {
2114
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
2115
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
2116
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
2117
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
2118
+ * cursor), 200 a request. */
2119
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
2120
+ /** Takes a cancelled or refunded order out of Bought together. */
2121
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
2122
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
2123
+ * within a week. 1,000 IDs a request. */
2124
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
2125
+ forgotten: number;
2126
+ }>;
2127
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
2128
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
2129
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
2130
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
2131
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
2132
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
2133
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
2134
+ * and its visitor. Answers with the API's response. */
2135
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
2136
+ };
2137
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
2138
+ * requests from web pages are refused. */
2139
+ analytics: {
2140
+ /** Searches, searches without results and response times, beside the same span before, and a series per
2141
+ * hour (up to 7 days, by default) or day. */
2142
+ searches: (options?: AnalyticsOptions & {
2143
+ interval?: "hour" | "day";
2144
+ }) => Promise<SearchesReport>;
2145
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
2146
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
2147
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
2148
+ list?: L | undefined;
2149
+ limit?: number;
2150
+ }) => Promise<QueriesReport<L>>;
2151
+ /** The filter fields and sorts searches used. */
2152
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
2153
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
2154
+ events: (options?: AnalyticsOptions & {
2155
+ interval?: "hour" | "day";
2156
+ }) => Promise<EventsReport>;
2157
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
2158
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
2159
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
2160
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
2161
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
2162
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
2163
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
2164
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
2165
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
2166
+ product: (objectID: string, options?: AnalyticsOptions & {
2167
+ interval?: "hour" | "day";
2168
+ }) => Promise<ProductReport>;
2169
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
2170
+ * product (0.20.0). */
2171
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
2172
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
2173
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
2174
+ banners: (options?: {
2175
+ range?: "7d" | "30d" | "90d";
2176
+ signal?: AbortSignal;
2177
+ }) => Promise<BannerStatsReport>;
2178
+ };
2179
+ upsert: (records: S_3[], signal?: AbortSignal) => Promise<WriteReceipt>;
2180
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S_3>>;
2181
+ upsertOne: (record: S_3, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2182
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2183
+ replaceAll: (records: S_3[], signal?: AbortSignal) => Promise<WriteReceipt>;
2184
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
2185
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S_3[], signal?: AbortSignal) => Promise<ReplacementStage>;
2186
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
2187
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
2188
+ replaceAllStaged: (records: Iterable<S_3> | AsyncIterable<S_3>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
2189
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
2190
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
2191
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
2192
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
2193
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
2194
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
2195
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
2196
+ * or normalization) throws a 409: save it first. Not counted as searches. */
2197
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
2198
+ /** The index's search tests. */
2199
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
2200
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
2201
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
2202
+ /** Waits until no settings rebuild is running; throws if it failed. */
2203
+ waitForSettings: (options?: {
2204
+ timeoutMs?: number;
2205
+ intervalMs?: number;
2206
+ signal?: AbortSignal;
2207
+ }) => Promise<CurrentSettings>;
2208
+ subindex: <S_4 extends {
2209
+ objectID: string;
2210
+ } = JsonRecord>(subindex: string) => {
2211
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
2212
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
2213
+ search: <R = S_4>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
2214
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
2215
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
2216
+ * a search. */
2217
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
2218
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
2219
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
2220
+ * 10, 5 by default. */
2221
+ querySuggestions: (query: {
2222
+ q: string;
2223
+ limit?: number;
2224
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2225
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
2226
+ * empty until the index has enough searches. The list changes daily. */
2227
+ popularSearches: (options?: {
2228
+ limit?: number;
2229
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2230
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
2231
+ * The API key needs the `search` scope; not counted as a search. */
2232
+ recommendations: <R = S_4>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
2233
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
2234
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
2235
+ * setting them up needs `write`. */
2236
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
2237
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
2238
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
2239
+ merchandising: {
2240
+ /** Every arranged category page and search. */
2241
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
2242
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
2243
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2244
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
2245
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
2246
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
2247
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
2248
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2249
+ };
2250
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
2251
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
2252
+ * later. Uploading and deleting need the `write` scope. */
2253
+ images: {
2254
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
2255
+ /** Uploads an image's bytes; the same image again gives the same URL. */
2256
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
2257
+ contentType?: string;
2258
+ signal?: AbortSignal;
2259
+ }) => Promise<UploadedImage & {
2260
+ error?: string;
2261
+ }>;
2262
+ /** Uploads the image at an https address, which Findlane fetches. */
2263
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
2264
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
2265
+ delete: (key: string, signal?: AbortSignal) => Promise<{
2266
+ deleted: true;
2267
+ }>;
2268
+ };
2269
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
2270
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
2271
+ * needs the `search` or `write` scope; the rest, `write`. */
2272
+ feed: {
2273
+ /** The feed and its last run, or null. */
2274
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
2275
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
2276
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
2277
+ /** Disconnects it; the index keeps its records. */
2278
+ remove: (signal?: AbortSignal) => Promise<void>;
2279
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
2280
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
2281
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
2282
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
2283
+ /** The last 20 runs, newest first. */
2284
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
2285
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
2286
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
2287
+ };
2288
+ lists: {
2289
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
2290
+ get: <R = S_4>(id: string, options?: {
2291
+ limit?: number;
2292
+ signal?: AbortSignal;
2293
+ }) => Promise<List<R>>;
2294
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
2295
+ getMany: <R = S_4>(ids?: string[], options?: {
2296
+ limit?: number;
2297
+ signal?: AbortSignal;
2298
+ }) => Promise<List<R>[]>;
2299
+ /** How the index's lists are set up, and their last build. */
2300
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
2301
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
2302
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
2303
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
2304
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
2305
+ /** Puts the lists in this order: every list's ID once. */
2306
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
2307
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
2308
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
2309
+ /** What a list would hold, built without saving it. */
2310
+ preview: <R = S_4>(list: ListDefinition, signal?: AbortSignal) => Promise<{
2311
+ list: List<R>;
2312
+ tookMs: number;
2313
+ }>;
2314
+ };
2315
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
2316
+ * scope. Orders count once each, however often they're sent. */
2317
+ events: {
2318
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
2319
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
2320
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
2321
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
2322
+ * cursor), 200 a request. */
2323
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
2324
+ /** Takes a cancelled or refunded order out of Bought together. */
2325
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
2326
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
2327
+ * within a week. 1,000 IDs a request. */
2328
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
2329
+ forgotten: number;
2330
+ }>;
2331
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
2332
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
2333
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
2334
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
2335
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
2336
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
2337
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
2338
+ * and its visitor. Answers with the API's response. */
2339
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
2340
+ };
2341
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
2342
+ * requests from web pages are refused. */
2343
+ analytics: {
2344
+ /** Searches, searches without results and response times, beside the same span before, and a series per
2345
+ * hour (up to 7 days, by default) or day. */
2346
+ searches: (options?: AnalyticsOptions & {
2347
+ interval?: "hour" | "day";
2348
+ }) => Promise<SearchesReport>;
2349
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
2350
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
2351
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
2352
+ list?: L | undefined;
2353
+ limit?: number;
2354
+ }) => Promise<QueriesReport<L>>;
2355
+ /** The filter fields and sorts searches used. */
2356
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
2357
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
2358
+ events: (options?: AnalyticsOptions & {
2359
+ interval?: "hour" | "day";
2360
+ }) => Promise<EventsReport>;
2361
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
2362
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
2363
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
2364
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
2365
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
2366
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
2367
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
2368
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
2369
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
2370
+ product: (objectID: string, options?: AnalyticsOptions & {
2371
+ interval?: "hour" | "day";
2372
+ }) => Promise<ProductReport>;
2373
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
2374
+ * product (0.20.0). */
2375
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
2376
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
2377
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
2378
+ banners: (options?: {
2379
+ range?: "7d" | "30d" | "90d";
2380
+ signal?: AbortSignal;
2381
+ }) => Promise<BannerStatsReport>;
2382
+ };
2383
+ upsert: (records: S_4[], signal?: AbortSignal) => Promise<WriteReceipt>;
2384
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S_4>>;
2385
+ upsertOne: (record: S_4, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2386
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2387
+ replaceAll: (records: S_4[], signal?: AbortSignal) => Promise<WriteReceipt>;
2388
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
2389
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S_4[], signal?: AbortSignal) => Promise<ReplacementStage>;
2390
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
2391
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
2392
+ replaceAllStaged: (records: Iterable<S_4> | AsyncIterable<S_4>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
2393
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
2394
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
2395
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
2396
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
2397
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
2398
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
2399
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
2400
+ * or normalization) throws a 409: save it first. Not counted as searches. */
2401
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
2402
+ /** The index's search tests. */
2403
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
2404
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
2405
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
2406
+ /** Waits until no settings rebuild is running; throws if it failed. */
2407
+ waitForSettings: (options?: {
2408
+ timeoutMs?: number;
2409
+ intervalMs?: number;
2410
+ signal?: AbortSignal;
2411
+ }) => Promise<CurrentSettings>;
2412
+ subindex: <S_5 extends {
2413
+ objectID: string;
2414
+ } = JsonRecord>(subindex: string) => {
2415
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
2416
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
2417
+ search: <R = S_5>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
2418
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
2419
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
2420
+ * a search. */
2421
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
2422
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
2423
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
2424
+ * 10, 5 by default. */
2425
+ querySuggestions: (query: {
2426
+ q: string;
2427
+ limit?: number;
2428
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2429
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
2430
+ * empty until the index has enough searches. The list changes daily. */
2431
+ popularSearches: (options?: {
2432
+ limit?: number;
2433
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2434
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
2435
+ * The API key needs the `search` scope; not counted as a search. */
2436
+ recommendations: <R = S_5>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
2437
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
2438
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
2439
+ * setting them up needs `write`. */
2440
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
2441
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
2442
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
2443
+ merchandising: {
2444
+ /** Every arranged category page and search. */
2445
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
2446
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
2447
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2448
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
2449
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
2450
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
2451
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
2452
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2453
+ };
2454
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
2455
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
2456
+ * later. Uploading and deleting need the `write` scope. */
2457
+ images: {
2458
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
2459
+ /** Uploads an image's bytes; the same image again gives the same URL. */
2460
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
2461
+ contentType?: string;
2462
+ signal?: AbortSignal;
2463
+ }) => Promise<UploadedImage & {
2464
+ error?: string;
2465
+ }>;
2466
+ /** Uploads the image at an https address, which Findlane fetches. */
2467
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
2468
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
2469
+ delete: (key: string, signal?: AbortSignal) => Promise<{
2470
+ deleted: true;
2471
+ }>;
2472
+ };
2473
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
2474
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
2475
+ * needs the `search` or `write` scope; the rest, `write`. */
2476
+ feed: {
2477
+ /** The feed and its last run, or null. */
2478
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
2479
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
2480
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
2481
+ /** Disconnects it; the index keeps its records. */
2482
+ remove: (signal?: AbortSignal) => Promise<void>;
2483
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
2484
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
2485
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
2486
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
2487
+ /** The last 20 runs, newest first. */
2488
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
2489
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
2490
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
2491
+ };
2492
+ lists: {
2493
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
2494
+ get: <R = S_5>(id: string, options?: {
2495
+ limit?: number;
2496
+ signal?: AbortSignal;
2497
+ }) => Promise<List<R>>;
2498
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
2499
+ getMany: <R = S_5>(ids?: string[], options?: {
2500
+ limit?: number;
2501
+ signal?: AbortSignal;
2502
+ }) => Promise<List<R>[]>;
2503
+ /** How the index's lists are set up, and their last build. */
2504
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
2505
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
2506
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
2507
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
2508
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
2509
+ /** Puts the lists in this order: every list's ID once. */
2510
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
2511
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
2512
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
2513
+ /** What a list would hold, built without saving it. */
2514
+ preview: <R = S_5>(list: ListDefinition, signal?: AbortSignal) => Promise<{
2515
+ list: List<R>;
2516
+ tookMs: number;
2517
+ }>;
2518
+ };
2519
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
2520
+ * scope. Orders count once each, however often they're sent. */
2521
+ events: {
2522
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
2523
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
2524
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
2525
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
2526
+ * cursor), 200 a request. */
2527
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
2528
+ /** Takes a cancelled or refunded order out of Bought together. */
2529
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
2530
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
2531
+ * within a week. 1,000 IDs a request. */
2532
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
2533
+ forgotten: number;
2534
+ }>;
2535
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
2536
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
2537
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
2538
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
2539
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
2540
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
2541
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
2542
+ * and its visitor. Answers with the API's response. */
2543
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
2544
+ };
2545
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
2546
+ * requests from web pages are refused. */
2547
+ analytics: {
2548
+ /** Searches, searches without results and response times, beside the same span before, and a series per
2549
+ * hour (up to 7 days, by default) or day. */
2550
+ searches: (options?: AnalyticsOptions & {
2551
+ interval?: "hour" | "day";
2552
+ }) => Promise<SearchesReport>;
2553
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
2554
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
2555
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
2556
+ list?: L | undefined;
2557
+ limit?: number;
2558
+ }) => Promise<QueriesReport<L>>;
2559
+ /** The filter fields and sorts searches used. */
2560
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
2561
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
2562
+ events: (options?: AnalyticsOptions & {
2563
+ interval?: "hour" | "day";
2564
+ }) => Promise<EventsReport>;
2565
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
2566
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
2567
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
2568
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
2569
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
2570
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
2571
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
2572
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
2573
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
2574
+ product: (objectID: string, options?: AnalyticsOptions & {
2575
+ interval?: "hour" | "day";
2576
+ }) => Promise<ProductReport>;
2577
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
2578
+ * product (0.20.0). */
2579
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
2580
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
2581
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
2582
+ banners: (options?: {
2583
+ range?: "7d" | "30d" | "90d";
2584
+ signal?: AbortSignal;
2585
+ }) => Promise<BannerStatsReport>;
2586
+ };
2587
+ upsert: (records: S_5[], signal?: AbortSignal) => Promise<WriteReceipt>;
2588
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S_5>>;
2589
+ upsertOne: (record: S_5, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2590
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2591
+ replaceAll: (records: S_5[], signal?: AbortSignal) => Promise<WriteReceipt>;
2592
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
2593
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S_5[], signal?: AbortSignal) => Promise<ReplacementStage>;
2594
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
2595
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
2596
+ replaceAllStaged: (records: Iterable<S_5> | AsyncIterable<S_5>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
2597
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
2598
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
2599
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
2600
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
2601
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
2602
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
2603
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
2604
+ * or normalization) throws a 409: save it first. Not counted as searches. */
2605
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
2606
+ /** The index's search tests. */
2607
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
2608
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
2609
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
2610
+ /** Waits until no settings rebuild is running; throws if it failed. */
2611
+ waitForSettings: (options?: {
2612
+ timeoutMs?: number;
2613
+ intervalMs?: number;
2614
+ signal?: AbortSignal;
2615
+ }) => Promise<CurrentSettings>;
2616
+ subindex: <S_6 extends {
2617
+ objectID: string;
2618
+ } = JsonRecord>(subindex: string) => {
2619
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
2620
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
2621
+ search: <R = S_6>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
2622
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
2623
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
2624
+ * a search. */
2625
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
2626
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
2627
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
2628
+ * 10, 5 by default. */
2629
+ querySuggestions: (query: {
2630
+ q: string;
2631
+ limit?: number;
2632
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2633
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
2634
+ * empty until the index has enough searches. The list changes daily. */
2635
+ popularSearches: (options?: {
2636
+ limit?: number;
2637
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2638
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
2639
+ * The API key needs the `search` scope; not counted as a search. */
2640
+ recommendations: <R = S_6>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
2641
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
2642
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
2643
+ * setting them up needs `write`. */
2644
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
2645
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
2646
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
2647
+ merchandising: {
2648
+ /** Every arranged category page and search. */
2649
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
2650
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
2651
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2652
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
2653
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
2654
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
2655
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
2656
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2657
+ };
2658
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
2659
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
2660
+ * later. Uploading and deleting need the `write` scope. */
2661
+ images: {
2662
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
2663
+ /** Uploads an image's bytes; the same image again gives the same URL. */
2664
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
2665
+ contentType?: string;
2666
+ signal?: AbortSignal;
2667
+ }) => Promise<UploadedImage & {
2668
+ error?: string;
2669
+ }>;
2670
+ /** Uploads the image at an https address, which Findlane fetches. */
2671
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
2672
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
2673
+ delete: (key: string, signal?: AbortSignal) => Promise<{
2674
+ deleted: true;
2675
+ }>;
2676
+ };
2677
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
2678
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
2679
+ * needs the `search` or `write` scope; the rest, `write`. */
2680
+ feed: {
2681
+ /** The feed and its last run, or null. */
2682
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
2683
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
2684
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
2685
+ /** Disconnects it; the index keeps its records. */
2686
+ remove: (signal?: AbortSignal) => Promise<void>;
2687
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
2688
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
2689
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
2690
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
2691
+ /** The last 20 runs, newest first. */
2692
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
2693
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
2694
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
2695
+ };
2696
+ lists: {
2697
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
2698
+ get: <R = S_6>(id: string, options?: {
2699
+ limit?: number;
2700
+ signal?: AbortSignal;
2701
+ }) => Promise<List<R>>;
2702
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
2703
+ getMany: <R = S_6>(ids?: string[], options?: {
2704
+ limit?: number;
2705
+ signal?: AbortSignal;
2706
+ }) => Promise<List<R>[]>;
2707
+ /** How the index's lists are set up, and their last build. */
2708
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
2709
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
2710
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
2711
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
2712
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
2713
+ /** Puts the lists in this order: every list's ID once. */
2714
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
2715
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
2716
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
2717
+ /** What a list would hold, built without saving it. */
2718
+ preview: <R = S_6>(list: ListDefinition, signal?: AbortSignal) => Promise<{
2719
+ list: List<R>;
2720
+ tookMs: number;
2721
+ }>;
2722
+ };
2723
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
2724
+ * scope. Orders count once each, however often they're sent. */
2725
+ events: {
2726
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
2727
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
2728
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
2729
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
2730
+ * cursor), 200 a request. */
2731
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
2732
+ /** Takes a cancelled or refunded order out of Bought together. */
2733
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
2734
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
2735
+ * within a week. 1,000 IDs a request. */
2736
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
2737
+ forgotten: number;
2738
+ }>;
2739
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
2740
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
2741
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
2742
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
2743
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
2744
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
2745
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
2746
+ * and its visitor. Answers with the API's response. */
2747
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
2748
+ };
2749
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
2750
+ * requests from web pages are refused. */
2751
+ analytics: {
2752
+ /** Searches, searches without results and response times, beside the same span before, and a series per
2753
+ * hour (up to 7 days, by default) or day. */
2754
+ searches: (options?: AnalyticsOptions & {
2755
+ interval?: "hour" | "day";
2756
+ }) => Promise<SearchesReport>;
2757
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
2758
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
2759
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
2760
+ list?: L | undefined;
2761
+ limit?: number;
2762
+ }) => Promise<QueriesReport<L>>;
2763
+ /** The filter fields and sorts searches used. */
2764
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
2765
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
2766
+ events: (options?: AnalyticsOptions & {
2767
+ interval?: "hour" | "day";
2768
+ }) => Promise<EventsReport>;
2769
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
2770
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
2771
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
2772
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
2773
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
2774
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
2775
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
2776
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
2777
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
2778
+ product: (objectID: string, options?: AnalyticsOptions & {
2779
+ interval?: "hour" | "day";
2780
+ }) => Promise<ProductReport>;
2781
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
2782
+ * product (0.20.0). */
2783
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
2784
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
2785
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
2786
+ banners: (options?: {
2787
+ range?: "7d" | "30d" | "90d";
2788
+ signal?: AbortSignal;
2789
+ }) => Promise<BannerStatsReport>;
2790
+ };
2791
+ upsert: (records: S_6[], signal?: AbortSignal) => Promise<WriteReceipt>;
2792
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S_6>>;
2793
+ upsertOne: (record: S_6, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2794
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2795
+ replaceAll: (records: S_6[], signal?: AbortSignal) => Promise<WriteReceipt>;
2796
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
2797
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S_6[], signal?: AbortSignal) => Promise<ReplacementStage>;
2798
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
2799
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
2800
+ replaceAllStaged: (records: Iterable<S_6> | AsyncIterable<S_6>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
2801
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
2802
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
2803
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
2804
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
2805
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
2806
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
2807
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
2808
+ * or normalization) throws a 409: save it first. Not counted as searches. */
2809
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
2810
+ /** The index's search tests. */
2811
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
2812
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
2813
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
2814
+ /** Waits until no settings rebuild is running; throws if it failed. */
2815
+ waitForSettings: (options?: {
2816
+ timeoutMs?: number;
2817
+ intervalMs?: number;
2818
+ signal?: AbortSignal;
2819
+ }) => Promise<CurrentSettings>;
2820
+ subindex: <S_7 extends {
2821
+ objectID: string;
2822
+ } = JsonRecord>(subindex: string) => {
2823
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
2824
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
2825
+ search: <R = S_7>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
2826
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
2827
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
2828
+ * a search. */
2829
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
2830
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
2831
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
2832
+ * 10, 5 by default. */
2833
+ querySuggestions: (query: {
2834
+ q: string;
2835
+ limit?: number;
2836
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2837
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
2838
+ * empty until the index has enough searches. The list changes daily. */
2839
+ popularSearches: (options?: {
2840
+ limit?: number;
2841
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
2842
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
2843
+ * The API key needs the `search` scope; not counted as a search. */
2844
+ recommendations: <R = S_7>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
2845
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
2846
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
2847
+ * setting them up needs `write`. */
2848
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
2849
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
2850
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
2851
+ merchandising: {
2852
+ /** Every arranged category page and search. */
2853
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
2854
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
2855
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2856
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
2857
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
2858
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
2859
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
2860
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
2861
+ };
2862
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
2863
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
2864
+ * later. Uploading and deleting need the `write` scope. */
2865
+ images: {
2866
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
2867
+ /** Uploads an image's bytes; the same image again gives the same URL. */
2868
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
2869
+ contentType?: string;
2870
+ signal?: AbortSignal;
2871
+ }) => Promise<UploadedImage & {
2872
+ error?: string;
2873
+ }>;
2874
+ /** Uploads the image at an https address, which Findlane fetches. */
2875
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
2876
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
2877
+ delete: (key: string, signal?: AbortSignal) => Promise<{
2878
+ deleted: true;
2879
+ }>;
2880
+ };
2881
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
2882
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
2883
+ * needs the `search` or `write` scope; the rest, `write`. */
2884
+ feed: {
2885
+ /** The feed and its last run, or null. */
2886
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
2887
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
2888
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
2889
+ /** Disconnects it; the index keeps its records. */
2890
+ remove: (signal?: AbortSignal) => Promise<void>;
2891
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
2892
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
2893
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
2894
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
2895
+ /** The last 20 runs, newest first. */
2896
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
2897
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
2898
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
2899
+ };
2900
+ lists: {
2901
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
2902
+ get: <R = S_7>(id: string, options?: {
2903
+ limit?: number;
2904
+ signal?: AbortSignal;
2905
+ }) => Promise<List<R>>;
2906
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
2907
+ getMany: <R = S_7>(ids?: string[], options?: {
2908
+ limit?: number;
2909
+ signal?: AbortSignal;
2910
+ }) => Promise<List<R>[]>;
2911
+ /** How the index's lists are set up, and their last build. */
2912
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
2913
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
2914
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
2915
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
2916
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
2917
+ /** Puts the lists in this order: every list's ID once. */
2918
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
2919
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
2920
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
2921
+ /** What a list would hold, built without saving it. */
2922
+ preview: <R = S_7>(list: ListDefinition, signal?: AbortSignal) => Promise<{
2923
+ list: List<R>;
2924
+ tookMs: number;
2925
+ }>;
2926
+ };
2927
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
2928
+ * scope. Orders count once each, however often they're sent. */
2929
+ events: {
2930
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
2931
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
2932
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
2933
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
2934
+ * cursor), 200 a request. */
2935
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
2936
+ /** Takes a cancelled or refunded order out of Bought together. */
2937
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
2938
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
2939
+ * within a week. 1,000 IDs a request. */
2940
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
2941
+ forgotten: number;
2942
+ }>;
2943
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
2944
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
2945
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
2946
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
2947
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
2948
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
2949
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
2950
+ * and its visitor. Answers with the API's response. */
2951
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
2952
+ };
2953
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
2954
+ * requests from web pages are refused. */
2955
+ analytics: {
2956
+ /** Searches, searches without results and response times, beside the same span before, and a series per
2957
+ * hour (up to 7 days, by default) or day. */
2958
+ searches: (options?: AnalyticsOptions & {
2959
+ interval?: "hour" | "day";
2960
+ }) => Promise<SearchesReport>;
2961
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
2962
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
2963
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
2964
+ list?: L | undefined;
2965
+ limit?: number;
2966
+ }) => Promise<QueriesReport<L>>;
2967
+ /** The filter fields and sorts searches used. */
2968
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
2969
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
2970
+ events: (options?: AnalyticsOptions & {
2971
+ interval?: "hour" | "day";
2972
+ }) => Promise<EventsReport>;
2973
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
2974
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
2975
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
2976
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
2977
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
2978
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
2979
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
2980
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
2981
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
2982
+ product: (objectID: string, options?: AnalyticsOptions & {
2983
+ interval?: "hour" | "day";
2984
+ }) => Promise<ProductReport>;
2985
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
2986
+ * product (0.20.0). */
2987
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
2988
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
2989
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
2990
+ banners: (options?: {
2991
+ range?: "7d" | "30d" | "90d";
2992
+ signal?: AbortSignal;
2993
+ }) => Promise<BannerStatsReport>;
2994
+ };
2995
+ upsert: (records: S_7[], signal?: AbortSignal) => Promise<WriteReceipt>;
2996
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S_7>>;
2997
+ upsertOne: (record: S_7, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2998
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
2999
+ replaceAll: (records: S_7[], signal?: AbortSignal) => Promise<WriteReceipt>;
3000
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
3001
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S_7[], signal?: AbortSignal) => Promise<ReplacementStage>;
3002
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
3003
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
3004
+ replaceAllStaged: (records: Iterable<S_7> | AsyncIterable<S_7>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
3005
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
3006
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
3007
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
3008
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
3009
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
3010
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
3011
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
3012
+ * or normalization) throws a 409: save it first. Not counted as searches. */
3013
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
3014
+ /** The index's search tests. */
3015
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
3016
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
3017
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
3018
+ /** Waits until no settings rebuild is running; throws if it failed. */
3019
+ waitForSettings: (options?: {
3020
+ timeoutMs?: number;
3021
+ intervalMs?: number;
3022
+ signal?: AbortSignal;
3023
+ }) => Promise<CurrentSettings>;
3024
+ subindex: <S_8 extends {
3025
+ objectID: string;
3026
+ } = JsonRecord>(subindex: string) => {
3027
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
3028
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
3029
+ search: <R = S_8>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
3030
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
3031
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
3032
+ * a search. */
3033
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
3034
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
3035
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
3036
+ * 10, 5 by default. */
3037
+ querySuggestions: (query: {
3038
+ q: string;
3039
+ limit?: number;
3040
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
3041
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
3042
+ * empty until the index has enough searches. The list changes daily. */
3043
+ popularSearches: (options?: {
3044
+ limit?: number;
3045
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
3046
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
3047
+ * The API key needs the `search` scope; not counted as a search. */
3048
+ recommendations: <R = S_8>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
3049
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
3050
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
3051
+ * setting them up needs `write`. */
3052
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
3053
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
3054
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
3055
+ merchandising: {
3056
+ /** Every arranged category page and search. */
3057
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
3058
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
3059
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
3060
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
3061
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
3062
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
3063
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
3064
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
3065
+ };
3066
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
3067
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
3068
+ * later. Uploading and deleting need the `write` scope. */
3069
+ images: {
3070
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
3071
+ /** Uploads an image's bytes; the same image again gives the same URL. */
3072
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
3073
+ contentType?: string;
3074
+ signal?: AbortSignal;
3075
+ }) => Promise<UploadedImage & {
3076
+ error?: string;
3077
+ }>;
3078
+ /** Uploads the image at an https address, which Findlane fetches. */
3079
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
3080
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
3081
+ delete: (key: string, signal?: AbortSignal) => Promise<{
3082
+ deleted: true;
3083
+ }>;
3084
+ };
3085
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
3086
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
3087
+ * needs the `search` or `write` scope; the rest, `write`. */
3088
+ feed: {
3089
+ /** The feed and its last run, or null. */
3090
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
3091
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
3092
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
3093
+ /** Disconnects it; the index keeps its records. */
3094
+ remove: (signal?: AbortSignal) => Promise<void>;
3095
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
3096
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
3097
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
3098
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
3099
+ /** The last 20 runs, newest first. */
3100
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
3101
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
3102
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
3103
+ };
3104
+ lists: {
3105
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
3106
+ get: <R = S_8>(id: string, options?: {
3107
+ limit?: number;
3108
+ signal?: AbortSignal;
3109
+ }) => Promise<List<R>>;
3110
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
3111
+ getMany: <R = S_8>(ids?: string[], options?: {
3112
+ limit?: number;
3113
+ signal?: AbortSignal;
3114
+ }) => Promise<List<R>[]>;
3115
+ /** How the index's lists are set up, and their last build. */
3116
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
3117
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
3118
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
3119
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
3120
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
3121
+ /** Puts the lists in this order: every list's ID once. */
3122
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
3123
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
3124
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
3125
+ /** What a list would hold, built without saving it. */
3126
+ preview: <R = S_8>(list: ListDefinition, signal?: AbortSignal) => Promise<{
3127
+ list: List<R>;
3128
+ tookMs: number;
3129
+ }>;
3130
+ };
3131
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
3132
+ * scope. Orders count once each, however often they're sent. */
3133
+ events: {
3134
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
3135
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
3136
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
3137
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
3138
+ * cursor), 200 a request. */
3139
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
3140
+ /** Takes a cancelled or refunded order out of Bought together. */
3141
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
3142
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
3143
+ * within a week. 1,000 IDs a request. */
3144
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
3145
+ forgotten: number;
3146
+ }>;
3147
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
3148
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
3149
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
3150
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
3151
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
3152
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
3153
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
3154
+ * and its visitor. Answers with the API's response. */
3155
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
3156
+ };
3157
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
3158
+ * requests from web pages are refused. */
3159
+ analytics: {
3160
+ /** Searches, searches without results and response times, beside the same span before, and a series per
3161
+ * hour (up to 7 days, by default) or day. */
3162
+ searches: (options?: AnalyticsOptions & {
3163
+ interval?: "hour" | "day";
3164
+ }) => Promise<SearchesReport>;
3165
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
3166
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
3167
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
3168
+ list?: L | undefined;
3169
+ limit?: number;
3170
+ }) => Promise<QueriesReport<L>>;
3171
+ /** The filter fields and sorts searches used. */
3172
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
3173
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
3174
+ events: (options?: AnalyticsOptions & {
3175
+ interval?: "hour" | "day";
3176
+ }) => Promise<EventsReport>;
3177
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
3178
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
3179
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
3180
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
3181
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
3182
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
3183
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
3184
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
3185
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
3186
+ product: (objectID: string, options?: AnalyticsOptions & {
3187
+ interval?: "hour" | "day";
3188
+ }) => Promise<ProductReport>;
3189
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
3190
+ * product (0.20.0). */
3191
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
3192
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
3193
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
3194
+ banners: (options?: {
3195
+ range?: "7d" | "30d" | "90d";
3196
+ signal?: AbortSignal;
3197
+ }) => Promise<BannerStatsReport>;
3198
+ };
3199
+ upsert: (records: S_8[], signal?: AbortSignal) => Promise<WriteReceipt>;
3200
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S_8>>;
3201
+ upsertOne: (record: S_8, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
3202
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
3203
+ replaceAll: (records: S_8[], signal?: AbortSignal) => Promise<WriteReceipt>;
3204
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
3205
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S_8[], signal?: AbortSignal) => Promise<ReplacementStage>;
3206
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
3207
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
3208
+ replaceAllStaged: (records: Iterable<S_8> | AsyncIterable<S_8>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
3209
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
3210
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
3211
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
3212
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
3213
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
3214
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
3215
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
3216
+ * or normalization) throws a 409: save it first. Not counted as searches. */
3217
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
3218
+ /** The index's search tests. */
3219
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
3220
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
3221
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
3222
+ /** Waits until no settings rebuild is running; throws if it failed. */
3223
+ waitForSettings: (options?: {
3224
+ timeoutMs?: number;
3225
+ intervalMs?: number;
3226
+ signal?: AbortSignal;
3227
+ }) => Promise<CurrentSettings>;
3228
+ subindex: <S_9 extends {
3229
+ objectID: string;
3230
+ } = JsonRecord>(subindex: string) => {
3231
+ /** Searches the index; the API key needs the `search` scope. Pass `{ visitor }` when searching for
3232
+ * someone, such as a shopper loading a server-rendered page (see Visitor). */
3233
+ search: <R = S_9>(query?: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<SearchResponse<R>>;
3234
+ /** Did you mean: for a search that found nothing, a query that finds something, or null. Takes the
3235
+ * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
3236
+ * a search. */
3237
+ suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
3238
+ /** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
3239
+ * index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
3240
+ * 10, 5 by default. */
3241
+ querySuggestions: (query: {
3242
+ q: string;
3243
+ limit?: number;
3244
+ }, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
3245
+ /** The index's popular searches, for an empty search box, with the owner's pinned searches first;
3246
+ * empty until the index has enough searches. The list changes daily. */
3247
+ popularSearches: (options?: {
3248
+ limit?: number;
3249
+ }, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
3250
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
3251
+ * The API key needs the `search` scope; not counted as a search. */
3252
+ recommendations: <R = S_9>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
3253
+ /** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
3254
+ * and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
3255
+ * setting them up needs `write`. */
3256
+ /** Merchandising (0.23.0): category pages and searches arranged as the dashboard's Merchandising page does:
3257
+ * the products placed first, hidden products, a banner, and when. Saved as the page's rule within seconds,
3258
+ * keeping anything else the rule does. Reading needs the `search` or `write` scope; changing, `write`. */
3259
+ merchandising: {
3260
+ /** Every arranged category page and search. */
3261
+ list: (signal?: AbortSignal) => Promise<Arrangement[]>;
3262
+ /** One page's or search's arrangement; status "none" when nothing is arranged. */
3263
+ get: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
3264
+ /** Changes one: `order` up to 20 objectIDs placed first, `hidden` up to 100, `banner` ({ image, link?, title?,
3265
+ * position? } or null), `active` ({ from?, until? } or null), `enabled`. Fields left out stay as they are. */
3266
+ set: (target: MerchandisingTarget, change: ArrangementChange, signal?: AbortSignal) => Promise<Arrangement>;
3267
+ /** Stops arranging one: its order, hidden products and banner go; the rule's other actions stay. */
3268
+ remove: (target: MerchandisingTarget, signal?: AbortSignal) => Promise<Arrangement>;
3269
+ };
3270
+ /** Images for the index's banners, hosted by Findlane at https://images.findlane.dev (0.23.0): JPEG, PNG,
3271
+ * WebP or AVIF, up to 2 MB each, 50 images and 100 MB an index. An image no rule shows is deleted a week
3272
+ * later. Uploading and deleting need the `write` scope. */
3273
+ images: {
3274
+ list: (signal?: AbortSignal) => Promise<ImageLibrary>;
3275
+ /** Uploads an image's bytes; the same image again gives the same URL. */
3276
+ upload: (image: Blob | ArrayBuffer | Uint8Array, options?: {
3277
+ contentType?: string;
3278
+ signal?: AbortSignal;
3279
+ }) => Promise<UploadedImage & {
3280
+ error?: string;
3281
+ }>;
3282
+ /** Uploads the image at an https address, which Findlane fetches. */
3283
+ uploadFromUrl: (url: string, signal?: AbortSignal) => Promise<UploadedImage>;
3284
+ /** Deletes an image by its key; refused (409) while a rule's banner shows it. */
3285
+ delete: (key: string, signal?: AbortSignal) => Promise<{
3286
+ deleted: true;
3287
+ }>;
3288
+ };
3289
+ /** The index's product feed (0.24.0): a store's Google or Meta product feed, or a JSON file of records, read on
3290
+ * a schedule (hourly, every 6 hours, or daily), replacing the index's records with only what changed. Reading
3291
+ * needs the `search` or `write` scope; the rest, `write`. */
3292
+ feed: {
3293
+ /** The feed and its last run, or null. */
3294
+ get: (signal?: AbortSignal) => Promise<ProductFeed | null>;
3295
+ /** Connects a feed or changes its setup; a new address, sign-in or variants setting reads it now. */
3296
+ set: (setup: FeedSetup, signal?: AbortSignal) => Promise<ProductFeed>;
3297
+ /** Disconnects it; the index keeps its records. */
3298
+ remove: (signal?: AbortSignal) => Promise<void>;
3299
+ /** Reads a feed's first 2,000 items without saving anything; without a url, the connected feed's. */
3300
+ check: (setup?: Pick<FeedSetup, "url" | "username" | "password" | "variants">, signal?: AbortSignal) => Promise<FeedCheck>;
3301
+ /** Reads it now, in full, in the background; `runs` shows how it went. */
3302
+ run: (signal?: AbortSignal) => Promise<FeedRun>;
3303
+ /** The last 20 runs, newest first. */
3304
+ runs: (signal?: AbortSignal) => Promise<FeedRun[]>;
3305
+ /** Imports a held run's copy of the feed as it is, without the checks that held it. */
3306
+ importHeld: (runId: string, signal?: AbortSignal) => Promise<FeedRun>;
3307
+ };
3308
+ lists: {
3309
+ /** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
3310
+ get: <R = S_9>(id: string, options?: {
3311
+ limit?: number;
3312
+ signal?: AbortSignal;
3313
+ }) => Promise<List<R>>;
3314
+ /** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
3315
+ getMany: <R = S_9>(ids?: string[], options?: {
3316
+ limit?: number;
3317
+ signal?: AbortSignal;
3318
+ }) => Promise<List<R>[]>;
3319
+ /** How the index's lists are set up, and their last build. */
3320
+ settings: (signal?: AbortSignal) => Promise<ListSettings>;
3321
+ /** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
3322
+ * refused with why (400), and a new list past the plan's lists per index too (402). */
3323
+ save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
3324
+ delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
3325
+ /** Puts the lists in this order: every list's ID once. */
3326
+ order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
3327
+ /** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
3328
+ refresh: (signal?: AbortSignal) => Promise<ListSettings>;
3329
+ /** What a list would hold, built without saving it. */
3330
+ preview: <R = S_9>(list: ListDefinition, signal?: AbortSignal) => Promise<{
3331
+ list: List<R>;
3332
+ tookMs: number;
3333
+ }>;
3334
+ };
3335
+ /** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
3336
+ * scope. Orders count once each, however often they're sent. */
3337
+ events: {
3338
+ /** Sends events, 200 a request. One bad event doesn't refuse the rest: they're in `rejected`. Pass
3339
+ * `{ visitorId, visitId }` to link a shopper's events (see EventOptions). */
3340
+ send: (events: ServerEvent[], options?: AbortSignal | EventOptions) => Promise<EventsResult>;
3341
+ /** Sends past orders, up to 400 days old, from a list or an async iterable (such as a database
3342
+ * cursor), 200 a request. */
3343
+ importOrders: (orders: Iterable<Order> | AsyncIterable<Order>, signal?: AbortSignal) => Promise<EventsResult>;
3344
+ /** Takes a cancelled or refunded order out of Bought together. */
3345
+ cancelOrder: (orderID: string, signal?: AbortSignal) => Promise<EventsResult>;
3346
+ /** Deletes the events sent with these visitor or visit IDs, for shoppers who asked: Findlane does it
3347
+ * within a week. 1,000 IDs a request. */
3348
+ forget: (ids: string[], signal?: AbortSignal) => Promise<{
3349
+ forgotten: number;
3350
+ }>;
3351
+ /** A route on your own domain for the browser SDK's events (its `eventsUrl` option): ad blockers
3352
+ * block beacons to other sites, not to the page's own. Give it the browser's POST request (Next.js
3353
+ * route handlers, Workers, Hono, Bun, Deno); it finds the visitor's address in the headers
3354
+ * Cloudflare, Vercel and most proxies set. Where a visitor reaches your server directly, they can
3355
+ * set those too, so pass `{ ip }`. The API key needs the `events` or `write` scope. */
3356
+ proxy: (request: Request, visitor?: Partial<RelayedVisitor>) => Promise<Response>;
3357
+ /** `proxy` for servers without Fetch API requests, such as Express: the request's body as text,
3358
+ * and its visitor. Answers with the API's response. */
3359
+ relay: (body: string, visitor: RelayedVisitor, signal?: AbortSignal) => Promise<Response>;
3360
+ };
3361
+ /** The index's search analytics. The API key needs the `analytics` scope, and works only from a server:
3362
+ * requests from web pages are refused. */
3363
+ analytics: {
3364
+ /** Searches, searches without results and response times, beside the same span before, and a series per
3365
+ * hour (up to 7 days, by default) or day. */
3366
+ searches: (options?: AnalyticsOptions & {
3367
+ interval?: "hour" | "day";
3368
+ }) => Promise<SearchesReport>;
3369
+ /** Queries, the most searched first: `top` (by default) with their clicks, `noResults`, or `noClicks`, which
3370
+ * found something nobody clicked. `limit` is 1 to 1,000, 100 by default. */
3371
+ queries: <L extends keyof QueryLists = "top">(options?: AnalyticsOptions & {
3372
+ list?: L | undefined;
3373
+ limit?: number;
3374
+ }) => Promise<QueriesReport<L>>;
3375
+ /** The filter fields and sorts searches used. */
3376
+ filters: (options?: AnalyticsOptions) => Promise<FiltersReport>;
3377
+ /** Events of each type over time and by source, orders stored, and refused and bot events. */
3378
+ events: (options?: AnalyticsOptions & {
3379
+ interval?: "hour" | "day";
3380
+ }) => Promise<EventsReport>;
3381
+ /** The most viewed, most added to carts and most ordered products, with titles and images, and those
3382
+ * viewed but rarely bought. Exact, from every event kept (13 months, from 9 October 2026). */
3383
+ products: (options?: AnalyticsOptions) => Promise<ProductListsReport>;
3384
+ /** Every product with events or orders in the window, the most viewed first, up to 50,000: views,
3385
+ * viewers, clicks, add-to-carts and orders. Exact, like `products`. */
3386
+ productCounts: (options?: AnalyticsOptions) => Promise<ProductCountsReport>;
3387
+ /** One product's views, viewers, clicks, add-to-carts and orders per hour or day, the queries it was
3388
+ * clicked from, and its clicks in recommendations. Fast, for a product page in an admin: views and
3389
+ * clicks are estimates when `sampled`, and an add-to-cart of several products counts toward the first. */
3390
+ product: (objectID: string, options?: AnalyticsOptions & {
3391
+ interval?: "hour" | "day";
3392
+ }) => Promise<ProductReport>;
3393
+ /** Revenue from search: orders and add-to-carts of products found through search, per query and per
3394
+ * product (0.20.0). */
3395
+ revenue: (options?: Omit<AnalyticsOptions, "source">) => Promise<RevenueReport>;
3396
+ /** Each banner's shown (shoppers' searches that showed it, cache answers included) and clicks, by its rule,
3397
+ * over the last 7, 30 (by default) or 90 days (0.23.0). */
3398
+ banners: (options?: {
3399
+ range?: "7d" | "30d" | "90d";
3400
+ signal?: AbortSignal;
3401
+ }) => Promise<BannerStatsReport>;
3402
+ };
3403
+ upsert: (records: S_9[], signal?: AbortSignal) => Promise<WriteReceipt>;
3404
+ getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<S_9>>;
3405
+ upsertOne: (record: S_9, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
3406
+ deleteOne: (objectID: string, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
3407
+ replaceAll: (records: S_9[], signal?: AbortSignal) => Promise<WriteReceipt>;
3408
+ beginReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
3409
+ uploadReplacementBatch: (stageId: string, batchId: string, records: S_9[], signal?: AbortSignal) => Promise<ReplacementStage>;
3410
+ commitReplacement: (stageId: string, expectedBatches: number, expectedRecords: number, signal?: AbortSignal) => Promise<WriteReceipt>;
3411
+ abortReplacement: (stageId: string, signal?: AbortSignal) => Promise<ReplacementStage>;
3412
+ replaceAllStaged: (records: Iterable<S_9> | AsyncIterable<S_9>, options?: StagedReplaceOptions) => Promise<WriteReceipt>;
3413
+ delete: (objectID: string, signal?: AbortSignal) => Promise<WriteReceipt>;
3414
+ getSettings: (signal?: AbortSignal) => Promise<CurrentSettings>;
3415
+ replaceSettings: (settings: IndexSettings, signal?: AbortSignal) => Promise<SavedSettings>;
3416
+ /** Compares settings with the index's own without saving them: its search tests, `queries`, and its 100
3417
+ * most searched queries of the last 30 days, each searched with both, for up to 20 seconds. Takes the
3418
+ * settings `replaceSettings` does, or only those that change; `{}` runs the tests with the current
3419
+ * settings. A change to what the index stores (new searchable, facet, filter, sort or location fields,
3420
+ * or normalization) throws a 409: save it first. Not counted as searches. */
3421
+ compareSettings: (settings: Partial<IndexSettings>, options?: CompareOptions) => Promise<SettingsComparison>;
3422
+ /** The index's search tests. */
3423
+ getSearchTests: (signal?: AbortSignal) => Promise<SearchTest[]>;
3424
+ /** Replaces the index's search tests, up to 200; `[]` deletes them. */
3425
+ setSearchTests: (tests: SearchTest[], signal?: AbortSignal) => Promise<SearchTest[]>;
3426
+ /** Waits until no settings rebuild is running; throws if it failed. */
3427
+ waitForSettings: (options?: {
3428
+ timeoutMs?: number;
3429
+ intervalMs?: number;
3430
+ signal?: AbortSignal;
3431
+ }) => Promise<CurrentSettings>;
3432
+ subindex: <S_10 extends {
3433
+ objectID: string;
3434
+ } = JsonRecord>(subindex: string) => /*elided*/ any;
3435
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3436
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3437
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3438
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3439
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3440
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3441
+ waitUntilIndexed: (revision: number, options?: {
3442
+ timeoutMs?: number;
3443
+ intervalMs?: number;
3444
+ signal?: AbortSignal;
3445
+ }) => Promise<IndexingStatus>;
3446
+ };
3447
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3448
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3449
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3450
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3451
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3452
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3453
+ waitUntilIndexed: (revision: number, options?: {
3454
+ timeoutMs?: number;
3455
+ intervalMs?: number;
3456
+ signal?: AbortSignal;
3457
+ }) => Promise<IndexingStatus>;
3458
+ };
3459
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3460
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3461
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3462
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3463
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3464
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3465
+ waitUntilIndexed: (revision: number, options?: {
3466
+ timeoutMs?: number;
3467
+ intervalMs?: number;
3468
+ signal?: AbortSignal;
3469
+ }) => Promise<IndexingStatus>;
3470
+ };
3471
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3472
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3473
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3474
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3475
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3476
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3477
+ waitUntilIndexed: (revision: number, options?: {
3478
+ timeoutMs?: number;
3479
+ intervalMs?: number;
3480
+ signal?: AbortSignal;
3481
+ }) => Promise<IndexingStatus>;
3482
+ };
3483
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3484
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3485
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3486
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3487
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3488
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3489
+ waitUntilIndexed: (revision: number, options?: {
3490
+ timeoutMs?: number;
3491
+ intervalMs?: number;
3492
+ signal?: AbortSignal;
3493
+ }) => Promise<IndexingStatus>;
3494
+ };
3495
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3496
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3497
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3498
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3499
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3500
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3501
+ waitUntilIndexed: (revision: number, options?: {
3502
+ timeoutMs?: number;
3503
+ intervalMs?: number;
3504
+ signal?: AbortSignal;
3505
+ }) => Promise<IndexingStatus>;
3506
+ };
3507
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3508
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3509
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3510
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3511
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3512
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3513
+ waitUntilIndexed: (revision: number, options?: {
3514
+ timeoutMs?: number;
3515
+ intervalMs?: number;
3516
+ signal?: AbortSignal;
3517
+ }) => Promise<IndexingStatus>;
3518
+ };
3519
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3520
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3521
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3522
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3523
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3524
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3525
+ waitUntilIndexed: (revision: number, options?: {
3526
+ timeoutMs?: number;
3527
+ intervalMs?: number;
3528
+ signal?: AbortSignal;
3529
+ }) => Promise<IndexingStatus>;
3530
+ };
3531
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3532
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3533
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3534
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3535
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3536
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3537
+ waitUntilIndexed: (revision: number, options?: {
3538
+ timeoutMs?: number;
3539
+ intervalMs?: number;
3540
+ signal?: AbortSignal;
3541
+ }) => Promise<IndexingStatus>;
3542
+ };
3543
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3544
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
3545
+ getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3546
+ setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
3547
+ usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
3548
+ getIndexingStatus: (signal?: AbortSignal) => Promise<IndexingStatus>;
3549
+ waitUntilIndexed: (revision: number, options?: {
3550
+ timeoutMs?: number;
3551
+ intervalMs?: number;
3552
+ signal?: AbortSignal;
3553
+ }) => Promise<IndexingStatus>;
3554
+ };
3555
+ /** The index's subindexes, with their records (0.25.0). Owners make them in the dashboard. */
3556
+ listSubindexes: (signal?: AbortSignal) => Promise<Subindex[]>;
1369
3557
  getPublicSearchAccess: (signal?: AbortSignal) => Promise<PublicSearchAccess>;
1370
3558
  setPublicSearchPolicy: (policy: PublicSearchPolicy, signal?: AbortSignal) => Promise<PublicSearchAccess>;
1371
3559
  usePublicIndexDefaults: (signal?: AbortSignal) => Promise<PublicSearchAccess>;