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