@torvion/rascador 0.1.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/LICENSE +21 -0
- package/README.md +176 -0
- package/dist/index.cjs +387 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +315 -0
- package/dist/index.d.ts +315 -0
- package/dist/index.js +355 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types for the Rascador `/v1` API.
|
|
3
|
+
*
|
|
4
|
+
* These mirror the gateway's zod schemas (`rascador-gateway/src/schemas/*.ts`)
|
|
5
|
+
* field for field, snake_case included, so what you read here is exactly what
|
|
6
|
+
* the API reference documents.
|
|
7
|
+
*/
|
|
8
|
+
type SourceId = "shein" | "aliexpress" | "amazon" | "backmarket";
|
|
9
|
+
interface Pagination {
|
|
10
|
+
total: number;
|
|
11
|
+
limit: number;
|
|
12
|
+
offset: number;
|
|
13
|
+
has_more: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface Meta {
|
|
16
|
+
request_id: string;
|
|
17
|
+
source?: SourceId;
|
|
18
|
+
/** Where the data came from: our cache, the stored catalogue, a live scrape, or the source registry. */
|
|
19
|
+
origin?: "cache" | "store" | "live" | "registry";
|
|
20
|
+
cached?: boolean;
|
|
21
|
+
took_ms: number;
|
|
22
|
+
/** This exact request in your dashboard log. */
|
|
23
|
+
log_url?: string;
|
|
24
|
+
/** Non-fatal degradation, e.g. `category_names_unavailable`. The response is still a 200. */
|
|
25
|
+
warnings?: string[];
|
|
26
|
+
/** With `only_new: true`: how many products were dropped as already seen this period. */
|
|
27
|
+
filtered_seen?: number;
|
|
28
|
+
pagination?: Pagination;
|
|
29
|
+
}
|
|
30
|
+
/** What every method resolves to: the gateway's success envelope, unchanged. */
|
|
31
|
+
interface Response<T> {
|
|
32
|
+
data: T;
|
|
33
|
+
meta: Meta;
|
|
34
|
+
}
|
|
35
|
+
interface Money {
|
|
36
|
+
/** On a multi-variant listing this is the cheapest variant; read it with `current_max`. */
|
|
37
|
+
current: number | null;
|
|
38
|
+
/** Top of the price range on a multi-variant listing. */
|
|
39
|
+
current_max?: number;
|
|
40
|
+
original: number | null;
|
|
41
|
+
/** ISO 4217 code as reported by the source. */
|
|
42
|
+
currency: string | null;
|
|
43
|
+
discount_percent: number | null;
|
|
44
|
+
on_sale: boolean;
|
|
45
|
+
}
|
|
46
|
+
interface CategoryRef {
|
|
47
|
+
id: number | null;
|
|
48
|
+
/** Null when the category catalogue is unavailable; see `meta.warnings`. */
|
|
49
|
+
name: string | null;
|
|
50
|
+
url: string | null;
|
|
51
|
+
}
|
|
52
|
+
interface Product {
|
|
53
|
+
/** Numeric on SHEIN/AliExpress, an ASIN string on Amazon. */
|
|
54
|
+
id: number | string | null;
|
|
55
|
+
source: SourceId;
|
|
56
|
+
url: string;
|
|
57
|
+
detail_url: string;
|
|
58
|
+
sku: string | null;
|
|
59
|
+
title: string | null;
|
|
60
|
+
brand: string | null;
|
|
61
|
+
description: string | null;
|
|
62
|
+
images: string[];
|
|
63
|
+
price: Money;
|
|
64
|
+
category: CategoryRef;
|
|
65
|
+
variants: {
|
|
66
|
+
colors: string[];
|
|
67
|
+
sizes: string[];
|
|
68
|
+
};
|
|
69
|
+
availability: "in_stock" | "out_of_stock" | "unknown";
|
|
70
|
+
availability_raw: string | null;
|
|
71
|
+
rating: {
|
|
72
|
+
value: number | null;
|
|
73
|
+
count: number | null;
|
|
74
|
+
};
|
|
75
|
+
/** ISO-8601 UTC instant this data was collected. */
|
|
76
|
+
scraped_at: string;
|
|
77
|
+
}
|
|
78
|
+
interface SearchHit {
|
|
79
|
+
product_id: number | string | null;
|
|
80
|
+
url: string;
|
|
81
|
+
/** Null when the id could not be determined. */
|
|
82
|
+
detail_url: string | null;
|
|
83
|
+
title: string | null;
|
|
84
|
+
image: string | null;
|
|
85
|
+
price: number | null;
|
|
86
|
+
original_price: number | null;
|
|
87
|
+
discount_percent: number | null;
|
|
88
|
+
currency: string | null;
|
|
89
|
+
rating: number | null;
|
|
90
|
+
}
|
|
91
|
+
interface Category {
|
|
92
|
+
/** On SHEIN, usable as `products.list({ category_id })`. */
|
|
93
|
+
id: number | null;
|
|
94
|
+
source_category_id: number | string | null;
|
|
95
|
+
name: string | null;
|
|
96
|
+
url: string;
|
|
97
|
+
/** Stable across sources, e.g. `Shoes/Boots`. */
|
|
98
|
+
path: string;
|
|
99
|
+
parent_path: string | null;
|
|
100
|
+
level: number;
|
|
101
|
+
/** Search term that lists this category, when it has no id-addressable listing. */
|
|
102
|
+
query: string | null;
|
|
103
|
+
first_seen_at: string | null;
|
|
104
|
+
last_seen_at: string | null;
|
|
105
|
+
}
|
|
106
|
+
interface Source {
|
|
107
|
+
id: string;
|
|
108
|
+
name: string;
|
|
109
|
+
capabilities: {
|
|
110
|
+
live_search: boolean;
|
|
111
|
+
live_product_fetch: boolean;
|
|
112
|
+
product_id_filter: boolean;
|
|
113
|
+
/** Filters `products.list` accepts for this source. */
|
|
114
|
+
filters: string[];
|
|
115
|
+
max_search_pages: number;
|
|
116
|
+
max_limit: number;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
interface Me {
|
|
120
|
+
client_id: string;
|
|
121
|
+
token_id: string;
|
|
122
|
+
token_name: string;
|
|
123
|
+
environment: "live" | "test";
|
|
124
|
+
plan: string;
|
|
125
|
+
scopes: string[];
|
|
126
|
+
quota: {
|
|
127
|
+
/** Billing period, YYYYMM (UTC). */
|
|
128
|
+
period: string;
|
|
129
|
+
limit: number;
|
|
130
|
+
used: number;
|
|
131
|
+
remaining: number;
|
|
132
|
+
resets_at: string;
|
|
133
|
+
topup_balance: number;
|
|
134
|
+
};
|
|
135
|
+
rate_limits: {
|
|
136
|
+
cheap_per_minute: number;
|
|
137
|
+
expensive_per_minute: number;
|
|
138
|
+
expensive_per_day: number;
|
|
139
|
+
expensive_concurrent: number;
|
|
140
|
+
};
|
|
141
|
+
/** Units charged per call type. */
|
|
142
|
+
quota_costs: Record<string, number>;
|
|
143
|
+
}
|
|
144
|
+
interface SourceParam {
|
|
145
|
+
/** Overrides the client's default source for this call. */
|
|
146
|
+
source?: SourceId;
|
|
147
|
+
}
|
|
148
|
+
interface PageParams {
|
|
149
|
+
/** 1–1000, default 50. */
|
|
150
|
+
limit?: number;
|
|
151
|
+
offset?: number;
|
|
152
|
+
}
|
|
153
|
+
interface SearchParams extends SourceParam {
|
|
154
|
+
q: string;
|
|
155
|
+
/** 1–5 result pages, roughly 120 products each. Default 1. */
|
|
156
|
+
pages?: number;
|
|
157
|
+
/** Bypass the 20-day result cache. Costs more quota. */
|
|
158
|
+
refresh?: boolean;
|
|
159
|
+
}
|
|
160
|
+
interface GetProductParams extends SourceParam {
|
|
161
|
+
/** Force a live re-scrape (20–90s, needs `search:live`, costs more quota). */
|
|
162
|
+
refresh?: boolean;
|
|
163
|
+
}
|
|
164
|
+
interface ListProductsParams extends SourceParam, PageParams {
|
|
165
|
+
category_id?: number;
|
|
166
|
+
sku?: string;
|
|
167
|
+
q?: string;
|
|
168
|
+
brand?: string;
|
|
169
|
+
min_price?: number;
|
|
170
|
+
max_price?: number;
|
|
171
|
+
color?: string;
|
|
172
|
+
size?: string;
|
|
173
|
+
on_sale?: boolean;
|
|
174
|
+
/** Drop products this key has already been served this billing period. */
|
|
175
|
+
only_new?: boolean;
|
|
176
|
+
}
|
|
177
|
+
interface ListCategoriesParams extends SourceParam, PageParams {
|
|
178
|
+
q?: string;
|
|
179
|
+
level?: number;
|
|
180
|
+
parent_path?: string;
|
|
181
|
+
}
|
|
182
|
+
/** Per-call overrides. */
|
|
183
|
+
interface RequestOptions {
|
|
184
|
+
signal?: AbortSignal;
|
|
185
|
+
timeoutMs?: number;
|
|
186
|
+
maxRetries?: number;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
type FetchPage<T> = (offset: number | undefined) => Promise<Response<T[]>>;
|
|
190
|
+
/**
|
|
191
|
+
* A single page you can `await`, or every item you can `for await`.
|
|
192
|
+
*
|
|
193
|
+
* `await list(...)` makes exactly one request. Iterating makes as many as
|
|
194
|
+
* `meta.pagination.has_more` calls for, starting from the offset you passed.
|
|
195
|
+
*/
|
|
196
|
+
declare class PagePromise<T> implements PromiseLike<Response<T[]>>, AsyncIterable<T> {
|
|
197
|
+
#private;
|
|
198
|
+
constructor(fetchPage: FetchPage<T>, startOffset: number | undefined);
|
|
199
|
+
then<R1 = Response<T[]>, R2 = never>(onfulfilled?: ((value: Response<T[]>) => R1 | PromiseLike<R1>) | null, onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | null): Promise<R1 | R2>;
|
|
200
|
+
catch<R = never>(onrejected?: ((reason: unknown) => R | PromiseLike<R>) | null): Promise<Response<T[]> | R>;
|
|
201
|
+
finally(onfinally?: (() => void) | null): Promise<Response<T[]>>;
|
|
202
|
+
/** Every page, in order. */
|
|
203
|
+
pages(): AsyncGenerator<Response<T[]>, void, undefined>;
|
|
204
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
declare const DEFAULT_BASE_URL = "https://api.rascador.store";
|
|
208
|
+
interface RascadorOptions {
|
|
209
|
+
/** Defaults to the `RASCADOR_API_KEY` environment variable where one exists. */
|
|
210
|
+
apiKey?: string;
|
|
211
|
+
/** Defaults to the production gateway. */
|
|
212
|
+
baseUrl?: string;
|
|
213
|
+
/** Source used when a call doesn't name one. The gateway's own default is `shein`. */
|
|
214
|
+
source?: SourceId;
|
|
215
|
+
/** Timeout for stored-data calls. Default 30s. */
|
|
216
|
+
timeoutMs?: number;
|
|
217
|
+
/** Timeout for live calls (`search`, `products.get` with `refresh`). Default 130s. */
|
|
218
|
+
liveTimeoutMs?: number;
|
|
219
|
+
/** Retries for errors the gateway marks retryable. Default 2. */
|
|
220
|
+
maxRetries?: number;
|
|
221
|
+
/** Custom fetch, e.g. for a proxy or tests. Defaults to the global `fetch`. */
|
|
222
|
+
fetch?: typeof fetch;
|
|
223
|
+
/** Extra headers sent with every request. */
|
|
224
|
+
headers?: Record<string, string>;
|
|
225
|
+
}
|
|
226
|
+
type Query = Record<string, string | number | boolean | undefined>;
|
|
227
|
+
interface CallSpec {
|
|
228
|
+
path: string;
|
|
229
|
+
query?: Query;
|
|
230
|
+
live?: boolean;
|
|
231
|
+
options?: RequestOptions;
|
|
232
|
+
}
|
|
233
|
+
declare class Rascador {
|
|
234
|
+
#private;
|
|
235
|
+
readonly products: ProductsResource;
|
|
236
|
+
readonly categories: CategoriesResource;
|
|
237
|
+
readonly sources: SourcesResource;
|
|
238
|
+
constructor(options?: RascadorOptions);
|
|
239
|
+
/** The key's scopes, limits and remaining quota. Never spends quota. */
|
|
240
|
+
me(options?: RequestOptions): Promise<Response<Me>>;
|
|
241
|
+
/**
|
|
242
|
+
* Live search on the source site. Slow (20–40s) and quota-heavy.
|
|
243
|
+
* Returns result cards; call `products.get` for full details.
|
|
244
|
+
*/
|
|
245
|
+
search(params: SearchParams, options?: RequestOptions): Promise<Response<SearchHit[]>>;
|
|
246
|
+
/** @internal */
|
|
247
|
+
_source(source: SourceId | undefined): SourceId | undefined;
|
|
248
|
+
/** @internal */
|
|
249
|
+
_get<T>(spec: CallSpec): Promise<Response<T>>;
|
|
250
|
+
/** The SDK version. Include it in bug reports. */
|
|
251
|
+
static readonly VERSION: string;
|
|
252
|
+
}
|
|
253
|
+
declare class ProductsResource {
|
|
254
|
+
#private;
|
|
255
|
+
constructor(client: Rascador);
|
|
256
|
+
/**
|
|
257
|
+
* One product. Instant when stored; otherwise (or with `refresh: true`) a
|
|
258
|
+
* live fetch, which needs the `search:live` scope.
|
|
259
|
+
*/
|
|
260
|
+
get(id: string | number, params?: GetProductParams, options?: RequestOptions): Promise<Response<Product>>;
|
|
261
|
+
/** Stored products. `await` for one page, `for await` for every product. */
|
|
262
|
+
list(params?: ListProductsParams, options?: RequestOptions): PagePromise<Product>;
|
|
263
|
+
}
|
|
264
|
+
declare class CategoriesResource {
|
|
265
|
+
#private;
|
|
266
|
+
constructor(client: Rascador);
|
|
267
|
+
/** The source's category tree. `await` for one page, `for await` for all. */
|
|
268
|
+
list(params?: ListCategoriesParams, options?: RequestOptions): PagePromise<Category>;
|
|
269
|
+
}
|
|
270
|
+
declare class SourcesResource {
|
|
271
|
+
#private;
|
|
272
|
+
constructor(client: Rascador);
|
|
273
|
+
/** Every source and what it supports. Never spends quota. */
|
|
274
|
+
list(options?: RequestOptions): Promise<Response<Source[]>>;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Codes the gateway can return, plus three the SDK raises itself:
|
|
279
|
+
* `network_error`, `timeout` and `invalid_response`.
|
|
280
|
+
*
|
|
281
|
+
* The `(string & {})` keeps autocomplete for the known codes while still
|
|
282
|
+
* accepting any code a newer gateway adds.
|
|
283
|
+
*/
|
|
284
|
+
type RascadorErrorCode = "invalid_request" | "unknown_source" | "missing_credentials" | "invalid_token" | "quota_exceeded" | "coverage_exceeded" | "payment_required" | "insufficient_scope" | "client_suspended" | "not_found" | "product_not_found" | "rate_limited" | "platform_busy" | "internal_error" | "gateway_misconfigured" | "upstream_error" | "upstream_unavailable" | "upstream_busy" | "upstream_timeout" | "network_error" | "timeout" | "invalid_response" | (string & {});
|
|
285
|
+
interface RascadorErrorInit {
|
|
286
|
+
code: RascadorErrorCode;
|
|
287
|
+
message: string;
|
|
288
|
+
status: number;
|
|
289
|
+
retryable: boolean;
|
|
290
|
+
retryAfterSeconds?: number;
|
|
291
|
+
requestId?: string;
|
|
292
|
+
logUrl?: string;
|
|
293
|
+
details?: Record<string, unknown>;
|
|
294
|
+
cause?: unknown;
|
|
295
|
+
}
|
|
296
|
+
/** Thrown for every failed call. Branch on `code`, not on `message`. */
|
|
297
|
+
declare class RascadorError extends Error {
|
|
298
|
+
readonly name = "RascadorError";
|
|
299
|
+
readonly code: RascadorErrorCode;
|
|
300
|
+
/** HTTP status, or 0 when no response arrived (network failure, timeout). */
|
|
301
|
+
readonly status: number;
|
|
302
|
+
readonly retryable: boolean;
|
|
303
|
+
readonly retryAfterSeconds: number | undefined;
|
|
304
|
+
/** Quote this when contacting support. */
|
|
305
|
+
readonly requestId: string | undefined;
|
|
306
|
+
/** This failure in your dashboard log. */
|
|
307
|
+
readonly logUrl: string | undefined;
|
|
308
|
+
readonly details: Record<string, unknown> | undefined;
|
|
309
|
+
constructor(init: RascadorErrorInit);
|
|
310
|
+
}
|
|
311
|
+
declare function isRascadorError(error: unknown): error is RascadorError;
|
|
312
|
+
|
|
313
|
+
declare const VERSION = "0.1.0";
|
|
314
|
+
|
|
315
|
+
export { CategoriesResource, type Category, type CategoryRef, DEFAULT_BASE_URL, type GetProductParams, type ListCategoriesParams, type ListProductsParams, type Me, type Meta, type Money, type PageParams, PagePromise, type Pagination, type Product, ProductsResource, Rascador, RascadorError, type RascadorErrorCode, type RascadorErrorInit, type RascadorOptions, type RequestOptions, type Response, type SearchHit, type SearchParams, type Source, type SourceId, type SourceParam, SourcesResource, VERSION, isRascadorError };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types for the Rascador `/v1` API.
|
|
3
|
+
*
|
|
4
|
+
* These mirror the gateway's zod schemas (`rascador-gateway/src/schemas/*.ts`)
|
|
5
|
+
* field for field, snake_case included, so what you read here is exactly what
|
|
6
|
+
* the API reference documents.
|
|
7
|
+
*/
|
|
8
|
+
type SourceId = "shein" | "aliexpress" | "amazon" | "backmarket";
|
|
9
|
+
interface Pagination {
|
|
10
|
+
total: number;
|
|
11
|
+
limit: number;
|
|
12
|
+
offset: number;
|
|
13
|
+
has_more: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface Meta {
|
|
16
|
+
request_id: string;
|
|
17
|
+
source?: SourceId;
|
|
18
|
+
/** Where the data came from: our cache, the stored catalogue, a live scrape, or the source registry. */
|
|
19
|
+
origin?: "cache" | "store" | "live" | "registry";
|
|
20
|
+
cached?: boolean;
|
|
21
|
+
took_ms: number;
|
|
22
|
+
/** This exact request in your dashboard log. */
|
|
23
|
+
log_url?: string;
|
|
24
|
+
/** Non-fatal degradation, e.g. `category_names_unavailable`. The response is still a 200. */
|
|
25
|
+
warnings?: string[];
|
|
26
|
+
/** With `only_new: true`: how many products were dropped as already seen this period. */
|
|
27
|
+
filtered_seen?: number;
|
|
28
|
+
pagination?: Pagination;
|
|
29
|
+
}
|
|
30
|
+
/** What every method resolves to: the gateway's success envelope, unchanged. */
|
|
31
|
+
interface Response<T> {
|
|
32
|
+
data: T;
|
|
33
|
+
meta: Meta;
|
|
34
|
+
}
|
|
35
|
+
interface Money {
|
|
36
|
+
/** On a multi-variant listing this is the cheapest variant; read it with `current_max`. */
|
|
37
|
+
current: number | null;
|
|
38
|
+
/** Top of the price range on a multi-variant listing. */
|
|
39
|
+
current_max?: number;
|
|
40
|
+
original: number | null;
|
|
41
|
+
/** ISO 4217 code as reported by the source. */
|
|
42
|
+
currency: string | null;
|
|
43
|
+
discount_percent: number | null;
|
|
44
|
+
on_sale: boolean;
|
|
45
|
+
}
|
|
46
|
+
interface CategoryRef {
|
|
47
|
+
id: number | null;
|
|
48
|
+
/** Null when the category catalogue is unavailable; see `meta.warnings`. */
|
|
49
|
+
name: string | null;
|
|
50
|
+
url: string | null;
|
|
51
|
+
}
|
|
52
|
+
interface Product {
|
|
53
|
+
/** Numeric on SHEIN/AliExpress, an ASIN string on Amazon. */
|
|
54
|
+
id: number | string | null;
|
|
55
|
+
source: SourceId;
|
|
56
|
+
url: string;
|
|
57
|
+
detail_url: string;
|
|
58
|
+
sku: string | null;
|
|
59
|
+
title: string | null;
|
|
60
|
+
brand: string | null;
|
|
61
|
+
description: string | null;
|
|
62
|
+
images: string[];
|
|
63
|
+
price: Money;
|
|
64
|
+
category: CategoryRef;
|
|
65
|
+
variants: {
|
|
66
|
+
colors: string[];
|
|
67
|
+
sizes: string[];
|
|
68
|
+
};
|
|
69
|
+
availability: "in_stock" | "out_of_stock" | "unknown";
|
|
70
|
+
availability_raw: string | null;
|
|
71
|
+
rating: {
|
|
72
|
+
value: number | null;
|
|
73
|
+
count: number | null;
|
|
74
|
+
};
|
|
75
|
+
/** ISO-8601 UTC instant this data was collected. */
|
|
76
|
+
scraped_at: string;
|
|
77
|
+
}
|
|
78
|
+
interface SearchHit {
|
|
79
|
+
product_id: number | string | null;
|
|
80
|
+
url: string;
|
|
81
|
+
/** Null when the id could not be determined. */
|
|
82
|
+
detail_url: string | null;
|
|
83
|
+
title: string | null;
|
|
84
|
+
image: string | null;
|
|
85
|
+
price: number | null;
|
|
86
|
+
original_price: number | null;
|
|
87
|
+
discount_percent: number | null;
|
|
88
|
+
currency: string | null;
|
|
89
|
+
rating: number | null;
|
|
90
|
+
}
|
|
91
|
+
interface Category {
|
|
92
|
+
/** On SHEIN, usable as `products.list({ category_id })`. */
|
|
93
|
+
id: number | null;
|
|
94
|
+
source_category_id: number | string | null;
|
|
95
|
+
name: string | null;
|
|
96
|
+
url: string;
|
|
97
|
+
/** Stable across sources, e.g. `Shoes/Boots`. */
|
|
98
|
+
path: string;
|
|
99
|
+
parent_path: string | null;
|
|
100
|
+
level: number;
|
|
101
|
+
/** Search term that lists this category, when it has no id-addressable listing. */
|
|
102
|
+
query: string | null;
|
|
103
|
+
first_seen_at: string | null;
|
|
104
|
+
last_seen_at: string | null;
|
|
105
|
+
}
|
|
106
|
+
interface Source {
|
|
107
|
+
id: string;
|
|
108
|
+
name: string;
|
|
109
|
+
capabilities: {
|
|
110
|
+
live_search: boolean;
|
|
111
|
+
live_product_fetch: boolean;
|
|
112
|
+
product_id_filter: boolean;
|
|
113
|
+
/** Filters `products.list` accepts for this source. */
|
|
114
|
+
filters: string[];
|
|
115
|
+
max_search_pages: number;
|
|
116
|
+
max_limit: number;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
interface Me {
|
|
120
|
+
client_id: string;
|
|
121
|
+
token_id: string;
|
|
122
|
+
token_name: string;
|
|
123
|
+
environment: "live" | "test";
|
|
124
|
+
plan: string;
|
|
125
|
+
scopes: string[];
|
|
126
|
+
quota: {
|
|
127
|
+
/** Billing period, YYYYMM (UTC). */
|
|
128
|
+
period: string;
|
|
129
|
+
limit: number;
|
|
130
|
+
used: number;
|
|
131
|
+
remaining: number;
|
|
132
|
+
resets_at: string;
|
|
133
|
+
topup_balance: number;
|
|
134
|
+
};
|
|
135
|
+
rate_limits: {
|
|
136
|
+
cheap_per_minute: number;
|
|
137
|
+
expensive_per_minute: number;
|
|
138
|
+
expensive_per_day: number;
|
|
139
|
+
expensive_concurrent: number;
|
|
140
|
+
};
|
|
141
|
+
/** Units charged per call type. */
|
|
142
|
+
quota_costs: Record<string, number>;
|
|
143
|
+
}
|
|
144
|
+
interface SourceParam {
|
|
145
|
+
/** Overrides the client's default source for this call. */
|
|
146
|
+
source?: SourceId;
|
|
147
|
+
}
|
|
148
|
+
interface PageParams {
|
|
149
|
+
/** 1–1000, default 50. */
|
|
150
|
+
limit?: number;
|
|
151
|
+
offset?: number;
|
|
152
|
+
}
|
|
153
|
+
interface SearchParams extends SourceParam {
|
|
154
|
+
q: string;
|
|
155
|
+
/** 1–5 result pages, roughly 120 products each. Default 1. */
|
|
156
|
+
pages?: number;
|
|
157
|
+
/** Bypass the 20-day result cache. Costs more quota. */
|
|
158
|
+
refresh?: boolean;
|
|
159
|
+
}
|
|
160
|
+
interface GetProductParams extends SourceParam {
|
|
161
|
+
/** Force a live re-scrape (20–90s, needs `search:live`, costs more quota). */
|
|
162
|
+
refresh?: boolean;
|
|
163
|
+
}
|
|
164
|
+
interface ListProductsParams extends SourceParam, PageParams {
|
|
165
|
+
category_id?: number;
|
|
166
|
+
sku?: string;
|
|
167
|
+
q?: string;
|
|
168
|
+
brand?: string;
|
|
169
|
+
min_price?: number;
|
|
170
|
+
max_price?: number;
|
|
171
|
+
color?: string;
|
|
172
|
+
size?: string;
|
|
173
|
+
on_sale?: boolean;
|
|
174
|
+
/** Drop products this key has already been served this billing period. */
|
|
175
|
+
only_new?: boolean;
|
|
176
|
+
}
|
|
177
|
+
interface ListCategoriesParams extends SourceParam, PageParams {
|
|
178
|
+
q?: string;
|
|
179
|
+
level?: number;
|
|
180
|
+
parent_path?: string;
|
|
181
|
+
}
|
|
182
|
+
/** Per-call overrides. */
|
|
183
|
+
interface RequestOptions {
|
|
184
|
+
signal?: AbortSignal;
|
|
185
|
+
timeoutMs?: number;
|
|
186
|
+
maxRetries?: number;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
type FetchPage<T> = (offset: number | undefined) => Promise<Response<T[]>>;
|
|
190
|
+
/**
|
|
191
|
+
* A single page you can `await`, or every item you can `for await`.
|
|
192
|
+
*
|
|
193
|
+
* `await list(...)` makes exactly one request. Iterating makes as many as
|
|
194
|
+
* `meta.pagination.has_more` calls for, starting from the offset you passed.
|
|
195
|
+
*/
|
|
196
|
+
declare class PagePromise<T> implements PromiseLike<Response<T[]>>, AsyncIterable<T> {
|
|
197
|
+
#private;
|
|
198
|
+
constructor(fetchPage: FetchPage<T>, startOffset: number | undefined);
|
|
199
|
+
then<R1 = Response<T[]>, R2 = never>(onfulfilled?: ((value: Response<T[]>) => R1 | PromiseLike<R1>) | null, onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | null): Promise<R1 | R2>;
|
|
200
|
+
catch<R = never>(onrejected?: ((reason: unknown) => R | PromiseLike<R>) | null): Promise<Response<T[]> | R>;
|
|
201
|
+
finally(onfinally?: (() => void) | null): Promise<Response<T[]>>;
|
|
202
|
+
/** Every page, in order. */
|
|
203
|
+
pages(): AsyncGenerator<Response<T[]>, void, undefined>;
|
|
204
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
declare const DEFAULT_BASE_URL = "https://api.rascador.store";
|
|
208
|
+
interface RascadorOptions {
|
|
209
|
+
/** Defaults to the `RASCADOR_API_KEY` environment variable where one exists. */
|
|
210
|
+
apiKey?: string;
|
|
211
|
+
/** Defaults to the production gateway. */
|
|
212
|
+
baseUrl?: string;
|
|
213
|
+
/** Source used when a call doesn't name one. The gateway's own default is `shein`. */
|
|
214
|
+
source?: SourceId;
|
|
215
|
+
/** Timeout for stored-data calls. Default 30s. */
|
|
216
|
+
timeoutMs?: number;
|
|
217
|
+
/** Timeout for live calls (`search`, `products.get` with `refresh`). Default 130s. */
|
|
218
|
+
liveTimeoutMs?: number;
|
|
219
|
+
/** Retries for errors the gateway marks retryable. Default 2. */
|
|
220
|
+
maxRetries?: number;
|
|
221
|
+
/** Custom fetch, e.g. for a proxy or tests. Defaults to the global `fetch`. */
|
|
222
|
+
fetch?: typeof fetch;
|
|
223
|
+
/** Extra headers sent with every request. */
|
|
224
|
+
headers?: Record<string, string>;
|
|
225
|
+
}
|
|
226
|
+
type Query = Record<string, string | number | boolean | undefined>;
|
|
227
|
+
interface CallSpec {
|
|
228
|
+
path: string;
|
|
229
|
+
query?: Query;
|
|
230
|
+
live?: boolean;
|
|
231
|
+
options?: RequestOptions;
|
|
232
|
+
}
|
|
233
|
+
declare class Rascador {
|
|
234
|
+
#private;
|
|
235
|
+
readonly products: ProductsResource;
|
|
236
|
+
readonly categories: CategoriesResource;
|
|
237
|
+
readonly sources: SourcesResource;
|
|
238
|
+
constructor(options?: RascadorOptions);
|
|
239
|
+
/** The key's scopes, limits and remaining quota. Never spends quota. */
|
|
240
|
+
me(options?: RequestOptions): Promise<Response<Me>>;
|
|
241
|
+
/**
|
|
242
|
+
* Live search on the source site. Slow (20–40s) and quota-heavy.
|
|
243
|
+
* Returns result cards; call `products.get` for full details.
|
|
244
|
+
*/
|
|
245
|
+
search(params: SearchParams, options?: RequestOptions): Promise<Response<SearchHit[]>>;
|
|
246
|
+
/** @internal */
|
|
247
|
+
_source(source: SourceId | undefined): SourceId | undefined;
|
|
248
|
+
/** @internal */
|
|
249
|
+
_get<T>(spec: CallSpec): Promise<Response<T>>;
|
|
250
|
+
/** The SDK version. Include it in bug reports. */
|
|
251
|
+
static readonly VERSION: string;
|
|
252
|
+
}
|
|
253
|
+
declare class ProductsResource {
|
|
254
|
+
#private;
|
|
255
|
+
constructor(client: Rascador);
|
|
256
|
+
/**
|
|
257
|
+
* One product. Instant when stored; otherwise (or with `refresh: true`) a
|
|
258
|
+
* live fetch, which needs the `search:live` scope.
|
|
259
|
+
*/
|
|
260
|
+
get(id: string | number, params?: GetProductParams, options?: RequestOptions): Promise<Response<Product>>;
|
|
261
|
+
/** Stored products. `await` for one page, `for await` for every product. */
|
|
262
|
+
list(params?: ListProductsParams, options?: RequestOptions): PagePromise<Product>;
|
|
263
|
+
}
|
|
264
|
+
declare class CategoriesResource {
|
|
265
|
+
#private;
|
|
266
|
+
constructor(client: Rascador);
|
|
267
|
+
/** The source's category tree. `await` for one page, `for await` for all. */
|
|
268
|
+
list(params?: ListCategoriesParams, options?: RequestOptions): PagePromise<Category>;
|
|
269
|
+
}
|
|
270
|
+
declare class SourcesResource {
|
|
271
|
+
#private;
|
|
272
|
+
constructor(client: Rascador);
|
|
273
|
+
/** Every source and what it supports. Never spends quota. */
|
|
274
|
+
list(options?: RequestOptions): Promise<Response<Source[]>>;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Codes the gateway can return, plus three the SDK raises itself:
|
|
279
|
+
* `network_error`, `timeout` and `invalid_response`.
|
|
280
|
+
*
|
|
281
|
+
* The `(string & {})` keeps autocomplete for the known codes while still
|
|
282
|
+
* accepting any code a newer gateway adds.
|
|
283
|
+
*/
|
|
284
|
+
type RascadorErrorCode = "invalid_request" | "unknown_source" | "missing_credentials" | "invalid_token" | "quota_exceeded" | "coverage_exceeded" | "payment_required" | "insufficient_scope" | "client_suspended" | "not_found" | "product_not_found" | "rate_limited" | "platform_busy" | "internal_error" | "gateway_misconfigured" | "upstream_error" | "upstream_unavailable" | "upstream_busy" | "upstream_timeout" | "network_error" | "timeout" | "invalid_response" | (string & {});
|
|
285
|
+
interface RascadorErrorInit {
|
|
286
|
+
code: RascadorErrorCode;
|
|
287
|
+
message: string;
|
|
288
|
+
status: number;
|
|
289
|
+
retryable: boolean;
|
|
290
|
+
retryAfterSeconds?: number;
|
|
291
|
+
requestId?: string;
|
|
292
|
+
logUrl?: string;
|
|
293
|
+
details?: Record<string, unknown>;
|
|
294
|
+
cause?: unknown;
|
|
295
|
+
}
|
|
296
|
+
/** Thrown for every failed call. Branch on `code`, not on `message`. */
|
|
297
|
+
declare class RascadorError extends Error {
|
|
298
|
+
readonly name = "RascadorError";
|
|
299
|
+
readonly code: RascadorErrorCode;
|
|
300
|
+
/** HTTP status, or 0 when no response arrived (network failure, timeout). */
|
|
301
|
+
readonly status: number;
|
|
302
|
+
readonly retryable: boolean;
|
|
303
|
+
readonly retryAfterSeconds: number | undefined;
|
|
304
|
+
/** Quote this when contacting support. */
|
|
305
|
+
readonly requestId: string | undefined;
|
|
306
|
+
/** This failure in your dashboard log. */
|
|
307
|
+
readonly logUrl: string | undefined;
|
|
308
|
+
readonly details: Record<string, unknown> | undefined;
|
|
309
|
+
constructor(init: RascadorErrorInit);
|
|
310
|
+
}
|
|
311
|
+
declare function isRascadorError(error: unknown): error is RascadorError;
|
|
312
|
+
|
|
313
|
+
declare const VERSION = "0.1.0";
|
|
314
|
+
|
|
315
|
+
export { CategoriesResource, type Category, type CategoryRef, DEFAULT_BASE_URL, type GetProductParams, type ListCategoriesParams, type ListProductsParams, type Me, type Meta, type Money, type PageParams, PagePromise, type Pagination, type Product, ProductsResource, Rascador, RascadorError, type RascadorErrorCode, type RascadorErrorInit, type RascadorOptions, type RequestOptions, type Response, type SearchHit, type SearchParams, type Source, type SourceId, type SourceParam, SourcesResource, VERSION, isRascadorError };
|