@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 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. Returns summary data (title, price, availability, category). For a product's structured `attributes`, the retailer's description and every offer, call `product(id)` with the product ID.
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., 'Sony', 'HP', 'Anker')
66
- - `options.category?: string` - Filter by product category (e.g., 'Laptops', 'Headphones')
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 including `offer_count` and, with constraints, `constraint_status` and the `status` and value of the constrained names, without `sources` (reduces payload by ~80%)
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'` (cheapest first), `'price_desc'` (most expensive first). With constraints, matched products still come first.
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 only: id, title, brand, price, availability, image_url, purchase_url
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), stock level, 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()` to get details for comparison or recommendations.
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
- stock: number;
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: product(), and searches with constraints (only the constrained names when lite), see below
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 sorted by price (returned by product())
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?: { retailer: string; field: "title" }[] } // sources: not in lite search results
193
- | { status: "conflicting"; values: { value: number; sources?: { retailer: string; field: "title" }[] }[] };
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: Product[];
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 (a lite search leaves out the sources)
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?: AttributeSource[];
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
- stock: number;
78
- delivery_time: string;
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
- specs: {
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 (in a lite search, only the constrained specs) */
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 sorted by price (full mode / getProduct only) */
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. Reduces payload size by ~80%. */
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' (cheapest first), 'price_desc' (most expensive first) */
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: Product[];
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. Returns summary data (title, price, availability, category).
169
- * For structured attributes, the retailer's description and every offer, call product(id).
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?: SearchOptions): Promise<SearchResponse>;
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
- * stock level, delivery time, and retailer source. Use after search() for
189
- * detailed comparison or recommendations.
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 (a lite search leaves out the sources)
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?: AttributeSource[];
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
- stock: number;
78
- delivery_time: string;
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
- specs: {
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 (in a lite search, only the constrained specs) */
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 sorted by price (full mode / getProduct only) */
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. Reduces payload size by ~80%. */
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' (cheapest first), 'price_desc' (most expensive first) */
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: Product[];
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. Returns summary data (title, price, availability, category).
169
- * For structured attributes, the retailer's description and every offer, call product(id).
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?: SearchOptions): Promise<SearchResponse>;
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
- * stock level, delivery time, and retailer source. Use after search() for
189
- * detailed comparison or recommendations.
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
- * stock level, delivery time, and retailer source. Use after search() for
112
- * detailed comparison or recommendations.
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
- * stock level, delivery time, and retailer source. Use after search() for
108
- * detailed comparison or recommendations.
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
@@ -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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustrails/sdk",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Official TypeScript SDK for TrustRails API",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",