tcgpriser 0.10.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 +175 -17
- package/dist/index.cjs +299 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +427 -62
- package/dist/index.d.ts +427 -62
- package/dist/index.js +299 -83
- 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",
|
|
@@ -48,6 +63,12 @@ var KNOWN_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
48
63
|
"creditsExhausted",
|
|
49
64
|
"internalError"
|
|
50
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
|
+
}
|
|
51
72
|
async function toApiError(res, url) {
|
|
52
73
|
const body = await res.text();
|
|
53
74
|
let code = "unknown";
|
|
@@ -62,18 +83,63 @@ async function toApiError(res, url) {
|
|
|
62
83
|
details = parsed.error?.details;
|
|
63
84
|
} catch {
|
|
64
85
|
}
|
|
65
|
-
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
|
+
};
|
|
66
125
|
}
|
|
67
126
|
var HttpClient = class {
|
|
68
127
|
baseUrl;
|
|
69
128
|
fetchImpl;
|
|
70
129
|
defaultHeaders;
|
|
71
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;
|
|
72
137
|
constructor(options) {
|
|
73
138
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
74
139
|
this.fetchImpl = options.fetch;
|
|
75
140
|
this.defaultHeaders = options.headers ?? {};
|
|
76
141
|
this.defaultAuthToken = options.authToken;
|
|
142
|
+
this.defaultTimeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
77
143
|
}
|
|
78
144
|
get(path, requestOptions) {
|
|
79
145
|
return this.request("GET", path, void 0, requestOptions);
|
|
@@ -84,17 +150,42 @@ var HttpClient = class {
|
|
|
84
150
|
patch(path, body, requestOptions) {
|
|
85
151
|
return this.request("PATCH", path, body, requestOptions);
|
|
86
152
|
}
|
|
153
|
+
delete(path, requestOptions) {
|
|
154
|
+
return this.request("DELETE", path, void 0, requestOptions);
|
|
155
|
+
}
|
|
87
156
|
async request(method, path, body, requestOptions) {
|
|
88
157
|
const url = `${this.baseUrl}${path}`;
|
|
89
158
|
const authToken = requestOptions && "authToken" in requestOptions ? requestOptions.authToken : this.defaultAuthToken;
|
|
90
159
|
const headers = { Accept: "application/json", ...this.defaultHeaders };
|
|
91
160
|
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
92
161
|
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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;
|
|
98
189
|
if (!res.ok) throw await toApiError(res, url);
|
|
99
190
|
if (res.status === 204) return void 0;
|
|
100
191
|
return nullsToUndefined(await res.json());
|
|
@@ -121,13 +212,14 @@ var BargainsResource = class {
|
|
|
121
212
|
* count is fixed by the API (no `limit`/`skip` on the public tier); `pagination.hasMore` tells
|
|
122
213
|
* you if more exist. */
|
|
123
214
|
list(params = {}) {
|
|
124
|
-
|
|
215
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
216
|
+
return this.http.get(`/bargains${toQueryString(query)}`, requestOptions);
|
|
125
217
|
}
|
|
126
218
|
/** `GET /bargains/search`: like `list()`, but with real pagination and filters (shop, discount
|
|
127
219
|
* threshold, card condition/grade, free-text search). Premium. */
|
|
128
220
|
search(params = {}) {
|
|
129
|
-
const [query,
|
|
130
|
-
return this.http.get(`/bargains/search${toQueryString(query)}`,
|
|
221
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
222
|
+
return this.http.get(`/bargains/search${toQueryString(query)}`, requestOptions);
|
|
131
223
|
}
|
|
132
224
|
};
|
|
133
225
|
|
|
@@ -137,34 +229,40 @@ var CardsResource = class {
|
|
|
137
229
|
this.http = http;
|
|
138
230
|
}
|
|
139
231
|
http;
|
|
140
|
-
/** `GET /cards`: search
|
|
232
|
+
/** `GET /cards`: list cards, newest first. No free-text search — use `search()` for that. */
|
|
141
233
|
list(params = {}) {
|
|
142
|
-
|
|
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);
|
|
143
241
|
}
|
|
144
242
|
/** `GET /cards/{id}`: fetch one card by its id or technicalName. */
|
|
145
|
-
get(idOrTechnicalName) {
|
|
146
|
-
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}
|
|
243
|
+
get(idOrTechnicalName, options = {}) {
|
|
244
|
+
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}`, options);
|
|
147
245
|
}
|
|
148
246
|
/** `GET /cards/{id}/matches`: current shop listings matched to this card (latest per shop). */
|
|
149
247
|
matches(idOrTechnicalName, params = {}) {
|
|
248
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
150
249
|
return this.http.get(
|
|
151
|
-
`/cards/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(
|
|
250
|
+
`/cards/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(query)}`,
|
|
251
|
+
requestOptions
|
|
152
252
|
);
|
|
153
253
|
}
|
|
154
254
|
/** `GET /cards/{id}/reference-prices`: Cardmarket/TCGplayer/eBay/Tradera price history. Premium. */
|
|
155
255
|
referencePrices(idOrTechnicalName, params = {}) {
|
|
156
|
-
const [query,
|
|
256
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
157
257
|
return this.http.get(
|
|
158
258
|
`/cards/${encodeURIComponent(idOrTechnicalName)}/reference-prices${toQueryString(query)}`,
|
|
159
|
-
|
|
259
|
+
requestOptions
|
|
160
260
|
);
|
|
161
261
|
}
|
|
162
262
|
/** `GET /cards/{id}/prices`: individual marketplace sale records. Premium. */
|
|
163
263
|
prices(idOrTechnicalName, params = {}) {
|
|
164
|
-
const [query,
|
|
165
|
-
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`,
|
|
166
|
-
authToken
|
|
167
|
-
});
|
|
264
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
265
|
+
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`, requestOptions);
|
|
168
266
|
}
|
|
169
267
|
/** `GET /cards/{id}/pricing/live`: computed fresh for this request, not read from the last
|
|
170
268
|
* stats job. Premium. */
|
|
@@ -175,16 +273,39 @@ var CardsResource = class {
|
|
|
175
273
|
* `estimatedValue`, `lowestShopOffer`, `referencePriceSnapshotsByProvider` — refreshed once a day
|
|
176
274
|
* by the nightly pricing/scraper jobs. `get()` returns content only; this is the separate,
|
|
177
275
|
* shorter-cached call for the part of a card that actually changes day to day. */
|
|
178
|
-
pricing(idOrTechnicalName) {
|
|
179
|
-
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/pricing
|
|
276
|
+
pricing(idOrTechnicalName, options = {}) {
|
|
277
|
+
return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/pricing`, options);
|
|
180
278
|
}
|
|
181
279
|
/** `GET /cards/pricing`: pricing for up to 200 cards in one request, keyed by `id` — the batch
|
|
182
280
|
* counterpart to `pricing()`, for a page of results (a search page, an expansion's contents) that
|
|
183
281
|
* needs pricing for many items at once. Unlike `get()`/`pricing()`, this only accepts `id`s, not
|
|
184
282
|
* technicalNames — pass the `id`s already on the cards you fetched. Ids with no match are
|
|
185
283
|
* silently omitted from the result rather than causing an error. */
|
|
186
|
-
pricingBatch(ids) {
|
|
187
|
-
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
|
+
);
|
|
188
309
|
}
|
|
189
310
|
};
|
|
190
311
|
|
|
@@ -195,30 +316,30 @@ var ExpansionsResource = class {
|
|
|
195
316
|
}
|
|
196
317
|
http;
|
|
197
318
|
/** `GET /expansions`: every expansion. Unwrapped to a plain array, nothing to paginate here. */
|
|
198
|
-
async list() {
|
|
199
|
-
const res = await this.http.get("/expansions");
|
|
319
|
+
async list(options = {}) {
|
|
320
|
+
const res = await this.http.get("/expansions", options);
|
|
200
321
|
return res.data;
|
|
201
322
|
}
|
|
202
323
|
/** `GET /expansions/{technicalName}`: metadata only — no cards or sealed products. Returns the
|
|
203
324
|
* smaller `ExpansionRef`, not the full `Expansion`: this is a plain lookup by technicalName, not
|
|
204
325
|
* the aggregation `list()` runs, so `sealedCount`/`cardCount`/`productCount` aren't available
|
|
205
326
|
* here. See `cards()` and `sealedProducts()` for this expansion's contents. */
|
|
206
|
-
get(technicalName) {
|
|
207
|
-
return this.http.get(`/expansions/${encodeURIComponent(technicalName)}
|
|
327
|
+
get(technicalName, options = {}) {
|
|
328
|
+
return this.http.get(`/expansions/${encodeURIComponent(technicalName)}`, options);
|
|
208
329
|
}
|
|
209
330
|
/** `GET /expansions/{technicalName}/cards`: every card in this expansion. Content only, no
|
|
210
331
|
* pricing fields — pass the `id`s from the result to `client.cards.pricingBatch()` if you need
|
|
211
332
|
* pricing too. Sealed products are a separate call — see `sealedProducts()` — never merged into
|
|
212
333
|
* this one. */
|
|
213
|
-
cards(technicalName) {
|
|
214
|
-
return this.http.get(`/expansions/${encodeURIComponent(technicalName)}/cards
|
|
334
|
+
cards(technicalName, options = {}) {
|
|
335
|
+
return this.http.get(`/expansions/${encodeURIComponent(technicalName)}/cards`, options);
|
|
215
336
|
}
|
|
216
337
|
/** `GET /expansions/{technicalName}/products`: every sealed product in this expansion. Content
|
|
217
338
|
* only, no pricing fields — pass the `id`s from the result to `client.products.pricingBatch()`
|
|
218
339
|
* if you need pricing too. Cards are a separate call — see `cards()` — never merged into this
|
|
219
340
|
* one. */
|
|
220
|
-
sealedProducts(technicalName) {
|
|
221
|
-
return this.http.get(`/expansions/${encodeURIComponent(technicalName)}/products
|
|
341
|
+
sealedProducts(technicalName, options = {}) {
|
|
342
|
+
return this.http.get(`/expansions/${encodeURIComponent(technicalName)}/products`, options);
|
|
222
343
|
}
|
|
223
344
|
/** `GET /expansions/{technicalName}/cards/live-pricing`: computed fresh for every card in this
|
|
224
345
|
* expansion, not read from the last stats job. Premium. */
|
|
@@ -246,13 +367,13 @@ var PackRatesResource = class {
|
|
|
246
367
|
http;
|
|
247
368
|
/** `GET /pack-rates`: pull-rate odds for every expansion that has them. Unwrapped to a plain
|
|
248
369
|
* array, nothing to paginate here. */
|
|
249
|
-
async list() {
|
|
250
|
-
const res = await this.http.get("/pack-rates");
|
|
370
|
+
async list(options = {}) {
|
|
371
|
+
const res = await this.http.get("/pack-rates", options);
|
|
251
372
|
return res.data;
|
|
252
373
|
}
|
|
253
374
|
/** `GET /pack-rates/{expansionId}`: pull-rate odds for one expansion. */
|
|
254
|
-
get(expansionId) {
|
|
255
|
-
return this.http.get(`/pack-rates/${encodeURIComponent(expansionId)}
|
|
375
|
+
get(expansionId, options = {}) {
|
|
376
|
+
return this.http.get(`/pack-rates/${encodeURIComponent(expansionId)}`, options);
|
|
256
377
|
}
|
|
257
378
|
};
|
|
258
379
|
|
|
@@ -264,17 +385,20 @@ var PriceStatsResource = class {
|
|
|
264
385
|
http;
|
|
265
386
|
/** `GET /price-stats/daily`: daily average price history, filtered to matching product(s). */
|
|
266
387
|
daily(params = {}) {
|
|
267
|
-
|
|
388
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
389
|
+
return this.http.get(`/price-stats/daily${toQueryString(query)}`, requestOptions);
|
|
268
390
|
}
|
|
269
391
|
/** `GET /price-stats/estimated-values`: current estimated market value, filtered to matching
|
|
270
392
|
* product(s). */
|
|
271
393
|
estimatedValues(params = {}) {
|
|
272
|
-
|
|
394
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
395
|
+
return this.http.get(`/price-stats/estimated-values${toQueryString(query)}`, requestOptions);
|
|
273
396
|
}
|
|
274
397
|
/** `GET /price-stats/top-products`: items ranked by shop availability (how many shops carry
|
|
275
398
|
* them), not by price. */
|
|
276
399
|
topProducts(params = {}) {
|
|
277
|
-
|
|
400
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
401
|
+
return this.http.get(`/price-stats/top-products${toQueryString(query)}`, requestOptions);
|
|
278
402
|
}
|
|
279
403
|
/** `GET /price-stats/product/{id}`: daily price history, current estimate, and a variant-count
|
|
280
404
|
* summary for one product. Premium. */
|
|
@@ -289,10 +413,10 @@ var PriceStatsResource = class {
|
|
|
289
413
|
/** `GET /price-stats/product/{id}/daily`: daily price history for one product, with a
|
|
290
414
|
* caller-chosen window. Premium. */
|
|
291
415
|
productDaily(idOrTechnicalName, params = {}) {
|
|
292
|
-
const [query,
|
|
416
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
293
417
|
return this.http.get(
|
|
294
418
|
`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily${toQueryString(query)}`,
|
|
295
|
-
|
|
419
|
+
requestOptions
|
|
296
420
|
);
|
|
297
421
|
}
|
|
298
422
|
/** `GET /price-stats/product/{id}/daily-last-30`: daily price history for the last 30 days
|
|
@@ -313,20 +437,20 @@ var PriceStatsResource = class {
|
|
|
313
437
|
/** `GET /price-stats/product/{id}/by-variant`: price stats broken out per card condition/grade.
|
|
314
438
|
* Premium. */
|
|
315
439
|
productByVariant(idOrTechnicalName, params = {}) {
|
|
316
|
-
const [query,
|
|
440
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
317
441
|
return this.http.get(
|
|
318
442
|
`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/by-variant${toQueryString(query)}`,
|
|
319
|
-
|
|
443
|
+
requestOptions
|
|
320
444
|
);
|
|
321
445
|
}
|
|
322
446
|
/** `GET /price-stats/product/{id}/daily-by-variant`: daily price history for one specific
|
|
323
447
|
* condition/grade. `condition` is required for `cardType: 'loose'`; `gradingCompany` and `grade`
|
|
324
448
|
* are required for `cardType: 'graded'`. Premium. */
|
|
325
449
|
productDailyByVariant(idOrTechnicalName, params) {
|
|
326
|
-
const [query,
|
|
450
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
327
451
|
return this.http.get(
|
|
328
452
|
`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily-by-variant${toQueryString(query)}`,
|
|
329
|
-
|
|
453
|
+
requestOptions
|
|
330
454
|
);
|
|
331
455
|
}
|
|
332
456
|
};
|
|
@@ -337,34 +461,43 @@ var ProductsResource = class {
|
|
|
337
461
|
this.http = http;
|
|
338
462
|
}
|
|
339
463
|
http;
|
|
340
|
-
/** `GET /product`:
|
|
464
|
+
/** `GET /product`: list sealed products, newest first. No free-text search — use `search()` for
|
|
465
|
+
* that. */
|
|
341
466
|
list(params = {}) {
|
|
342
|
-
|
|
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);
|
|
343
474
|
}
|
|
344
475
|
/** `GET /product/{id}`: fetch one sealed product by its id or technicalName. */
|
|
345
|
-
get(idOrTechnicalName) {
|
|
346
|
-
return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}
|
|
476
|
+
get(idOrTechnicalName, options = {}) {
|
|
477
|
+
return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}`, options);
|
|
347
478
|
}
|
|
348
479
|
/** `GET /product/{id}/matches`: current shop listings matched to this product (latest per shop). */
|
|
349
480
|
matches(idOrTechnicalName, params = {}) {
|
|
481
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
350
482
|
return this.http.get(
|
|
351
|
-
`/product/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(
|
|
483
|
+
`/product/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(query)}`,
|
|
484
|
+
requestOptions
|
|
352
485
|
);
|
|
353
486
|
}
|
|
354
487
|
/** `GET /product/{id}/reference-prices`: Cardmarket/TCGplayer/Tradera price history. Premium. */
|
|
355
488
|
referencePrices(idOrTechnicalName, params = {}) {
|
|
356
|
-
const [query,
|
|
489
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
357
490
|
return this.http.get(
|
|
358
491
|
`/product/${encodeURIComponent(idOrTechnicalName)}/reference-prices${toQueryString(query)}`,
|
|
359
|
-
|
|
492
|
+
requestOptions
|
|
360
493
|
);
|
|
361
494
|
}
|
|
362
495
|
/** `GET /product/{id}/prices`: individual marketplace sale records. Premium. */
|
|
363
496
|
prices(idOrTechnicalName, params = {}) {
|
|
364
|
-
const [query,
|
|
497
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
365
498
|
return this.http.get(
|
|
366
499
|
`/product/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`,
|
|
367
|
-
|
|
500
|
+
requestOptions
|
|
368
501
|
);
|
|
369
502
|
}
|
|
370
503
|
/** `GET /product/{id}/pricing/live`: computed fresh for this request, not read from the last
|
|
@@ -376,16 +509,38 @@ var ProductsResource = class {
|
|
|
376
509
|
* `estimatedValue`, `lowestShopOffer`, `referencePriceSnapshotsByProvider` — refreshed once a day
|
|
377
510
|
* by the nightly pricing/scraper jobs. `get()` returns content only; this is the separate,
|
|
378
511
|
* shorter-cached call for the part of a product that actually changes day to day. */
|
|
379
|
-
pricing(idOrTechnicalName) {
|
|
380
|
-
return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}/pricing
|
|
512
|
+
pricing(idOrTechnicalName, options = {}) {
|
|
513
|
+
return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}/pricing`, options);
|
|
381
514
|
}
|
|
382
515
|
/** `GET /product/pricing`: pricing for up to 200 sealed products in one request, keyed by `id` —
|
|
383
516
|
* the batch counterpart to `pricing()`, for a page of results (a search page, an expansion's
|
|
384
517
|
* contents) that needs pricing for many items at once. Unlike `get()`/`pricing()`, this only
|
|
385
518
|
* accepts `id`s, not technicalNames — pass the `id`s already on the products you fetched. Ids with
|
|
386
519
|
* no match are silently omitted from the result rather than causing an error. */
|
|
387
|
-
pricingBatch(ids) {
|
|
388
|
-
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
|
+
);
|
|
389
544
|
}
|
|
390
545
|
};
|
|
391
546
|
|
|
@@ -397,24 +552,22 @@ var ShopMatchStatsResource = class {
|
|
|
397
552
|
http;
|
|
398
553
|
/** `GET /shop-match-stats/product/{productId}`: one product's price history, broken out per shop. */
|
|
399
554
|
forProduct(productId, params = {}) {
|
|
400
|
-
const [query,
|
|
555
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
401
556
|
return this.http.get(
|
|
402
557
|
`/shop-match-stats/product/${encodeURIComponent(productId)}${toQueryString(query)}`,
|
|
403
|
-
|
|
558
|
+
requestOptions
|
|
404
559
|
);
|
|
405
560
|
}
|
|
406
561
|
/** `GET /shop-match-stats/shop/{shop}`: one shop's price history, broken out per product. */
|
|
407
562
|
forShop(shop, params = {}) {
|
|
408
|
-
const [query,
|
|
409
|
-
return this.http.get(`/shop-match-stats/shop/${encodeURIComponent(shop)}${toQueryString(query)}`,
|
|
410
|
-
authToken
|
|
411
|
-
});
|
|
563
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
564
|
+
return this.http.get(`/shop-match-stats/shop/${encodeURIComponent(shop)}${toQueryString(query)}`, requestOptions);
|
|
412
565
|
}
|
|
413
566
|
/** `GET /shop-match-stats/compare`: one product's price at every shop that carries it, as of
|
|
414
567
|
* one date (defaults to the latest). */
|
|
415
568
|
compare(params) {
|
|
416
|
-
const [query,
|
|
417
|
-
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);
|
|
418
571
|
}
|
|
419
572
|
};
|
|
420
573
|
|
|
@@ -426,15 +579,21 @@ var ShopMatchesResource = class {
|
|
|
426
579
|
http;
|
|
427
580
|
/** `GET /shop-matches`: every current match across every shop (latest record per url+shop). */
|
|
428
581
|
list(params = {}) {
|
|
429
|
-
|
|
582
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
583
|
+
return this.http.get(`/shop-matches${toQueryString(query)}`, requestOptions);
|
|
430
584
|
}
|
|
431
585
|
/** `GET /shop-matches/{shop}`: every current match at one shop (latest record per url). */
|
|
432
586
|
forShop(technicalName, params = {}) {
|
|
433
|
-
|
|
587
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
588
|
+
return this.http.get(
|
|
589
|
+
`/shop-matches/${encodeURIComponent(technicalName)}${toQueryString(query)}`,
|
|
590
|
+
requestOptions
|
|
591
|
+
);
|
|
434
592
|
}
|
|
435
593
|
/** `GET /shop-matches/shops`: match counts per shop (based on latest records only). */
|
|
436
594
|
shopStats(params = {}) {
|
|
437
|
-
|
|
595
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
596
|
+
return this.http.get(`/shop-matches/shops${toQueryString(query)}`, requestOptions);
|
|
438
597
|
}
|
|
439
598
|
};
|
|
440
599
|
|
|
@@ -446,16 +605,16 @@ var ShopUrlsResource = class {
|
|
|
446
605
|
http;
|
|
447
606
|
/** `POST /shop-urls/submit`: submit a shop URL for scraping. */
|
|
448
607
|
submit(params) {
|
|
449
|
-
const {
|
|
450
|
-
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);
|
|
451
610
|
}
|
|
452
611
|
/** `PATCH /shop-urls/{id}/product`: manually assign (or clear) the product a shop URL resolves to. */
|
|
453
612
|
assignProduct(shopUrlId, params) {
|
|
454
|
-
const {
|
|
613
|
+
const { productId, ...requestOptions } = params;
|
|
455
614
|
return this.http.patch(
|
|
456
615
|
`/shop-urls/${encodeURIComponent(shopUrlId)}/product`,
|
|
457
616
|
{ productId },
|
|
458
|
-
|
|
617
|
+
requestOptions
|
|
459
618
|
);
|
|
460
619
|
}
|
|
461
620
|
};
|
|
@@ -468,12 +627,13 @@ var ShopsResource = class {
|
|
|
468
627
|
http;
|
|
469
628
|
/** `GET /shops`: every tracked shop. Unwrapped to a plain array, nothing to paginate here. */
|
|
470
629
|
async list(params = {}) {
|
|
471
|
-
const
|
|
630
|
+
const [query, requestOptions] = splitRequestOptions(params);
|
|
631
|
+
const res = await this.http.get(`/shops${toQueryString(query)}`, requestOptions);
|
|
472
632
|
return res.data;
|
|
473
633
|
}
|
|
474
634
|
/** `GET /shops/{id}`: fetch one shop by its id or technicalName. */
|
|
475
|
-
get(idOrTechnicalName) {
|
|
476
|
-
return this.http.get(`/shops/${encodeURIComponent(idOrTechnicalName)}
|
|
635
|
+
get(idOrTechnicalName, options = {}) {
|
|
636
|
+
return this.http.get(`/shops/${encodeURIComponent(idOrTechnicalName)}`, options);
|
|
477
637
|
}
|
|
478
638
|
};
|
|
479
639
|
|
|
@@ -484,8 +644,40 @@ var StatsResource = class {
|
|
|
484
644
|
}
|
|
485
645
|
http;
|
|
486
646
|
/** `GET /stats`: platform-wide overview counts (shops, expansions, products, prices tracked). */
|
|
487
|
-
platform() {
|
|
488
|
-
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);
|
|
489
681
|
}
|
|
490
682
|
};
|
|
491
683
|
|
|
@@ -503,6 +695,9 @@ var TcgPriser = class {
|
|
|
503
695
|
bargains;
|
|
504
696
|
packRates;
|
|
505
697
|
stats;
|
|
698
|
+
webhooks;
|
|
699
|
+
/** Holds the `HttpClient` so `creditsRemaining` can read the running value off it. */
|
|
700
|
+
http;
|
|
506
701
|
/**
|
|
507
702
|
* @param optionsOrAuthToken A subscriber's API token (`new TcgPriser(myApiToken)`), a full
|
|
508
703
|
* `TcgPriserOptions` object, or omit it entirely for an anonymous, public-only client.
|
|
@@ -516,6 +711,7 @@ var TcgPriser = class {
|
|
|
516
711
|
);
|
|
517
712
|
}
|
|
518
713
|
const http = new HttpClient({
|
|
714
|
+
timeoutMs: advanced.timeoutMs,
|
|
519
715
|
baseUrl: advanced.baseUrl ?? DEFAULT_BASE_URL,
|
|
520
716
|
// Bound to globalThis: both browsers and Node's undici implement fetch as a method that
|
|
521
717
|
// checks its receiver, so an unbound reference throws "Illegal invocation" the moment it's
|
|
@@ -536,10 +732,31 @@ var TcgPriser = class {
|
|
|
536
732
|
this.bargains = new BargainsResource(http);
|
|
537
733
|
this.packRates = new PackRatesResource(http);
|
|
538
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;
|
|
539
755
|
}
|
|
540
756
|
};
|
|
541
757
|
|
|
542
758
|
exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
|
|
759
|
+
exports.DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_MS;
|
|
543
760
|
exports.TcgPriser = TcgPriser;
|
|
544
761
|
exports.TcgPriserError = TcgPriserError;
|
|
545
762
|
//# sourceMappingURL=index.cjs.map
|