tcgpriser 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,7 +36,12 @@ import { TcgPriser } from 'tcgpriser';
36
36
  const tcgpriser = new TcgPriser();
37
37
 
38
38
  const card = await tcgpriser.cards.get('mega-evolution-ascended-heroes-fezandipiti-ex');
39
- console.log(card.retailPrice, card.lowestShopOffer?.shop.name);
39
+ console.log(card.name, card.expansion?.name);
40
+
41
+ // Content (name, images, expansion, ...) and pricing (retailPrice, lowestShopOffer, ...) are
42
+ // separate, differently-cached calls — see "Pricing" below.
43
+ const pricing = await tcgpriser.cards.pricing('mega-evolution-ascended-heroes-fezandipiti-ex');
44
+ console.log(pricing.retailPrice, pricing.lowestShopOffer?.shop.name);
40
45
 
41
46
  const { data: bargains } = await tcgpriser.bargains.list({ type: 'card' });
42
47
  ```
@@ -65,7 +70,24 @@ await tcgpriser.products.matches('scarlet-violet-booster-pack');
65
70
 
66
71
  ```typescript
67
72
  await tcgpriser.expansions.list();
68
- await tcgpriser.expansions.products('eng-scarlet-violet-journey-together'); // { expansion, cards, sealed }
73
+ await tcgpriser.expansions.products('eng-scarlet-violet-journey-together'); // { expansion, cards, sealed } — content only
74
+ ```
75
+
76
+ ### Pricing
77
+
78
+ `list()`/`get()`/`expansions.products()` all return catalog content only — name, images, brand,
79
+ expansion, rarity. Pricing (`retailPrice`, `estimatedValue`, `lowestShopOffer`,
80
+ `referencePriceSnapshotsByProvider`) is a separate, shorter-cached call: content changes on an
81
+ admin edit or catalog import, pricing refreshes daily, so each is cached at the TTL its own
82
+ freshness supports.
83
+
84
+ ```typescript
85
+ await tcgpriser.cards.pricing('fezandipiti-ex'); // id or technicalName
86
+ await tcgpriser.products.pricing('scarlet-violet-booster-pack');
87
+
88
+ // Batch form, up to 200 ids at once — ids only, not technicalNames.
89
+ const { data: cards } = await tcgpriser.cards.list({ search: 'pikachu' });
90
+ await tcgpriser.cards.pricingBatch(cards.map((card) => card.id));
69
91
  ```
70
92
 
71
93
  ### Shops
@@ -188,14 +210,14 @@ await tcgpriser.packRates.get(expansionId);
188
210
  |---|---|
189
211
  | `platform()` | Platform-wide overview counts |
190
212
 
191
- 🔒 = premium, needs a subscriber's token. See below.
213
+ 🔒 = premium, needs an API token. See below.
192
214
 
193
215
  ## Authentication
194
216
 
195
- Public methods work with no setup. Premium methods (marked 🔒 above) need a signed-in subscriber's JWT from tcgpriser.se. This package doesn't handle login itself; the site's sign-in is OAuth-based, so a headless client can't drive it. Pass a token your own app already has:
217
+ Public methods work with no setup. Premium methods (marked 🔒 above) need a Premium subscriber's API token, generated from **tcgpriser.se/account/api-token**. Unlike the site's own session login, which is OAuth-based and can't be driven headlessly, the API token is a long-lived, revocable secret made specifically for scripts and other programmatic callers. Generate it once from your account page and pass it in:
196
218
 
197
219
  ```typescript
198
- const tcgpriser = new TcgPriser(myJwt); // shorthand for { authToken: myJwt }
220
+ const tcgpriser = new TcgPriser(myApiToken); // shorthand for { authToken: myApiToken }
199
221
  await tcgpriser.cards.livePricing('fezandipiti-ex');
200
222
  ```
201
223
 
@@ -203,7 +225,7 @@ Or set no default and pass a token per call, which fits better when one client i
203
225
 
204
226
  ```typescript
205
227
  const tcgpriser = new TcgPriser();
206
- await tcgpriser.cards.livePricing('fezandipiti-ex', { authToken: requestUserJwt });
228
+ await tcgpriser.cards.livePricing('fezandipiti-ex', { authToken: requestUserApiToken });
207
229
  ```
208
230
 
209
231
  A missing or invalid token gets `401 unauthorized`. A valid token without an active subscription gets `403 premiumRequired`. Both come back as `TcgPriserError`:
@@ -223,11 +245,11 @@ try {
223
245
  ## Options
224
246
 
225
247
  ```typescript
226
- new TcgPriser(myJwt); // shorthand for { authToken: myJwt }
227
- new TcgPriser(); // no token, public methods only
248
+ new TcgPriser(myApiToken); // shorthand for { authToken: myApiToken }
249
+ new TcgPriser(); // no token, public methods only
228
250
 
229
251
  new TcgPriser({
230
- authToken: myJwt,
252
+ authToken: myApiToken,
231
253
 
232
254
  // Local dev, self-hosting or tests only. Leave this out for normal use.
233
255
  advanced: {
@@ -256,6 +278,47 @@ type CardSchema = components['schemas']['CardWithPricing'];
256
278
 
257
279
  Most types in `tcgpriser` are direct aliases onto that generated schema, so a field in your code and a field in the API docs are the same field, always.
258
280
 
281
+ ## Images
282
+
283
+ `imageUrl`, `logoUrl` and `symbolUrl` fields point at tcgpriser.se's own CDN, which is sized for
284
+ tcgpriser.se's own traffic, not for hotlinking from other sites and apps. **For best performance,
285
+ rehost these images on your own storage/CDN and cache them there** instead of linking to them
286
+ directly — one less hop, tuned to your own traffic and geography, and no dependency on
287
+ infrastructure that isn't yours.
288
+
289
+ A simple way to do this: fetch the image once, save it under the URL's path (e.g.
290
+ `products/eng-scarlet-violet-booster-pack.png`) as a stable local key, serve it from your own
291
+ storage from then on, and periodically re-fetch (a nightly job is plenty) using a conditional
292
+ `GET` so you only pay for images that actually changed:
293
+
294
+ ```typescript
295
+ import { mkdir, writeFile } from 'node:fs/promises';
296
+ import { dirname, join } from 'node:path';
297
+
298
+ const etags = new Map<string, string>(); // persist this however you persist anything else
299
+
300
+ async function rehostImage(imageUrl: string, cacheDir: string): Promise<string> {
301
+ const key = new URL(imageUrl).pathname.replace(/^\/[^/]+\//, ''); // "products/....webp"
302
+ const localPath = join(cacheDir, key);
303
+ const knownEtag = etags.get(key);
304
+
305
+ const res = await fetch(imageUrl, { headers: knownEtag ? { 'If-None-Match': knownEtag } : {} });
306
+ if (res.status === 304) return localPath; // unchanged since last sync
307
+
308
+ if (!res.ok) throw new Error(`Failed to fetch ${imageUrl}: ${res.status}`);
309
+ await mkdir(dirname(localPath), { recursive: true });
310
+ await writeFile(localPath, Buffer.from(await res.arrayBuffer()));
311
+
312
+ const etag = res.headers.get('etag');
313
+ if (etag) etags.set(key, etag);
314
+ return localPath;
315
+ }
316
+ ```
317
+
318
+ Swap the `fs`/`mkdir`/`writeFile` calls for your own storage's SDK (S3, R2, Cloudflare Images, ...)
319
+ if you're not caching to local disk. See `examples/rehost-images.ts` for a runnable version of this
320
+ against a live `tcgpriser` response.
321
+
259
322
  ## Scripts
260
323
 
261
324
  ### Build
@@ -290,6 +353,12 @@ yarn example
290
353
 
291
354
  Runs `examples/basic.ts` against a local dev API. Set `TCGPRISER_AUTH_TOKEN` to see the premium call succeed instead of the expected 401.
292
355
 
356
+ ```bash
357
+ yarn example:rehost-images
358
+ ```
359
+
360
+ Runs `examples/rehost-images.ts` — the "Images" section's rehosting pattern against a handful of real product images. Run it twice to see the second pass come back as `304`s.
361
+
293
362
  ## Scope
294
363
 
295
364
  Covers the API's full documented surface: public catalog, price and bargain reads, plus the premium endpoints above. Not covered: the admin/scraper/auth surface, which isn't part of any published contract.
package/dist/index.cjs CHANGED
@@ -169,6 +169,21 @@ var CardsResource = class {
169
169
  livePricing(idOrTechnicalName, options = {}) {
170
170
  return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/pricing/live`, options);
171
171
  }
172
+ /** `GET /cards/{id}/pricing`: this card's current pricing snapshot — `retailPrice`,
173
+ * `estimatedValue`, `lowestShopOffer`, `referencePriceSnapshotsByProvider` — refreshed once a day
174
+ * by the nightly pricing/scraper jobs. `get()` returns content only; this is the separate,
175
+ * 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`);
178
+ }
179
+ /** `GET /cards/pricing`: pricing for up to 200 cards in one request, keyed by `id` — the batch
180
+ * counterpart to `pricing()`, for a page of results (a search page, an expansion's contents) that
181
+ * needs pricing for many items at once. Unlike `get()`/`pricing()`, this only accepts `id`s, not
182
+ * technicalNames — pass the `id`s already on the cards you fetched. Ids with no match are
183
+ * 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(",")}`);
186
+ }
172
187
  };
173
188
 
174
189
  // src/resources/expansions.ts
@@ -183,7 +198,10 @@ var ExpansionsResource = class {
183
198
  return res.data;
184
199
  }
185
200
  /** `GET /expansions/{technicalName}/products`: every card and sealed product in one
186
- * expansion, kept as separate `cards`/`sealed` groups. */
201
+ * expansion, kept as separate `cards`/`sealed` groups. Content only, no pricing fields — pass the
202
+ * `id`s from the result to `client.cards.pricingBatch()` / `client.products.pricingBatch()` if you
203
+ * need pricing too. This mirrors the API 1:1 rather than fetching pricing for you, since pricing
204
+ * for every item in an expansion is a second, separately-cached call the caller may not want. */
187
205
  products(technicalName) {
188
206
  return this.http.get(`/expansions/${encodeURIComponent(technicalName)}/products?grouped=true`);
189
207
  }
@@ -331,6 +349,21 @@ var ProductsResource = class {
331
349
  livePricing(idOrTechnicalName, options = {}) {
332
350
  return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}/pricing/live`, options);
333
351
  }
352
+ /** `GET /product/{id}/pricing`: this product's current pricing snapshot — `retailPrice`,
353
+ * `estimatedValue`, `lowestShopOffer`, `referencePriceSnapshotsByProvider` — refreshed once a day
354
+ * by the nightly pricing/scraper jobs. `get()` returns content only; this is the separate,
355
+ * 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`);
358
+ }
359
+ /** `GET /product/pricing`: pricing for up to 200 sealed products in one request, keyed by `id` —
360
+ * the batch counterpart to `pricing()`, for a page of results (a search page, an expansion's
361
+ * contents) that needs pricing for many items at once. Unlike `get()`/`pricing()`, this only
362
+ * accepts `id`s, not technicalNames — pass the `id`s already on the products you fetched. Ids with
363
+ * 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(",")}`);
366
+ }
334
367
  };
335
368
 
336
369
  // src/resources/shopMatchStats.ts
@@ -448,7 +481,7 @@ var TcgPriser = class {
448
481
  packRates;
449
482
  stats;
450
483
  /**
451
- * @param optionsOrAuthToken A subscriber JWT (`new TcgPriser(myJwt)`), a full
484
+ * @param optionsOrAuthToken A subscriber's API token (`new TcgPriser(myApiToken)`), a full
452
485
  * `TcgPriserOptions` object, or omit it entirely for an anonymous, public-only client.
453
486
  */
454
487
  constructor(optionsOrAuthToken) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/bargains.ts","../src/resources/cards.ts","../src/resources/expansions.ts","../src/resources/packRates.ts","../src/resources/priceStats.ts","../src/resources/products.ts","../src/resources/shopMatchStats.ts","../src/resources/shopMatches.ts","../src/resources/shopUrls.ts","../src/resources/shops.ts","../src/resources/stats.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/** The stable error codes the API's `error.code` field can hold. */\nexport type TcgPriserErrorCode =\n | 'validationFailed'\n | 'unauthorized'\n | 'forbidden'\n | 'notFound'\n | 'conflict'\n | 'readOnlyField'\n | 'rateLimited'\n | 'premiumRequired'\n | 'internalError'\n /** Response body wasn't the `{ error: { code, message } }` shape. Probably a proxy or gateway\n * error in front of the API. */\n | 'unknown';\n\n/** Thrown for any non-2xx response. Carries the parsed `{ code, message }` when the body matched\n * the API's error envelope, plus the raw status/body regardless so nothing gets lost. */\nexport class TcgPriserError extends Error {\n readonly statusCode: number;\n readonly statusText: string;\n readonly url: string;\n readonly code: TcgPriserErrorCode;\n readonly details: unknown;\n /** The raw response body, for debugging when `code`/`details` don't cover what you need. */\n readonly body: string;\n\n constructor(params: {\n statusCode: number;\n statusText: string;\n url: string;\n code: TcgPriserErrorCode;\n message: string;\n details?: unknown;\n body: string;\n }) {\n super(`tcgpriser: ${params.statusCode} ${params.code} - ${params.message} (${params.url})`);\n this.name = 'TcgPriserError';\n this.statusCode = params.statusCode;\n this.statusText = params.statusText;\n this.url = params.url;\n this.code = params.code;\n this.details = params.details;\n this.body = params.body;\n }\n}\n","import { TcgPriserError, type TcgPriserErrorCode } from './errors.js';\n\nexport type QueryValue = string | number | boolean | undefined | null;\n\n/**\n * Builds a query string from a params object, dropping `undefined`/`null` entries so callers can\n * pass params straight through without filtering first.\n *\n * Takes a generic `object` instead of `Record<string, QueryValue>` on purpose. The params\n * interfaces (`ListCardsParams` etc.) intentionally have no index signature, otherwise any string\n * key would type-check.\n */\nexport function toQueryString<T extends object>(params: T): string {\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params) as [string, QueryValue][]) {\n if (value === undefined || value === null) continue;\n search.set(key, String(value));\n }\n const query = search.toString();\n return query ? `?${query}` : '';\n}\n\n/** Splits `authToken` off a params object so it goes on the `Authorization` header, not through\n * `toQueryString`. A bearer token has no business in a URL: query strings end up in server logs,\n * browser history, `Referer` headers. */\nexport function splitAuthToken<T extends { authToken?: string }>(\n params: T,\n): [Omit<T, 'authToken'>, string | undefined] {\n const { authToken, ...rest } = params;\n return [rest, authToken];\n}\n\nconst KNOWN_ERROR_CODES: ReadonlySet<string> = new Set([\n 'validationFailed',\n 'unauthorized',\n 'forbidden',\n 'notFound',\n 'conflict',\n 'readOnlyField',\n 'rateLimited',\n 'premiumRequired',\n 'internalError',\n]);\n\nasync function toApiError(res: Response, url: string): Promise<TcgPriserError> {\n const body = await res.text();\n let code: TcgPriserErrorCode = 'unknown';\n let message = res.statusText || 'Request failed';\n let details: unknown;\n\n try {\n const parsed = JSON.parse(body) as { error?: { code?: string; message?: string; details?: unknown } };\n if (parsed.error?.code && KNOWN_ERROR_CODES.has(parsed.error.code)) {\n code = parsed.error.code as TcgPriserErrorCode;\n }\n if (parsed.error?.message) message = parsed.error.message;\n details = parsed.error?.details;\n } catch {\n // Not the standard error envelope, maybe a proxy's HTML error page. Fall back to the status\n // text and leave `body` for anyone who wants to dig in.\n }\n\n return new TcgPriserError({ statusCode: res.status, statusText: res.statusText, url, code, message, details, body });\n}\n\nexport interface HttpClientOptions {\n baseUrl: string;\n fetch: typeof fetch;\n headers?: Record<string, string>;\n /** Default bearer token for premium endpoints, used when a call doesn't pass its own `authToken`. */\n authToken?: string;\n}\n\n/** Per-call auth override for a premium endpoint, on top of the client's default `authToken`.\n * Every premium method takes one of these, either standalone or merged into its params object via\n * `splitAuthToken`. */\nexport interface PremiumOptions {\n /** Overrides the client's default `authToken` for this call. Pass `undefined` explicitly to\n * force an anonymous request even when the client has a default token. */\n authToken?: string;\n}\n\n/** Thin wrapper around `fetch`: joins the base URL, adds default headers, turns non-2xx responses\n * into a `TcgPriserError`. Every resource method goes through this instead of calling `fetch`\n * directly. */\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly defaultHeaders: Record<string, string>;\n private readonly defaultAuthToken: string | undefined;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n this.fetchImpl = options.fetch;\n this.defaultHeaders = options.headers ?? {};\n this.defaultAuthToken = options.authToken;\n }\n\n get<T>(path: string, requestOptions?: PremiumOptions): Promise<T> {\n return this.request<T>('GET', path, undefined, requestOptions);\n }\n\n post<T>(path: string, body: unknown, requestOptions?: PremiumOptions): Promise<T> {\n return this.request<T>('POST', path, body, requestOptions);\n }\n\n patch<T>(path: string, body: unknown, requestOptions?: PremiumOptions): Promise<T> {\n return this.request<T>('PATCH', path, body, requestOptions);\n }\n\n private async request<T>(\n method: string,\n path: string,\n body: unknown,\n requestOptions?: PremiumOptions,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const authToken = requestOptions && 'authToken' in requestOptions ? requestOptions.authToken : this.defaultAuthToken;\n\n const headers: Record<string, string> = { Accept: 'application/json', ...this.defaultHeaders };\n if (authToken) headers.Authorization = `Bearer ${authToken}`;\n if (body !== undefined) headers['Content-Type'] = 'application/json';\n\n const res = await this.fetchImpl(url, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (!res.ok) throw await toApiError(res, url);\n if (res.status === 204) return undefined as T;\n return nullsToUndefined(await res.json()) as T;\n }\n}\n\n/** JSON has no `undefined`, so every optional field the API omits comes back over the wire as\n * `null`. Recurses through the parsed response and turns those into `undefined` so callers work\n * with idiomatic `foo?.bar` / `foo ?? fallback` instead of `foo !== null`, and so responses match\n * the `T | undefined` types in `generated/openapi.d.ts` (see `scripts/generate-types.mjs`). */\nfunction nullsToUndefined<T>(value: T): T {\n if (value === null) return undefined as T;\n if (Array.isArray(value)) return value.map(nullsToUndefined) as T;\n if (typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value)) result[key] = nullsToUndefined(v);\n return result as T;\n }\n return value;\n}\n","import type { HttpClient } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type {\n Bargain,\n BargainReferenceSource,\n CardType,\n GradingCompany,\n ItemCondition,\n ListResponse,\n PaginationParams,\n} from '../types/index.js';\n\nexport interface ListBargainsParams {\n type?: 'sealed' | 'card' | 'all';\n}\n\nexport interface SearchBargainsParams extends PaginationParams {\n authToken?: string;\n type?: 'sealed' | 'card' | 'all';\n /** Filter by shop technicalName. */\n shop?: string;\n /** Filter by which reference price source qualified the bargain. */\n referenceSource?: BargainReferenceSource;\n /** Minimum discount percentage. Default 10. */\n minDiscount?: number;\n /** Default `true`. */\n inStock?: boolean;\n cardType?: CardType;\n itemCondition?: ItemCondition;\n gradingCompany?: GradingCompany;\n grade?: number;\n /** Free-text search on product name / technicalName. */\n search?: string;\n}\n\nexport class BargainsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /bargains`: current shop listings priced notably below their reference price. Result\n * count is fixed by the API (no `limit`/`skip` on the public tier); `pagination.hasMore` tells\n * you if more exist. */\n list(params: ListBargainsParams = {}): Promise<ListResponse<Bargain>> {\n return this.http.get(`/bargains${toQueryString(params)}`);\n }\n\n /** `GET /bargains/search`: like `list()`, but with real pagination and filters (shop, discount\n * threshold, card condition/grade, free-text search). Premium. */\n search(params: SearchBargainsParams = {}): Promise<ListResponse<Bargain>> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(`/bargains/search${toQueryString(query)}`, { authToken });\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type {\n Card,\n CardType,\n ItemReferencePrices,\n ItemShopMatches,\n ItemSoldPrices,\n ListResponse,\n LivePricingForItem,\n PaginationParams,\n ReferencePriceCardVariant,\n ReferencePriceProvider,\n} from '../types/index.js';\n\nexport interface ListCardsParams extends PaginationParams {\n /** Free-text search over card and set names. */\n search?: string;\n}\n\nexport interface CardMatchesParams extends PaginationParams {\n /** Keep only matches whose shop currently has stock. */\n inStock?: boolean;\n}\n\nexport interface CardReferencePricesParams {\n /** Bearer token for this call. Overrides the client's default `authToken`. */\n authToken?: string;\n /** Rolling window ending today, in days. Ignored when `from`/`to` are supplied. Default 90. */\n days?: number;\n /** `YYYY-MM-DD` */\n from?: string;\n /** `YYYY-MM-DD` */\n to?: string;\n provider?: ReferencePriceProvider;\n cardType?: CardType;\n variant?: ReferencePriceCardVariant;\n}\n\nexport interface CardPricesParams extends PaginationParams {\n authToken?: string;\n}\n\nexport class CardsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /cards`: search or list cards. */\n list(params: ListCardsParams = {}): Promise<ListResponse<Card>> {\n return this.http.get(`/cards${toQueryString(params)}`);\n }\n\n /** `GET /cards/{id}`: fetch one card by its id or technicalName. */\n get(idOrTechnicalName: string): Promise<Card> {\n return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}`);\n }\n\n /** `GET /cards/{id}/matches`: current shop listings matched to this card (latest per shop). */\n matches(idOrTechnicalName: string, params: CardMatchesParams = {}): Promise<ItemShopMatches> {\n return this.http.get(\n `/cards/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(params)}`,\n );\n }\n\n /** `GET /cards/{id}/reference-prices`: Cardmarket/TCGplayer/eBay/Tradera price history. Premium. */\n referencePrices(\n idOrTechnicalName: string,\n params: CardReferencePricesParams = {},\n ): Promise<ItemReferencePrices> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/cards/${encodeURIComponent(idOrTechnicalName)}/reference-prices${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /cards/{id}/prices`: individual marketplace sale records. Premium. */\n prices(idOrTechnicalName: string, params: CardPricesParams = {}): Promise<ItemSoldPrices> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`, {\n authToken,\n });\n }\n\n /** `GET /cards/{id}/pricing/live`: computed fresh for this request, not read from the last\n * stats job. Premium. */\n livePricing(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<LivePricingForItem> {\n return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/pricing/live`, options);\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport type { Expansion, ExpansionContents, ExpansionLivePricing } from '../types/index.js';\n\nexport class ExpansionsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /expansions`: every expansion. Unwrapped to a plain array, nothing to paginate here. */\n async list(): Promise<Expansion[]> {\n const res = await this.http.get<{ data: Expansion[] }>('/expansions');\n return res.data;\n }\n\n /** `GET /expansions/{technicalName}/products`: every card and sealed product in one\n * expansion, kept as separate `cards`/`sealed` groups. */\n products(technicalName: string): Promise<ExpansionContents> {\n return this.http.get(`/expansions/${encodeURIComponent(technicalName)}/products?grouped=true`);\n }\n\n /** `GET /expansions/{technicalName}/products/live-pricing`: computed fresh for every item in\n * this expansion, not read from the last stats job. Premium. */\n livePricing(technicalName: string, options: PremiumOptions = {}): Promise<ExpansionLivePricing> {\n return this.http.get(\n `/expansions/${encodeURIComponent(technicalName)}/products/live-pricing`,\n options,\n );\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type { PackRate } from '../types/index.js';\n\nexport class PackRatesResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /pack-rates`: pull-rate odds for every expansion that has them. Unwrapped to a plain\n * array, nothing to paginate here. */\n async list(): Promise<PackRate[]> {\n const res = await this.http.get<{ data: PackRate[] }>('/pack-rates');\n return res.data;\n }\n\n /** `GET /pack-rates/{expansionId}`: pull-rate odds for one expansion. */\n get(expansionId: string): Promise<PackRate> {\n return this.http.get(`/pack-rates/${encodeURIComponent(expansionId)}`);\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type {\n CardType,\n GradingCompany,\n ItemCondition,\n ItemDailyStats,\n ItemEstimatedValue,\n ItemFullStats,\n ItemStats,\n ItemVariantDailyStats,\n ItemVariantStats,\n ListResponse,\n TopItem,\n} from '../types/index.js';\n\n/** Filters shared by `daily()` and `estimatedValues()`: all narrow which product(s) the stats\n * cover; combine as many as you like. */\nexport interface ProductFilterParams {\n productName?: string;\n technicalName?: string;\n priceChartingId?: string;\n modelNumber?: string;\n /** Category technicalName. */\n category?: string;\n /** Expansion technicalName. */\n expansion?: string;\n}\n\nexport interface DailyPriceStatsParams extends ProductFilterParams {\n /** `YYYY-MM-DD` */\n startDate?: string;\n /** `YYYY-MM-DD` */\n endDate?: string;\n /** Expansion id (ObjectId), an alternative to `expansion` (technicalName). */\n expansionId?: string;\n /** Category id (ObjectId), an alternative to `category` (technicalName). */\n categoryId?: string;\n}\n\nexport interface EstimatedValuesParams extends ProductFilterParams {\n page?: number;\n limit?: number;\n}\n\nexport interface TopProductsParams {\n limit?: number;\n}\n\nexport interface ProductDailyStatsParams {\n authToken?: string;\n /** Number of days to retrieve, from today backwards. Default 30. */\n days?: number;\n}\n\nexport interface ProductByVariantParams {\n authToken?: string;\n /** Number of days to include in the average calculation. Default 30. */\n days?: number;\n}\n\nexport interface ProductDailyByVariantParams {\n authToken?: string;\n cardType: CardType;\n /** Required when `cardType` is `'loose'`. */\n condition?: ItemCondition;\n /** Required when `cardType` is `'graded'`. */\n gradingCompany?: GradingCompany;\n /** Required when `cardType` is `'graded'`. */\n grade?: number;\n /** Number of days to retrieve. Default 30. */\n days?: number;\n}\n\nexport class PriceStatsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /price-stats/daily`: daily average price history, filtered to matching product(s). */\n daily(params: DailyPriceStatsParams = {}): Promise<ListResponse<ItemDailyStats>> {\n return this.http.get(`/price-stats/daily${toQueryString(params)}`);\n }\n\n /** `GET /price-stats/estimated-values`: current estimated market value, filtered to matching\n * product(s). */\n estimatedValues(params: EstimatedValuesParams = {}): Promise<ListResponse<ItemEstimatedValue>> {\n return this.http.get(`/price-stats/estimated-values${toQueryString(params)}`);\n }\n\n /** `GET /price-stats/top-products`: items ranked by shop availability (how many shops carry\n * them), not by price. */\n topProducts(params: TopProductsParams = {}): Promise<ListResponse<TopItem>> {\n return this.http.get(`/price-stats/top-products${toQueryString(params)}`);\n }\n\n /** `GET /price-stats/product/{id}`: daily price history, current estimate, and a variant-count\n * summary for one product. Premium. */\n product(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<ItemStats> {\n return this.http.get(`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}`, options);\n }\n\n /** `GET /price-stats/product/{id}/full`: everything `product()` has, plus the item's current\n * shop matches. Premium. */\n productFull(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<ItemFullStats> {\n return this.http.get(`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/full`, options);\n }\n\n /** `GET /price-stats/product/{id}/daily`: daily price history for one product, with a\n * caller-chosen window. Premium. */\n productDaily(\n idOrTechnicalName: string,\n params: ProductDailyStatsParams = {},\n ): Promise<ItemDailyStats> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /price-stats/product/{id}/daily-last-30`: daily price history for the last 30 days\n * exactly (no window param, for callers that want a stable cache key). Premium. */\n productDailyLast30(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<ItemDailyStats> {\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily-last-30`,\n options,\n );\n }\n\n /** `GET /price-stats/product/{id}/estimated-value`: current estimated value only. Premium. */\n productEstimatedValue(\n idOrTechnicalName: string,\n options: PremiumOptions = {},\n ): Promise<ItemEstimatedValue> {\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/estimated-value`,\n options,\n );\n }\n\n /** `GET /price-stats/product/{id}/by-variant`: price stats broken out per card condition/grade.\n * Premium. */\n productByVariant(\n idOrTechnicalName: string,\n params: ProductByVariantParams = {},\n ): Promise<ItemVariantStats> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/by-variant${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /price-stats/product/{id}/daily-by-variant`: daily price history for one specific\n * condition/grade. `condition` is required for `cardType: 'loose'`; `gradingCompany` and `grade`\n * are required for `cardType: 'graded'`. Premium. */\n productDailyByVariant(\n idOrTechnicalName: string,\n params: ProductDailyByVariantParams,\n ): Promise<ItemVariantDailyStats> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily-by-variant${toQueryString(query)}`,\n { authToken },\n );\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type {\n CardType,\n GradingCompany,\n ItemCondition,\n ItemReferencePrices,\n ItemShopMatches,\n ItemSoldPrices,\n ListResponse,\n LivePricingForItem,\n PaginationParams,\n ReferencePriceProvider,\n SealedProduct,\n} from '../types/index.js';\n\nexport interface ListProductsParams extends PaginationParams {\n /** Whitespace-separated tokens, each matched against the start of a word. */\n search?: string;\n}\n\nexport interface ProductMatchesParams extends PaginationParams {\n /** Keep only matches whose shop currently has stock. */\n inStock?: boolean;\n cardType?: CardType;\n condition?: ItemCondition;\n gradingCompany?: GradingCompany;\n grade?: number;\n}\n\nexport interface ProductReferencePricesParams {\n authToken?: string;\n /** Rolling window ending today, in days. Ignored when `from`/`to` are supplied. Default 90. */\n days?: number;\n /** `YYYY-MM-DD` */\n from?: string;\n /** `YYYY-MM-DD` */\n to?: string;\n provider?: ReferencePriceProvider;\n}\n\nexport interface ProductPricesParams extends PaginationParams {\n authToken?: string;\n}\n\n/** Sealed products: booster boxes, ETBs, tins, and the like. Single cards live under\n * `client.cards` instead. */\nexport class ProductsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /product`: search or list sealed products. */\n list(params: ListProductsParams = {}): Promise<ListResponse<SealedProduct>> {\n return this.http.get(`/product${toQueryString(params)}`);\n }\n\n /** `GET /product/{id}`: fetch one sealed product by its id or technicalName. */\n get(idOrTechnicalName: string): Promise<SealedProduct> {\n return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}`);\n }\n\n /** `GET /product/{id}/matches`: current shop listings matched to this product (latest per shop). */\n matches(idOrTechnicalName: string, params: ProductMatchesParams = {}): Promise<ItemShopMatches> {\n return this.http.get(\n `/product/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(params)}`,\n );\n }\n\n /** `GET /product/{id}/reference-prices`: Cardmarket/TCGplayer/Tradera price history. Premium. */\n referencePrices(\n idOrTechnicalName: string,\n params: ProductReferencePricesParams = {},\n ): Promise<ItemReferencePrices> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/product/${encodeURIComponent(idOrTechnicalName)}/reference-prices${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /product/{id}/prices`: individual marketplace sale records. Premium. */\n prices(idOrTechnicalName: string, params: ProductPricesParams = {}): Promise<ItemSoldPrices> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/product/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /product/{id}/pricing/live`: computed fresh for this request, not read from the last\n * stats job. Premium. */\n livePricing(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<LivePricingForItem> {\n return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}/pricing/live`, options);\n }\n}\n","import type { HttpClient } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type { ItemPriceComparison, ItemShopPriceHistory, ShopPriceHistoryList } from '../types/index.js';\n\nexport interface ShopMatchStatsForProductParams {\n authToken?: string;\n /** `YYYY-MM-DD` */\n startDate?: string;\n /** `YYYY-MM-DD` */\n endDate?: string;\n /** Filter to one shop's technicalName. */\n shop?: string;\n}\n\nexport interface ShopMatchStatsForShopParams {\n authToken?: string;\n /** `YYYY-MM-DD` */\n startDate?: string;\n /** `YYYY-MM-DD` */\n endDate?: string;\n /** Maximum products to return. Default 100. */\n limit?: number;\n}\n\nexport interface CompareShopPricesParams {\n authToken?: string;\n /** Product/card id or technicalName. */\n productId: string;\n /** `YYYY-MM-DD`: defaults to the latest date with data. */\n date?: string;\n}\n\n/** Historical shop-vs-price data, distinct from `client.shopMatches` (which is the current/latest\n * match state). Everything here is premium. */\nexport class ShopMatchStatsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /shop-match-stats/product/{productId}`: one product's price history, broken out per shop. */\n forProduct(\n productId: string,\n params: ShopMatchStatsForProductParams = {},\n ): Promise<ItemShopPriceHistory> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/shop-match-stats/product/${encodeURIComponent(productId)}${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /shop-match-stats/shop/{shop}`: one shop's price history, broken out per product. */\n forShop(shop: string, params: ShopMatchStatsForShopParams = {}): Promise<ShopPriceHistoryList> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(`/shop-match-stats/shop/${encodeURIComponent(shop)}${toQueryString(query)}`, {\n authToken,\n });\n }\n\n /** `GET /shop-match-stats/compare`: one product's price at every shop that carries it, as of\n * one date (defaults to the latest). */\n compare(params: CompareShopPricesParams): Promise<ItemPriceComparison> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(`/shop-match-stats/compare${toQueryString(query)}`, { authToken });\n }\n}\n","import type { HttpClient } from '../http.js';\nimport { toQueryString } from '../http.js';\nimport type { ListResponse, PaginationParams, ShopMatch, ShopMatchesForShop, ShopMatchStats } from '../types/index.js';\n\nexport interface ListShopMatchesParams extends PaginationParams {\n /** Filter to one shop's technicalName. */\n shop?: string;\n inStock?: boolean;\n /** Filter by whether the listing has been resolved to a catalog item. */\n linked?: boolean;\n}\n\nexport interface ShopMatchesForShopParams extends PaginationParams {\n inStock?: boolean;\n}\n\n/** Raw shop-to-catalog match data: what's currently listed where, independent of which item or\n * shop you start from. For \"what does this card cost at each shop\" or \"what's in stock at this\n * shop\", prefer `client.cards.matches()` / `client.products.matches()` /\n * `client.shopMatches.forShop()`. */\nexport class ShopMatchesResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /shop-matches`: every current match across every shop (latest record per url+shop). */\n list(params: ListShopMatchesParams = {}): Promise<ListResponse<ShopMatch>> {\n return this.http.get(`/shop-matches${toQueryString(params)}`);\n }\n\n /** `GET /shop-matches/{shop}`: every current match at one shop (latest record per url). */\n forShop(technicalName: string, params: ShopMatchesForShopParams = {}): Promise<ShopMatchesForShop> {\n return this.http.get(`/shop-matches/${encodeURIComponent(technicalName)}${toQueryString(params)}`);\n }\n\n /** `GET /shop-matches/shops`: match counts per shop (based on latest records only). */\n shopStats(params: PaginationParams = {}): Promise<ListResponse<ShopMatchStats>> {\n return this.http.get(`/shop-matches/shops${toQueryString(params)}`);\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport type { ShopUrlMutationResult } from '../types/index.js';\n\nexport interface SubmitShopUrlParams extends PremiumOptions {\n url: string;\n /** Shop technicalName. Auto-created if it doesn't exist yet. */\n shop: string;\n}\n\nexport interface AssignShopUrlProductParams extends PremiumOptions {\n /** Product/card id to link, or `null` to unlink and let auto-matching resume. */\n productId: string | null;\n}\n\n/** Lets a signed-in subscriber contribute to the catalog: submit a shop URL for scraping, or\n * manually correct which product/card a URL resolves to. Both premium, both mutating. */\nexport class ShopUrlsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `POST /shop-urls/submit`: submit a shop URL for scraping. */\n submit(params: SubmitShopUrlParams): Promise<ShopUrlMutationResult> {\n const { authToken, url, shop } = params;\n return this.http.post('/shop-urls/submit', { url, shop }, { authToken });\n }\n\n /** `PATCH /shop-urls/{id}/product`: manually assign (or clear) the product a shop URL resolves to. */\n assignProduct(\n shopUrlId: string,\n params: AssignShopUrlProductParams,\n ): Promise<ShopUrlMutationResult> {\n const { authToken, productId } = params;\n return this.http.patch(\n `/shop-urls/${encodeURIComponent(shopUrlId)}/product`,\n { productId },\n { authToken },\n );\n }\n}\n","import type { HttpClient } from '../http.js';\nimport { toQueryString } from '../http.js';\nimport type { Shop } from '../types/index.js';\n\nexport interface ListShopsParams {\n active?: boolean;\n}\n\nexport class ShopsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /shops`: every tracked shop. Unwrapped to a plain array, nothing to paginate here. */\n async list(params: ListShopsParams = {}): Promise<Shop[]> {\n const res = await this.http.get<{ data: Shop[] }>(`/shops${toQueryString(params)}`);\n return res.data;\n }\n\n /** `GET /shops/{id}`: fetch one shop by its id or technicalName. */\n get(idOrTechnicalName: string): Promise<Shop> {\n return this.http.get(`/shops/${encodeURIComponent(idOrTechnicalName)}`);\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type { PlatformStats } from '../types/index.js';\n\nexport class StatsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /stats`: platform-wide overview counts (shops, expansions, products, prices tracked). */\n platform(): Promise<PlatformStats> {\n return this.http.get('/stats');\n }\n}\n","import { HttpClient } from './http.js';\nimport { BargainsResource } from './resources/bargains.js';\nimport { CardsResource } from './resources/cards.js';\nimport { ExpansionsResource } from './resources/expansions.js';\nimport { PackRatesResource } from './resources/packRates.js';\nimport { PriceStatsResource } from './resources/priceStats.js';\nimport { ProductsResource } from './resources/products.js';\nimport { ShopMatchStatsResource } from './resources/shopMatchStats.js';\nimport { ShopMatchesResource } from './resources/shopMatches.js';\nimport { ShopUrlsResource } from './resources/shopUrls.js';\nimport { ShopsResource } from './resources/shops.js';\nimport { StatsResource } from './resources/stats.js';\n\nexport const DEFAULT_BASE_URL = 'https://api.tcgpriser.se';\n\n/** Local dev, self-hosting and testing overrides. Most integrations never touch these. */\nexport interface TcgPriserAdvancedOptions {\n /** Point at a local dev server or a self-hosted instance. Defaults to production. */\n baseUrl?: string;\n /** Extra headers sent on every request, e.g. a custom `User-Agent`. */\n headers?: Record<string, string>;\n /** Swap in a different `fetch` (older Node, testing, a proxying agent). Defaults to global `fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface TcgPriserOptions {\n /**\n * A signed-in subscriber's JWT. Only needed for premium methods (`cards.prices()`,\n * `priceStats.product()`, `bargains.search()` etc.); public methods work fine without it.\n * This client has no login flow of its own, so bring your own token.\n *\n * Every premium method also accepts its own `authToken` to override this per call. Useful when\n * one server-side client is shared across requests for several different signed-in users.\n */\n authToken?: string;\n /** Local dev / self-hosting / testing overrides. Leave unset unless you know you need it. */\n advanced?: TcgPriserAdvancedOptions;\n}\n\n/**\n * Client for the tcgpriser.se API: Pokémon TCG price data, catalog, shop matches and bargains for\n * shops tracked in Sweden.\n *\n * ```ts\n * import { TcgPriser } from 'tcgpriser';\n *\n * const tcgpriser = new TcgPriser();\n * const card = await tcgpriser.cards.get('mega-evolution-ascended-heroes-fezandipiti-ex');\n * console.log(card.retailPrice, card.lowestShopOffer?.shop.name);\n * ```\n *\n * That example needs no token. Most of the API is public (https://api.tcgpriser.se/docs). A\n * smaller set of premium methods (live pricing, per-condition history, shop comparison, bargain\n * search, shop-URL submission) need a subscriber's JWT (https://api.tcgpriser.se/premium-docs):\n *\n * ```ts\n * const tcgpriser = new TcgPriser(myJwt); // shorthand for { authToken: myJwt }\n * await tcgpriser.cards.livePricing('fezandipiti-ex');\n * ```\n */\nexport class TcgPriser {\n readonly cards: CardsResource;\n readonly products: ProductsResource;\n readonly expansions: ExpansionsResource;\n readonly shops: ShopsResource;\n readonly shopMatches: ShopMatchesResource;\n readonly shopMatchStats: ShopMatchStatsResource;\n readonly shopUrls: ShopUrlsResource;\n readonly priceStats: PriceStatsResource;\n readonly bargains: BargainsResource;\n readonly packRates: PackRatesResource;\n readonly stats: StatsResource;\n\n /**\n * @param optionsOrAuthToken A subscriber JWT (`new TcgPriser(myJwt)`), a full\n * `TcgPriserOptions` object, or omit it entirely for an anonymous, public-only client.\n */\n constructor(optionsOrAuthToken?: string | TcgPriserOptions) {\n const options: TcgPriserOptions =\n typeof optionsOrAuthToken === 'string' ? { authToken: optionsOrAuthToken } : (optionsOrAuthToken ?? {});\n const advanced = options.advanced ?? {};\n\n if (!advanced.fetch && typeof fetch === 'undefined') {\n throw new Error(\n 'tcgpriser: no global fetch found. Pass { advanced: { fetch } } explicitly on Node < 18, or run on Node 18+.',\n );\n }\n\n const http = new HttpClient({\n baseUrl: advanced.baseUrl ?? DEFAULT_BASE_URL,\n // Bound to globalThis: both browsers and Node's undici implement fetch as a method that\n // checks its receiver, so an unbound reference throws \"Illegal invocation\" the moment it's\n // called through anything other than `window.fetch(...)`/`globalThis.fetch(...)` — which is\n // exactly what happens once HttpClient stores it and calls `this.fetchImpl(...)`.\n fetch: advanced.fetch ?? fetch.bind(globalThis),\n headers: advanced.headers,\n authToken: options.authToken,\n });\n\n this.cards = new CardsResource(http);\n this.products = new ProductsResource(http);\n this.expansions = new ExpansionsResource(http);\n this.shops = new ShopsResource(http);\n this.shopMatches = new ShopMatchesResource(http);\n this.shopMatchStats = new ShopMatchStatsResource(http);\n this.shopUrls = new ShopUrlsResource(http);\n this.priceStats = new PriceStatsResource(http);\n this.bargains = new BargainsResource(http);\n this.packRates = new PackRatesResource(http);\n this.stats = new StatsResource(http);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/bargains.ts","../src/resources/cards.ts","../src/resources/expansions.ts","../src/resources/packRates.ts","../src/resources/priceStats.ts","../src/resources/products.ts","../src/resources/shopMatchStats.ts","../src/resources/shopMatches.ts","../src/resources/shopUrls.ts","../src/resources/shops.ts","../src/resources/stats.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/** The stable error codes the API's `error.code` field can hold. */\nexport type TcgPriserErrorCode =\n | 'validationFailed'\n | 'unauthorized'\n | 'forbidden'\n | 'notFound'\n | 'conflict'\n | 'readOnlyField'\n | 'rateLimited'\n | 'premiumRequired'\n | 'internalError'\n /** Response body wasn't the `{ error: { code, message } }` shape. Probably a proxy or gateway\n * error in front of the API. */\n | 'unknown';\n\n/** Thrown for any non-2xx response. Carries the parsed `{ code, message }` when the body matched\n * the API's error envelope, plus the raw status/body regardless so nothing gets lost. */\nexport class TcgPriserError extends Error {\n readonly statusCode: number;\n readonly statusText: string;\n readonly url: string;\n readonly code: TcgPriserErrorCode;\n readonly details: unknown;\n /** The raw response body, for debugging when `code`/`details` don't cover what you need. */\n readonly body: string;\n\n constructor(params: {\n statusCode: number;\n statusText: string;\n url: string;\n code: TcgPriserErrorCode;\n message: string;\n details?: unknown;\n body: string;\n }) {\n super(`tcgpriser: ${params.statusCode} ${params.code} - ${params.message} (${params.url})`);\n this.name = 'TcgPriserError';\n this.statusCode = params.statusCode;\n this.statusText = params.statusText;\n this.url = params.url;\n this.code = params.code;\n this.details = params.details;\n this.body = params.body;\n }\n}\n","import { TcgPriserError, type TcgPriserErrorCode } from './errors.js';\n\nexport type QueryValue = string | number | boolean | undefined | null;\n\n/**\n * Builds a query string from a params object, dropping `undefined`/`null` entries so callers can\n * pass params straight through without filtering first.\n *\n * Takes a generic `object` instead of `Record<string, QueryValue>` on purpose. The params\n * interfaces (`ListCardsParams` etc.) intentionally have no index signature, otherwise any string\n * key would type-check.\n */\nexport function toQueryString<T extends object>(params: T): string {\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params) as [string, QueryValue][]) {\n if (value === undefined || value === null) continue;\n search.set(key, String(value));\n }\n const query = search.toString();\n return query ? `?${query}` : '';\n}\n\n/** Splits `authToken` off a params object so it goes on the `Authorization` header, not through\n * `toQueryString`. A bearer token has no business in a URL: query strings end up in server logs,\n * browser history, `Referer` headers. */\nexport function splitAuthToken<T extends { authToken?: string }>(\n params: T,\n): [Omit<T, 'authToken'>, string | undefined] {\n const { authToken, ...rest } = params;\n return [rest, authToken];\n}\n\nconst KNOWN_ERROR_CODES: ReadonlySet<string> = new Set([\n 'validationFailed',\n 'unauthorized',\n 'forbidden',\n 'notFound',\n 'conflict',\n 'readOnlyField',\n 'rateLimited',\n 'premiumRequired',\n 'internalError',\n]);\n\nasync function toApiError(res: Response, url: string): Promise<TcgPriserError> {\n const body = await res.text();\n let code: TcgPriserErrorCode = 'unknown';\n let message = res.statusText || 'Request failed';\n let details: unknown;\n\n try {\n const parsed = JSON.parse(body) as { error?: { code?: string; message?: string; details?: unknown } };\n if (parsed.error?.code && KNOWN_ERROR_CODES.has(parsed.error.code)) {\n code = parsed.error.code as TcgPriserErrorCode;\n }\n if (parsed.error?.message) message = parsed.error.message;\n details = parsed.error?.details;\n } catch {\n // Not the standard error envelope, maybe a proxy's HTML error page. Fall back to the status\n // text and leave `body` for anyone who wants to dig in.\n }\n\n return new TcgPriserError({ statusCode: res.status, statusText: res.statusText, url, code, message, details, body });\n}\n\nexport interface HttpClientOptions {\n baseUrl: string;\n fetch: typeof fetch;\n headers?: Record<string, string>;\n /** Default bearer token for premium endpoints, used when a call doesn't pass its own `authToken`. */\n authToken?: string;\n}\n\n/** Per-call auth override for a premium endpoint, on top of the client's default `authToken`.\n * Every premium method takes one of these, either standalone or merged into its params object via\n * `splitAuthToken`. */\nexport interface PremiumOptions {\n /** Overrides the client's default `authToken` for this call. Pass `undefined` explicitly to\n * force an anonymous request even when the client has a default token. */\n authToken?: string;\n}\n\n/** Thin wrapper around `fetch`: joins the base URL, adds default headers, turns non-2xx responses\n * into a `TcgPriserError`. Every resource method goes through this instead of calling `fetch`\n * directly. */\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly defaultHeaders: Record<string, string>;\n private readonly defaultAuthToken: string | undefined;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n this.fetchImpl = options.fetch;\n this.defaultHeaders = options.headers ?? {};\n this.defaultAuthToken = options.authToken;\n }\n\n get<T>(path: string, requestOptions?: PremiumOptions): Promise<T> {\n return this.request<T>('GET', path, undefined, requestOptions);\n }\n\n post<T>(path: string, body: unknown, requestOptions?: PremiumOptions): Promise<T> {\n return this.request<T>('POST', path, body, requestOptions);\n }\n\n patch<T>(path: string, body: unknown, requestOptions?: PremiumOptions): Promise<T> {\n return this.request<T>('PATCH', path, body, requestOptions);\n }\n\n private async request<T>(\n method: string,\n path: string,\n body: unknown,\n requestOptions?: PremiumOptions,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const authToken = requestOptions && 'authToken' in requestOptions ? requestOptions.authToken : this.defaultAuthToken;\n\n const headers: Record<string, string> = { Accept: 'application/json', ...this.defaultHeaders };\n if (authToken) headers.Authorization = `Bearer ${authToken}`;\n if (body !== undefined) headers['Content-Type'] = 'application/json';\n\n const res = await this.fetchImpl(url, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (!res.ok) throw await toApiError(res, url);\n if (res.status === 204) return undefined as T;\n return nullsToUndefined(await res.json()) as T;\n }\n}\n\n/** JSON has no `undefined`, so every optional field the API omits comes back over the wire as\n * `null`. Recurses through the parsed response and turns those into `undefined` so callers work\n * with idiomatic `foo?.bar` / `foo ?? fallback` instead of `foo !== null`, and so responses match\n * the `T | undefined` types in `generated/openapi.d.ts` (see `scripts/generate-types.mjs`). */\nfunction nullsToUndefined<T>(value: T): T {\n if (value === null) return undefined as T;\n if (Array.isArray(value)) return value.map(nullsToUndefined) as T;\n if (typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value)) result[key] = nullsToUndefined(v);\n return result as T;\n }\n return value;\n}\n","import type { HttpClient } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type {\n Bargain,\n BargainReferenceSource,\n CardType,\n GradingCompany,\n ItemCondition,\n ListResponse,\n PaginationParams,\n} from '../types/index.js';\n\nexport interface ListBargainsParams {\n type?: 'sealed' | 'card' | 'all';\n}\n\nexport interface SearchBargainsParams extends PaginationParams {\n authToken?: string;\n type?: 'sealed' | 'card' | 'all';\n /** Filter by shop technicalName. */\n shop?: string;\n /** Filter by which reference price source qualified the bargain. */\n referenceSource?: BargainReferenceSource;\n /** Minimum discount percentage. Default 10. */\n minDiscount?: number;\n /** Default `true`. */\n inStock?: boolean;\n cardType?: CardType;\n itemCondition?: ItemCondition;\n gradingCompany?: GradingCompany;\n grade?: number;\n /** Free-text search on product name / technicalName. */\n search?: string;\n}\n\nexport class BargainsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /bargains`: current shop listings priced notably below their reference price. Result\n * count is fixed by the API (no `limit`/`skip` on the public tier); `pagination.hasMore` tells\n * you if more exist. */\n list(params: ListBargainsParams = {}): Promise<ListResponse<Bargain>> {\n return this.http.get(`/bargains${toQueryString(params)}`);\n }\n\n /** `GET /bargains/search`: like `list()`, but with real pagination and filters (shop, discount\n * threshold, card condition/grade, free-text search). Premium. */\n search(params: SearchBargainsParams = {}): Promise<ListResponse<Bargain>> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(`/bargains/search${toQueryString(query)}`, { authToken });\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type {\n Card,\n CardType,\n CatalogItemPricing,\n ItemReferencePrices,\n ItemShopMatches,\n ItemSoldPrices,\n ListResponse,\n LivePricingForItem,\n PaginationParams,\n ReferencePriceCardVariant,\n ReferencePriceProvider,\n} from '../types/index.js';\n\nexport interface ListCardsParams extends PaginationParams {\n /** Free-text search over card and set names. */\n search?: string;\n}\n\nexport interface CardMatchesParams extends PaginationParams {\n /** Keep only matches whose shop currently has stock. */\n inStock?: boolean;\n}\n\nexport interface CardReferencePricesParams {\n /** Bearer token for this call. Overrides the client's default `authToken`. */\n authToken?: string;\n /** Rolling window ending today, in days. Ignored when `from`/`to` are supplied. Default 90. */\n days?: number;\n /** `YYYY-MM-DD` */\n from?: string;\n /** `YYYY-MM-DD` */\n to?: string;\n provider?: ReferencePriceProvider;\n cardType?: CardType;\n variant?: ReferencePriceCardVariant;\n}\n\nexport interface CardPricesParams extends PaginationParams {\n authToken?: string;\n}\n\nexport class CardsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /cards`: search or list cards. */\n list(params: ListCardsParams = {}): Promise<ListResponse<Card>> {\n return this.http.get(`/cards${toQueryString(params)}`);\n }\n\n /** `GET /cards/{id}`: fetch one card by its id or technicalName. */\n get(idOrTechnicalName: string): Promise<Card> {\n return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}`);\n }\n\n /** `GET /cards/{id}/matches`: current shop listings matched to this card (latest per shop). */\n matches(idOrTechnicalName: string, params: CardMatchesParams = {}): Promise<ItemShopMatches> {\n return this.http.get(\n `/cards/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(params)}`,\n );\n }\n\n /** `GET /cards/{id}/reference-prices`: Cardmarket/TCGplayer/eBay/Tradera price history. Premium. */\n referencePrices(\n idOrTechnicalName: string,\n params: CardReferencePricesParams = {},\n ): Promise<ItemReferencePrices> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/cards/${encodeURIComponent(idOrTechnicalName)}/reference-prices${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /cards/{id}/prices`: individual marketplace sale records. Premium. */\n prices(idOrTechnicalName: string, params: CardPricesParams = {}): Promise<ItemSoldPrices> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`, {\n authToken,\n });\n }\n\n /** `GET /cards/{id}/pricing/live`: computed fresh for this request, not read from the last\n * stats job. Premium. */\n livePricing(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<LivePricingForItem> {\n return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/pricing/live`, options);\n }\n\n /** `GET /cards/{id}/pricing`: this card's current pricing snapshot — `retailPrice`,\n * `estimatedValue`, `lowestShopOffer`, `referencePriceSnapshotsByProvider` — refreshed once a day\n * by the nightly pricing/scraper jobs. `get()` returns content only; this is the separate,\n * shorter-cached call for the part of a card that actually changes day to day. */\n pricing(idOrTechnicalName: string): Promise<CatalogItemPricing> {\n return this.http.get(`/cards/${encodeURIComponent(idOrTechnicalName)}/pricing`);\n }\n\n /** `GET /cards/pricing`: pricing for up to 200 cards in one request, keyed by `id` — the batch\n * counterpart to `pricing()`, for a page of results (a search page, an expansion's contents) that\n * needs pricing for many items at once. Unlike `get()`/`pricing()`, this only accepts `id`s, not\n * technicalNames — pass the `id`s already on the cards you fetched. Ids with no match are\n * silently omitted from the result rather than causing an error. */\n pricingBatch(ids: string[]): Promise<ListResponse<CatalogItemPricing>> {\n return this.http.get(`/cards/pricing?ids=${ids.map(encodeURIComponent).join(',')}`);\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport type { Expansion, ExpansionContents, ExpansionLivePricing } from '../types/index.js';\n\nexport class ExpansionsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /expansions`: every expansion. Unwrapped to a plain array, nothing to paginate here. */\n async list(): Promise<Expansion[]> {\n const res = await this.http.get<{ data: Expansion[] }>('/expansions');\n return res.data;\n }\n\n /** `GET /expansions/{technicalName}/products`: every card and sealed product in one\n * expansion, kept as separate `cards`/`sealed` groups. Content only, no pricing fields — pass the\n * `id`s from the result to `client.cards.pricingBatch()` / `client.products.pricingBatch()` if you\n * need pricing too. This mirrors the API 1:1 rather than fetching pricing for you, since pricing\n * for every item in an expansion is a second, separately-cached call the caller may not want. */\n products(technicalName: string): Promise<ExpansionContents> {\n return this.http.get(`/expansions/${encodeURIComponent(technicalName)}/products?grouped=true`);\n }\n\n /** `GET /expansions/{technicalName}/products/live-pricing`: computed fresh for every item in\n * this expansion, not read from the last stats job. Premium. */\n livePricing(technicalName: string, options: PremiumOptions = {}): Promise<ExpansionLivePricing> {\n return this.http.get(\n `/expansions/${encodeURIComponent(technicalName)}/products/live-pricing`,\n options,\n );\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type { PackRate } from '../types/index.js';\n\nexport class PackRatesResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /pack-rates`: pull-rate odds for every expansion that has them. Unwrapped to a plain\n * array, nothing to paginate here. */\n async list(): Promise<PackRate[]> {\n const res = await this.http.get<{ data: PackRate[] }>('/pack-rates');\n return res.data;\n }\n\n /** `GET /pack-rates/{expansionId}`: pull-rate odds for one expansion. */\n get(expansionId: string): Promise<PackRate> {\n return this.http.get(`/pack-rates/${encodeURIComponent(expansionId)}`);\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type {\n CardType,\n GradingCompany,\n ItemCondition,\n ItemDailyStats,\n ItemEstimatedValue,\n ItemFullStats,\n ItemStats,\n ItemVariantDailyStats,\n ItemVariantStats,\n ListResponse,\n TopItem,\n} from '../types/index.js';\n\n/** Filters shared by `daily()` and `estimatedValues()`: all narrow which product(s) the stats\n * cover; combine as many as you like. */\nexport interface ProductFilterParams {\n productName?: string;\n technicalName?: string;\n priceChartingId?: string;\n modelNumber?: string;\n /** Category technicalName. */\n category?: string;\n /** Expansion technicalName. */\n expansion?: string;\n}\n\nexport interface DailyPriceStatsParams extends ProductFilterParams {\n /** `YYYY-MM-DD` */\n startDate?: string;\n /** `YYYY-MM-DD` */\n endDate?: string;\n /** Expansion id (ObjectId), an alternative to `expansion` (technicalName). */\n expansionId?: string;\n /** Category id (ObjectId), an alternative to `category` (technicalName). */\n categoryId?: string;\n}\n\nexport interface EstimatedValuesParams extends ProductFilterParams {\n page?: number;\n limit?: number;\n}\n\nexport interface TopProductsParams {\n limit?: number;\n}\n\nexport interface ProductDailyStatsParams {\n authToken?: string;\n /** Number of days to retrieve, from today backwards. Default 30. */\n days?: number;\n}\n\nexport interface ProductByVariantParams {\n authToken?: string;\n /** Number of days to include in the average calculation. Default 30. */\n days?: number;\n}\n\nexport interface ProductDailyByVariantParams {\n authToken?: string;\n cardType: CardType;\n /** Required when `cardType` is `'loose'`. */\n condition?: ItemCondition;\n /** Required when `cardType` is `'graded'`. */\n gradingCompany?: GradingCompany;\n /** Required when `cardType` is `'graded'`. */\n grade?: number;\n /** Number of days to retrieve. Default 30. */\n days?: number;\n}\n\nexport class PriceStatsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /price-stats/daily`: daily average price history, filtered to matching product(s). */\n daily(params: DailyPriceStatsParams = {}): Promise<ListResponse<ItemDailyStats>> {\n return this.http.get(`/price-stats/daily${toQueryString(params)}`);\n }\n\n /** `GET /price-stats/estimated-values`: current estimated market value, filtered to matching\n * product(s). */\n estimatedValues(params: EstimatedValuesParams = {}): Promise<ListResponse<ItemEstimatedValue>> {\n return this.http.get(`/price-stats/estimated-values${toQueryString(params)}`);\n }\n\n /** `GET /price-stats/top-products`: items ranked by shop availability (how many shops carry\n * them), not by price. */\n topProducts(params: TopProductsParams = {}): Promise<ListResponse<TopItem>> {\n return this.http.get(`/price-stats/top-products${toQueryString(params)}`);\n }\n\n /** `GET /price-stats/product/{id}`: daily price history, current estimate, and a variant-count\n * summary for one product. Premium. */\n product(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<ItemStats> {\n return this.http.get(`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}`, options);\n }\n\n /** `GET /price-stats/product/{id}/full`: everything `product()` has, plus the item's current\n * shop matches. Premium. */\n productFull(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<ItemFullStats> {\n return this.http.get(`/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/full`, options);\n }\n\n /** `GET /price-stats/product/{id}/daily`: daily price history for one product, with a\n * caller-chosen window. Premium. */\n productDaily(\n idOrTechnicalName: string,\n params: ProductDailyStatsParams = {},\n ): Promise<ItemDailyStats> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /price-stats/product/{id}/daily-last-30`: daily price history for the last 30 days\n * exactly (no window param, for callers that want a stable cache key). Premium. */\n productDailyLast30(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<ItemDailyStats> {\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily-last-30`,\n options,\n );\n }\n\n /** `GET /price-stats/product/{id}/estimated-value`: current estimated value only. Premium. */\n productEstimatedValue(\n idOrTechnicalName: string,\n options: PremiumOptions = {},\n ): Promise<ItemEstimatedValue> {\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/estimated-value`,\n options,\n );\n }\n\n /** `GET /price-stats/product/{id}/by-variant`: price stats broken out per card condition/grade.\n * Premium. */\n productByVariant(\n idOrTechnicalName: string,\n params: ProductByVariantParams = {},\n ): Promise<ItemVariantStats> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/by-variant${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /price-stats/product/{id}/daily-by-variant`: daily price history for one specific\n * condition/grade. `condition` is required for `cardType: 'loose'`; `gradingCompany` and `grade`\n * are required for `cardType: 'graded'`. Premium. */\n productDailyByVariant(\n idOrTechnicalName: string,\n params: ProductDailyByVariantParams,\n ): Promise<ItemVariantDailyStats> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/price-stats/product/${encodeURIComponent(idOrTechnicalName)}/daily-by-variant${toQueryString(query)}`,\n { authToken },\n );\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type {\n CardType,\n CatalogItemPricing,\n GradingCompany,\n ItemCondition,\n ItemReferencePrices,\n ItemShopMatches,\n ItemSoldPrices,\n ListResponse,\n LivePricingForItem,\n PaginationParams,\n ReferencePriceProvider,\n SealedProduct,\n} from '../types/index.js';\n\nexport interface ListProductsParams extends PaginationParams {\n /** Whitespace-separated tokens, each matched against the start of a word. */\n search?: string;\n}\n\nexport interface ProductMatchesParams extends PaginationParams {\n /** Keep only matches whose shop currently has stock. */\n inStock?: boolean;\n cardType?: CardType;\n condition?: ItemCondition;\n gradingCompany?: GradingCompany;\n grade?: number;\n}\n\nexport interface ProductReferencePricesParams {\n authToken?: string;\n /** Rolling window ending today, in days. Ignored when `from`/`to` are supplied. Default 90. */\n days?: number;\n /** `YYYY-MM-DD` */\n from?: string;\n /** `YYYY-MM-DD` */\n to?: string;\n provider?: ReferencePriceProvider;\n}\n\nexport interface ProductPricesParams extends PaginationParams {\n authToken?: string;\n}\n\n/** Sealed products: booster boxes, ETBs, tins, and the like. Single cards live under\n * `client.cards` instead. */\nexport class ProductsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /product`: search or list sealed products. */\n list(params: ListProductsParams = {}): Promise<ListResponse<SealedProduct>> {\n return this.http.get(`/product${toQueryString(params)}`);\n }\n\n /** `GET /product/{id}`: fetch one sealed product by its id or technicalName. */\n get(idOrTechnicalName: string): Promise<SealedProduct> {\n return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}`);\n }\n\n /** `GET /product/{id}/matches`: current shop listings matched to this product (latest per shop). */\n matches(idOrTechnicalName: string, params: ProductMatchesParams = {}): Promise<ItemShopMatches> {\n return this.http.get(\n `/product/${encodeURIComponent(idOrTechnicalName)}/matches${toQueryString(params)}`,\n );\n }\n\n /** `GET /product/{id}/reference-prices`: Cardmarket/TCGplayer/Tradera price history. Premium. */\n referencePrices(\n idOrTechnicalName: string,\n params: ProductReferencePricesParams = {},\n ): Promise<ItemReferencePrices> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/product/${encodeURIComponent(idOrTechnicalName)}/reference-prices${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /product/{id}/prices`: individual marketplace sale records. Premium. */\n prices(idOrTechnicalName: string, params: ProductPricesParams = {}): Promise<ItemSoldPrices> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/product/${encodeURIComponent(idOrTechnicalName)}/prices${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /product/{id}/pricing/live`: computed fresh for this request, not read from the last\n * stats job. Premium. */\n livePricing(idOrTechnicalName: string, options: PremiumOptions = {}): Promise<LivePricingForItem> {\n return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}/pricing/live`, options);\n }\n\n /** `GET /product/{id}/pricing`: this product's current pricing snapshot — `retailPrice`,\n * `estimatedValue`, `lowestShopOffer`, `referencePriceSnapshotsByProvider` — refreshed once a day\n * by the nightly pricing/scraper jobs. `get()` returns content only; this is the separate,\n * shorter-cached call for the part of a product that actually changes day to day. */\n pricing(idOrTechnicalName: string): Promise<CatalogItemPricing> {\n return this.http.get(`/product/${encodeURIComponent(idOrTechnicalName)}/pricing`);\n }\n\n /** `GET /product/pricing`: pricing for up to 200 sealed products in one request, keyed by `id` —\n * the batch counterpart to `pricing()`, for a page of results (a search page, an expansion's\n * contents) that needs pricing for many items at once. Unlike `get()`/`pricing()`, this only\n * accepts `id`s, not technicalNames — pass the `id`s already on the products you fetched. Ids with\n * no match are silently omitted from the result rather than causing an error. */\n pricingBatch(ids: string[]): Promise<ListResponse<CatalogItemPricing>> {\n return this.http.get(`/product/pricing?ids=${ids.map(encodeURIComponent).join(',')}`);\n }\n}\n","import type { HttpClient } from '../http.js';\nimport { splitAuthToken, toQueryString } from '../http.js';\nimport type { ItemPriceComparison, ItemShopPriceHistory, ShopPriceHistoryList } from '../types/index.js';\n\nexport interface ShopMatchStatsForProductParams {\n authToken?: string;\n /** `YYYY-MM-DD` */\n startDate?: string;\n /** `YYYY-MM-DD` */\n endDate?: string;\n /** Filter to one shop's technicalName. */\n shop?: string;\n}\n\nexport interface ShopMatchStatsForShopParams {\n authToken?: string;\n /** `YYYY-MM-DD` */\n startDate?: string;\n /** `YYYY-MM-DD` */\n endDate?: string;\n /** Maximum products to return. Default 100. */\n limit?: number;\n}\n\nexport interface CompareShopPricesParams {\n authToken?: string;\n /** Product/card id or technicalName. */\n productId: string;\n /** `YYYY-MM-DD`: defaults to the latest date with data. */\n date?: string;\n}\n\n/** Historical shop-vs-price data, distinct from `client.shopMatches` (which is the current/latest\n * match state). Everything here is premium. */\nexport class ShopMatchStatsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /shop-match-stats/product/{productId}`: one product's price history, broken out per shop. */\n forProduct(\n productId: string,\n params: ShopMatchStatsForProductParams = {},\n ): Promise<ItemShopPriceHistory> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(\n `/shop-match-stats/product/${encodeURIComponent(productId)}${toQueryString(query)}`,\n { authToken },\n );\n }\n\n /** `GET /shop-match-stats/shop/{shop}`: one shop's price history, broken out per product. */\n forShop(shop: string, params: ShopMatchStatsForShopParams = {}): Promise<ShopPriceHistoryList> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(`/shop-match-stats/shop/${encodeURIComponent(shop)}${toQueryString(query)}`, {\n authToken,\n });\n }\n\n /** `GET /shop-match-stats/compare`: one product's price at every shop that carries it, as of\n * one date (defaults to the latest). */\n compare(params: CompareShopPricesParams): Promise<ItemPriceComparison> {\n const [query, authToken] = splitAuthToken(params);\n return this.http.get(`/shop-match-stats/compare${toQueryString(query)}`, { authToken });\n }\n}\n","import type { HttpClient } from '../http.js';\nimport { toQueryString } from '../http.js';\nimport type { ListResponse, PaginationParams, ShopMatch, ShopMatchesForShop, ShopMatchStats } from '../types/index.js';\n\nexport interface ListShopMatchesParams extends PaginationParams {\n /** Filter to one shop's technicalName. */\n shop?: string;\n inStock?: boolean;\n /** Filter by whether the listing has been resolved to a catalog item. */\n linked?: boolean;\n}\n\nexport interface ShopMatchesForShopParams extends PaginationParams {\n inStock?: boolean;\n}\n\n/** Raw shop-to-catalog match data: what's currently listed where, independent of which item or\n * shop you start from. For \"what does this card cost at each shop\" or \"what's in stock at this\n * shop\", prefer `client.cards.matches()` / `client.products.matches()` /\n * `client.shopMatches.forShop()`. */\nexport class ShopMatchesResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /shop-matches`: every current match across every shop (latest record per url+shop). */\n list(params: ListShopMatchesParams = {}): Promise<ListResponse<ShopMatch>> {\n return this.http.get(`/shop-matches${toQueryString(params)}`);\n }\n\n /** `GET /shop-matches/{shop}`: every current match at one shop (latest record per url). */\n forShop(technicalName: string, params: ShopMatchesForShopParams = {}): Promise<ShopMatchesForShop> {\n return this.http.get(`/shop-matches/${encodeURIComponent(technicalName)}${toQueryString(params)}`);\n }\n\n /** `GET /shop-matches/shops`: match counts per shop (based on latest records only). */\n shopStats(params: PaginationParams = {}): Promise<ListResponse<ShopMatchStats>> {\n return this.http.get(`/shop-matches/shops${toQueryString(params)}`);\n }\n}\n","import type { HttpClient, PremiumOptions } from '../http.js';\nimport type { ShopUrlMutationResult } from '../types/index.js';\n\nexport interface SubmitShopUrlParams extends PremiumOptions {\n url: string;\n /** Shop technicalName. Auto-created if it doesn't exist yet. */\n shop: string;\n}\n\nexport interface AssignShopUrlProductParams extends PremiumOptions {\n /** Product/card id to link, or `null` to unlink and let auto-matching resume. */\n productId: string | null;\n}\n\n/** Lets a signed-in subscriber contribute to the catalog: submit a shop URL for scraping, or\n * manually correct which product/card a URL resolves to. Both premium, both mutating. */\nexport class ShopUrlsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `POST /shop-urls/submit`: submit a shop URL for scraping. */\n submit(params: SubmitShopUrlParams): Promise<ShopUrlMutationResult> {\n const { authToken, url, shop } = params;\n return this.http.post('/shop-urls/submit', { url, shop }, { authToken });\n }\n\n /** `PATCH /shop-urls/{id}/product`: manually assign (or clear) the product a shop URL resolves to. */\n assignProduct(\n shopUrlId: string,\n params: AssignShopUrlProductParams,\n ): Promise<ShopUrlMutationResult> {\n const { authToken, productId } = params;\n return this.http.patch(\n `/shop-urls/${encodeURIComponent(shopUrlId)}/product`,\n { productId },\n { authToken },\n );\n }\n}\n","import type { HttpClient } from '../http.js';\nimport { toQueryString } from '../http.js';\nimport type { Shop } from '../types/index.js';\n\nexport interface ListShopsParams {\n active?: boolean;\n}\n\nexport class ShopsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /shops`: every tracked shop. Unwrapped to a plain array, nothing to paginate here. */\n async list(params: ListShopsParams = {}): Promise<Shop[]> {\n const res = await this.http.get<{ data: Shop[] }>(`/shops${toQueryString(params)}`);\n return res.data;\n }\n\n /** `GET /shops/{id}`: fetch one shop by its id or technicalName. */\n get(idOrTechnicalName: string): Promise<Shop> {\n return this.http.get(`/shops/${encodeURIComponent(idOrTechnicalName)}`);\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type { PlatformStats } from '../types/index.js';\n\nexport class StatsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** `GET /stats`: platform-wide overview counts (shops, expansions, products, prices tracked). */\n platform(): Promise<PlatformStats> {\n return this.http.get('/stats');\n }\n}\n","import { HttpClient } from './http.js';\nimport { BargainsResource } from './resources/bargains.js';\nimport { CardsResource } from './resources/cards.js';\nimport { ExpansionsResource } from './resources/expansions.js';\nimport { PackRatesResource } from './resources/packRates.js';\nimport { PriceStatsResource } from './resources/priceStats.js';\nimport { ProductsResource } from './resources/products.js';\nimport { ShopMatchStatsResource } from './resources/shopMatchStats.js';\nimport { ShopMatchesResource } from './resources/shopMatches.js';\nimport { ShopUrlsResource } from './resources/shopUrls.js';\nimport { ShopsResource } from './resources/shops.js';\nimport { StatsResource } from './resources/stats.js';\n\nexport const DEFAULT_BASE_URL = 'https://api.tcgpriser.se';\n\n/** Local dev, self-hosting and testing overrides. Most integrations never touch these. */\nexport interface TcgPriserAdvancedOptions {\n /** Point at a local dev server or a self-hosted instance. Defaults to production. */\n baseUrl?: string;\n /** Extra headers sent on every request, e.g. a custom `User-Agent`. */\n headers?: Record<string, string>;\n /** Swap in a different `fetch` (older Node, testing, a proxying agent). Defaults to global `fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface TcgPriserOptions {\n /**\n * A per-account API token, generated from your account page at tcgpriser.se/account/api-token.\n * Only needed for premium methods (`cards.prices()`, `priceStats.product()`,\n * `bargains.search()` etc.); public methods work fine without it. This client has no login flow\n * of its own, and doesn't need one: the API token is a long-lived, revocable secret made for\n * exactly this, so there's no OAuth dance to drive.\n *\n * Every premium method also accepts its own `authToken` to override this per call. Useful when\n * one server-side client is shared across requests for several different signed-in users.\n */\n authToken?: string;\n /** Local dev / self-hosting / testing overrides. Leave unset unless you know you need it. */\n advanced?: TcgPriserAdvancedOptions;\n}\n\n/**\n * Client for the tcgpriser.se API: Pokémon TCG price data, catalog, shop matches and bargains for\n * shops tracked in Sweden.\n *\n * ```ts\n * import { TcgPriser } from 'tcgpriser';\n *\n * const tcgpriser = new TcgPriser();\n * const card = await tcgpriser.cards.get('mega-evolution-ascended-heroes-fezandipiti-ex');\n * console.log(card.retailPrice, card.lowestShopOffer?.shop.name);\n * ```\n *\n * That example needs no token. Most of the API is public (https://api.tcgpriser.se/docs). A\n * smaller set of premium methods (live pricing, per-condition history, shop comparison, bargain\n * search, shop-URL submission) need a Premium subscriber's API token\n * (https://api.tcgpriser.se/premium-docs), generated from tcgpriser.se/account/api-token:\n *\n * ```ts\n * const tcgpriser = new TcgPriser(myApiToken); // shorthand for { authToken: myApiToken }\n * await tcgpriser.cards.livePricing('fezandipiti-ex');\n * ```\n */\nexport class TcgPriser {\n readonly cards: CardsResource;\n readonly products: ProductsResource;\n readonly expansions: ExpansionsResource;\n readonly shops: ShopsResource;\n readonly shopMatches: ShopMatchesResource;\n readonly shopMatchStats: ShopMatchStatsResource;\n readonly shopUrls: ShopUrlsResource;\n readonly priceStats: PriceStatsResource;\n readonly bargains: BargainsResource;\n readonly packRates: PackRatesResource;\n readonly stats: StatsResource;\n\n /**\n * @param optionsOrAuthToken A subscriber's API token (`new TcgPriser(myApiToken)`), a full\n * `TcgPriserOptions` object, or omit it entirely for an anonymous, public-only client.\n */\n constructor(optionsOrAuthToken?: string | TcgPriserOptions) {\n const options: TcgPriserOptions =\n typeof optionsOrAuthToken === 'string' ? { authToken: optionsOrAuthToken } : (optionsOrAuthToken ?? {});\n const advanced = options.advanced ?? {};\n\n if (!advanced.fetch && typeof fetch === 'undefined') {\n throw new Error(\n 'tcgpriser: no global fetch found. Pass { advanced: { fetch } } explicitly on Node < 18, or run on Node 18+.',\n );\n }\n\n const http = new HttpClient({\n baseUrl: advanced.baseUrl ?? DEFAULT_BASE_URL,\n // Bound to globalThis: both browsers and Node's undici implement fetch as a method that\n // checks its receiver, so an unbound reference throws \"Illegal invocation\" the moment it's\n // called through anything other than `window.fetch(...)`/`globalThis.fetch(...)` — which is\n // exactly what happens once HttpClient stores it and calls `this.fetchImpl(...)`.\n fetch: advanced.fetch ?? fetch.bind(globalThis),\n headers: advanced.headers,\n authToken: options.authToken,\n });\n\n this.cards = new CardsResource(http);\n this.products = new ProductsResource(http);\n this.expansions = new ExpansionsResource(http);\n this.shops = new ShopsResource(http);\n this.shopMatches = new ShopMatchesResource(http);\n this.shopMatchStats = new ShopMatchStatsResource(http);\n this.shopUrls = new ShopUrlsResource(http);\n this.priceStats = new PriceStatsResource(http);\n this.bargains = new BargainsResource(http);\n this.packRates = new PackRatesResource(http);\n this.stats = new StatsResource(http);\n }\n}\n"]}