@trustrails/sdk 0.5.0 → 0.6.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 +57 -21
- package/dist/index.d.mts +63 -21
- package/dist/index.d.ts +63 -21
- package/dist/index.js +3 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,18 +58,18 @@ new TrustRails({ apiKey: string, baseUrl?: string })
|
|
|
58
58
|
|
|
59
59
|
##### `search(options?: SearchOptions): Promise<SearchResponse>`
|
|
60
60
|
|
|
61
|
-
Search for products.
|
|
61
|
+
Search for products. With `lite: true` the products are trimmed (`SearchResponse<LiteProduct>`). Search results leave out `specs` and `delivery_time`, but carry every known `attributes` spec, so compare specs from the search results (a missing name is unknown, `conflicting` means retailers disagree). Call `product(id)` only for your final 1-3 picks, and only for what search lacks: the retailer's description (processor, GPU, ports, weight, battery; if it doesn't give a detail, it isn't listed), or every retailer's offer and buy link (search carries only the best offer's `purchase_url`, cheapest in stock). State the value of a `confirmed` or `inferred` attribute; only `conflicting` or a missing name needs a caveat. See [What each call returns](#what-each-call-returns).
|
|
62
62
|
|
|
63
63
|
**Parameters:**
|
|
64
64
|
- `options.query?: string` - Refinement terms only — model lines, series, variants, model numbers (e.g. `'neo'`, `'ultra'`, `'oled'`, `'WH-1000XM5'`). Never put category names here (`query='tablet'` → use `category='Tablets'`). Never put brand names or prices — use filters. Omit entirely when browsing a category or brand. Query words must appear in the title, except words naming the category (`'router'` in Networking) and bare numbers or specs (`'4070'`, `'16GB'`), which are ignored when finding products and only rank them: put a model number with its prefix (`'RTX 4070'`, not `'4070'`) and check each title for it.
|
|
65
|
-
- `options.brand?: string` - Filter by brand name (e.g
|
|
66
|
-
- `options.category?: string` - Filter by product category
|
|
65
|
+
- `options.brand?: string` - Filter by brand name (exact match, case-insensitive), e.g. 'Sony', 'HP', 'Anker'
|
|
66
|
+
- `options.category?: string` - Filter by product category. Use only these exact values: Laptops, Desktops, Tablets, Phones, TVs, Monitors, Headphones, Speakers, Cameras, Keyboards, Mice, Printers, Networking, Storage, Gaming, Wearables, Drones, Audio, Cables & Chargers. 'Smartphones' is not valid (use 'Phones'), nor is 'Televisions' (use 'TVs')
|
|
67
67
|
- `options.minPrice?: number` - Minimum price filter in GBP
|
|
68
68
|
- `options.maxPrice?: number` - Maximum price filter in GBP
|
|
69
|
-
- `options.constraints?: Constraints` - Hard spec requirements, checked per product against its `attributes`, e.g. `{ memory_gb: { gte: 24 }, storage_gb: { gte: 1000 }, screen_in: { eq: 15 } }`. Operators: `eq`, `gte`, `lte`; a range is `{ gte, lte }`. Names and units: `memory_gb` (RAM, GB), `storage_gb` (GB, 1TB = 1000), `screen_in` (inches), `resolution_p` (pixels high: 4K = 2160, QHD = 1440, Full HD = 1080), `refresh_hz` (Hz), `power_w` (W), `wifi_gen` (Wi-Fi generation: 6, 6E = 6.5, 7). On every operator, storage matches within 3% (1TB = 1000–1024GB, so `gte 1024` accepts a 1TB drive but not 960GB) and screen size within 0.5 inch; a whole-number screen size N also covers up to N+1 on `eq` and `lte` (`eq 13` accepts 13.6", `lte 15` accepts 15.6"); other specs exactly. Each product then has `constraint_status` per name: `matched` (a retailer's title states a value that meets it), or `unverified` (not known to meet it, never treat as a match: in `attributes[name]`, `conflicting` means retailers disagree and missing means unknown). Products whose stated value fails are left out and counted in `excluded_by_constraints`. Wins over the same spec written in `query`, where only a spec that says what it is counts (`24GB RAM`, `1TB`; a bare `24GB` does not) and means at least, except screen size. Always pass a `category` or `brand` with constraints. A bad request (unknown name or operator, negative value, a combination nothing can meet such as `gte` above `lte`, or constraints with no category, brand or search words) throws a `TrustRailsError` with `statusCode` 400; the message naming the valid values is in `error.response`, the raw JSON body.
|
|
70
|
-
- `options.lite?: boolean` - Return trimmed product objects with only essential fields
|
|
69
|
+
- `options.constraints?: Constraints` - Hard spec requirements, checked per product against its `attributes`, e.g. `{ memory_gb: { gte: 24 }, storage_gb: { gte: 1000 }, screen_in: { eq: 15 } }`. Operators: `eq`, `gte`, `lte`; a range is `{ gte, lte }`. Names and units: `memory_gb` (RAM, GB), `storage_gb` (GB, 1TB = 1000), `screen_in` (inches), `resolution_p` (pixels high: 4K = 2160, QHD = 1440, Full HD = 1080), `refresh_hz` (Hz), `power_w` (W), `wifi_gen` (Wi-Fi generation: 6, 6E = 6.5, 7). On every operator, storage matches within 3% (1TB = 1000–1024GB, so `gte 1024` accepts a 1TB drive but not 960GB) and screen size within 0.5 inch; a whole-number screen size N also covers up to N+1 on `eq` and `lte` (`eq 13` accepts 13.6", `lte 15` accepts 15.6"); other specs exactly. Each product then has `constraint_status` per name: `matched` (a retailer's title states a value that meets it), or `unverified` (not known to meet it, never treat as a match: in `attributes[name]`, `conflicting` means retailers disagree and missing means unknown). Products whose stated value fails are left out and counted in `excluded_by_constraints`. Wins over the same spec written in `query`, where only a spec that says what it is counts (`24GB RAM`, `1TB`; a bare `24GB` does not) and means at least, except screen size. A plain `16GB` or `65W` is `gte`; use `eq` only when the user says exactly, and for screen size. Always pass a `category` or `brand` with constraints. A bad request (unknown name or operator, negative value, a combination nothing can meet such as `gte` above `lte`, or constraints with no category, brand or search words) throws a `TrustRailsError` with `statusCode` 400; the message naming the valid values is in `error.response`, the raw JSON body.
|
|
70
|
+
- `options.lite?: boolean` - Return trimmed product objects (`LiteProduct`) with only essential fields (id, title, brand, price, currency, availability, image_url, purchase_url, offer_count), every known `attributes` spec as `status` and value without `sources`, and, with constraints, `constraint_status`
|
|
71
71
|
- `options.limit?: number` - Maximum number of results (default: 50, max: 100)
|
|
72
|
-
- `options.sort?: string` - Sort order: `'relevance'` (default), `'price_asc'` (
|
|
72
|
+
- `options.sort?: string` - Sort order: `'relevance'` (default), `'price_asc'` (in stock first, then cheapest), `'price_desc'` (in stock first, then most expensive). With constraints, matched products still come first.
|
|
73
73
|
|
|
74
74
|
**Returns:** Promise resolving to `SearchResponse` containing `products` array and `total` count, plus `constraints` (as applied), `excluded_by_constraints` and `unverified_total` whenever constraints applied, whether passed or written in `query`. With constraints, `total` counts the products that match every constraint and `unverified_total` the unverified products that passed the other filters (only some may be in `products`). `candidates_truncated: true` means the first 2,000 candidates in the chosen sort order were checked and more exist: add a brand or category, or a narrower query, and search again. If the search was already narrowed, the results may be incomplete.
|
|
75
75
|
|
|
@@ -110,13 +110,13 @@ const results = await trustrails.search({
|
|
|
110
110
|
const results = await trustrails.search({
|
|
111
111
|
brand: 'Anker',
|
|
112
112
|
category: 'Cables & Chargers',
|
|
113
|
-
lite: true // Returns
|
|
113
|
+
lite: true // Returns id, title, brand, price, currency, availability, image_url, purchase_url, offer_count and attributes (without sources)
|
|
114
114
|
});
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
##### `product(id: string): Promise<Product>`
|
|
118
118
|
|
|
119
|
-
Get full details for a single product. Returns `attributes`: structured specs read from retailer titles, each with its `status` and the retailers behind it (see `Product`). Also returns `specs.description` (the retailer's own prose, for processor, ports, graphics and anything `attributes` does not cover),
|
|
119
|
+
Get full details for a single product. Returns `attributes`: structured specs read from retailer titles, each with its `status` and the retailers behind it (see `Product`). Also returns `specs.description` (the retailer's own prose, for processor, ports, graphics and anything `attributes` does not cover), availability, delivery time, and all retailer offers with per-retailer pricing. `specs.description` can describe another configuration or a maximum ("up to 32GB"), so it never overrides or fills in an attribute: a spec missing from `attributes` is unknown. Accepts canonical product IDs or original retailer offer IDs. Use this after `search()` only for your final 1-3 picks, and only for what search lacks (the description's details, or every offer and buy link): `search()` already carries `attributes` to compare specs.
|
|
120
120
|
|
|
121
121
|
**Parameters:**
|
|
122
122
|
- `id: string` - The product ID
|
|
@@ -128,6 +128,16 @@ Get full details for a single product. Returns `attributes`: structured specs re
|
|
|
128
128
|
const product = await trustrails.product('prod_123');
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
+
### What each call returns
|
|
132
|
+
|
|
133
|
+
| Call | Returns |
|
|
134
|
+
|------|---------|
|
|
135
|
+
| `search({ lite: true })` (what agents should always set) | id, title, brand, price, currency, availability, image_url, purchase_url, offer_count, every known `attributes` spec as `{ status, value }` (or `{ status, values }` when conflicting) without sources, and `constraint_status` when constrained |
|
|
136
|
+
| `search()` | the lite fields plus ean, category, product_type, provenance and every attribute with its `sources` |
|
|
137
|
+
| `product(id)` | everything: `specs.description` and dimensions, `offers`, `delivery_time` |
|
|
138
|
+
|
|
139
|
+
A product with no `attributes` key has none of these seven specs known.
|
|
140
|
+
|
|
131
141
|
## Types
|
|
132
142
|
|
|
133
143
|
### `Product`
|
|
@@ -140,13 +150,12 @@ interface Product {
|
|
|
140
150
|
brand?: string;
|
|
141
151
|
price: number;
|
|
142
152
|
currency: string;
|
|
143
|
-
availability: "in_stock" | "low_stock" | "out_of_stock";
|
|
144
|
-
|
|
145
|
-
delivery_time: string;
|
|
153
|
+
availability: "in_stock" | "low_stock" | "out_of_stock" | "unknown";
|
|
154
|
+
delivery_time?: string; // product() only
|
|
146
155
|
image_url?: string;
|
|
147
156
|
category: string;
|
|
148
157
|
product_type: "product" | "accessory";
|
|
149
|
-
specs
|
|
158
|
+
specs?: { // product() only
|
|
150
159
|
description?: string; // Retailer prose; never overrides attributes
|
|
151
160
|
model_number?: string;
|
|
152
161
|
dimensions?: string;
|
|
@@ -156,10 +165,10 @@ interface Product {
|
|
|
156
165
|
last_updated: string;
|
|
157
166
|
};
|
|
158
167
|
purchase_url: string;
|
|
159
|
-
attributes?: Attributes; // structured specs
|
|
168
|
+
attributes?: Attributes; // structured specs (search results and product()), absent when none is known, see below
|
|
160
169
|
constraint_status?: Partial<Record<ConstraintName, "matched" | "unverified">>; // only when constraints applied
|
|
161
|
-
offer_count?: number; // number of retailer offers (when >1, call product() to compare prices)
|
|
162
|
-
offers?: Offer[]; // per-retailer offers
|
|
170
|
+
offer_count?: number; // number of retailer offers (when >1, call product() for your final picks to compare prices)
|
|
171
|
+
offers?: Offer[]; // per-retailer offers, in stock first then cheapest (returned by product())
|
|
163
172
|
}
|
|
164
173
|
|
|
165
174
|
interface Offer {
|
|
@@ -168,8 +177,8 @@ interface Offer {
|
|
|
168
177
|
title: string;
|
|
169
178
|
price: number;
|
|
170
179
|
currency: string;
|
|
171
|
-
availability: "in_stock" | "low_stock" | "out_of_stock";
|
|
172
|
-
stock: number;
|
|
180
|
+
availability: "in_stock" | "low_stock" | "out_of_stock" | "unknown";
|
|
181
|
+
stock: number | null; // unit count only when the retailer supplies one
|
|
173
182
|
delivery_time: string;
|
|
174
183
|
purchase_url: string;
|
|
175
184
|
image_url?: string;
|
|
@@ -189,8 +198,15 @@ Structured specs read from retailer titles, by name. A name nobody states is abs
|
|
|
189
198
|
type Attributes = Partial<Record<ConstraintName, Attribute>>;
|
|
190
199
|
|
|
191
200
|
type Attribute =
|
|
192
|
-
| { status: "confirmed" | "inferred"; value: number; sources
|
|
193
|
-
| { status: "conflicting"; values: { value: number; sources
|
|
201
|
+
| { status: "confirmed" | "inferred"; value: number; sources: { retailer: string; field: "title" }[] }
|
|
202
|
+
| { status: "conflicting"; values: { value: number; sources: { retailer: string; field: "title" }[] }[] };
|
|
203
|
+
|
|
204
|
+
// An attribute without its sources, as a lite search result carries it
|
|
205
|
+
type LiteAttribute =
|
|
206
|
+
| { status: "confirmed" | "inferred"; value: number }
|
|
207
|
+
| { status: "conflicting"; values: { value: number }[] };
|
|
208
|
+
|
|
209
|
+
type LiteAttributes = Partial<Record<ConstraintName, LiteAttribute>>;
|
|
194
210
|
|
|
195
211
|
// memory_gb (RAM, GB), storage_gb (GB, 1TB = 1000), screen_in (inches), resolution_p (pixels high: 4K = 2160),
|
|
196
212
|
// refresh_hz (Hz), power_w (W), wifi_gen (Wi-Fi generation: 6, 6E = 6.5, 7)
|
|
@@ -199,6 +215,26 @@ type ConstraintName = "memory_gb" | "storage_gb" | "screen_in" | "resolution_p"
|
|
|
199
215
|
type Constraints = Partial<Record<ConstraintName, { eq?: number; gte?: number; lte?: number }>>;
|
|
200
216
|
```
|
|
201
217
|
|
|
218
|
+
### `LiteProduct`
|
|
219
|
+
|
|
220
|
+
A product in a `lite: true` search.
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
interface LiteProduct {
|
|
224
|
+
id: string;
|
|
225
|
+
title: string;
|
|
226
|
+
brand?: string;
|
|
227
|
+
price: number;
|
|
228
|
+
currency: string;
|
|
229
|
+
availability: "in_stock" | "low_stock" | "out_of_stock" | "unknown";
|
|
230
|
+
image_url?: string;
|
|
231
|
+
purchase_url: string;
|
|
232
|
+
offer_count: number;
|
|
233
|
+
constraint_status?: Partial<Record<ConstraintName, "matched" | "unverified">>; // only when constraints applied
|
|
234
|
+
attributes?: LiteAttributes; // every known spec, without sources; absent when none is known
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
202
238
|
### `SearchOptions`
|
|
203
239
|
|
|
204
240
|
```typescript
|
|
@@ -218,8 +254,8 @@ interface SearchOptions {
|
|
|
218
254
|
### `SearchResponse`
|
|
219
255
|
|
|
220
256
|
```typescript
|
|
221
|
-
interface SearchResponse {
|
|
222
|
-
products:
|
|
257
|
+
interface SearchResponse<P = Product> { // SearchResponse<LiteProduct> when lite: true
|
|
258
|
+
products: P[];
|
|
223
259
|
total: number; // with constraints: the products that match every constraint
|
|
224
260
|
constraints?: Constraints; // as applied, when constraints applied
|
|
225
261
|
excluded_by_constraints?: number; // products left out because their stated value fails a constraint
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Product availability status
|
|
3
3
|
*/
|
|
4
|
-
type Availability = "in_stock" | "low_stock" | "out_of_stock";
|
|
4
|
+
type Availability = "in_stock" | "low_stock" | "out_of_stock" | "unknown";
|
|
5
5
|
/**
|
|
6
6
|
* Per-retailer offer for a product
|
|
7
7
|
*/
|
|
@@ -12,7 +12,7 @@ interface Offer {
|
|
|
12
12
|
price: number;
|
|
13
13
|
currency: string;
|
|
14
14
|
availability: Availability;
|
|
15
|
-
stock: number;
|
|
15
|
+
stock: number | null;
|
|
16
16
|
delivery_time: string;
|
|
17
17
|
purchase_url: string;
|
|
18
18
|
image_url?: string;
|
|
@@ -26,23 +26,39 @@ interface AttributeSource {
|
|
|
26
26
|
field: 'title';
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* One structured spec of a product, with the evidence for it
|
|
29
|
+
* One structured spec of a product, with the evidence for it
|
|
30
30
|
*/
|
|
31
31
|
type Attribute = {
|
|
32
32
|
status: 'confirmed' | 'inferred';
|
|
33
33
|
value: number;
|
|
34
|
-
sources
|
|
34
|
+
sources: AttributeSource[];
|
|
35
|
+
} | {
|
|
36
|
+
status: 'conflicting';
|
|
37
|
+
values: Array<{
|
|
38
|
+
value: number;
|
|
39
|
+
sources: AttributeSource[];
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* An attribute without its sources, as a lite search result carries it
|
|
44
|
+
*/
|
|
45
|
+
type LiteAttribute = {
|
|
46
|
+
status: 'confirmed' | 'inferred';
|
|
47
|
+
value: number;
|
|
35
48
|
} | {
|
|
36
49
|
status: 'conflicting';
|
|
37
50
|
values: Array<{
|
|
38
51
|
value: number;
|
|
39
|
-
sources?: AttributeSource[];
|
|
40
52
|
}>;
|
|
41
53
|
};
|
|
42
54
|
/**
|
|
43
55
|
* Structured specs of a product, by name
|
|
44
56
|
*/
|
|
45
57
|
type Attributes = Partial<Record<ConstraintName, Attribute>>;
|
|
58
|
+
/**
|
|
59
|
+
* Structured specs of a lite search result, by name
|
|
60
|
+
*/
|
|
61
|
+
type LiteAttributes = Partial<Record<ConstraintName, LiteAttribute>>;
|
|
46
62
|
/**
|
|
47
63
|
* Spec names for `constraints` and `attributes`
|
|
48
64
|
*/
|
|
@@ -74,12 +90,13 @@ interface Product {
|
|
|
74
90
|
price: number;
|
|
75
91
|
currency: string;
|
|
76
92
|
availability: Availability;
|
|
77
|
-
|
|
78
|
-
delivery_time
|
|
93
|
+
/** Product detail only: absent from search results */
|
|
94
|
+
delivery_time?: string;
|
|
79
95
|
image_url?: string;
|
|
80
96
|
category: string;
|
|
81
97
|
product_type: 'product' | 'accessory';
|
|
82
|
-
|
|
98
|
+
/** Product detail only: absent from search results */
|
|
99
|
+
specs?: {
|
|
83
100
|
description?: string;
|
|
84
101
|
model_number?: string;
|
|
85
102
|
dimensions?: string;
|
|
@@ -89,15 +106,33 @@ interface Product {
|
|
|
89
106
|
last_updated: string;
|
|
90
107
|
};
|
|
91
108
|
purchase_url: string;
|
|
92
|
-
/** Structured specs read from retailer titles
|
|
109
|
+
/** Structured specs read from retailer titles: absent when none is known */
|
|
93
110
|
attributes?: Attributes;
|
|
94
111
|
/** Per-constraint result (only in searches with constraints) */
|
|
95
112
|
constraint_status?: Partial<Record<ConstraintName, ConstraintStatus>>;
|
|
96
113
|
/** Number of retailer offers available for this product */
|
|
97
114
|
offer_count?: number;
|
|
98
|
-
/** Per-retailer offers
|
|
115
|
+
/** Per-retailer offers, in stock first then cheapest (product() only) */
|
|
99
116
|
offers?: Offer[];
|
|
100
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Trimmed product returned by a lite search
|
|
120
|
+
*/
|
|
121
|
+
interface LiteProduct {
|
|
122
|
+
id: string;
|
|
123
|
+
title: string;
|
|
124
|
+
brand?: string;
|
|
125
|
+
price: number;
|
|
126
|
+
currency: string;
|
|
127
|
+
availability: Availability;
|
|
128
|
+
image_url?: string;
|
|
129
|
+
purchase_url: string;
|
|
130
|
+
offer_count: number;
|
|
131
|
+
/** Per-constraint result (only in searches with constraints) */
|
|
132
|
+
constraint_status?: Partial<Record<ConstraintName, ConstraintStatus>>;
|
|
133
|
+
/** Every known spec, without sources: absent when none is known */
|
|
134
|
+
attributes?: LiteAttributes;
|
|
135
|
+
}
|
|
101
136
|
/**
|
|
102
137
|
* Options for searching products
|
|
103
138
|
*/
|
|
@@ -109,18 +144,18 @@ interface SearchOptions {
|
|
|
109
144
|
category?: string;
|
|
110
145
|
/** Hard spec requirements checked against each product's attributes */
|
|
111
146
|
constraints?: Constraints;
|
|
112
|
-
/** Return trimmed product objects with only essential fields.
|
|
147
|
+
/** Return trimmed product objects (see LiteProduct) with only essential fields. */
|
|
113
148
|
lite?: boolean;
|
|
114
149
|
/** Maximum number of products to return (default 50, max 100) */
|
|
115
150
|
limit?: number;
|
|
116
|
-
/** Sort order: 'relevance' (default), 'price_asc' (
|
|
151
|
+
/** Sort order: 'relevance' (default), 'price_asc' (in stock first, then cheapest), 'price_desc' (in stock first, then most expensive) */
|
|
117
152
|
sort?: 'relevance' | 'price_asc' | 'price_desc';
|
|
118
153
|
}
|
|
119
154
|
/**
|
|
120
155
|
* Response from product search endpoint
|
|
121
156
|
*/
|
|
122
|
-
interface SearchResponse {
|
|
123
|
-
products:
|
|
157
|
+
interface SearchResponse<P = Product> {
|
|
158
|
+
products: P[];
|
|
124
159
|
/** With constraints: the products with every constraint matched */
|
|
125
160
|
total: number;
|
|
126
161
|
/** The constraints applied */
|
|
@@ -165,8 +200,8 @@ declare class TrustRails {
|
|
|
165
200
|
*/
|
|
166
201
|
constructor(config: TrustRailsConfig);
|
|
167
202
|
/**
|
|
168
|
-
* Search for products.
|
|
169
|
-
*
|
|
203
|
+
* Search for products. With `lite: true` the products are trimmed (see LiteProduct).
|
|
204
|
+
* Search results carry every known `attributes` spec (with sources unless lite); for the retailer's description and every offer, call product(id).
|
|
170
205
|
*
|
|
171
206
|
* @param options - Search parameters
|
|
172
207
|
* @returns Promise resolving to search results
|
|
@@ -178,15 +213,22 @@ declare class TrustRails {
|
|
|
178
213
|
* brand: 'Anker',
|
|
179
214
|
* minPrice: 20,
|
|
180
215
|
* maxPrice: 50,
|
|
181
|
-
* category: 'Chargers'
|
|
216
|
+
* category: 'Cables & Chargers'
|
|
182
217
|
* });
|
|
183
218
|
* ```
|
|
184
219
|
*/
|
|
185
|
-
search(options
|
|
220
|
+
search(options: SearchOptions & {
|
|
221
|
+
lite: true;
|
|
222
|
+
}): Promise<SearchResponse<LiteProduct>>;
|
|
223
|
+
search(options?: SearchOptions & {
|
|
224
|
+
lite?: false;
|
|
225
|
+
}): Promise<SearchResponse>;
|
|
226
|
+
search(options: SearchOptions): Promise<SearchResponse<Product | LiteProduct>>;
|
|
186
227
|
/**
|
|
187
228
|
* Get full details for a single product. Returns complete specs, description,
|
|
188
|
-
*
|
|
189
|
-
*
|
|
229
|
+
* availability, delivery time, and every retailer offer. Use after search() for
|
|
230
|
+
* your final 1-3 picks, and only for what search() lacks (the description's details, every offer):
|
|
231
|
+
* compare specs from the attributes search() already returns.
|
|
190
232
|
*
|
|
191
233
|
* @param id - The product ID
|
|
192
234
|
* @returns Promise resolving to complete product details
|
|
@@ -208,4 +250,4 @@ declare class TrustRailsError extends Error {
|
|
|
208
250
|
constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
|
|
209
251
|
}
|
|
210
252
|
|
|
211
|
-
export { type Attribute, type AttributeSource, type Attributes, type ConstraintName, type ConstraintOps, type ConstraintStatus, type Constraints, type Product, type SearchOptions, type SearchResponse, TrustRails, type TrustRailsConfig, TrustRailsError, TrustRails as default };
|
|
253
|
+
export { type Attribute, type AttributeSource, type Attributes, type ConstraintName, type ConstraintOps, type ConstraintStatus, type Constraints, type LiteAttribute, type LiteAttributes, type LiteProduct, type Product, type SearchOptions, type SearchResponse, TrustRails, type TrustRailsConfig, TrustRailsError, TrustRails as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Product availability status
|
|
3
3
|
*/
|
|
4
|
-
type Availability = "in_stock" | "low_stock" | "out_of_stock";
|
|
4
|
+
type Availability = "in_stock" | "low_stock" | "out_of_stock" | "unknown";
|
|
5
5
|
/**
|
|
6
6
|
* Per-retailer offer for a product
|
|
7
7
|
*/
|
|
@@ -12,7 +12,7 @@ interface Offer {
|
|
|
12
12
|
price: number;
|
|
13
13
|
currency: string;
|
|
14
14
|
availability: Availability;
|
|
15
|
-
stock: number;
|
|
15
|
+
stock: number | null;
|
|
16
16
|
delivery_time: string;
|
|
17
17
|
purchase_url: string;
|
|
18
18
|
image_url?: string;
|
|
@@ -26,23 +26,39 @@ interface AttributeSource {
|
|
|
26
26
|
field: 'title';
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* One structured spec of a product, with the evidence for it
|
|
29
|
+
* One structured spec of a product, with the evidence for it
|
|
30
30
|
*/
|
|
31
31
|
type Attribute = {
|
|
32
32
|
status: 'confirmed' | 'inferred';
|
|
33
33
|
value: number;
|
|
34
|
-
sources
|
|
34
|
+
sources: AttributeSource[];
|
|
35
|
+
} | {
|
|
36
|
+
status: 'conflicting';
|
|
37
|
+
values: Array<{
|
|
38
|
+
value: number;
|
|
39
|
+
sources: AttributeSource[];
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* An attribute without its sources, as a lite search result carries it
|
|
44
|
+
*/
|
|
45
|
+
type LiteAttribute = {
|
|
46
|
+
status: 'confirmed' | 'inferred';
|
|
47
|
+
value: number;
|
|
35
48
|
} | {
|
|
36
49
|
status: 'conflicting';
|
|
37
50
|
values: Array<{
|
|
38
51
|
value: number;
|
|
39
|
-
sources?: AttributeSource[];
|
|
40
52
|
}>;
|
|
41
53
|
};
|
|
42
54
|
/**
|
|
43
55
|
* Structured specs of a product, by name
|
|
44
56
|
*/
|
|
45
57
|
type Attributes = Partial<Record<ConstraintName, Attribute>>;
|
|
58
|
+
/**
|
|
59
|
+
* Structured specs of a lite search result, by name
|
|
60
|
+
*/
|
|
61
|
+
type LiteAttributes = Partial<Record<ConstraintName, LiteAttribute>>;
|
|
46
62
|
/**
|
|
47
63
|
* Spec names for `constraints` and `attributes`
|
|
48
64
|
*/
|
|
@@ -74,12 +90,13 @@ interface Product {
|
|
|
74
90
|
price: number;
|
|
75
91
|
currency: string;
|
|
76
92
|
availability: Availability;
|
|
77
|
-
|
|
78
|
-
delivery_time
|
|
93
|
+
/** Product detail only: absent from search results */
|
|
94
|
+
delivery_time?: string;
|
|
79
95
|
image_url?: string;
|
|
80
96
|
category: string;
|
|
81
97
|
product_type: 'product' | 'accessory';
|
|
82
|
-
|
|
98
|
+
/** Product detail only: absent from search results */
|
|
99
|
+
specs?: {
|
|
83
100
|
description?: string;
|
|
84
101
|
model_number?: string;
|
|
85
102
|
dimensions?: string;
|
|
@@ -89,15 +106,33 @@ interface Product {
|
|
|
89
106
|
last_updated: string;
|
|
90
107
|
};
|
|
91
108
|
purchase_url: string;
|
|
92
|
-
/** Structured specs read from retailer titles
|
|
109
|
+
/** Structured specs read from retailer titles: absent when none is known */
|
|
93
110
|
attributes?: Attributes;
|
|
94
111
|
/** Per-constraint result (only in searches with constraints) */
|
|
95
112
|
constraint_status?: Partial<Record<ConstraintName, ConstraintStatus>>;
|
|
96
113
|
/** Number of retailer offers available for this product */
|
|
97
114
|
offer_count?: number;
|
|
98
|
-
/** Per-retailer offers
|
|
115
|
+
/** Per-retailer offers, in stock first then cheapest (product() only) */
|
|
99
116
|
offers?: Offer[];
|
|
100
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Trimmed product returned by a lite search
|
|
120
|
+
*/
|
|
121
|
+
interface LiteProduct {
|
|
122
|
+
id: string;
|
|
123
|
+
title: string;
|
|
124
|
+
brand?: string;
|
|
125
|
+
price: number;
|
|
126
|
+
currency: string;
|
|
127
|
+
availability: Availability;
|
|
128
|
+
image_url?: string;
|
|
129
|
+
purchase_url: string;
|
|
130
|
+
offer_count: number;
|
|
131
|
+
/** Per-constraint result (only in searches with constraints) */
|
|
132
|
+
constraint_status?: Partial<Record<ConstraintName, ConstraintStatus>>;
|
|
133
|
+
/** Every known spec, without sources: absent when none is known */
|
|
134
|
+
attributes?: LiteAttributes;
|
|
135
|
+
}
|
|
101
136
|
/**
|
|
102
137
|
* Options for searching products
|
|
103
138
|
*/
|
|
@@ -109,18 +144,18 @@ interface SearchOptions {
|
|
|
109
144
|
category?: string;
|
|
110
145
|
/** Hard spec requirements checked against each product's attributes */
|
|
111
146
|
constraints?: Constraints;
|
|
112
|
-
/** Return trimmed product objects with only essential fields.
|
|
147
|
+
/** Return trimmed product objects (see LiteProduct) with only essential fields. */
|
|
113
148
|
lite?: boolean;
|
|
114
149
|
/** Maximum number of products to return (default 50, max 100) */
|
|
115
150
|
limit?: number;
|
|
116
|
-
/** Sort order: 'relevance' (default), 'price_asc' (
|
|
151
|
+
/** Sort order: 'relevance' (default), 'price_asc' (in stock first, then cheapest), 'price_desc' (in stock first, then most expensive) */
|
|
117
152
|
sort?: 'relevance' | 'price_asc' | 'price_desc';
|
|
118
153
|
}
|
|
119
154
|
/**
|
|
120
155
|
* Response from product search endpoint
|
|
121
156
|
*/
|
|
122
|
-
interface SearchResponse {
|
|
123
|
-
products:
|
|
157
|
+
interface SearchResponse<P = Product> {
|
|
158
|
+
products: P[];
|
|
124
159
|
/** With constraints: the products with every constraint matched */
|
|
125
160
|
total: number;
|
|
126
161
|
/** The constraints applied */
|
|
@@ -165,8 +200,8 @@ declare class TrustRails {
|
|
|
165
200
|
*/
|
|
166
201
|
constructor(config: TrustRailsConfig);
|
|
167
202
|
/**
|
|
168
|
-
* Search for products.
|
|
169
|
-
*
|
|
203
|
+
* Search for products. With `lite: true` the products are trimmed (see LiteProduct).
|
|
204
|
+
* Search results carry every known `attributes` spec (with sources unless lite); for the retailer's description and every offer, call product(id).
|
|
170
205
|
*
|
|
171
206
|
* @param options - Search parameters
|
|
172
207
|
* @returns Promise resolving to search results
|
|
@@ -178,15 +213,22 @@ declare class TrustRails {
|
|
|
178
213
|
* brand: 'Anker',
|
|
179
214
|
* minPrice: 20,
|
|
180
215
|
* maxPrice: 50,
|
|
181
|
-
* category: 'Chargers'
|
|
216
|
+
* category: 'Cables & Chargers'
|
|
182
217
|
* });
|
|
183
218
|
* ```
|
|
184
219
|
*/
|
|
185
|
-
search(options
|
|
220
|
+
search(options: SearchOptions & {
|
|
221
|
+
lite: true;
|
|
222
|
+
}): Promise<SearchResponse<LiteProduct>>;
|
|
223
|
+
search(options?: SearchOptions & {
|
|
224
|
+
lite?: false;
|
|
225
|
+
}): Promise<SearchResponse>;
|
|
226
|
+
search(options: SearchOptions): Promise<SearchResponse<Product | LiteProduct>>;
|
|
186
227
|
/**
|
|
187
228
|
* Get full details for a single product. Returns complete specs, description,
|
|
188
|
-
*
|
|
189
|
-
*
|
|
229
|
+
* availability, delivery time, and every retailer offer. Use after search() for
|
|
230
|
+
* your final 1-3 picks, and only for what search() lacks (the description's details, every offer):
|
|
231
|
+
* compare specs from the attributes search() already returns.
|
|
190
232
|
*
|
|
191
233
|
* @param id - The product ID
|
|
192
234
|
* @returns Promise resolving to complete product details
|
|
@@ -208,4 +250,4 @@ declare class TrustRailsError extends Error {
|
|
|
208
250
|
constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
|
|
209
251
|
}
|
|
210
252
|
|
|
211
|
-
export { type Attribute, type AttributeSource, type Attributes, type ConstraintName, type ConstraintOps, type ConstraintStatus, type Constraints, type Product, type SearchOptions, type SearchResponse, TrustRails, type TrustRailsConfig, TrustRailsError, TrustRails as default };
|
|
253
|
+
export { type Attribute, type AttributeSource, type Attributes, type ConstraintName, type ConstraintOps, type ConstraintStatus, type Constraints, type LiteAttribute, type LiteAttributes, type LiteProduct, type Product, type SearchOptions, type SearchResponse, TrustRails, type TrustRailsConfig, TrustRailsError, TrustRails as default };
|
package/dist/index.js
CHANGED
|
@@ -33,24 +33,6 @@ var TrustRails = class {
|
|
|
33
33
|
throw new Error("apiKey is required");
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Search for products. Returns summary data (title, price, availability, category).
|
|
38
|
-
* For structured attributes, the retailer's description and every offer, call product(id).
|
|
39
|
-
*
|
|
40
|
-
* @param options - Search parameters
|
|
41
|
-
* @returns Promise resolving to search results
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```typescript
|
|
45
|
-
* const results = await client.search({
|
|
46
|
-
* query: 'USB-C charger',
|
|
47
|
-
* brand: 'Anker',
|
|
48
|
-
* minPrice: 20,
|
|
49
|
-
* maxPrice: 50,
|
|
50
|
-
* category: 'Chargers'
|
|
51
|
-
* });
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
36
|
async search(options = {}) {
|
|
55
37
|
const url = new URL(`${this.baseUrl}/api/search`);
|
|
56
38
|
if (options.query) {
|
|
@@ -108,8 +90,9 @@ var TrustRails = class {
|
|
|
108
90
|
}
|
|
109
91
|
/**
|
|
110
92
|
* Get full details for a single product. Returns complete specs, description,
|
|
111
|
-
*
|
|
112
|
-
*
|
|
93
|
+
* availability, delivery time, and every retailer offer. Use after search() for
|
|
94
|
+
* your final 1-3 picks, and only for what search() lacks (the description's details, every offer):
|
|
95
|
+
* compare specs from the attributes search() already returns.
|
|
113
96
|
*
|
|
114
97
|
* @param id - The product ID
|
|
115
98
|
* @returns Promise resolving to complete product details
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Custom error class for TrustRails API errors\n */\nexport class TrustRailsError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public response?: any\n ) {\n super(message);\n this.name = 'TrustRailsError';\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TrustRailsError);\n }\n }\n}\n","import { TrustRailsError } from './errors';\nimport type { Product, SearchOptions, SearchResponse, TrustRailsConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://trustrails.app';\n\n/**\n * TrustRails SDK\n *\n * @example\n * ```typescript\n * const trustrails = new TrustRails('your-api-key');\n * const results = await trustrails.search({ query: 'laptop', maxPrice: 1000 });\n * ```\n */\nexport class TrustRails {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n /**\n * Creates a new TrustRails SDK instance\n *\n * @param apiKey - Your TrustRails API key\n */\n constructor(apiKey: string);\n /**\n * Creates a new TrustRails SDK instance with custom configuration\n *\n * @param config - Configuration object with apiKey and optional baseUrl\n */\n constructor(config: TrustRailsConfig);\n constructor(apiKeyOrConfig: string | TrustRailsConfig) {\n if (typeof apiKeyOrConfig === 'string') {\n this.apiKey = apiKeyOrConfig;\n this.baseUrl = DEFAULT_BASE_URL;\n } else {\n this.apiKey = apiKeyOrConfig.apiKey;\n this.baseUrl = apiKeyOrConfig.baseUrl || DEFAULT_BASE_URL;\n }\n\n // Clean up baseUrl - remove trailing slash\n if (this.baseUrl.endsWith('/')) {\n this.baseUrl = this.baseUrl.slice(0, -1);\n }\n\n if (!this.apiKey) {\n throw new Error('apiKey is required');\n }\n }\n\n /**\n * Search for products. Returns summary data (title, price, availability, category).\n * For structured attributes, the retailer's description and every offer, call product(id).\n *\n * @param options - Search parameters\n * @returns Promise resolving to search results\n *\n * @example\n * ```typescript\n * const results = await client.search({\n * query: 'USB-C charger',\n * brand: 'Anker',\n * minPrice: 20,\n * maxPrice: 50,\n * category: 'Chargers'\n * });\n * ```\n */\n async search(options: SearchOptions = {}): Promise<SearchResponse> {\n const url = new URL(`${this.baseUrl}/api/search`);\n\n if (options.query) {\n url.searchParams.set('query', options.query);\n }\n\n if (options.minPrice) {\n url.searchParams.set('min_price', options.minPrice.toString());\n }\n\n if (options.maxPrice) {\n url.searchParams.set('max_price', options.maxPrice.toString());\n }\n\n if (options.brand) {\n url.searchParams.set('brand', options.brand);\n }\n\n if (options.category) {\n url.searchParams.set('category', options.category);\n }\n\n if (options.constraints) {\n url.searchParams.set('constraints', JSON.stringify(options.constraints));\n }\n\n if (options.lite) {\n url.searchParams.set('lite', 'true');\n }\n\n if (options.limit) {\n url.searchParams.set('limit', options.limit.toString());\n }\n\n if (options.sort) {\n url.searchParams.set('sort', options.sort);\n }\n\n try {\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new TrustRailsError(\n `Search failed: ${response.statusText}`,\n response.status,\n await response.text()\n );\n }\n\n const data = await response.json() as SearchResponse;\n return data;\n } catch (error) {\n if (error instanceof TrustRailsError) {\n throw error;\n }\n throw new TrustRailsError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n /**\n * Get full details for a single product. Returns complete specs, description,\n * stock level, delivery time, and retailer source. Use after search() for\n * detailed comparison or recommendations.\n *\n * @param id - The product ID\n * @returns Promise resolving to complete product details\n *\n * @example\n * ```typescript\n * const product = await client.product('prod_123');\n * ```\n */\n async product(id: string): Promise<Product> {\n if (!id) {\n throw new Error('Product ID is required');\n }\n\n const url = `${this.baseUrl}/api/product/${encodeURIComponent(id)}`;\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new TrustRailsError(`Product not found: ${id}`, 404);\n }\n throw new TrustRailsError(\n `Get product failed: ${response.statusText}`,\n response.status,\n await response.text()\n );\n }\n\n const product = await response.json() as Product;\n return product;\n } catch (error) {\n if (error instanceof TrustRailsError) {\n throw error;\n }\n throw new TrustRailsError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Custom error class for TrustRails API errors\n */\nexport class TrustRailsError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public response?: any\n ) {\n super(message);\n this.name = 'TrustRailsError';\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TrustRailsError);\n }\n }\n}\n","import { TrustRailsError } from './errors';\nimport type { LiteProduct, Product, SearchOptions, SearchResponse, TrustRailsConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://trustrails.app';\n\n/**\n * TrustRails SDK\n *\n * @example\n * ```typescript\n * const trustrails = new TrustRails('your-api-key');\n * const results = await trustrails.search({ query: 'laptop', maxPrice: 1000 });\n * ```\n */\nexport class TrustRails {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n /**\n * Creates a new TrustRails SDK instance\n *\n * @param apiKey - Your TrustRails API key\n */\n constructor(apiKey: string);\n /**\n * Creates a new TrustRails SDK instance with custom configuration\n *\n * @param config - Configuration object with apiKey and optional baseUrl\n */\n constructor(config: TrustRailsConfig);\n constructor(apiKeyOrConfig: string | TrustRailsConfig) {\n if (typeof apiKeyOrConfig === 'string') {\n this.apiKey = apiKeyOrConfig;\n this.baseUrl = DEFAULT_BASE_URL;\n } else {\n this.apiKey = apiKeyOrConfig.apiKey;\n this.baseUrl = apiKeyOrConfig.baseUrl || DEFAULT_BASE_URL;\n }\n\n // Clean up baseUrl - remove trailing slash\n if (this.baseUrl.endsWith('/')) {\n this.baseUrl = this.baseUrl.slice(0, -1);\n }\n\n if (!this.apiKey) {\n throw new Error('apiKey is required');\n }\n }\n\n /**\n * Search for products. With `lite: true` the products are trimmed (see LiteProduct).\n * Search results carry every known `attributes` spec (with sources unless lite); for the retailer's description and every offer, call product(id).\n *\n * @param options - Search parameters\n * @returns Promise resolving to search results\n *\n * @example\n * ```typescript\n * const results = await client.search({\n * query: 'USB-C charger',\n * brand: 'Anker',\n * minPrice: 20,\n * maxPrice: 50,\n * category: 'Cables & Chargers'\n * });\n * ```\n */\n search(options: SearchOptions & { lite: true }): Promise<SearchResponse<LiteProduct>>;\n search(options?: SearchOptions & { lite?: false }): Promise<SearchResponse>;\n search(options: SearchOptions): Promise<SearchResponse<Product | LiteProduct>>;\n async search(options: SearchOptions = {}): Promise<SearchResponse<Product | LiteProduct>> {\n const url = new URL(`${this.baseUrl}/api/search`);\n\n if (options.query) {\n url.searchParams.set('query', options.query);\n }\n\n if (options.minPrice) {\n url.searchParams.set('min_price', options.minPrice.toString());\n }\n\n if (options.maxPrice) {\n url.searchParams.set('max_price', options.maxPrice.toString());\n }\n\n if (options.brand) {\n url.searchParams.set('brand', options.brand);\n }\n\n if (options.category) {\n url.searchParams.set('category', options.category);\n }\n\n if (options.constraints) {\n url.searchParams.set('constraints', JSON.stringify(options.constraints));\n }\n\n if (options.lite) {\n url.searchParams.set('lite', 'true');\n }\n\n if (options.limit) {\n url.searchParams.set('limit', options.limit.toString());\n }\n\n if (options.sort) {\n url.searchParams.set('sort', options.sort);\n }\n\n try {\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new TrustRailsError(\n `Search failed: ${response.statusText}`,\n response.status,\n await response.text()\n );\n }\n\n const data = await response.json() as SearchResponse<Product | LiteProduct>;\n return data;\n } catch (error) {\n if (error instanceof TrustRailsError) {\n throw error;\n }\n throw new TrustRailsError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n /**\n * Get full details for a single product. Returns complete specs, description,\n * availability, delivery time, and every retailer offer. Use after search() for\n * your final 1-3 picks, and only for what search() lacks (the description's details, every offer):\n * compare specs from the attributes search() already returns.\n *\n * @param id - The product ID\n * @returns Promise resolving to complete product details\n *\n * @example\n * ```typescript\n * const product = await client.product('prod_123');\n * ```\n */\n async product(id: string): Promise<Product> {\n if (!id) {\n throw new Error('Product ID is required');\n }\n\n const url = `${this.baseUrl}/api/product/${encodeURIComponent(id)}`;\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new TrustRailsError(`Product not found: ${id}`, 404);\n }\n throw new TrustRailsError(\n `Get product failed: ${response.statusText}`,\n response.status,\n await response.text()\n );\n }\n\n const product = await response.json() as Product;\n return product;\n } catch (error) {\n if (error instanceof TrustRailsError) {\n throw error;\n }\n throw new TrustRailsError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -29,24 +29,6 @@ var TrustRails = class {
|
|
|
29
29
|
throw new Error("apiKey is required");
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Search for products. Returns summary data (title, price, availability, category).
|
|
34
|
-
* For structured attributes, the retailer's description and every offer, call product(id).
|
|
35
|
-
*
|
|
36
|
-
* @param options - Search parameters
|
|
37
|
-
* @returns Promise resolving to search results
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```typescript
|
|
41
|
-
* const results = await client.search({
|
|
42
|
-
* query: 'USB-C charger',
|
|
43
|
-
* brand: 'Anker',
|
|
44
|
-
* minPrice: 20,
|
|
45
|
-
* maxPrice: 50,
|
|
46
|
-
* category: 'Chargers'
|
|
47
|
-
* });
|
|
48
|
-
* ```
|
|
49
|
-
*/
|
|
50
32
|
async search(options = {}) {
|
|
51
33
|
const url = new URL(`${this.baseUrl}/api/search`);
|
|
52
34
|
if (options.query) {
|
|
@@ -104,8 +86,9 @@ var TrustRails = class {
|
|
|
104
86
|
}
|
|
105
87
|
/**
|
|
106
88
|
* Get full details for a single product. Returns complete specs, description,
|
|
107
|
-
*
|
|
108
|
-
*
|
|
89
|
+
* availability, delivery time, and every retailer offer. Use after search() for
|
|
90
|
+
* your final 1-3 picks, and only for what search() lacks (the description's details, every offer):
|
|
91
|
+
* compare specs from the attributes search() already returns.
|
|
109
92
|
*
|
|
110
93
|
* @param id - The product ID
|
|
111
94
|
* @returns Promise resolving to complete product details
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Custom error class for TrustRails API errors\n */\nexport class TrustRailsError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public response?: any\n ) {\n super(message);\n this.name = 'TrustRailsError';\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TrustRailsError);\n }\n }\n}\n","import { TrustRailsError } from './errors';\nimport type { Product, SearchOptions, SearchResponse, TrustRailsConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://trustrails.app';\n\n/**\n * TrustRails SDK\n *\n * @example\n * ```typescript\n * const trustrails = new TrustRails('your-api-key');\n * const results = await trustrails.search({ query: 'laptop', maxPrice: 1000 });\n * ```\n */\nexport class TrustRails {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n /**\n * Creates a new TrustRails SDK instance\n *\n * @param apiKey - Your TrustRails API key\n */\n constructor(apiKey: string);\n /**\n * Creates a new TrustRails SDK instance with custom configuration\n *\n * @param config - Configuration object with apiKey and optional baseUrl\n */\n constructor(config: TrustRailsConfig);\n constructor(apiKeyOrConfig: string | TrustRailsConfig) {\n if (typeof apiKeyOrConfig === 'string') {\n this.apiKey = apiKeyOrConfig;\n this.baseUrl = DEFAULT_BASE_URL;\n } else {\n this.apiKey = apiKeyOrConfig.apiKey;\n this.baseUrl = apiKeyOrConfig.baseUrl || DEFAULT_BASE_URL;\n }\n\n // Clean up baseUrl - remove trailing slash\n if (this.baseUrl.endsWith('/')) {\n this.baseUrl = this.baseUrl.slice(0, -1);\n }\n\n if (!this.apiKey) {\n throw new Error('apiKey is required');\n }\n }\n\n /**\n * Search for products. Returns summary data (title, price, availability, category).\n * For structured attributes, the retailer's description and every offer, call product(id).\n *\n * @param options - Search parameters\n * @returns Promise resolving to search results\n *\n * @example\n * ```typescript\n * const results = await client.search({\n * query: 'USB-C charger',\n * brand: 'Anker',\n * minPrice: 20,\n * maxPrice: 50,\n * category: 'Chargers'\n * });\n * ```\n */\n async search(options: SearchOptions = {}): Promise<SearchResponse> {\n const url = new URL(`${this.baseUrl}/api/search`);\n\n if (options.query) {\n url.searchParams.set('query', options.query);\n }\n\n if (options.minPrice) {\n url.searchParams.set('min_price', options.minPrice.toString());\n }\n\n if (options.maxPrice) {\n url.searchParams.set('max_price', options.maxPrice.toString());\n }\n\n if (options.brand) {\n url.searchParams.set('brand', options.brand);\n }\n\n if (options.category) {\n url.searchParams.set('category', options.category);\n }\n\n if (options.constraints) {\n url.searchParams.set('constraints', JSON.stringify(options.constraints));\n }\n\n if (options.lite) {\n url.searchParams.set('lite', 'true');\n }\n\n if (options.limit) {\n url.searchParams.set('limit', options.limit.toString());\n }\n\n if (options.sort) {\n url.searchParams.set('sort', options.sort);\n }\n\n try {\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new TrustRailsError(\n `Search failed: ${response.statusText}`,\n response.status,\n await response.text()\n );\n }\n\n const data = await response.json() as SearchResponse;\n return data;\n } catch (error) {\n if (error instanceof TrustRailsError) {\n throw error;\n }\n throw new TrustRailsError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n /**\n * Get full details for a single product. Returns complete specs, description,\n * stock level, delivery time, and retailer source. Use after search() for\n * detailed comparison or recommendations.\n *\n * @param id - The product ID\n * @returns Promise resolving to complete product details\n *\n * @example\n * ```typescript\n * const product = await client.product('prod_123');\n * ```\n */\n async product(id: string): Promise<Product> {\n if (!id) {\n throw new Error('Product ID is required');\n }\n\n const url = `${this.baseUrl}/api/product/${encodeURIComponent(id)}`;\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new TrustRailsError(`Product not found: ${id}`, 404);\n }\n throw new TrustRailsError(\n `Get product failed: ${response.statusText}`,\n response.status,\n await response.text()\n );\n }\n\n const product = await response.json() as Product;\n return product;\n } catch (error) {\n if (error instanceof TrustRailsError) {\n throw error;\n }\n throw new TrustRailsError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Custom error class for TrustRails API errors\n */\nexport class TrustRailsError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public response?: any\n ) {\n super(message);\n this.name = 'TrustRailsError';\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TrustRailsError);\n }\n }\n}\n","import { TrustRailsError } from './errors';\nimport type { LiteProduct, Product, SearchOptions, SearchResponse, TrustRailsConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://trustrails.app';\n\n/**\n * TrustRails SDK\n *\n * @example\n * ```typescript\n * const trustrails = new TrustRails('your-api-key');\n * const results = await trustrails.search({ query: 'laptop', maxPrice: 1000 });\n * ```\n */\nexport class TrustRails {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n /**\n * Creates a new TrustRails SDK instance\n *\n * @param apiKey - Your TrustRails API key\n */\n constructor(apiKey: string);\n /**\n * Creates a new TrustRails SDK instance with custom configuration\n *\n * @param config - Configuration object with apiKey and optional baseUrl\n */\n constructor(config: TrustRailsConfig);\n constructor(apiKeyOrConfig: string | TrustRailsConfig) {\n if (typeof apiKeyOrConfig === 'string') {\n this.apiKey = apiKeyOrConfig;\n this.baseUrl = DEFAULT_BASE_URL;\n } else {\n this.apiKey = apiKeyOrConfig.apiKey;\n this.baseUrl = apiKeyOrConfig.baseUrl || DEFAULT_BASE_URL;\n }\n\n // Clean up baseUrl - remove trailing slash\n if (this.baseUrl.endsWith('/')) {\n this.baseUrl = this.baseUrl.slice(0, -1);\n }\n\n if (!this.apiKey) {\n throw new Error('apiKey is required');\n }\n }\n\n /**\n * Search for products. With `lite: true` the products are trimmed (see LiteProduct).\n * Search results carry every known `attributes` spec (with sources unless lite); for the retailer's description and every offer, call product(id).\n *\n * @param options - Search parameters\n * @returns Promise resolving to search results\n *\n * @example\n * ```typescript\n * const results = await client.search({\n * query: 'USB-C charger',\n * brand: 'Anker',\n * minPrice: 20,\n * maxPrice: 50,\n * category: 'Cables & Chargers'\n * });\n * ```\n */\n search(options: SearchOptions & { lite: true }): Promise<SearchResponse<LiteProduct>>;\n search(options?: SearchOptions & { lite?: false }): Promise<SearchResponse>;\n search(options: SearchOptions): Promise<SearchResponse<Product | LiteProduct>>;\n async search(options: SearchOptions = {}): Promise<SearchResponse<Product | LiteProduct>> {\n const url = new URL(`${this.baseUrl}/api/search`);\n\n if (options.query) {\n url.searchParams.set('query', options.query);\n }\n\n if (options.minPrice) {\n url.searchParams.set('min_price', options.minPrice.toString());\n }\n\n if (options.maxPrice) {\n url.searchParams.set('max_price', options.maxPrice.toString());\n }\n\n if (options.brand) {\n url.searchParams.set('brand', options.brand);\n }\n\n if (options.category) {\n url.searchParams.set('category', options.category);\n }\n\n if (options.constraints) {\n url.searchParams.set('constraints', JSON.stringify(options.constraints));\n }\n\n if (options.lite) {\n url.searchParams.set('lite', 'true');\n }\n\n if (options.limit) {\n url.searchParams.set('limit', options.limit.toString());\n }\n\n if (options.sort) {\n url.searchParams.set('sort', options.sort);\n }\n\n try {\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new TrustRailsError(\n `Search failed: ${response.statusText}`,\n response.status,\n await response.text()\n );\n }\n\n const data = await response.json() as SearchResponse<Product | LiteProduct>;\n return data;\n } catch (error) {\n if (error instanceof TrustRailsError) {\n throw error;\n }\n throw new TrustRailsError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n /**\n * Get full details for a single product. Returns complete specs, description,\n * availability, delivery time, and every retailer offer. Use after search() for\n * your final 1-3 picks, and only for what search() lacks (the description's details, every offer):\n * compare specs from the attributes search() already returns.\n *\n * @param id - The product ID\n * @returns Promise resolving to complete product details\n *\n * @example\n * ```typescript\n * const product = await client.product('prod_123');\n * ```\n */\n async product(id: string): Promise<Product> {\n if (!id) {\n throw new Error('Product ID is required');\n }\n\n const url = `${this.baseUrl}/api/product/${encodeURIComponent(id)}`;\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new TrustRailsError(`Product not found: ${id}`, 404);\n }\n throw new TrustRailsError(\n `Get product failed: ${response.statusText}`,\n response.status,\n await response.text()\n );\n }\n\n const product = await response.json() as Product;\n return product;\n } catch (error) {\n if (error instanceof TrustRailsError) {\n throw error;\n }\n throw new TrustRailsError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n}\n"]}
|