@findlane/server 0.15.0 → 0.17.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/README.md +1 -0
- package/dist/index.cjs +21 -0
- package/dist/index.d.cts +147 -1
- package/dist/index.d.ts +147 -1
- package/dist/index.js +21 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@ const results = await products.search({ q }, { visitor: { ip: req.ip, userAgent:
|
|
|
48
48
|
| Method | What it does |
|
|
49
49
|
| --- | --- |
|
|
50
50
|
| `search(request, options?)` | Search with a query, filters, facets, a sort, paging, and highlighting. `explain: true` says why each hit matched and what ordered it, and `appliedRules` lists the index's query rules the query fired. `options` takes `signal` and `visitor` (below). |
|
|
51
|
+
| `querySuggestions({ q, limit? })`, `popularSearches({ limit? })` | Whole searches to offer as a visitor types, `[{ q, typed }]`, and the index's popular searches for an empty box. Not counted as searches. |
|
|
51
52
|
| `upsert(records)` | Add or update up to 1,000 records by `objectID`. |
|
|
52
53
|
| `replaceAll(records)` | Replace the whole catalog in one request (up to 100,000 records). |
|
|
53
54
|
| `replaceAllStaged(records, options)` | Replace a larger catalog in batches under one stage ID, switched in one commit. Retrying with the same `stageId` resumes. `abortReplacement(stageId)` drops a stage. |
|
package/dist/index.cjs
CHANGED
|
@@ -253,10 +253,31 @@ class SearchServerClient {
|
|
|
253
253
|
const { signal, visitor } = requestOptions(options);
|
|
254
254
|
return (await request("/suggest", "POST", query, signal, visitorHeaders(visitor))).didYouMean;
|
|
255
255
|
},
|
|
256
|
+
querySuggestions: async (query, options) => {
|
|
257
|
+
const { signal } = requestOptions(options);
|
|
258
|
+
return (await request("/query-suggestions", "POST", query, signal)).suggestions;
|
|
259
|
+
},
|
|
260
|
+
popularSearches: async (options = {}, requestOptionsOrSignal) => {
|
|
261
|
+
const { signal } = requestOptions(requestOptionsOrSignal);
|
|
262
|
+
return (await request("/query-suggestions", "POST", { q: "", limit: options.limit ?? 6 }, signal)).suggestions;
|
|
263
|
+
},
|
|
256
264
|
recommendations: (objectID, options = {}, requestOptionsOrSignal) => {
|
|
257
265
|
const { signal, visitor } = requestOptions(requestOptionsOrSignal);
|
|
258
266
|
return request("/recommendations", "POST", { objectID, ...options }, signal, visitorHeaders(visitor));
|
|
259
267
|
},
|
|
268
|
+
lists: {
|
|
269
|
+
get: (id, options = {}) => request(`/lists/${encodeURIComponent(id)}${options.limit ? `?limit=${options.limit}` : ""}`, "GET", undefined, options.signal),
|
|
270
|
+
getMany: async (ids, options = {}) => {
|
|
271
|
+
const query = new URLSearchParams({ ...ids ? { ids: ids.join(",") } : {}, ...options.limit ? { limit: String(options.limit) } : {} }).toString();
|
|
272
|
+
return (await request(`/lists${query ? `?${query}` : ""}`, "GET", undefined, options.signal)).lists;
|
|
273
|
+
},
|
|
274
|
+
settings: (signal) => request("/lists/settings", "GET", undefined, signal),
|
|
275
|
+
save: (id, list, signal) => request(`/lists/${encodeURIComponent(id)}`, "PUT", list, signal),
|
|
276
|
+
delete: (id, signal) => request(`/lists/${encodeURIComponent(id)}`, "DELETE", undefined, signal),
|
|
277
|
+
order: (ids, signal) => request("/lists/order", "PUT", { lists: ids }, signal),
|
|
278
|
+
refresh: (signal) => request("/lists/refresh", "POST", undefined, signal),
|
|
279
|
+
preview: (list, signal) => request("/lists/preview", "POST", list, signal)
|
|
280
|
+
},
|
|
260
281
|
events: {
|
|
261
282
|
send: (events, options) => sendEvents(events, options === undefined ? {} : ("aborted" in options) ? { signal: options } : options),
|
|
262
283
|
importOrders: (orders, signal) => sendEvents(async function* () {
|
package/dist/index.d.cts
CHANGED
|
@@ -63,6 +63,9 @@ export type IndexSettings = {
|
|
|
63
63
|
* Exact matches rank first. Searches use it once the index's word list is built, a minute or so after it's
|
|
64
64
|
* turned on. Off by default. */
|
|
65
65
|
compoundWords?: boolean;
|
|
66
|
+
/** Query suggestions: whole searches offered as shoppers type, and popular searches for an empty box,
|
|
67
|
+
* from what shoppers search and the catalog. On by default. Changes apply within a few minutes. */
|
|
68
|
+
querySuggestions?: QuerySuggestionSettings;
|
|
66
69
|
normalizationProfile?: "default-v2" | "default-v1" | "icelandic-ascii-v1";
|
|
67
70
|
/** An object field of lat and lng numbers, such as `_geoloc`, that `near` and `within` search; null for none. */
|
|
68
71
|
locationField?: string | null;
|
|
@@ -71,6 +74,33 @@ export type IndexSettings = {
|
|
|
71
74
|
fieldTypes?: Record<string, "text" | "number" | "boolean" | "text[]" | "number[]" | "boolean[]" | "location">;
|
|
72
75
|
[key: string]: unknown;
|
|
73
76
|
};
|
|
77
|
+
/** An index's query suggestion settings. When saving, give only those to change. */
|
|
78
|
+
export type QuerySuggestionSettings = {
|
|
79
|
+
/** false: no suggestions. */
|
|
80
|
+
enabled?: boolean;
|
|
81
|
+
/** false: no popular searches for an empty box; typed suggestions still use them. */
|
|
82
|
+
popularSearches?: boolean;
|
|
83
|
+
/** Popular searches an empty box shows, 1 to 10; 6 by default. */
|
|
84
|
+
popularCount?: number;
|
|
85
|
+
/** A shoppers' search becomes a suggestion once searched this many times (3 by default), on this many
|
|
86
|
+
* different days (2 by default). */
|
|
87
|
+
minSearches?: number;
|
|
88
|
+
minDays?: number;
|
|
89
|
+
/** The facet and searchable fields whose values are suggested, up to 20; null (the default) chooses
|
|
90
|
+
* readable ones from the records, leaving out slugs, sizes and long text. */
|
|
91
|
+
fields?: string[] | null;
|
|
92
|
+
/** Searches to suggest, up to 1,000; to show first, in the empty box too, up to 20; and never to show,
|
|
93
|
+
* nor anything with their words in a row, up to 1,000. One to six words each. */
|
|
94
|
+
added?: string[];
|
|
95
|
+
pinned?: string[];
|
|
96
|
+
hidden?: string[];
|
|
97
|
+
};
|
|
98
|
+
/** A whole search to offer as a shopper types, or in an empty box. `typed`: where what was typed is in
|
|
99
|
+
* `q`, as UTF-16 offsets (end exclusive); popular searches have none. */
|
|
100
|
+
export type QuerySuggestion = {
|
|
101
|
+
q: string;
|
|
102
|
+
typed?: [number, number];
|
|
103
|
+
};
|
|
74
104
|
/** When a query is, starts with, or contains one of a rule's phrases, the rule's `then` applies to that
|
|
75
105
|
* search. Phrases are compared as synonyms are: without case, accents, or the index languages' stop words,
|
|
76
106
|
* and with their plurals and other spellings, so a rule for "balloon" also fires for "balloons". */
|
|
@@ -391,6 +421,9 @@ export type SearchResponse<T> = {
|
|
|
391
421
|
totalRelation?: "eq" | "gte";
|
|
392
422
|
hasMore?: boolean;
|
|
393
423
|
facets: Record<string, Record<string, number>>;
|
|
424
|
+
/** True when the facet counts are estimates: on an index of 100,000 records or more, a search with more
|
|
425
|
+
* than 2,000 matches counts its facets on a sample of them, scaled to the total. */
|
|
426
|
+
facetsEstimated?: boolean;
|
|
394
427
|
/** With `facetStats`: each field's smallest and largest value across the matches, without the field's
|
|
395
428
|
* own filter. A field no match has is left out. */
|
|
396
429
|
facetStats?: Record<string, {
|
|
@@ -410,6 +443,75 @@ export type SearchResponse<T> = {
|
|
|
410
443
|
rewrittenQuery?: string;
|
|
411
444
|
};
|
|
412
445
|
/** How a product's recommendations are served (the recommendations add-on). */
|
|
446
|
+
/** One of an index's lists (docs: Lists): a row of products, such as Popular or New arrivals, from a filter and
|
|
447
|
+
* a sort, with products pinned first and others hidden; or a facet field's values with how many products each
|
|
448
|
+
* has, for a menu. `updatedAt` is when what the index's lists hold last changed. */
|
|
449
|
+
export type ProductList<T = JsonRecord> = {
|
|
450
|
+
id: string;
|
|
451
|
+
title: string;
|
|
452
|
+
kind: "products";
|
|
453
|
+
hits: {
|
|
454
|
+
id: string;
|
|
455
|
+
record: T;
|
|
456
|
+
}[];
|
|
457
|
+
/** The list's filters and sort, for a "See all" link to the same products as a search. */
|
|
458
|
+
browse: {
|
|
459
|
+
sort: string;
|
|
460
|
+
filters: Record<string, unknown>;
|
|
461
|
+
};
|
|
462
|
+
updatedAt: string;
|
|
463
|
+
};
|
|
464
|
+
export type ValueList = {
|
|
465
|
+
id: string;
|
|
466
|
+
title: string;
|
|
467
|
+
kind: "values";
|
|
468
|
+
field: string;
|
|
469
|
+
values: {
|
|
470
|
+
value: string;
|
|
471
|
+
count: number;
|
|
472
|
+
}[];
|
|
473
|
+
browse: {
|
|
474
|
+
filters: Record<string, unknown>;
|
|
475
|
+
};
|
|
476
|
+
updatedAt: string;
|
|
477
|
+
};
|
|
478
|
+
export type List<T = JsonRecord> = ProductList<T> | ValueList;
|
|
479
|
+
/** A list as it's set up. Products (`source` "sort", the default): `sort` is one of the index's sorts (its
|
|
480
|
+
* default without one), `limit` 1 to 50 (10 by default), `pinned` and `hidden` objectIDs, and
|
|
481
|
+
* `attributesToRetrieve` the fields each product has. Values (`source` "values"): `field` is a facet field,
|
|
482
|
+
* `sort` "count" (the default) or "name", `limit` 1 to 1,000, and `pinned` and `hidden` values. `filters` are
|
|
483
|
+
* a search's, for either. */
|
|
484
|
+
export type ListDefinition = {
|
|
485
|
+
title?: string;
|
|
486
|
+
source?: "sort" | "values";
|
|
487
|
+
sort?: string;
|
|
488
|
+
filters?: SearchRequest["filters"];
|
|
489
|
+
limit?: number;
|
|
490
|
+
pinned?: string[];
|
|
491
|
+
hidden?: string[];
|
|
492
|
+
attributesToRetrieve?: string[];
|
|
493
|
+
field?: string;
|
|
494
|
+
};
|
|
495
|
+
/** An index's lists as set up, each with how many products or values it held at its last build and why that
|
|
496
|
+
* failed, if it did; the last build; and how many lists the plan allows per index. */
|
|
497
|
+
export type ListSettings = {
|
|
498
|
+
lists: Array<ListDefinition & {
|
|
499
|
+
id: string;
|
|
500
|
+
title: string;
|
|
501
|
+
source: "sort" | "values";
|
|
502
|
+
updatedAt: string;
|
|
503
|
+
items: number | null;
|
|
504
|
+
error: string | null;
|
|
505
|
+
}>;
|
|
506
|
+
build: {
|
|
507
|
+
version: number;
|
|
508
|
+
builtAt: string | null;
|
|
509
|
+
buildMs: number | null;
|
|
510
|
+
updatedAt: string | null;
|
|
511
|
+
refreshAvailableAt: string | null;
|
|
512
|
+
} | null;
|
|
513
|
+
cap: number;
|
|
514
|
+
};
|
|
413
515
|
export type RecommendationsRequest = {
|
|
414
516
|
/** Up to this many products in each list, 1 to 20; 8 by default. */
|
|
415
517
|
limit?: number;
|
|
@@ -619,13 +721,15 @@ export type SearchesReport = AnalyticsReport & {
|
|
|
619
721
|
noResults: number;
|
|
620
722
|
}[];
|
|
621
723
|
};
|
|
622
|
-
/** A top query: its clicks on results
|
|
724
|
+
/** A top query: its clicks on results, or null when clicks aren't available; their average place, and the
|
|
725
|
+
* middle click's, which a few clicks far down don't pull up (from 1, or null without clicks that say their place). */
|
|
623
726
|
export type TopQuery = {
|
|
624
727
|
query: string;
|
|
625
728
|
searches: number;
|
|
626
729
|
noResults: number;
|
|
627
730
|
clicks: number | null;
|
|
628
731
|
averagePosition: number | null;
|
|
732
|
+
middlePosition: number | null;
|
|
629
733
|
};
|
|
630
734
|
export type QueryCount = {
|
|
631
735
|
query: string;
|
|
@@ -758,9 +862,51 @@ export declare class SearchServerClient {
|
|
|
758
862
|
* same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
|
|
759
863
|
* a search. */
|
|
760
864
|
suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
|
|
865
|
+
/** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
|
|
866
|
+
* index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
|
|
867
|
+
* 10, 5 by default. */
|
|
868
|
+
querySuggestions: (query: {
|
|
869
|
+
q: string;
|
|
870
|
+
limit?: number;
|
|
871
|
+
}, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
|
|
872
|
+
/** The index's popular searches, for an empty search box, with the owner's pinned searches first;
|
|
873
|
+
* empty until the index has enough searches. The list changes daily. */
|
|
874
|
+
popularSearches: (options?: {
|
|
875
|
+
limit?: number;
|
|
876
|
+
}, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
|
|
761
877
|
/** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
|
|
762
878
|
* The API key needs the `search` scope; not counted as a search. */
|
|
763
879
|
recommendations: <R = T>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
|
|
880
|
+
/** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
|
|
881
|
+
* and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
|
|
882
|
+
* setting them up needs `write`. */
|
|
883
|
+
lists: {
|
|
884
|
+
/** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
|
|
885
|
+
get: <R = T>(id: string, options?: {
|
|
886
|
+
limit?: number;
|
|
887
|
+
signal?: AbortSignal;
|
|
888
|
+
}) => Promise<List<R>>;
|
|
889
|
+
/** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
|
|
890
|
+
getMany: <R = T>(ids?: string[], options?: {
|
|
891
|
+
limit?: number;
|
|
892
|
+
signal?: AbortSignal;
|
|
893
|
+
}) => Promise<List<R>[]>;
|
|
894
|
+
/** How the index's lists are set up, and their last build. */
|
|
895
|
+
settings: (signal?: AbortSignal) => Promise<ListSettings>;
|
|
896
|
+
/** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
|
|
897
|
+
* refused with why (400), and a new list past the plan's lists per index too (402). */
|
|
898
|
+
save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
|
|
899
|
+
delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
|
|
900
|
+
/** Puts the lists in this order: every list's ID once. */
|
|
901
|
+
order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
|
|
902
|
+
/** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
|
|
903
|
+
refresh: (signal?: AbortSignal) => Promise<ListSettings>;
|
|
904
|
+
/** What a list would hold, built without saving it. */
|
|
905
|
+
preview: <R = T>(list: ListDefinition, signal?: AbortSignal) => Promise<{
|
|
906
|
+
list: List<R>;
|
|
907
|
+
tookMs: number;
|
|
908
|
+
}>;
|
|
909
|
+
};
|
|
764
910
|
/** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
|
|
765
911
|
* scope. Orders count once each, however often they're sent. */
|
|
766
912
|
events: {
|
package/dist/index.d.ts
CHANGED
|
@@ -63,6 +63,9 @@ export type IndexSettings = {
|
|
|
63
63
|
* Exact matches rank first. Searches use it once the index's word list is built, a minute or so after it's
|
|
64
64
|
* turned on. Off by default. */
|
|
65
65
|
compoundWords?: boolean;
|
|
66
|
+
/** Query suggestions: whole searches offered as shoppers type, and popular searches for an empty box,
|
|
67
|
+
* from what shoppers search and the catalog. On by default. Changes apply within a few minutes. */
|
|
68
|
+
querySuggestions?: QuerySuggestionSettings;
|
|
66
69
|
normalizationProfile?: "default-v2" | "default-v1" | "icelandic-ascii-v1";
|
|
67
70
|
/** An object field of lat and lng numbers, such as `_geoloc`, that `near` and `within` search; null for none. */
|
|
68
71
|
locationField?: string | null;
|
|
@@ -71,6 +74,33 @@ export type IndexSettings = {
|
|
|
71
74
|
fieldTypes?: Record<string, "text" | "number" | "boolean" | "text[]" | "number[]" | "boolean[]" | "location">;
|
|
72
75
|
[key: string]: unknown;
|
|
73
76
|
};
|
|
77
|
+
/** An index's query suggestion settings. When saving, give only those to change. */
|
|
78
|
+
export type QuerySuggestionSettings = {
|
|
79
|
+
/** false: no suggestions. */
|
|
80
|
+
enabled?: boolean;
|
|
81
|
+
/** false: no popular searches for an empty box; typed suggestions still use them. */
|
|
82
|
+
popularSearches?: boolean;
|
|
83
|
+
/** Popular searches an empty box shows, 1 to 10; 6 by default. */
|
|
84
|
+
popularCount?: number;
|
|
85
|
+
/** A shoppers' search becomes a suggestion once searched this many times (3 by default), on this many
|
|
86
|
+
* different days (2 by default). */
|
|
87
|
+
minSearches?: number;
|
|
88
|
+
minDays?: number;
|
|
89
|
+
/** The facet and searchable fields whose values are suggested, up to 20; null (the default) chooses
|
|
90
|
+
* readable ones from the records, leaving out slugs, sizes and long text. */
|
|
91
|
+
fields?: string[] | null;
|
|
92
|
+
/** Searches to suggest, up to 1,000; to show first, in the empty box too, up to 20; and never to show,
|
|
93
|
+
* nor anything with their words in a row, up to 1,000. One to six words each. */
|
|
94
|
+
added?: string[];
|
|
95
|
+
pinned?: string[];
|
|
96
|
+
hidden?: string[];
|
|
97
|
+
};
|
|
98
|
+
/** A whole search to offer as a shopper types, or in an empty box. `typed`: where what was typed is in
|
|
99
|
+
* `q`, as UTF-16 offsets (end exclusive); popular searches have none. */
|
|
100
|
+
export type QuerySuggestion = {
|
|
101
|
+
q: string;
|
|
102
|
+
typed?: [number, number];
|
|
103
|
+
};
|
|
74
104
|
/** When a query is, starts with, or contains one of a rule's phrases, the rule's `then` applies to that
|
|
75
105
|
* search. Phrases are compared as synonyms are: without case, accents, or the index languages' stop words,
|
|
76
106
|
* and with their plurals and other spellings, so a rule for "balloon" also fires for "balloons". */
|
|
@@ -391,6 +421,9 @@ export type SearchResponse<T> = {
|
|
|
391
421
|
totalRelation?: "eq" | "gte";
|
|
392
422
|
hasMore?: boolean;
|
|
393
423
|
facets: Record<string, Record<string, number>>;
|
|
424
|
+
/** True when the facet counts are estimates: on an index of 100,000 records or more, a search with more
|
|
425
|
+
* than 2,000 matches counts its facets on a sample of them, scaled to the total. */
|
|
426
|
+
facetsEstimated?: boolean;
|
|
394
427
|
/** With `facetStats`: each field's smallest and largest value across the matches, without the field's
|
|
395
428
|
* own filter. A field no match has is left out. */
|
|
396
429
|
facetStats?: Record<string, {
|
|
@@ -410,6 +443,75 @@ export type SearchResponse<T> = {
|
|
|
410
443
|
rewrittenQuery?: string;
|
|
411
444
|
};
|
|
412
445
|
/** How a product's recommendations are served (the recommendations add-on). */
|
|
446
|
+
/** One of an index's lists (docs: Lists): a row of products, such as Popular or New arrivals, from a filter and
|
|
447
|
+
* a sort, with products pinned first and others hidden; or a facet field's values with how many products each
|
|
448
|
+
* has, for a menu. `updatedAt` is when what the index's lists hold last changed. */
|
|
449
|
+
export type ProductList<T = JsonRecord> = {
|
|
450
|
+
id: string;
|
|
451
|
+
title: string;
|
|
452
|
+
kind: "products";
|
|
453
|
+
hits: {
|
|
454
|
+
id: string;
|
|
455
|
+
record: T;
|
|
456
|
+
}[];
|
|
457
|
+
/** The list's filters and sort, for a "See all" link to the same products as a search. */
|
|
458
|
+
browse: {
|
|
459
|
+
sort: string;
|
|
460
|
+
filters: Record<string, unknown>;
|
|
461
|
+
};
|
|
462
|
+
updatedAt: string;
|
|
463
|
+
};
|
|
464
|
+
export type ValueList = {
|
|
465
|
+
id: string;
|
|
466
|
+
title: string;
|
|
467
|
+
kind: "values";
|
|
468
|
+
field: string;
|
|
469
|
+
values: {
|
|
470
|
+
value: string;
|
|
471
|
+
count: number;
|
|
472
|
+
}[];
|
|
473
|
+
browse: {
|
|
474
|
+
filters: Record<string, unknown>;
|
|
475
|
+
};
|
|
476
|
+
updatedAt: string;
|
|
477
|
+
};
|
|
478
|
+
export type List<T = JsonRecord> = ProductList<T> | ValueList;
|
|
479
|
+
/** A list as it's set up. Products (`source` "sort", the default): `sort` is one of the index's sorts (its
|
|
480
|
+
* default without one), `limit` 1 to 50 (10 by default), `pinned` and `hidden` objectIDs, and
|
|
481
|
+
* `attributesToRetrieve` the fields each product has. Values (`source` "values"): `field` is a facet field,
|
|
482
|
+
* `sort` "count" (the default) or "name", `limit` 1 to 1,000, and `pinned` and `hidden` values. `filters` are
|
|
483
|
+
* a search's, for either. */
|
|
484
|
+
export type ListDefinition = {
|
|
485
|
+
title?: string;
|
|
486
|
+
source?: "sort" | "values";
|
|
487
|
+
sort?: string;
|
|
488
|
+
filters?: SearchRequest["filters"];
|
|
489
|
+
limit?: number;
|
|
490
|
+
pinned?: string[];
|
|
491
|
+
hidden?: string[];
|
|
492
|
+
attributesToRetrieve?: string[];
|
|
493
|
+
field?: string;
|
|
494
|
+
};
|
|
495
|
+
/** An index's lists as set up, each with how many products or values it held at its last build and why that
|
|
496
|
+
* failed, if it did; the last build; and how many lists the plan allows per index. */
|
|
497
|
+
export type ListSettings = {
|
|
498
|
+
lists: Array<ListDefinition & {
|
|
499
|
+
id: string;
|
|
500
|
+
title: string;
|
|
501
|
+
source: "sort" | "values";
|
|
502
|
+
updatedAt: string;
|
|
503
|
+
items: number | null;
|
|
504
|
+
error: string | null;
|
|
505
|
+
}>;
|
|
506
|
+
build: {
|
|
507
|
+
version: number;
|
|
508
|
+
builtAt: string | null;
|
|
509
|
+
buildMs: number | null;
|
|
510
|
+
updatedAt: string | null;
|
|
511
|
+
refreshAvailableAt: string | null;
|
|
512
|
+
} | null;
|
|
513
|
+
cap: number;
|
|
514
|
+
};
|
|
413
515
|
export type RecommendationsRequest = {
|
|
414
516
|
/** Up to this many products in each list, 1 to 20; 8 by default. */
|
|
415
517
|
limit?: number;
|
|
@@ -619,13 +721,15 @@ export type SearchesReport = AnalyticsReport & {
|
|
|
619
721
|
noResults: number;
|
|
620
722
|
}[];
|
|
621
723
|
};
|
|
622
|
-
/** A top query: its clicks on results
|
|
724
|
+
/** A top query: its clicks on results, or null when clicks aren't available; their average place, and the
|
|
725
|
+
* middle click's, which a few clicks far down don't pull up (from 1, or null without clicks that say their place). */
|
|
623
726
|
export type TopQuery = {
|
|
624
727
|
query: string;
|
|
625
728
|
searches: number;
|
|
626
729
|
noResults: number;
|
|
627
730
|
clicks: number | null;
|
|
628
731
|
averagePosition: number | null;
|
|
732
|
+
middlePosition: number | null;
|
|
629
733
|
};
|
|
630
734
|
export type QueryCount = {
|
|
631
735
|
query: string;
|
|
@@ -758,9 +862,51 @@ export declare class SearchServerClient {
|
|
|
758
862
|
* same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
|
|
759
863
|
* a search. */
|
|
760
864
|
suggest: (query: SearchRequest, options?: AbortSignal | RequestOptions) => Promise<DidYouMean | null>;
|
|
865
|
+
/** Query suggestions: whole searches to offer as a shopper types `q`, the best first; with `q` empty, the
|
|
866
|
+
* index's popular searches. The API key needs the `search` scope; not counted as searches. `limit`: 1 to
|
|
867
|
+
* 10, 5 by default. */
|
|
868
|
+
querySuggestions: (query: {
|
|
869
|
+
q: string;
|
|
870
|
+
limit?: number;
|
|
871
|
+
}, options?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
|
|
872
|
+
/** The index's popular searches, for an empty search box, with the owner's pinned searches first;
|
|
873
|
+
* empty until the index has enough searches. The list changes daily. */
|
|
874
|
+
popularSearches: (options?: {
|
|
875
|
+
limit?: number;
|
|
876
|
+
}, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<QuerySuggestion[]>;
|
|
761
877
|
/** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
|
|
762
878
|
* The API key needs the `search` scope; not counted as a search. */
|
|
763
879
|
recommendations: <R = T>(objectID: string, options?: RecommendationsRequest, requestOptionsOrSignal?: AbortSignal | RequestOptions) => Promise<RecommendationsResponse<R>>;
|
|
880
|
+
/** The index's lists: rows of products, such as Popular, and facet fields' values for menus, built ahead
|
|
881
|
+
* and served from a cache, so reading them isn't counted as searches. Reading needs the `search` scope;
|
|
882
|
+
* setting them up needs `write`. */
|
|
883
|
+
lists: {
|
|
884
|
+
/** One list by its ID; throws a FindlaneError with status 404 when there's none. `limit` shows fewer. */
|
|
885
|
+
get: <R = T>(id: string, options?: {
|
|
886
|
+
limit?: number;
|
|
887
|
+
signal?: AbortSignal;
|
|
888
|
+
}) => Promise<List<R>>;
|
|
889
|
+
/** Several lists (up to 10) in the order asked, leaving out IDs without a list; without IDs, all of them. */
|
|
890
|
+
getMany: <R = T>(ids?: string[], options?: {
|
|
891
|
+
limit?: number;
|
|
892
|
+
signal?: AbortSignal;
|
|
893
|
+
}) => Promise<List<R>[]>;
|
|
894
|
+
/** How the index's lists are set up, and their last build. */
|
|
895
|
+
settings: (signal?: AbortSignal) => Promise<ListSettings>;
|
|
896
|
+
/** Creates or changes a list and builds it; one whose filters, sort or field don't work on the index is
|
|
897
|
+
* refused with why (400), and a new list past the plan's lists per index too (402). */
|
|
898
|
+
save: (id: string, list: ListDefinition, signal?: AbortSignal) => Promise<ListSettings>;
|
|
899
|
+
delete: (id: string, signal?: AbortSignal) => Promise<ListSettings>;
|
|
900
|
+
/** Puts the lists in this order: every list's ID once. */
|
|
901
|
+
order: (ids: string[], signal?: AbortSignal) => Promise<ListSettings>;
|
|
902
|
+
/** Builds the lists again now, rather than within the minute they take to follow the index; once a minute. */
|
|
903
|
+
refresh: (signal?: AbortSignal) => Promise<ListSettings>;
|
|
904
|
+
/** What a list would hold, built without saving it. */
|
|
905
|
+
preview: <R = T>(list: ListDefinition, signal?: AbortSignal) => Promise<{
|
|
906
|
+
list: List<R>;
|
|
907
|
+
tookMs: number;
|
|
908
|
+
}>;
|
|
909
|
+
};
|
|
764
910
|
/** Events for the index's Events page and Bought together; the API key needs the `events` or `write`
|
|
765
911
|
* scope. Orders count once each, however often they're sent. */
|
|
766
912
|
events: {
|
package/dist/index.js
CHANGED
|
@@ -207,10 +207,31 @@ class SearchServerClient {
|
|
|
207
207
|
const { signal, visitor } = requestOptions(options);
|
|
208
208
|
return (await request("/suggest", "POST", query, signal, visitorHeaders(visitor))).didYouMean;
|
|
209
209
|
},
|
|
210
|
+
querySuggestions: async (query, options) => {
|
|
211
|
+
const { signal } = requestOptions(options);
|
|
212
|
+
return (await request("/query-suggestions", "POST", query, signal)).suggestions;
|
|
213
|
+
},
|
|
214
|
+
popularSearches: async (options = {}, requestOptionsOrSignal) => {
|
|
215
|
+
const { signal } = requestOptions(requestOptionsOrSignal);
|
|
216
|
+
return (await request("/query-suggestions", "POST", { q: "", limit: options.limit ?? 6 }, signal)).suggestions;
|
|
217
|
+
},
|
|
210
218
|
recommendations: (objectID, options = {}, requestOptionsOrSignal) => {
|
|
211
219
|
const { signal, visitor } = requestOptions(requestOptionsOrSignal);
|
|
212
220
|
return request("/recommendations", "POST", { objectID, ...options }, signal, visitorHeaders(visitor));
|
|
213
221
|
},
|
|
222
|
+
lists: {
|
|
223
|
+
get: (id, options = {}) => request(`/lists/${encodeURIComponent(id)}${options.limit ? `?limit=${options.limit}` : ""}`, "GET", undefined, options.signal),
|
|
224
|
+
getMany: async (ids, options = {}) => {
|
|
225
|
+
const query = new URLSearchParams({ ...ids ? { ids: ids.join(",") } : {}, ...options.limit ? { limit: String(options.limit) } : {} }).toString();
|
|
226
|
+
return (await request(`/lists${query ? `?${query}` : ""}`, "GET", undefined, options.signal)).lists;
|
|
227
|
+
},
|
|
228
|
+
settings: (signal) => request("/lists/settings", "GET", undefined, signal),
|
|
229
|
+
save: (id, list, signal) => request(`/lists/${encodeURIComponent(id)}`, "PUT", list, signal),
|
|
230
|
+
delete: (id, signal) => request(`/lists/${encodeURIComponent(id)}`, "DELETE", undefined, signal),
|
|
231
|
+
order: (ids, signal) => request("/lists/order", "PUT", { lists: ids }, signal),
|
|
232
|
+
refresh: (signal) => request("/lists/refresh", "POST", undefined, signal),
|
|
233
|
+
preview: (list, signal) => request("/lists/preview", "POST", list, signal)
|
|
234
|
+
},
|
|
214
235
|
events: {
|
|
215
236
|
send: (events, options) => sendEvents(events, options === undefined ? {} : ("aborted" in options) ? { signal: options } : options),
|
|
216
237
|
importOrders: (orders, signal) => sendEvents(async function* () {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@findlane/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Findlane server SDK: upload catalogs, manage index settings, and search with a secret workspace API key.",
|
|
5
5
|
"keywords": ["findlane", "search", "full-text search", "typo tolerance", "ecommerce search", "search api", "sdk"],
|
|
6
6
|
"license": "MIT",
|