@findlane/server 0.5.0 → 0.7.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.cjs CHANGED
@@ -144,6 +144,7 @@ class SearchServerClient {
144
144
  return {
145
145
  search: (query = {}, signal) => request("/search", "POST", query, signal),
146
146
  suggest: async (query, signal) => (await request("/suggest", "POST", query, signal)).didYouMean,
147
+ recommendations: (objectID, options = {}, signal) => request("/recommendations", "POST", { objectID, ...options }, signal),
147
148
  upsert: (records, signal) => request("/records", "POST", { records }, signal),
148
149
  getRecord: (objectID, signal) => request(`/records/${encodeURIComponent(objectID)}`, "GET", undefined, signal),
149
150
  upsertOne: (record, options) => writeOne("upsert", record.objectID, record, options),
package/dist/index.d.cts CHANGED
@@ -34,6 +34,9 @@ export type IndexSettings = {
34
34
  normalizationProfile?: "default-v2" | "default-v1" | "icelandic-ascii-v1";
35
35
  /** An object field of lat and lng numbers, such as `_geoloc`, that `near` and `within` search; null for none. */
36
36
  locationField?: string | null;
37
+ /** When saving: types for fields no record has yet, so settings can come before the records. Records'
38
+ * types win once they have the field. Not returned when reading settings. */
39
+ fieldTypes?: Record<string, "text" | "number" | "boolean" | "text[]" | "number[]" | "boolean[]" | "location">;
37
40
  [key: string]: unknown;
38
41
  };
39
42
  /** A settings change running in the background (with the settings it applies), or the last one's
@@ -170,6 +173,31 @@ export type SearchResponse<T> = {
170
173
  * results matched some words only by typo ("Showing results for …"), else null. */
171
174
  didYouMean?: DidYouMean | null;
172
175
  };
176
+ /** How a product's recommendations are served (the recommendations add-on). */
177
+ export type RecommendationsRequest = {
178
+ /** Up to this many products in each list, 1 to 20; 8 by default. */
179
+ limit?: number;
180
+ /** Only picks matching these filters, as in a search (fields configured as filters), such as `{ inStock: true }`. */
181
+ filters?: SearchRequest["filters"];
182
+ /** The record fields to return; all of them by default. */
183
+ attributesToRetrieve?: string[];
184
+ /** Goes with picks cost at most this many times the product; null for no limit. By default, the index's setting. */
185
+ maxPriceRatio?: number | null;
186
+ };
187
+ export type RecommendationHit<T> = {
188
+ id: string;
189
+ score: number | null;
190
+ record: T;
191
+ };
192
+ export type RecommendationsResponse<T> = {
193
+ objectID: string;
194
+ /** false until the product's lists are built; both lists are empty meanwhile. */
195
+ ready: boolean;
196
+ /** Products a shopper would want with this one, the likeliest first; `score` is the decision model's probability. */
197
+ goesWith: RecommendationHit<T>[];
198
+ /** Products a shopper could choose instead, then (unless the index hides them) variants of this one. */
199
+ similar: RecommendationHit<T>[];
200
+ };
173
201
  /** A corrected query. `applied`: the results are already for `q` (a search's "Showing results for");
174
202
  * otherwise searching `q` finds results that the query didn't ("Did you mean"). `changes` lists each
175
203
  * changed part of the query as typed. */
@@ -240,6 +268,9 @@ export declare class SearchServerClient {
240
268
  * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
241
269
  * a search. */
242
270
  suggest: (query: SearchRequest, signal?: AbortSignal) => Promise<DidYouMean | null>;
271
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
272
+ * The API key needs the `search` scope; not counted as a search. */
273
+ recommendations: <R = T>(objectID: string, options?: RecommendationsRequest, signal?: AbortSignal) => Promise<RecommendationsResponse<R>>;
243
274
  upsert: (records: T[], signal?: AbortSignal) => Promise<WriteReceipt>;
244
275
  getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<T>>;
245
276
  upsertOne: (record: T, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
package/dist/index.d.ts CHANGED
@@ -34,6 +34,9 @@ export type IndexSettings = {
34
34
  normalizationProfile?: "default-v2" | "default-v1" | "icelandic-ascii-v1";
35
35
  /** An object field of lat and lng numbers, such as `_geoloc`, that `near` and `within` search; null for none. */
36
36
  locationField?: string | null;
37
+ /** When saving: types for fields no record has yet, so settings can come before the records. Records'
38
+ * types win once they have the field. Not returned when reading settings. */
39
+ fieldTypes?: Record<string, "text" | "number" | "boolean" | "text[]" | "number[]" | "boolean[]" | "location">;
37
40
  [key: string]: unknown;
38
41
  };
39
42
  /** A settings change running in the background (with the settings it applies), or the last one's
@@ -170,6 +173,31 @@ export type SearchResponse<T> = {
170
173
  * results matched some words only by typo ("Showing results for …"), else null. */
171
174
  didYouMean?: DidYouMean | null;
172
175
  };
176
+ /** How a product's recommendations are served (the recommendations add-on). */
177
+ export type RecommendationsRequest = {
178
+ /** Up to this many products in each list, 1 to 20; 8 by default. */
179
+ limit?: number;
180
+ /** Only picks matching these filters, as in a search (fields configured as filters), such as `{ inStock: true }`. */
181
+ filters?: SearchRequest["filters"];
182
+ /** The record fields to return; all of them by default. */
183
+ attributesToRetrieve?: string[];
184
+ /** Goes with picks cost at most this many times the product; null for no limit. By default, the index's setting. */
185
+ maxPriceRatio?: number | null;
186
+ };
187
+ export type RecommendationHit<T> = {
188
+ id: string;
189
+ score: number | null;
190
+ record: T;
191
+ };
192
+ export type RecommendationsResponse<T> = {
193
+ objectID: string;
194
+ /** false until the product's lists are built; both lists are empty meanwhile. */
195
+ ready: boolean;
196
+ /** Products a shopper would want with this one, the likeliest first; `score` is the decision model's probability. */
197
+ goesWith: RecommendationHit<T>[];
198
+ /** Products a shopper could choose instead, then (unless the index hides them) variants of this one. */
199
+ similar: RecommendationHit<T>[];
200
+ };
173
201
  /** A corrected query. `applied`: the results are already for `q` (a search's "Showing results for");
174
202
  * otherwise searching `q` finds results that the query didn't ("Did you mean"). `changes` lists each
175
203
  * changed part of the query as typed. */
@@ -240,6 +268,9 @@ export declare class SearchServerClient {
240
268
  * same request (its q, filters, and fields matter). Needs the `didYouMean` setting; not counted as
241
269
  * a search. */
242
270
  suggest: (query: SearchRequest, signal?: AbortSignal) => Promise<DidYouMean | null>;
271
+ /** A product's recommendations (the recommendations add-on): products that go with it, and similar products.
272
+ * The API key needs the `search` scope; not counted as a search. */
273
+ recommendations: <R = T>(objectID: string, options?: RecommendationsRequest, signal?: AbortSignal) => Promise<RecommendationsResponse<R>>;
243
274
  upsert: (records: T[], signal?: AbortSignal) => Promise<WriteReceipt>;
244
275
  getRecord: (objectID: string, signal?: AbortSignal) => Promise<RecordSnapshot<T>>;
245
276
  upsertOne: (record: T, options: ConditionalWriteOptions) => Promise<WriteReceipt>;
package/dist/index.js CHANGED
@@ -98,6 +98,7 @@ class SearchServerClient {
98
98
  return {
99
99
  search: (query = {}, signal) => request("/search", "POST", query, signal),
100
100
  suggest: async (query, signal) => (await request("/suggest", "POST", query, signal)).didYouMean,
101
+ recommendations: (objectID, options = {}, signal) => request("/recommendations", "POST", { objectID, ...options }, signal),
101
102
  upsert: (records, signal) => request("/records", "POST", { records }, signal),
102
103
  getRecord: (objectID, signal) => request(`/records/${encodeURIComponent(objectID)}`, "GET", undefined, signal),
103
104
  upsertOne: (record, options) => writeOne("upsert", record.objectID, record, options),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@findlane/server",
3
- "version": "0.5.0",
3
+ "version": "0.7.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",