@trustrails/sdk 0.4.10 → 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 +98 -21
- package/dist/index.d.mts +119 -17
- package/dist/index.d.ts +119 -17
- package/dist/index.js +6 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,19 +58,20 @@ 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
|
-
- `options.query?: string` - Refinement terms
|
|
65
|
-
- `options.brand?: string` - Filter by brand name (e.g
|
|
66
|
-
- `options.category?: string` - Filter by product category
|
|
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 (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.
|
|
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`
|
|
70
71
|
- `options.limit?: number` - Maximum number of results (default: 50, max: 100)
|
|
71
|
-
- `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.
|
|
72
73
|
|
|
73
|
-
**Returns:** Promise resolving to `SearchResponse` containing `products` array and `total` count.
|
|
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.
|
|
74
75
|
|
|
75
76
|
**Example — brand + category only (query omitted):**
|
|
76
77
|
```typescript
|
|
@@ -93,18 +94,29 @@ const results = await trustrails.search({
|
|
|
93
94
|
});
|
|
94
95
|
```
|
|
95
96
|
|
|
97
|
+
**Example — spec constraints:**
|
|
98
|
+
```typescript
|
|
99
|
+
// MacBooks whose titles state 24GB+ RAM and 1TB+ storage come back as 'matched'
|
|
100
|
+
const results = await trustrails.search({
|
|
101
|
+
brand: 'Apple',
|
|
102
|
+
category: 'Laptops',
|
|
103
|
+
maxPrice: 2000,
|
|
104
|
+
constraints: { memory_gb: { gte: 24 }, storage_gb: { gte: 1000 } },
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
96
108
|
**Lite mode (faster responses, smaller payloads):**
|
|
97
109
|
```typescript
|
|
98
110
|
const results = await trustrails.search({
|
|
99
111
|
brand: 'Anker',
|
|
100
112
|
category: 'Cables & Chargers',
|
|
101
|
-
lite: true // Returns
|
|
113
|
+
lite: true // Returns id, title, brand, price, currency, availability, image_url, purchase_url, offer_count and attributes (without sources)
|
|
102
114
|
});
|
|
103
115
|
```
|
|
104
116
|
|
|
105
117
|
##### `product(id: string): Promise<Product>`
|
|
106
118
|
|
|
107
|
-
Get full details for a single product. Returns
|
|
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.
|
|
108
120
|
|
|
109
121
|
**Parameters:**
|
|
110
122
|
- `id: string` - The product ID
|
|
@@ -116,6 +128,16 @@ Get full details for a single product. Returns complete technical specifications
|
|
|
116
128
|
const product = await trustrails.product('prod_123');
|
|
117
129
|
```
|
|
118
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
|
+
|
|
119
141
|
## Types
|
|
120
142
|
|
|
121
143
|
### `Product`
|
|
@@ -128,14 +150,13 @@ interface Product {
|
|
|
128
150
|
brand?: string;
|
|
129
151
|
price: number;
|
|
130
152
|
currency: string;
|
|
131
|
-
availability: "in_stock" | "low_stock" | "out_of_stock";
|
|
132
|
-
|
|
133
|
-
delivery_time: string;
|
|
153
|
+
availability: "in_stock" | "low_stock" | "out_of_stock" | "unknown";
|
|
154
|
+
delivery_time?: string; // product() only
|
|
134
155
|
image_url?: string;
|
|
135
156
|
category: string;
|
|
136
157
|
product_type: "product" | "accessory";
|
|
137
|
-
specs
|
|
138
|
-
description?: string; //
|
|
158
|
+
specs?: { // product() only
|
|
159
|
+
description?: string; // Retailer prose; never overrides attributes
|
|
139
160
|
model_number?: string;
|
|
140
161
|
dimensions?: string;
|
|
141
162
|
};
|
|
@@ -144,8 +165,10 @@ interface Product {
|
|
|
144
165
|
last_updated: string;
|
|
145
166
|
};
|
|
146
167
|
purchase_url: string;
|
|
147
|
-
|
|
148
|
-
|
|
168
|
+
attributes?: Attributes; // structured specs (search results and product()), absent when none is known, see below
|
|
169
|
+
constraint_status?: Partial<Record<ConstraintName, "matched" | "unverified">>; // only when constraints applied
|
|
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())
|
|
149
172
|
}
|
|
150
173
|
|
|
151
174
|
interface Offer {
|
|
@@ -154,8 +177,8 @@ interface Offer {
|
|
|
154
177
|
title: string;
|
|
155
178
|
price: number;
|
|
156
179
|
currency: string;
|
|
157
|
-
availability: "in_stock" | "low_stock" | "out_of_stock";
|
|
158
|
-
stock: number;
|
|
180
|
+
availability: "in_stock" | "low_stock" | "out_of_stock" | "unknown";
|
|
181
|
+
stock: number | null; // unit count only when the retailer supplies one
|
|
159
182
|
delivery_time: string;
|
|
160
183
|
purchase_url: string;
|
|
161
184
|
image_url?: string;
|
|
@@ -163,6 +186,55 @@ interface Offer {
|
|
|
163
186
|
}
|
|
164
187
|
```
|
|
165
188
|
|
|
189
|
+
### `Attributes`
|
|
190
|
+
|
|
191
|
+
Structured specs read from retailer titles, by name. A name nobody states is absent (unknown).
|
|
192
|
+
|
|
193
|
+
- `confirmed`: two or more retailers state the same value.
|
|
194
|
+
- `inferred`: one retailer's title states it.
|
|
195
|
+
- `conflicting`: retailers state different values; none is picked, so any constraint on it is `unverified`. A retailer can appear under two values, so check `offers[].title`.
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
type Attributes = Partial<Record<ConstraintName, Attribute>>;
|
|
199
|
+
|
|
200
|
+
type Attribute =
|
|
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>>;
|
|
210
|
+
|
|
211
|
+
// memory_gb (RAM, GB), storage_gb (GB, 1TB = 1000), screen_in (inches), resolution_p (pixels high: 4K = 2160),
|
|
212
|
+
// refresh_hz (Hz), power_w (W), wifi_gen (Wi-Fi generation: 6, 6E = 6.5, 7)
|
|
213
|
+
type ConstraintName = "memory_gb" | "storage_gb" | "screen_in" | "resolution_p" | "refresh_hz" | "power_w" | "wifi_gen";
|
|
214
|
+
|
|
215
|
+
type Constraints = Partial<Record<ConstraintName, { eq?: number; gte?: number; lte?: number }>>;
|
|
216
|
+
```
|
|
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
|
+
|
|
166
238
|
### `SearchOptions`
|
|
167
239
|
|
|
168
240
|
```typescript
|
|
@@ -172,6 +244,7 @@ interface SearchOptions {
|
|
|
172
244
|
category?: string;
|
|
173
245
|
minPrice?: number;
|
|
174
246
|
maxPrice?: number;
|
|
247
|
+
constraints?: Constraints;
|
|
175
248
|
lite?: boolean;
|
|
176
249
|
limit?: number;
|
|
177
250
|
sort?: 'relevance' | 'price_asc' | 'price_desc';
|
|
@@ -181,9 +254,13 @@ interface SearchOptions {
|
|
|
181
254
|
### `SearchResponse`
|
|
182
255
|
|
|
183
256
|
```typescript
|
|
184
|
-
interface SearchResponse {
|
|
185
|
-
products:
|
|
186
|
-
total: number;
|
|
257
|
+
interface SearchResponse<P = Product> { // SearchResponse<LiteProduct> when lite: true
|
|
258
|
+
products: P[];
|
|
259
|
+
total: number; // with constraints: the products that match every constraint
|
|
260
|
+
constraints?: Constraints; // as applied, when constraints applied
|
|
261
|
+
excluded_by_constraints?: number; // products left out because their stated value fails a constraint
|
|
262
|
+
unverified_total?: number; // with constraints: the unverified products that passed the other filters; only some may be in products
|
|
263
|
+
candidates_truncated?: boolean; // true when the first 2,000 candidates were checked and more exist
|
|
187
264
|
}
|
|
188
265
|
```
|
|
189
266
|
|
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,12 +12,73 @@ 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;
|
|
19
19
|
last_updated: string;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Where a stated value came from
|
|
23
|
+
*/
|
|
24
|
+
interface AttributeSource {
|
|
25
|
+
retailer: string;
|
|
26
|
+
field: 'title';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* One structured spec of a product, with the evidence for it
|
|
30
|
+
*/
|
|
31
|
+
type Attribute = {
|
|
32
|
+
status: 'confirmed' | 'inferred';
|
|
33
|
+
value: number;
|
|
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;
|
|
48
|
+
} | {
|
|
49
|
+
status: 'conflicting';
|
|
50
|
+
values: Array<{
|
|
51
|
+
value: number;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Structured specs of a product, by name
|
|
56
|
+
*/
|
|
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>>;
|
|
62
|
+
/**
|
|
63
|
+
* Spec names for `constraints` and `attributes`
|
|
64
|
+
*/
|
|
65
|
+
type ConstraintName = 'memory_gb' | 'storage_gb' | 'screen_in' | 'resolution_p' | 'refresh_hz' | 'power_w' | 'wifi_gen';
|
|
66
|
+
/**
|
|
67
|
+
* Operators for one spec
|
|
68
|
+
*/
|
|
69
|
+
interface ConstraintOps {
|
|
70
|
+
eq?: number;
|
|
71
|
+
gte?: number;
|
|
72
|
+
lte?: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Hard spec requirements
|
|
76
|
+
*/
|
|
77
|
+
type Constraints = Partial<Record<ConstraintName, ConstraintOps>>;
|
|
78
|
+
/**
|
|
79
|
+
* Whether a product meets one constraint
|
|
80
|
+
*/
|
|
81
|
+
type ConstraintStatus = 'matched' | 'unverified';
|
|
21
82
|
/**
|
|
22
83
|
* Product information returned by TrustRails API
|
|
23
84
|
*/
|
|
@@ -29,12 +90,13 @@ interface Product {
|
|
|
29
90
|
price: number;
|
|
30
91
|
currency: string;
|
|
31
92
|
availability: Availability;
|
|
32
|
-
|
|
33
|
-
delivery_time
|
|
93
|
+
/** Product detail only: absent from search results */
|
|
94
|
+
delivery_time?: string;
|
|
34
95
|
image_url?: string;
|
|
35
96
|
category: string;
|
|
36
97
|
product_type: 'product' | 'accessory';
|
|
37
|
-
|
|
98
|
+
/** Product detail only: absent from search results */
|
|
99
|
+
specs?: {
|
|
38
100
|
description?: string;
|
|
39
101
|
model_number?: string;
|
|
40
102
|
dimensions?: string;
|
|
@@ -44,11 +106,33 @@ interface Product {
|
|
|
44
106
|
last_updated: string;
|
|
45
107
|
};
|
|
46
108
|
purchase_url: string;
|
|
109
|
+
/** Structured specs read from retailer titles: absent when none is known */
|
|
110
|
+
attributes?: Attributes;
|
|
111
|
+
/** Per-constraint result (only in searches with constraints) */
|
|
112
|
+
constraint_status?: Partial<Record<ConstraintName, ConstraintStatus>>;
|
|
47
113
|
/** Number of retailer offers available for this product */
|
|
48
114
|
offer_count?: number;
|
|
49
|
-
/** Per-retailer offers
|
|
115
|
+
/** Per-retailer offers, in stock first then cheapest (product() only) */
|
|
50
116
|
offers?: Offer[];
|
|
51
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
|
+
}
|
|
52
136
|
/**
|
|
53
137
|
* Options for searching products
|
|
54
138
|
*/
|
|
@@ -58,19 +142,30 @@ interface SearchOptions {
|
|
|
58
142
|
maxPrice?: number;
|
|
59
143
|
brand?: string;
|
|
60
144
|
category?: string;
|
|
61
|
-
/**
|
|
145
|
+
/** Hard spec requirements checked against each product's attributes */
|
|
146
|
+
constraints?: Constraints;
|
|
147
|
+
/** Return trimmed product objects (see LiteProduct) with only essential fields. */
|
|
62
148
|
lite?: boolean;
|
|
63
149
|
/** Maximum number of products to return (default 50, max 100) */
|
|
64
150
|
limit?: number;
|
|
65
|
-
/** 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) */
|
|
66
152
|
sort?: 'relevance' | 'price_asc' | 'price_desc';
|
|
67
153
|
}
|
|
68
154
|
/**
|
|
69
155
|
* Response from product search endpoint
|
|
70
156
|
*/
|
|
71
|
-
interface SearchResponse {
|
|
72
|
-
products:
|
|
157
|
+
interface SearchResponse<P = Product> {
|
|
158
|
+
products: P[];
|
|
159
|
+
/** With constraints: the products with every constraint matched */
|
|
73
160
|
total: number;
|
|
161
|
+
/** The constraints applied */
|
|
162
|
+
constraints?: Constraints;
|
|
163
|
+
/** Products left out for failing a constraint */
|
|
164
|
+
excluded_by_constraints?: number;
|
|
165
|
+
/** With constraints: the unverified products that passed the other filters; only some may be in products */
|
|
166
|
+
unverified_total?: number;
|
|
167
|
+
/** True when the first 2,000 candidates in the sort order were checked and more exist */
|
|
168
|
+
candidates_truncated?: boolean;
|
|
74
169
|
}
|
|
75
170
|
/**
|
|
76
171
|
* Configuration options for TrustRails client
|
|
@@ -105,8 +200,8 @@ declare class TrustRails {
|
|
|
105
200
|
*/
|
|
106
201
|
constructor(config: TrustRailsConfig);
|
|
107
202
|
/**
|
|
108
|
-
* Search for products.
|
|
109
|
-
*
|
|
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).
|
|
110
205
|
*
|
|
111
206
|
* @param options - Search parameters
|
|
112
207
|
* @returns Promise resolving to search results
|
|
@@ -118,15 +213,22 @@ declare class TrustRails {
|
|
|
118
213
|
* brand: 'Anker',
|
|
119
214
|
* minPrice: 20,
|
|
120
215
|
* maxPrice: 50,
|
|
121
|
-
* category: 'Chargers'
|
|
216
|
+
* category: 'Cables & Chargers'
|
|
122
217
|
* });
|
|
123
218
|
* ```
|
|
124
219
|
*/
|
|
125
|
-
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>>;
|
|
126
227
|
/**
|
|
127
228
|
* Get full details for a single product. Returns complete specs, description,
|
|
128
|
-
*
|
|
129
|
-
*
|
|
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.
|
|
130
232
|
*
|
|
131
233
|
* @param id - The product ID
|
|
132
234
|
* @returns Promise resolving to complete product details
|
|
@@ -148,4 +250,4 @@ declare class TrustRailsError extends Error {
|
|
|
148
250
|
constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
|
|
149
251
|
}
|
|
150
252
|
|
|
151
|
-
export { 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,12 +12,73 @@ 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;
|
|
19
19
|
last_updated: string;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Where a stated value came from
|
|
23
|
+
*/
|
|
24
|
+
interface AttributeSource {
|
|
25
|
+
retailer: string;
|
|
26
|
+
field: 'title';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* One structured spec of a product, with the evidence for it
|
|
30
|
+
*/
|
|
31
|
+
type Attribute = {
|
|
32
|
+
status: 'confirmed' | 'inferred';
|
|
33
|
+
value: number;
|
|
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;
|
|
48
|
+
} | {
|
|
49
|
+
status: 'conflicting';
|
|
50
|
+
values: Array<{
|
|
51
|
+
value: number;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Structured specs of a product, by name
|
|
56
|
+
*/
|
|
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>>;
|
|
62
|
+
/**
|
|
63
|
+
* Spec names for `constraints` and `attributes`
|
|
64
|
+
*/
|
|
65
|
+
type ConstraintName = 'memory_gb' | 'storage_gb' | 'screen_in' | 'resolution_p' | 'refresh_hz' | 'power_w' | 'wifi_gen';
|
|
66
|
+
/**
|
|
67
|
+
* Operators for one spec
|
|
68
|
+
*/
|
|
69
|
+
interface ConstraintOps {
|
|
70
|
+
eq?: number;
|
|
71
|
+
gte?: number;
|
|
72
|
+
lte?: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Hard spec requirements
|
|
76
|
+
*/
|
|
77
|
+
type Constraints = Partial<Record<ConstraintName, ConstraintOps>>;
|
|
78
|
+
/**
|
|
79
|
+
* Whether a product meets one constraint
|
|
80
|
+
*/
|
|
81
|
+
type ConstraintStatus = 'matched' | 'unverified';
|
|
21
82
|
/**
|
|
22
83
|
* Product information returned by TrustRails API
|
|
23
84
|
*/
|
|
@@ -29,12 +90,13 @@ interface Product {
|
|
|
29
90
|
price: number;
|
|
30
91
|
currency: string;
|
|
31
92
|
availability: Availability;
|
|
32
|
-
|
|
33
|
-
delivery_time
|
|
93
|
+
/** Product detail only: absent from search results */
|
|
94
|
+
delivery_time?: string;
|
|
34
95
|
image_url?: string;
|
|
35
96
|
category: string;
|
|
36
97
|
product_type: 'product' | 'accessory';
|
|
37
|
-
|
|
98
|
+
/** Product detail only: absent from search results */
|
|
99
|
+
specs?: {
|
|
38
100
|
description?: string;
|
|
39
101
|
model_number?: string;
|
|
40
102
|
dimensions?: string;
|
|
@@ -44,11 +106,33 @@ interface Product {
|
|
|
44
106
|
last_updated: string;
|
|
45
107
|
};
|
|
46
108
|
purchase_url: string;
|
|
109
|
+
/** Structured specs read from retailer titles: absent when none is known */
|
|
110
|
+
attributes?: Attributes;
|
|
111
|
+
/** Per-constraint result (only in searches with constraints) */
|
|
112
|
+
constraint_status?: Partial<Record<ConstraintName, ConstraintStatus>>;
|
|
47
113
|
/** Number of retailer offers available for this product */
|
|
48
114
|
offer_count?: number;
|
|
49
|
-
/** Per-retailer offers
|
|
115
|
+
/** Per-retailer offers, in stock first then cheapest (product() only) */
|
|
50
116
|
offers?: Offer[];
|
|
51
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
|
+
}
|
|
52
136
|
/**
|
|
53
137
|
* Options for searching products
|
|
54
138
|
*/
|
|
@@ -58,19 +142,30 @@ interface SearchOptions {
|
|
|
58
142
|
maxPrice?: number;
|
|
59
143
|
brand?: string;
|
|
60
144
|
category?: string;
|
|
61
|
-
/**
|
|
145
|
+
/** Hard spec requirements checked against each product's attributes */
|
|
146
|
+
constraints?: Constraints;
|
|
147
|
+
/** Return trimmed product objects (see LiteProduct) with only essential fields. */
|
|
62
148
|
lite?: boolean;
|
|
63
149
|
/** Maximum number of products to return (default 50, max 100) */
|
|
64
150
|
limit?: number;
|
|
65
|
-
/** 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) */
|
|
66
152
|
sort?: 'relevance' | 'price_asc' | 'price_desc';
|
|
67
153
|
}
|
|
68
154
|
/**
|
|
69
155
|
* Response from product search endpoint
|
|
70
156
|
*/
|
|
71
|
-
interface SearchResponse {
|
|
72
|
-
products:
|
|
157
|
+
interface SearchResponse<P = Product> {
|
|
158
|
+
products: P[];
|
|
159
|
+
/** With constraints: the products with every constraint matched */
|
|
73
160
|
total: number;
|
|
161
|
+
/** The constraints applied */
|
|
162
|
+
constraints?: Constraints;
|
|
163
|
+
/** Products left out for failing a constraint */
|
|
164
|
+
excluded_by_constraints?: number;
|
|
165
|
+
/** With constraints: the unverified products that passed the other filters; only some may be in products */
|
|
166
|
+
unverified_total?: number;
|
|
167
|
+
/** True when the first 2,000 candidates in the sort order were checked and more exist */
|
|
168
|
+
candidates_truncated?: boolean;
|
|
74
169
|
}
|
|
75
170
|
/**
|
|
76
171
|
* Configuration options for TrustRails client
|
|
@@ -105,8 +200,8 @@ declare class TrustRails {
|
|
|
105
200
|
*/
|
|
106
201
|
constructor(config: TrustRailsConfig);
|
|
107
202
|
/**
|
|
108
|
-
* Search for products.
|
|
109
|
-
*
|
|
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).
|
|
110
205
|
*
|
|
111
206
|
* @param options - Search parameters
|
|
112
207
|
* @returns Promise resolving to search results
|
|
@@ -118,15 +213,22 @@ declare class TrustRails {
|
|
|
118
213
|
* brand: 'Anker',
|
|
119
214
|
* minPrice: 20,
|
|
120
215
|
* maxPrice: 50,
|
|
121
|
-
* category: 'Chargers'
|
|
216
|
+
* category: 'Cables & Chargers'
|
|
122
217
|
* });
|
|
123
218
|
* ```
|
|
124
219
|
*/
|
|
125
|
-
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>>;
|
|
126
227
|
/**
|
|
127
228
|
* Get full details for a single product. Returns complete specs, description,
|
|
128
|
-
*
|
|
129
|
-
*
|
|
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.
|
|
130
232
|
*
|
|
131
233
|
* @param id - The product ID
|
|
132
234
|
* @returns Promise resolving to complete product details
|
|
@@ -148,4 +250,4 @@ declare class TrustRailsError extends Error {
|
|
|
148
250
|
constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
|
|
149
251
|
}
|
|
150
252
|
|
|
151
|
-
export { 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 full technical specs, call product(id) with the 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) {
|
|
@@ -68,6 +50,9 @@ var TrustRails = class {
|
|
|
68
50
|
if (options.category) {
|
|
69
51
|
url.searchParams.set("category", options.category);
|
|
70
52
|
}
|
|
53
|
+
if (options.constraints) {
|
|
54
|
+
url.searchParams.set("constraints", JSON.stringify(options.constraints));
|
|
55
|
+
}
|
|
71
56
|
if (options.lite) {
|
|
72
57
|
url.searchParams.set("lite", "true");
|
|
73
58
|
}
|
|
@@ -105,8 +90,9 @@ var TrustRails = class {
|
|
|
105
90
|
}
|
|
106
91
|
/**
|
|
107
92
|
* Get full details for a single product. Returns complete specs, description,
|
|
108
|
-
*
|
|
109
|
-
*
|
|
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.
|
|
110
96
|
*
|
|
111
97
|
* @param id - The product ID
|
|
112
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 full technical specs, call product(id) with the 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.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 full technical specs, call product(id) with the 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) {
|
|
@@ -64,6 +46,9 @@ var TrustRails = class {
|
|
|
64
46
|
if (options.category) {
|
|
65
47
|
url.searchParams.set("category", options.category);
|
|
66
48
|
}
|
|
49
|
+
if (options.constraints) {
|
|
50
|
+
url.searchParams.set("constraints", JSON.stringify(options.constraints));
|
|
51
|
+
}
|
|
67
52
|
if (options.lite) {
|
|
68
53
|
url.searchParams.set("lite", "true");
|
|
69
54
|
}
|
|
@@ -101,8 +86,9 @@ var TrustRails = class {
|
|
|
101
86
|
}
|
|
102
87
|
/**
|
|
103
88
|
* Get full details for a single product. Returns complete specs, description,
|
|
104
|
-
*
|
|
105
|
-
*
|
|
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.
|
|
106
92
|
*
|
|
107
93
|
* @param id - The product ID
|
|
108
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 full technical specs, call product(id) with the 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.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"]}
|