tcgpriser 0.7.0 → 0.13.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 +240 -51
- package/dist/index.cjs +327 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +456 -108
- package/dist/index.d.ts +456 -108
- package/dist/index.js +327 -88
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,8 +7,21 @@ var TcgPriserError = class extends Error {
|
|
|
7
7
|
details;
|
|
8
8
|
/** The raw response body, for debugging when `code`/`details` don't cover what you need. */
|
|
9
9
|
body;
|
|
10
|
+
/**
|
|
11
|
+
* Seconds to wait before retrying, from the `Retry-After` header. Present on `rateLimited`, and
|
|
12
|
+
* on anything else a proxy in front of the API decides to send it with. Absent otherwise — an
|
|
13
|
+
* error without it is not one that says retrying will help.
|
|
14
|
+
*/
|
|
15
|
+
retryAfter;
|
|
16
|
+
/**
|
|
17
|
+
* Credits left in this week's allowance, from `X-Credits-Remaining`. Present on errors from
|
|
18
|
+
* charged routes — notably `creditsExhausted`, where it is `0`. Absent on uncharged routes and on
|
|
19
|
+
* anything a proxy answered instead of the API.
|
|
20
|
+
*/
|
|
21
|
+
creditsRemaining;
|
|
10
22
|
constructor(params) {
|
|
11
|
-
|
|
23
|
+
const status = params.statusCode === 0 ? "" : `${params.statusCode} `;
|
|
24
|
+
super(`tcgpriser: ${status}${params.code} - ${params.message} (${params.url})`);
|
|
12
25
|
this.name = "TcgPriserError";
|
|
13
26
|
this.statusCode = params.statusCode;
|
|
14
27
|
this.statusText = params.statusText;
|
|
@@ -16,6 +29,8 @@ var TcgPriserError = class extends Error {
|
|
|
16
29
|
this.code = params.code;
|
|
17
30
|
this.details = params.details;
|
|
18
31
|
this.body = params.body;
|
|
32
|
+
this.retryAfter = params.retryAfter;
|
|
33
|
+
this.creditsRemaining = params.creditsRemaining;
|
|
19
34
|
}
|
|
20
35
|
};
|
|
21
36
|
|
|
@@ -29,9 +44,9 @@ function toQueryString(params) {
|
|
|
29
44
|
const query = search.toString();
|
|
30
45
|
return query ? `?${query}` : "";
|
|
31
46
|
}
|
|
32
|
-
function
|
|
33
|
-
const { authToken, ...rest } = params;
|
|
34
|
-
return [rest, authToken];
|
|
47
|
+
function splitRequestOptions(params) {
|
|
48
|
+
const { authToken, signal, timeoutMs, ...rest } = params;
|
|
49
|
+
return [rest, { authToken, signal, timeoutMs }];
|
|
35
50
|
}
|
|
36
51
|
var KNOWN_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
37
52
|
"validationFailed",
|
|
@@ -42,8 +57,16 @@ var KNOWN_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
42
57
|
"readOnlyField",
|
|
43
58
|
"rateLimited",
|
|
44
59
|
"premiumRequired",
|
|
60
|
+
"businessRequired",
|
|
61
|
+
"creditsExhausted",
|
|
45
62
|
"internalError"
|
|
46
63
|
]);
|
|
64
|
+
function readIntHeader(res, name) {
|
|
65
|
+
const raw = res.headers.get(name);
|
|
66
|
+
if (raw === null) return void 0;
|
|
67
|
+
const parsed = Number(raw);
|
|
68
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
69
|
+
}
|
|
47
70
|
async function toApiError(res, url) {
|
|
48
71
|
const body = await res.text();
|
|
49
72
|
let code = "unknown";
|
|
@@ -58,18 +81,63 @@ async function toApiError(res, url) {
|
|
|
58
81
|
details = parsed.error?.details;
|
|
59
82
|
} catch {
|
|
60
83
|
}
|
|
61
|
-
return new TcgPriserError({
|
|
84
|
+
return new TcgPriserError({
|
|
85
|
+
statusCode: res.status,
|
|
86
|
+
statusText: res.statusText,
|
|
87
|
+
url,
|
|
88
|
+
code,
|
|
89
|
+
message,
|
|
90
|
+
details,
|
|
91
|
+
body,
|
|
92
|
+
// Both are most useful on exactly the errors that carry them: `retryAfter` on `rateLimited`,
|
|
93
|
+
// `creditsRemaining` on `creditsExhausted` (where it is 0) and on any error from a charged
|
|
94
|
+
// route. Read unconditionally rather than branching on the code, since a proxy can return a
|
|
95
|
+
// 429 with `Retry-After` and no envelope at all.
|
|
96
|
+
retryAfter: readIntHeader(res, "Retry-After"),
|
|
97
|
+
creditsRemaining: readIntHeader(res, "X-Credits-Remaining")
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
101
|
+
function withTimeout(timeoutMs, callerSignal) {
|
|
102
|
+
if (timeoutMs <= 0) return { signal: callerSignal, clear: () => {
|
|
103
|
+
}, timedOut: () => false };
|
|
104
|
+
const controller = new AbortController();
|
|
105
|
+
let expired = false;
|
|
106
|
+
const timer = setTimeout(() => {
|
|
107
|
+
expired = true;
|
|
108
|
+
controller.abort();
|
|
109
|
+
}, timeoutMs);
|
|
110
|
+
const onCallerAbort = () => controller.abort();
|
|
111
|
+
if (callerSignal) {
|
|
112
|
+
if (callerSignal.aborted) controller.abort();
|
|
113
|
+
else callerSignal.addEventListener("abort", onCallerAbort, { once: true });
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
signal: controller.signal,
|
|
117
|
+
clear: () => {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
120
|
+
},
|
|
121
|
+
timedOut: () => expired
|
|
122
|
+
};
|
|
62
123
|
}
|
|
63
124
|
var HttpClient = class {
|
|
64
125
|
baseUrl;
|
|
65
126
|
fetchImpl;
|
|
66
127
|
defaultHeaders;
|
|
67
128
|
defaultAuthToken;
|
|
129
|
+
defaultTimeoutMs;
|
|
130
|
+
/**
|
|
131
|
+
* The `X-Credits-Remaining` value from the most recent charged response, or `undefined` if no
|
|
132
|
+
* charged call has been made yet. See `TcgPriser.creditsRemaining`.
|
|
133
|
+
*/
|
|
134
|
+
creditsRemaining;
|
|
68
135
|
constructor(options) {
|
|
69
136
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
70
137
|
this.fetchImpl = options.fetch;
|
|
71
138
|
this.defaultHeaders = options.headers ?? {};
|
|
72
139
|
this.defaultAuthToken = options.authToken;
|
|
140
|
+
this.defaultTimeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
73
141
|
}
|
|
74
142
|
get(path, requestOptions) {
|
|
75
143
|
return this.request("GET", path, void 0, requestOptions);
|
|
@@ -80,17 +148,42 @@ var HttpClient = class {
|
|
|
80
148
|
patch(path, body, requestOptions) {
|
|
81
149
|
return this.request("PATCH", path, body, requestOptions);
|
|
82
150
|
}
|
|
151
|
+
delete(path, requestOptions) {
|
|
152
|
+
return this.request("DELETE", path, void 0, requestOptions);
|
|
153
|
+
}
|
|
83
154
|
async request(method, path, body, requestOptions) {
|
|
84
155
|
const url = `${this.baseUrl}${path}`;
|
|
85
156
|
const authToken = requestOptions && "authToken" in requestOptions ? requestOptions.authToken : this.defaultAuthToken;
|
|
86
157
|
const headers = { Accept: "application/json", ...this.defaultHeaders };
|
|
87
158
|
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
88
159
|
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
160
|
+
const timeoutMs = requestOptions?.timeoutMs ?? this.defaultTimeoutMs;
|
|
161
|
+
const timeout = withTimeout(timeoutMs, requestOptions?.signal);
|
|
162
|
+
let res;
|
|
163
|
+
try {
|
|
164
|
+
res = await this.fetchImpl(url, {
|
|
165
|
+
method,
|
|
166
|
+
headers,
|
|
167
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
168
|
+
signal: timeout.signal
|
|
169
|
+
});
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (timeout.timedOut()) {
|
|
172
|
+
throw new TcgPriserError({
|
|
173
|
+
statusCode: 0,
|
|
174
|
+
statusText: "Timeout",
|
|
175
|
+
url,
|
|
176
|
+
code: "timeout",
|
|
177
|
+
message: `Request timed out after ${timeoutMs}ms`,
|
|
178
|
+
body: ""
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
throw error;
|
|
182
|
+
} finally {
|
|
183
|
+
timeout.clear();
|
|
184
|
+
}
|
|
185
|
+
const credits = readIntHeader(res, "X-Credits-Remaining");
|
|
186
|
+
if (credits !== void 0) this.creditsRemaining = credits;
|
|
94
187
|
if (!res.ok) throw await toApiError(res, url);
|
|
95
188
|
if (res.status === 204) return void 0;
|
|
96
189
|
return nullsToUndefined(await res.json());
|
|
@@ -117,13 +210,14 @@ var BargainsResource = class {
|
|
|
117
210
|
* count is fixed by the API (no `limit`/`skip` on the public tier); `pagination.hasMore` tells
|
|
118
211
|
* you if more exist. */
|
|
119
212
|
list(params = {}) {
|
|
120
|
-
|
|
213
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
214
|
+
return this.http.get(`/bargains${toQueryString(query)}`, requestOptions);
|
|
121
215
|
}
|
|
122
216
|
/** `GET /bargains/search`: like `list()`, but with real pagination and filters (shop, discount
|
|
123
217
|
* threshold, card condition/grade, free-text search). Premium. */
|
|
124
218
|
search(params = {}) {
|
|
125
|
-
const [query,
|
|
126
|
-
return this.http.get(`/bargains/search${toQueryString(query)}`,
|
|
219
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
220
|
+
return this.http.get(`/bargains/search${toQueryString(query)}`, requestOptions);
|
|
127
221
|
}
|
|
128
222
|
};
|
|
129
223
|
|
|
@@ -133,34 +227,40 @@ var CardsResource = class {
|
|
|
133
227
|
this.http = http;
|
|
134
228
|
}
|
|
135
229
|
http;
|
|
136
|
-
/** `GET /cards`: search
|
|
230
|
+
/** `GET /cards`: list cards, newest first. No free-text search — use `search()` for that. */
|
|
137
231
|
list(params = {}) {
|
|
138
|
-
|
|
232
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
233
|
+
return this.http.get(`/cards${toQueryString(query)}`, requestOptions);
|
|
234
|
+
}
|
|
235
|
+
/** `GET /cards/search`: like `list()`, but with free-text search on card and set names. Premium. */
|
|
236
|
+
search(params = {}) {
|
|
237
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
238
|
+
return this.http.get(`/cards/search${toQueryString(query)}`, requestOptions);
|
|
139
239
|
}
|
|
140
240
|
/** `GET /cards/{id}`: fetch one card by its id or technicalName. */
|
|
141
|
-
get(idOrTechnicalName) {
|
|
142
|
-
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}
|
|
241
|
+
get(idOrTechnicalName, options = {}) {
|
|
242
|
+
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}`, options);
|
|
143
243
|
}
|
|
144
244
|
/** `GET /cards/{id}/matches`: current shop listings matched to this card (latest per shop). */
|
|
145
245
|
matches(idOrTechnicalName, params = {}) {
|
|
246
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
146
247
|
return this.http.get(
|
|
147
|
-
`/cards/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(
|
|
248
|
+
`/cards/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(query)}`,
|
|
249
|
+
requestOptions
|
|
148
250
|
);
|
|
149
251
|
}
|
|
150
252
|
/** `GET /cards/{id}/reference-prices`: Cardmarket/TCGplayer/eBay/Tradera price history. Premium. */
|
|
151
253
|
referencePrices(idOrTechnicalName, params = {}) {
|
|
152
|
-
const [query,
|
|
254
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
153
255
|
return this.http.get(
|
|
154
256
|
`/cards/${encodeURIComponent(idOrTechnicalName)}/reference-prices${toQueryString(query)}`,
|
|
155
|
-
|
|
257
|
+
requestOptions
|
|
156
258
|
);
|
|
157
259
|
}
|
|
158
260
|
/** `GET /cards/{id}/prices`: individual marketplace sale records. Premium. */
|
|
159
261
|
prices(idOrTechnicalName, params = {}) {
|
|
160
|
-
const [query,
|
|
161
|
-
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`,
|
|
162
|
-
authToken
|
|
163
|
-
});
|
|
262
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
263
|
+
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`, requestOptions);
|
|
164
264
|
}
|
|
165
265
|
/** `GET /cards/{id}/pricing/live`: computed fresh for this request, not read from the last
|
|
166
266
|
* stats job. Premium. */
|
|
@@ -171,16 +271,39 @@ var CardsResource = class {
|
|
|
171
271
|
* `estimatedValue`, `lowestShopOffer`, `referencePriceSnapshotsByProvider` — refreshed once a day
|
|
172
272
|
* by the nightly pricing/scraper jobs. `get()` returns content only; this is the separate,
|
|
173
273
|
* shorter-cached call for the part of a card that actually changes day to day. */
|
|
174
|
-
pricing(idOrTechnicalName) {
|
|
175
|
-
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/pricing
|
|
274
|
+
pricing(idOrTechnicalName, options = {}) {
|
|
275
|
+
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/pricing`, options);
|
|
176
276
|
}
|
|
177
277
|
/** `GET /cards/pricing`: pricing for up to 200 cards in one request, keyed by `id` — the batch
|
|
178
278
|
* counterpart to `pricing()`, for a page of results (a search page, an expansion's contents) that
|
|
179
279
|
* needs pricing for many items at once. Unlike `get()`/`pricing()`, this only accepts `id`s, not
|
|
180
280
|
* technicalNames — pass the `id`s already on the cards you fetched. Ids with no match are
|
|
181
281
|
* silently omitted from the result rather than causing an error. */
|
|
182
|
-
pricingBatch(ids) {
|
|
183
|
-
return this.http.get(`/cards/pricing?ids=${ids.map(encodeURIComponent).join(",")}
|
|
282
|
+
pricingBatch(ids, options = {}) {
|
|
283
|
+
return this.http.get(`/cards/pricing?ids=${ids.map(encodeURIComponent).join(",")}`, options);
|
|
284
|
+
}
|
|
285
|
+
/** `GET /cards/technical-names`: every card's `technicalName` and `updatedAt`, unpaginated and
|
|
286
|
+
* with no pricing joins. Built for enumerating the whole catalog cheaply — a sitemap, or working
|
|
287
|
+
* out which items changed since your last sync — where `list()` would make you page through full
|
|
288
|
+
* card documents to learn the same two fields. */
|
|
289
|
+
technicalNames(options = {}) {
|
|
290
|
+
return this.http.get("/cards/technical-names", options);
|
|
291
|
+
}
|
|
292
|
+
/** `GET /cards/price-stats/daily`: daily average price history, cards only. The same data as
|
|
293
|
+
* `client.priceStats.daily()`, scoped to the card catalog so a filter like `expansion` can't pull
|
|
294
|
+
* in that expansion's sealed products too. */
|
|
295
|
+
dailyStats(params = {}) {
|
|
296
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
297
|
+
return this.http.get(`/cards/price-stats/daily${toQueryString(query)}`, requestOptions);
|
|
298
|
+
}
|
|
299
|
+
/** `GET /cards/price-stats/estimated-values`: current estimated market value, cards only. The
|
|
300
|
+
* card-scoped counterpart to `client.priceStats.estimatedValues()`. */
|
|
301
|
+
estimatedValues(params = {}) {
|
|
302
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
303
|
+
return this.http.get(
|
|
304
|
+
`/cards/price-stats/estimated-values${toQueryString(query)}`,
|
|
305
|
+
requestOptions
|
|
306
|
+
);
|
|
184
307
|
}
|
|
185
308
|
};
|
|
186
309
|
|
|
@@ -191,21 +314,42 @@ var ExpansionsResource = class {
|
|
|
191
314
|
}
|
|
192
315
|
http;
|
|
193
316
|
/** `GET /expansions`: every expansion. Unwrapped to a plain array, nothing to paginate here. */
|
|
194
|
-
async list() {
|
|
195
|
-
const res = await this.http.get("/expansions");
|
|
317
|
+
async list(options = {}) {
|
|
318
|
+
const res = await this.http.get("/expansions", options);
|
|
196
319
|
return res.data;
|
|
197
320
|
}
|
|
198
|
-
/** `GET /expansions/{technicalName}
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
*
|
|
208
|
-
|
|
321
|
+
/** `GET /expansions/{technicalName}`: metadata only — no cards or sealed products. Returns the
|
|
322
|
+
* smaller `ExpansionRef`, not the full `Expansion`: this is a plain lookup by technicalName, not
|
|
323
|
+
* the aggregation `list()` runs, so `sealedCount`/`cardCount`/`productCount` aren't available
|
|
324
|
+
* here. See `cards()` and `sealedProducts()` for this expansion's contents. */
|
|
325
|
+
get(technicalName, options = {}) {
|
|
326
|
+
return this.http.get(`/expansions/${encodeURIComponent(technicalName)}`, options);
|
|
327
|
+
}
|
|
328
|
+
/** `GET /expansions/{technicalName}/cards`: every card in this expansion. Content only, no
|
|
329
|
+
* pricing fields — pass the `id`s from the result to `client.cards.pricingBatch()` if you need
|
|
330
|
+
* pricing too. Sealed products are a separate call — see `sealedProducts()` — never merged into
|
|
331
|
+
* this one. */
|
|
332
|
+
cards(technicalName, options = {}) {
|
|
333
|
+
return this.http.get(`/expansions/${encodeURIComponent(technicalName)}/cards`, options);
|
|
334
|
+
}
|
|
335
|
+
/** `GET /expansions/{technicalName}/products`: every sealed product in this expansion. Content
|
|
336
|
+
* only, no pricing fields — pass the `id`s from the result to `client.products.pricingBatch()`
|
|
337
|
+
* if you need pricing too. Cards are a separate call — see `cards()` — never merged into this
|
|
338
|
+
* one. */
|
|
339
|
+
sealedProducts(technicalName, options = {}) {
|
|
340
|
+
return this.http.get(`/expansions/${encodeURIComponent(technicalName)}/products`, options);
|
|
341
|
+
}
|
|
342
|
+
/** `GET /expansions/{technicalName}/cards/live-pricing`: computed fresh for every card in this
|
|
343
|
+
* expansion, not read from the last stats job. Premium. */
|
|
344
|
+
cardsLivePricing(technicalName, options = {}) {
|
|
345
|
+
return this.http.get(
|
|
346
|
+
`/expansions/${encodeURIComponent(technicalName)}/cards/live-pricing`,
|
|
347
|
+
options
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
/** `GET /expansions/{technicalName}/products/live-pricing`: computed fresh for every sealed
|
|
351
|
+
* product in this expansion, not read from the last stats job. Premium. */
|
|
352
|
+
productsLivePricing(technicalName, options = {}) {
|
|
209
353
|
return this.http.get(
|
|
210
354
|
`/expansions/${encodeURIComponent(technicalName)}/products/live-pricing`,
|
|
211
355
|
options
|
|
@@ -221,13 +365,13 @@ var PackRatesResource = class {
|
|
|
221
365
|
http;
|
|
222
366
|
/** `GET /pack-rates`: pull-rate odds for every expansion that has them. Unwrapped to a plain
|
|
223
367
|
* array, nothing to paginate here. */
|
|
224
|
-
async list() {
|
|
225
|
-
const res = await this.http.get("/pack-rates");
|
|
368
|
+
async list(options = {}) {
|
|
369
|
+
const res = await this.http.get("/pack-rates", options);
|
|
226
370
|
return res.data;
|
|
227
371
|
}
|
|
228
372
|
/** `GET /pack-rates/{expansionId}`: pull-rate odds for one expansion. */
|
|
229
|
-
get(expansionId) {
|
|
230
|
-
return this.http.get(`/pack-rates/${encodeURIComponent(expansionId)}
|
|
373
|
+
get(expansionId, options = {}) {
|
|
374
|
+
return this.http.get(`/pack-rates/${encodeURIComponent(expansionId)}`, options);
|
|
231
375
|
}
|
|
232
376
|
};
|
|
233
377
|
|
|
@@ -239,17 +383,20 @@ var PriceStatsResource = class {
|
|
|
239
383
|
http;
|
|
240
384
|
/** `GET /price-stats/daily`: daily average price history, filtered to matching product(s). */
|
|
241
385
|
daily(params = {}) {
|
|
242
|
-
|
|
386
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
387
|
+
return this.http.get(`/price-stats/daily${toQueryString(query)}`, requestOptions);
|
|
243
388
|
}
|
|
244
389
|
/** `GET /price-stats/estimated-values`: current estimated market value, filtered to matching
|
|
245
390
|
* product(s). */
|
|
246
391
|
estimatedValues(params = {}) {
|
|
247
|
-
|
|
392
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
393
|
+
return this.http.get(`/price-stats/estimated-values${toQueryString(query)}`, requestOptions);
|
|
248
394
|
}
|
|
249
395
|
/** `GET /price-stats/top-products`: items ranked by shop availability (how many shops carry
|
|
250
396
|
* them), not by price. */
|
|
251
397
|
topProducts(params = {}) {
|
|
252
|
-
|
|
398
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
399
|
+
return this.http.get(`/price-stats/top-products${toQueryString(query)}`, requestOptions);
|
|
253
400
|
}
|
|
254
401
|
/** `GET /price-stats/product/{id}`: daily price history, current estimate, and a variant-count
|
|
255
402
|
* summary for one product. Premium. */
|
|
@@ -264,10 +411,10 @@ var PriceStatsResource = class {
|
|
|
264
411
|
/** `GET /price-stats/product/{id}/daily`: daily price history for one product, with a
|
|
265
412
|
* caller-chosen window. Premium. */
|
|
266
413
|
productDaily(idOrTechnicalName, params = {}) {
|
|
267
|
-
const [query,
|
|
414
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
268
415
|
return this.http.get(
|
|
269
416
|
`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily${toQueryString(query)}`,
|
|
270
|
-
|
|
417
|
+
requestOptions
|
|
271
418
|
);
|
|
272
419
|
}
|
|
273
420
|
/** `GET /price-stats/product/{id}/daily-last-30`: daily price history for the last 30 days
|
|
@@ -288,20 +435,20 @@ var PriceStatsResource = class {
|
|
|
288
435
|
/** `GET /price-stats/product/{id}/by-variant`: price stats broken out per card condition/grade.
|
|
289
436
|
* Premium. */
|
|
290
437
|
productByVariant(idOrTechnicalName, params = {}) {
|
|
291
|
-
const [query,
|
|
438
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
292
439
|
return this.http.get(
|
|
293
440
|
`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/by-variant${toQueryString(query)}`,
|
|
294
|
-
|
|
441
|
+
requestOptions
|
|
295
442
|
);
|
|
296
443
|
}
|
|
297
444
|
/** `GET /price-stats/product/{id}/daily-by-variant`: daily price history for one specific
|
|
298
445
|
* condition/grade. `condition` is required for `cardType: 'loose'`; `gradingCompany` and `grade`
|
|
299
446
|
* are required for `cardType: 'graded'`. Premium. */
|
|
300
447
|
productDailyByVariant(idOrTechnicalName, params) {
|
|
301
|
-
const [query,
|
|
448
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
302
449
|
return this.http.get(
|
|
303
450
|
`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily-by-variant${toQueryString(query)}`,
|
|
304
|
-
|
|
451
|
+
requestOptions
|
|
305
452
|
);
|
|
306
453
|
}
|
|
307
454
|
};
|
|
@@ -312,34 +459,43 @@ var ProductsResource = class {
|
|
|
312
459
|
this.http = http;
|
|
313
460
|
}
|
|
314
461
|
http;
|
|
315
|
-
/** `GET /product`:
|
|
462
|
+
/** `GET /product`: list sealed products, newest first. No free-text search — use `search()` for
|
|
463
|
+
* that. */
|
|
316
464
|
list(params = {}) {
|
|
317
|
-
|
|
465
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
466
|
+
return this.http.get(`/product${toQueryString(query)}`, requestOptions);
|
|
467
|
+
}
|
|
468
|
+
/** `GET /product/search`: like `list()`, but with free-text search on the product name. Premium. */
|
|
469
|
+
search(params = {}) {
|
|
470
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
471
|
+
return this.http.get(`/product/search${toQueryString(query)}`, requestOptions);
|
|
318
472
|
}
|
|
319
473
|
/** `GET /product/{id}`: fetch one sealed product by its id or technicalName. */
|
|
320
|
-
get(idOrTechnicalName) {
|
|
321
|
-
return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}
|
|
474
|
+
get(idOrTechnicalName, options = {}) {
|
|
475
|
+
return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}`, options);
|
|
322
476
|
}
|
|
323
477
|
/** `GET /product/{id}/matches`: current shop listings matched to this product (latest per shop). */
|
|
324
478
|
matches(idOrTechnicalName, params = {}) {
|
|
479
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
325
480
|
return this.http.get(
|
|
326
|
-
`/product/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(
|
|
481
|
+
`/product/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(query)}`,
|
|
482
|
+
requestOptions
|
|
327
483
|
);
|
|
328
484
|
}
|
|
329
485
|
/** `GET /product/{id}/reference-prices`: Cardmarket/TCGplayer/Tradera price history. Premium. */
|
|
330
486
|
referencePrices(idOrTechnicalName, params = {}) {
|
|
331
|
-
const [query,
|
|
487
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
332
488
|
return this.http.get(
|
|
333
489
|
`/product/${encodeURIComponent(idOrTechnicalName)}/reference-prices${toQueryString(query)}`,
|
|
334
|
-
|
|
490
|
+
requestOptions
|
|
335
491
|
);
|
|
336
492
|
}
|
|
337
493
|
/** `GET /product/{id}/prices`: individual marketplace sale records. Premium. */
|
|
338
494
|
prices(idOrTechnicalName, params = {}) {
|
|
339
|
-
const [query,
|
|
495
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
340
496
|
return this.http.get(
|
|
341
497
|
`/product/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`,
|
|
342
|
-
|
|
498
|
+
requestOptions
|
|
343
499
|
);
|
|
344
500
|
}
|
|
345
501
|
/** `GET /product/{id}/pricing/live`: computed fresh for this request, not read from the last
|
|
@@ -351,16 +507,38 @@ var ProductsResource = class {
|
|
|
351
507
|
* `estimatedValue`, `lowestShopOffer`, `referencePriceSnapshotsByProvider` — refreshed once a day
|
|
352
508
|
* by the nightly pricing/scraper jobs. `get()` returns content only; this is the separate,
|
|
353
509
|
* shorter-cached call for the part of a product that actually changes day to day. */
|
|
354
|
-
pricing(idOrTechnicalName) {
|
|
355
|
-
return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}/pricing
|
|
510
|
+
pricing(idOrTechnicalName, options = {}) {
|
|
511
|
+
return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}/pricing`, options);
|
|
356
512
|
}
|
|
357
513
|
/** `GET /product/pricing`: pricing for up to 200 sealed products in one request, keyed by `id` —
|
|
358
514
|
* the batch counterpart to `pricing()`, for a page of results (a search page, an expansion's
|
|
359
515
|
* contents) that needs pricing for many items at once. Unlike `get()`/`pricing()`, this only
|
|
360
516
|
* accepts `id`s, not technicalNames — pass the `id`s already on the products you fetched. Ids with
|
|
361
517
|
* no match are silently omitted from the result rather than causing an error. */
|
|
362
|
-
pricingBatch(ids) {
|
|
363
|
-
return this.http.get(`/product/pricing?ids=${ids.map(encodeURIComponent).join(",")}
|
|
518
|
+
pricingBatch(ids, options = {}) {
|
|
519
|
+
return this.http.get(`/product/pricing?ids=${ids.map(encodeURIComponent).join(",")}`, options);
|
|
520
|
+
}
|
|
521
|
+
/** `GET /product/technical-names`: every sealed product's `technicalName` and `updatedAt`,
|
|
522
|
+
* unpaginated and with no pricing joins. The sealed counterpart to
|
|
523
|
+
* `client.cards.technicalNames()` — for sitemaps and incremental syncs. */
|
|
524
|
+
technicalNames(options = {}) {
|
|
525
|
+
return this.http.get("/product/technical-names", options);
|
|
526
|
+
}
|
|
527
|
+
/** `GET /product/price-stats/daily`: daily average price history, sealed products only. The same
|
|
528
|
+
* data as `client.priceStats.daily()`, scoped to the sealed catalog so a filter like `expansion`
|
|
529
|
+
* can't pull in that expansion's single cards too. */
|
|
530
|
+
dailyStats(params = {}) {
|
|
531
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
532
|
+
return this.http.get(`/product/price-stats/daily${toQueryString(query)}`, requestOptions);
|
|
533
|
+
}
|
|
534
|
+
/** `GET /product/price-stats/estimated-values`: current estimated market value, sealed products
|
|
535
|
+
* only. The sealed-scoped counterpart to `client.priceStats.estimatedValues()`. */
|
|
536
|
+
estimatedValues(params = {}) {
|
|
537
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
538
|
+
return this.http.get(
|
|
539
|
+
`/product/price-stats/estimated-values${toQueryString(query)}`,
|
|
540
|
+
requestOptions
|
|
541
|
+
);
|
|
364
542
|
}
|
|
365
543
|
};
|
|
366
544
|
|
|
@@ -372,24 +550,22 @@ var ShopMatchStatsResource = class {
|
|
|
372
550
|
http;
|
|
373
551
|
/** `GET /shop-match-stats/product/{productId}`: one product's price history, broken out per shop. */
|
|
374
552
|
forProduct(productId, params = {}) {
|
|
375
|
-
const [query,
|
|
553
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
376
554
|
return this.http.get(
|
|
377
555
|
`/shop-match-stats/product/${encodeURIComponent(productId)}${toQueryString(query)}`,
|
|
378
|
-
|
|
556
|
+
requestOptions
|
|
379
557
|
);
|
|
380
558
|
}
|
|
381
559
|
/** `GET /shop-match-stats/shop/{shop}`: one shop's price history, broken out per product. */
|
|
382
560
|
forShop(shop, params = {}) {
|
|
383
|
-
const [query,
|
|
384
|
-
return this.http.get(`/shop-match-stats/shop/${encodeURIComponent(shop)}${toQueryString(query)}`,
|
|
385
|
-
authToken
|
|
386
|
-
});
|
|
561
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
562
|
+
return this.http.get(`/shop-match-stats/shop/${encodeURIComponent(shop)}${toQueryString(query)}`, requestOptions);
|
|
387
563
|
}
|
|
388
564
|
/** `GET /shop-match-stats/compare`: one product's price at every shop that carries it, as of
|
|
389
565
|
* one date (defaults to the latest). */
|
|
390
566
|
compare(params) {
|
|
391
|
-
const [query,
|
|
392
|
-
return this.http.get(`/shop-match-stats/compare${toQueryString(query)}`,
|
|
567
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
568
|
+
return this.http.get(`/shop-match-stats/compare${toQueryString(query)}`, requestOptions);
|
|
393
569
|
}
|
|
394
570
|
};
|
|
395
571
|
|
|
@@ -401,15 +577,21 @@ var ShopMatchesResource = class {
|
|
|
401
577
|
http;
|
|
402
578
|
/** `GET /shop-matches`: every current match across every shop (latest record per url+shop). */
|
|
403
579
|
list(params = {}) {
|
|
404
|
-
|
|
580
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
581
|
+
return this.http.get(`/shop-matches${toQueryString(query)}`, requestOptions);
|
|
405
582
|
}
|
|
406
583
|
/** `GET /shop-matches/{shop}`: every current match at one shop (latest record per url). */
|
|
407
584
|
forShop(technicalName, params = {}) {
|
|
408
|
-
|
|
585
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
586
|
+
return this.http.get(
|
|
587
|
+
`/shop-matches/${encodeURIComponent(technicalName)}${toQueryString(query)}`,
|
|
588
|
+
requestOptions
|
|
589
|
+
);
|
|
409
590
|
}
|
|
410
591
|
/** `GET /shop-matches/shops`: match counts per shop (based on latest records only). */
|
|
411
592
|
shopStats(params = {}) {
|
|
412
|
-
|
|
593
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
594
|
+
return this.http.get(`/shop-matches/shops${toQueryString(query)}`, requestOptions);
|
|
413
595
|
}
|
|
414
596
|
};
|
|
415
597
|
|
|
@@ -421,16 +603,16 @@ var ShopUrlsResource = class {
|
|
|
421
603
|
http;
|
|
422
604
|
/** `POST /shop-urls/submit`: submit a shop URL for scraping. */
|
|
423
605
|
submit(params) {
|
|
424
|
-
const {
|
|
425
|
-
return this.http.post("/shop-urls/submit", { url, shop },
|
|
606
|
+
const { url, shop, ...requestOptions } = params;
|
|
607
|
+
return this.http.post("/shop-urls/submit", { url, shop }, requestOptions);
|
|
426
608
|
}
|
|
427
609
|
/** `PATCH /shop-urls/{id}/product`: manually assign (or clear) the product a shop URL resolves to. */
|
|
428
610
|
assignProduct(shopUrlId, params) {
|
|
429
|
-
const {
|
|
611
|
+
const { productId, ...requestOptions } = params;
|
|
430
612
|
return this.http.patch(
|
|
431
613
|
`/shop-urls/${encodeURIComponent(shopUrlId)}/product`,
|
|
432
614
|
{ productId },
|
|
433
|
-
|
|
615
|
+
requestOptions
|
|
434
616
|
);
|
|
435
617
|
}
|
|
436
618
|
};
|
|
@@ -443,12 +625,13 @@ var ShopsResource = class {
|
|
|
443
625
|
http;
|
|
444
626
|
/** `GET /shops`: every tracked shop. Unwrapped to a plain array, nothing to paginate here. */
|
|
445
627
|
async list(params = {}) {
|
|
446
|
-
const
|
|
628
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
629
|
+
const res = await this.http.get(`/shops${toQueryString(query)}`, requestOptions);
|
|
447
630
|
return res.data;
|
|
448
631
|
}
|
|
449
632
|
/** `GET /shops/{id}`: fetch one shop by its id or technicalName. */
|
|
450
|
-
get(idOrTechnicalName) {
|
|
451
|
-
return this.http.get(`/shops/${encodeURIComponent(idOrTechnicalName)}
|
|
633
|
+
get(idOrTechnicalName, options = {}) {
|
|
634
|
+
return this.http.get(`/shops/${encodeURIComponent(idOrTechnicalName)}`, options);
|
|
452
635
|
}
|
|
453
636
|
};
|
|
454
637
|
|
|
@@ -459,8 +642,40 @@ var StatsResource = class {
|
|
|
459
642
|
}
|
|
460
643
|
http;
|
|
461
644
|
/** `GET /stats`: platform-wide overview counts (shops, expansions, products, prices tracked). */
|
|
462
|
-
platform() {
|
|
463
|
-
return this.http.get("/stats");
|
|
645
|
+
platform(options = {}) {
|
|
646
|
+
return this.http.get("/stats", options);
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
// src/resources/webhooks.ts
|
|
651
|
+
var WebhooksResource = class {
|
|
652
|
+
constructor(http) {
|
|
653
|
+
this.http = http;
|
|
654
|
+
}
|
|
655
|
+
http;
|
|
656
|
+
/**
|
|
657
|
+
* `POST /webhooks`: register a new webhook.
|
|
658
|
+
*
|
|
659
|
+
* The returned `secret` is the only copy you will ever get — sign-verification depends on it and
|
|
660
|
+
* no endpoint reads it back. Persist it here, at creation, or delete the webhook and make a new
|
|
661
|
+
* one.
|
|
662
|
+
*/
|
|
663
|
+
create(params) {
|
|
664
|
+
const { url, events, ...requestOptions } = params;
|
|
665
|
+
return this.http.post("/webhooks", { url, events }, requestOptions);
|
|
666
|
+
}
|
|
667
|
+
/** `GET /webhooks`: every webhook registered on this account. Secrets are never included. */
|
|
668
|
+
list(options = {}) {
|
|
669
|
+
return this.http.get("/webhooks", options);
|
|
670
|
+
}
|
|
671
|
+
/** `DELETE /webhooks/{id}`: revoke a webhook. Deliveries stop immediately; its secret is void. */
|
|
672
|
+
delete(webhookId, options = {}) {
|
|
673
|
+
return this.http.delete(`/webhooks/${encodeURIComponent(webhookId)}`, options);
|
|
674
|
+
}
|
|
675
|
+
/** `POST /webhooks/{id}/test`: send a sample delivery to the registered URL, so you can verify
|
|
676
|
+
* your endpoint and your signature check before waiting on a real event. */
|
|
677
|
+
test(webhookId, options = {}) {
|
|
678
|
+
return this.http.post(`/webhooks/${encodeURIComponent(webhookId)}/test`, void 0, options);
|
|
464
679
|
}
|
|
465
680
|
};
|
|
466
681
|
|
|
@@ -478,6 +693,9 @@ var TcgPriser = class {
|
|
|
478
693
|
bargains;
|
|
479
694
|
packRates;
|
|
480
695
|
stats;
|
|
696
|
+
webhooks;
|
|
697
|
+
/** Holds the `HttpClient` so `creditsRemaining` can read the running value off it. */
|
|
698
|
+
http;
|
|
481
699
|
/**
|
|
482
700
|
* @param optionsOrAuthToken A subscriber's API token (`new TcgPriser(myApiToken)`), a full
|
|
483
701
|
* `TcgPriserOptions` object, or omit it entirely for an anonymous, public-only client.
|
|
@@ -491,6 +709,7 @@ var TcgPriser = class {
|
|
|
491
709
|
);
|
|
492
710
|
}
|
|
493
711
|
const http = new HttpClient({
|
|
712
|
+
timeoutMs: advanced.timeoutMs,
|
|
494
713
|
baseUrl: advanced.baseUrl ?? DEFAULT_BASE_URL,
|
|
495
714
|
// Bound to globalThis: both browsers and Node's undici implement fetch as a method that
|
|
496
715
|
// checks its receiver, so an unbound reference throws "Illegal invocation" the moment it's
|
|
@@ -511,9 +730,29 @@ var TcgPriser = class {
|
|
|
511
730
|
this.bargains = new BargainsResource(http);
|
|
512
731
|
this.packRates = new PackRatesResource(http);
|
|
513
732
|
this.stats = new StatsResource(http);
|
|
733
|
+
this.webhooks = new WebhooksResource(http);
|
|
734
|
+
this.http = http;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Credits left in this week's allowance, as of the last charged call this client made.
|
|
738
|
+
*
|
|
739
|
+
* The API returns `X-Credits-Remaining` on every response it charges for, so this needs no extra
|
|
740
|
+
* request — but it is only as current as your last premium call, and it is `undefined` until you
|
|
741
|
+
* make one. Uncharged calls (every public method, and any call authenticated with something other
|
|
742
|
+
* than an API token) don't update it, because the API doesn't meter them.
|
|
743
|
+
*
|
|
744
|
+
* ```ts
|
|
745
|
+
* await tcgpriser.cards.livePricing('fezandipiti-ex');
|
|
746
|
+
* if ((tcgpriser.creditsRemaining ?? Infinity) < 100) scheduleFewerRefreshes();
|
|
747
|
+
* ```
|
|
748
|
+
*
|
|
749
|
+
* Reading it in a browser additionally needs the API to expose the header via CORS, which it does.
|
|
750
|
+
*/
|
|
751
|
+
get creditsRemaining() {
|
|
752
|
+
return this.http.creditsRemaining;
|
|
514
753
|
}
|
|
515
754
|
};
|
|
516
755
|
|
|
517
|
-
export { DEFAULT_BASE_URL, TcgPriser, TcgPriserError };
|
|
756
|
+
export { DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, TcgPriser, TcgPriserError };
|
|
518
757
|
//# sourceMappingURL=index.js.map
|
|
519
758
|
//# sourceMappingURL=index.js.map
|