tmdb-kit 0.1.0-beta.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +216 -12
- package/dist/index.cjs +375 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +236 -11
- package/dist/index.d.ts +236 -11
- package/dist/index.js +357 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -37,13 +37,86 @@ var TMDBResponseError = class extends TMDBError {
|
|
|
37
37
|
this.statusCode = body?.status_code;
|
|
38
38
|
this.statusMessage = body?.status_message;
|
|
39
39
|
}
|
|
40
|
+
/** True when TMDB has asked the caller to back off (HTTP 429). */
|
|
41
|
+
get isRateLimit() {
|
|
42
|
+
return this.status === 429;
|
|
43
|
+
}
|
|
44
|
+
/** True when the resource was not found (HTTP 404). */
|
|
45
|
+
get isNotFound() {
|
|
46
|
+
return this.status === 404;
|
|
47
|
+
}
|
|
48
|
+
/** True when the request was rejected as unauthenticated (HTTP 401). */
|
|
49
|
+
get isUnauthorized() {
|
|
50
|
+
return this.status === 401;
|
|
51
|
+
}
|
|
52
|
+
/** True when the request was rejected as forbidden (HTTP 403). */
|
|
53
|
+
get isForbidden() {
|
|
54
|
+
return this.status === 403;
|
|
55
|
+
}
|
|
56
|
+
/** True when the upstream returned a 5xx server error. */
|
|
57
|
+
get isServerError() {
|
|
58
|
+
return this.status >= 500 && this.status < 600;
|
|
59
|
+
}
|
|
40
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Thrown when TMDB returns HTTP 429.
|
|
63
|
+
*
|
|
64
|
+
* The SDK does NOT retry automatically — that decision is left to the
|
|
65
|
+
* caller, since retry policies vary widely. Inspect `retryAfter` to
|
|
66
|
+
* implement your own backoff.
|
|
67
|
+
*/
|
|
68
|
+
var TMDBRateLimitError = class extends TMDBResponseError {
|
|
69
|
+
retryAfter;
|
|
70
|
+
constructor(message, options) {
|
|
71
|
+
super(message, options);
|
|
72
|
+
this.name = "TMDBRateLimitError";
|
|
73
|
+
this.retryAfter = options.retryAfter;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Create the appropriate error instance for a non-2xx TMDB response.
|
|
78
|
+
*
|
|
79
|
+
* Returns a {@link TMDBRateLimitError} when the status is 429, otherwise
|
|
80
|
+
* a generic {@link TMDBResponseError}. The error message is extracted from
|
|
81
|
+
* the TMDB `status_message` field when available.
|
|
82
|
+
*/
|
|
41
83
|
function createResponseError(options) {
|
|
42
|
-
|
|
84
|
+
const message = (isErrorResponseBody(options.body) ? options.body : void 0)?.status_message ?? `${options.status} ${options.statusText}`.trim();
|
|
85
|
+
if (options.status === 429) return new TMDBRateLimitError(message, {
|
|
86
|
+
...options,
|
|
87
|
+
retryAfter: parseRetryAfter(options.headers.get("retry-after"))
|
|
88
|
+
});
|
|
89
|
+
return new TMDBResponseError(message, options);
|
|
43
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Parse the `Retry-After` header into seconds. Accepts both delta-seconds
|
|
93
|
+
* (e.g. "5") and HTTP-date forms (e.g. "Wed, 21 Oct 2026 07:28:00 GMT").
|
|
94
|
+
* Falls back to 1 second when the header is missing or unparseable.
|
|
95
|
+
*/
|
|
96
|
+
function parseRetryAfter(value) {
|
|
97
|
+
if (value === null || value === void 0) return 1;
|
|
98
|
+
const trimmed = value.trim();
|
|
99
|
+
if (trimmed === "") return 1;
|
|
100
|
+
const seconds = Number(trimmed);
|
|
101
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds;
|
|
102
|
+
const date = Date.parse(trimmed);
|
|
103
|
+
if (Number.isFinite(date)) {
|
|
104
|
+
const deltaMs = date - Date.now();
|
|
105
|
+
return Math.max(1, Math.ceil(deltaMs / 1e3));
|
|
106
|
+
}
|
|
107
|
+
return 1;
|
|
108
|
+
}
|
|
109
|
+
/** Check if a value has the shape of a TMDB error response body. */
|
|
44
110
|
function isErrorResponseBody(value) {
|
|
45
111
|
return typeof value === "object" && value !== null;
|
|
46
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Append query parameters to a URL object.
|
|
115
|
+
*
|
|
116
|
+
* - `undefined` and `null` values are skipped.
|
|
117
|
+
* - Arrays are joined with commas (TMDB convention).
|
|
118
|
+
* - All values are converted to strings.
|
|
119
|
+
*/
|
|
47
120
|
function appendQuery(url, query) {
|
|
48
121
|
for (const [key, value] of Object.entries(query)) {
|
|
49
122
|
if (value === void 0 || value === null) continue;
|
|
@@ -54,6 +127,12 @@ function appendQuery(url, query) {
|
|
|
54
127
|
url.searchParams.set(key, String(value));
|
|
55
128
|
}
|
|
56
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Merge a language query parameter with client defaults.
|
|
132
|
+
*
|
|
133
|
+
* If `query.language` is set it takes precedence; otherwise the client's
|
|
134
|
+
* default language is used.
|
|
135
|
+
*/
|
|
57
136
|
function withLanguage(defaults, query) {
|
|
58
137
|
const language = typeof query.language === "string" ? query.language : defaults.language;
|
|
59
138
|
return {
|
|
@@ -61,6 +140,11 @@ function withLanguage(defaults, query) {
|
|
|
61
140
|
language
|
|
62
141
|
};
|
|
63
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Merge language and region defaults for list endpoints.
|
|
145
|
+
*
|
|
146
|
+
* Combines {@link withLanguage} with region fallback from client defaults.
|
|
147
|
+
*/
|
|
64
148
|
function withListDefaults(defaults, query) {
|
|
65
149
|
const region = typeof query.region === "string" ? query.region : defaults.region;
|
|
66
150
|
return {
|
|
@@ -68,19 +152,40 @@ function withListDefaults(defaults, query) {
|
|
|
68
152
|
region
|
|
69
153
|
};
|
|
70
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Convert a string or string array to a comma-separated list.
|
|
157
|
+
*
|
|
158
|
+
* Returns `undefined` if the input is `undefined` (skips the parameter).
|
|
159
|
+
* TMDB uses comma-separated values for multi-value query parameters.
|
|
160
|
+
*/
|
|
71
161
|
function toCommaList(value) {
|
|
72
162
|
if (value === void 0) return;
|
|
73
163
|
return typeof value === "string" ? value : value.join(",");
|
|
74
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Validate and convert a numeric ID to a string for use in URL paths.
|
|
167
|
+
*
|
|
168
|
+
* @throws {TMDBRequestError} If `value` is not a positive integer.
|
|
169
|
+
*/
|
|
75
170
|
function toId(value, name) {
|
|
76
171
|
if (!Number.isInteger(value) || value <= 0) throw new TMDBRequestError(`${name} must be a positive integer`);
|
|
77
172
|
return String(value);
|
|
78
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Validate and trim a search query string.
|
|
176
|
+
*
|
|
177
|
+
* @throws {TMDBRequestError} If the query is empty after trimming.
|
|
178
|
+
*/
|
|
79
179
|
function toSearchQuery(value) {
|
|
80
180
|
const query = value.trim();
|
|
81
181
|
if (query.length === 0) throw new TMDBRequestError("search query must not be empty");
|
|
82
182
|
return query;
|
|
83
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Validate a trending time window value.
|
|
186
|
+
*
|
|
187
|
+
* @throws {TMDBRequestError} If `value` is not `"day"` or `"week"`.
|
|
188
|
+
*/
|
|
84
189
|
function toTimeWindow(value) {
|
|
85
190
|
if (value !== "day" && value !== "week") throw new TMDBRequestError("timeWindow must be either \"day\" or \"week\"");
|
|
86
191
|
return value;
|
|
@@ -91,15 +196,27 @@ function toTimeWindow(value) {
|
|
|
91
196
|
*/
|
|
92
197
|
function buildImageUrl(path, size = "original", options = {}) {
|
|
93
198
|
if (path === void 0 || path === null || path.trim() === "") return null;
|
|
94
|
-
|
|
199
|
+
const url = `${normalizeBaseUrl(options.baseUrl ?? "https://image.tmdb.org/t/p")}/${size}${path.startsWith("/") ? path : `/${path}`}`;
|
|
200
|
+
return options.transform ? options.transform(url) : url;
|
|
95
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Strip trailing slashes from a base URL to ensure consistent path joining.
|
|
204
|
+
*/
|
|
96
205
|
function normalizeBaseUrl(baseUrl) {
|
|
97
206
|
return baseUrl.replace(/\/+$/, "");
|
|
98
207
|
}
|
|
99
208
|
//#endregion
|
|
100
209
|
//#region src/http.ts
|
|
210
|
+
/**
|
|
211
|
+
* Built-in HTTP transport that uses the Fetch API.
|
|
212
|
+
*
|
|
213
|
+
* Handles authentication (API key or access token), URL construction,
|
|
214
|
+
* request headers, and response parsing. Use the factory function
|
|
215
|
+
* {@link createTMDB} instead of instantiating this directly.
|
|
216
|
+
*/
|
|
101
217
|
var TMDBHttpClient = class {
|
|
102
218
|
defaults;
|
|
219
|
+
imageTransform;
|
|
103
220
|
#apiKey;
|
|
104
221
|
#accessToken;
|
|
105
222
|
#baseUrl;
|
|
@@ -109,25 +226,25 @@ var TMDBHttpClient = class {
|
|
|
109
226
|
const accessToken = normalizeSecret("accessToken" in options ? options.accessToken : void 0);
|
|
110
227
|
const apiKey = normalizeSecret("apiKey" in options ? options.apiKey : void 0);
|
|
111
228
|
if (Boolean(accessToken) === Boolean(apiKey)) throw new TMDBRequestError("Provide exactly one of accessToken or apiKey");
|
|
112
|
-
const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
113
|
-
if (typeof fetchImpl !== "function") throw new TMDBRequestError("No fetch implementation found. Pass options.fetch in this runtime.");
|
|
114
229
|
this.#accessToken = accessToken;
|
|
115
230
|
this.#apiKey = apiKey;
|
|
116
231
|
this.#baseUrl = normalizeBaseUrl(options.baseUrl ?? "https://api.themoviedb.org/3");
|
|
117
|
-
this.#fetch =
|
|
232
|
+
this.#fetch = options.fetch;
|
|
118
233
|
this.#headers = options.headers;
|
|
119
234
|
this.defaults = {
|
|
120
235
|
imageBaseUrl: normalizeBaseUrl(options.imageBaseUrl ?? "https://image.tmdb.org/t/p"),
|
|
121
236
|
language: options.defaultLanguage,
|
|
122
237
|
region: options.defaultRegion
|
|
123
238
|
};
|
|
239
|
+
this.imageTransform = options.images?.transform;
|
|
124
240
|
}
|
|
125
241
|
async get(path, options = {}) {
|
|
126
242
|
const url = this.#createUrl(path, options.query ?? {});
|
|
127
243
|
const headers = this.#createHeaders(options.headers);
|
|
244
|
+
const fetchImpl = this.#resolveFetch();
|
|
128
245
|
let response;
|
|
129
246
|
try {
|
|
130
|
-
response = await
|
|
247
|
+
response = await fetchImpl(url, {
|
|
131
248
|
headers,
|
|
132
249
|
method: "GET",
|
|
133
250
|
signal: options.signal
|
|
@@ -145,6 +262,12 @@ var TMDBHttpClient = class {
|
|
|
145
262
|
});
|
|
146
263
|
return body;
|
|
147
264
|
}
|
|
265
|
+
#resolveFetch() {
|
|
266
|
+
if (this.#fetch !== void 0) return this.#fetch;
|
|
267
|
+
const globalFetch = globalThis.fetch;
|
|
268
|
+
if (typeof globalFetch !== "function") throw new TMDBRequestError("No fetch implementation found. Pass options.fetch in this runtime.");
|
|
269
|
+
return globalFetch.bind(globalThis);
|
|
270
|
+
}
|
|
148
271
|
#createHeaders(headersInit) {
|
|
149
272
|
const headers = new Headers(this.#headers);
|
|
150
273
|
const requestHeaders = new Headers(headersInit);
|
|
@@ -163,6 +286,13 @@ var TMDBHttpClient = class {
|
|
|
163
286
|
return url.toString();
|
|
164
287
|
}
|
|
165
288
|
};
|
|
289
|
+
/**
|
|
290
|
+
* Read and parse a JSON response body.
|
|
291
|
+
*
|
|
292
|
+
* Returns `undefined` for empty responses. If the body is not valid JSON,
|
|
293
|
+
* the raw text string is returned as-is (TMDB occasionally returns plain
|
|
294
|
+
* text for certain error conditions).
|
|
295
|
+
*/
|
|
166
296
|
async function readJson(response) {
|
|
167
297
|
const text = await response.text();
|
|
168
298
|
if (text.length === 0) return;
|
|
@@ -172,12 +302,21 @@ async function readJson(response) {
|
|
|
172
302
|
return text;
|
|
173
303
|
}
|
|
174
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Normalize a secret value by trimming whitespace and treating empty
|
|
307
|
+
* strings as undefined (not provided).
|
|
308
|
+
*/
|
|
175
309
|
function normalizeSecret(value) {
|
|
176
310
|
const secret = value?.trim();
|
|
177
311
|
return secret === "" ? void 0 : secret;
|
|
178
312
|
}
|
|
179
313
|
//#endregion
|
|
180
314
|
//#region src/resources/configuration.ts
|
|
315
|
+
/**
|
|
316
|
+
* Access TMDB configuration data (image sizes, countries, languages, etc.).
|
|
317
|
+
*
|
|
318
|
+
* Obtained via `client.configuration`.
|
|
319
|
+
*/
|
|
181
320
|
var ConfigurationResource = class {
|
|
182
321
|
#transport;
|
|
183
322
|
constructor(transport) {
|
|
@@ -204,6 +343,11 @@ var ConfigurationResource = class {
|
|
|
204
343
|
};
|
|
205
344
|
//#endregion
|
|
206
345
|
//#region src/resources/genres.ts
|
|
346
|
+
/**
|
|
347
|
+
* Access TMDB genre lists for movies and TV shows.
|
|
348
|
+
*
|
|
349
|
+
* Obtained via `client.genres`.
|
|
350
|
+
*/
|
|
207
351
|
var GenresResource = class {
|
|
208
352
|
#transport;
|
|
209
353
|
constructor(transport) {
|
|
@@ -218,17 +362,33 @@ var GenresResource = class {
|
|
|
218
362
|
};
|
|
219
363
|
//#endregion
|
|
220
364
|
//#region src/resources/images.ts
|
|
365
|
+
/**
|
|
366
|
+
* Helper for constructing TMDB image URLs from API path values.
|
|
367
|
+
*
|
|
368
|
+
* Obtained via `client.images`. Supports optional URL transforms for
|
|
369
|
+
* routing image requests through a CDN or proxy.
|
|
370
|
+
*/
|
|
221
371
|
var ImagesHelper = class {
|
|
222
372
|
#transport;
|
|
223
|
-
|
|
373
|
+
#transform;
|
|
374
|
+
constructor(transport, transform) {
|
|
224
375
|
this.#transport = transport;
|
|
376
|
+
this.#transform = transform;
|
|
225
377
|
}
|
|
226
|
-
url(path, size = "original") {
|
|
227
|
-
return buildImageUrl(path, size, {
|
|
378
|
+
url(path, size = "original", options = {}) {
|
|
379
|
+
return buildImageUrl(path, size, {
|
|
380
|
+
baseUrl: this.#transport.defaults.imageBaseUrl,
|
|
381
|
+
transform: options.transform ?? this.#transform
|
|
382
|
+
});
|
|
228
383
|
}
|
|
229
384
|
};
|
|
230
385
|
//#endregion
|
|
231
386
|
//#region src/resources/movies.ts
|
|
387
|
+
/**
|
|
388
|
+
* Access TMDB movie endpoints (popular, top-rated, details, credits, etc.).
|
|
389
|
+
*
|
|
390
|
+
* Obtained via `client.movies`.
|
|
391
|
+
*/
|
|
232
392
|
var MoviesResource = class {
|
|
233
393
|
#transport;
|
|
234
394
|
constructor(transport) {
|
|
@@ -271,6 +431,7 @@ var MoviesResource = class {
|
|
|
271
431
|
return this.#transport.get(`/movie/${toId(movieId, "movieId")}/videos`, { query: withLanguage(this.#transport.defaults, { language: options.language }) });
|
|
272
432
|
}
|
|
273
433
|
};
|
|
434
|
+
/** Build query parameters for movie list endpoints. */
|
|
274
435
|
function movieListQuery(transport, options) {
|
|
275
436
|
return withListDefaults(transport.defaults, {
|
|
276
437
|
language: options.language,
|
|
@@ -280,6 +441,11 @@ function movieListQuery(transport, options) {
|
|
|
280
441
|
}
|
|
281
442
|
//#endregion
|
|
282
443
|
//#region src/resources/people.ts
|
|
444
|
+
/**
|
|
445
|
+
* Access TMDB person (actor, director, etc.) endpoints.
|
|
446
|
+
*
|
|
447
|
+
* Obtained via `client.people`.
|
|
448
|
+
*/
|
|
283
449
|
var PeopleResource = class {
|
|
284
450
|
#transport;
|
|
285
451
|
constructor(transport) {
|
|
@@ -292,6 +458,7 @@ var PeopleResource = class {
|
|
|
292
458
|
return this.#transport.get(`/person/${toId(personId, "personId")}`, { query: withLanguage(this.#transport.defaults, { language: options.language }) });
|
|
293
459
|
}
|
|
294
460
|
};
|
|
461
|
+
/** Build query parameters for person list endpoints. */
|
|
295
462
|
function peopleListQuery(transport, options) {
|
|
296
463
|
return withLanguage(transport.defaults, {
|
|
297
464
|
language: options.language,
|
|
@@ -300,6 +467,11 @@ function peopleListQuery(transport, options) {
|
|
|
300
467
|
}
|
|
301
468
|
//#endregion
|
|
302
469
|
//#region src/resources/search.ts
|
|
470
|
+
/**
|
|
471
|
+
* Search TMDB for movies, TV shows, people, or all media types.
|
|
472
|
+
*
|
|
473
|
+
* Obtained via `client.search`.
|
|
474
|
+
*/
|
|
303
475
|
var SearchResource = class {
|
|
304
476
|
#transport;
|
|
305
477
|
constructor(transport) {
|
|
@@ -314,10 +486,18 @@ var SearchResource = class {
|
|
|
314
486
|
people(query, options = {}) {
|
|
315
487
|
return this.#transport.get("/search/person", { query: commonSearchQuery(this.#transport, query, options) });
|
|
316
488
|
}
|
|
317
|
-
multi(query, options = {}) {
|
|
318
|
-
|
|
489
|
+
async multi(query, options = {}) {
|
|
490
|
+
const response = await this.#transport.get("/search/multi", { query: commonSearchQuery(this.#transport, query, options) });
|
|
491
|
+
const excluded = options.exclude;
|
|
492
|
+
if (excluded === void 0 || excluded.length === 0) return response;
|
|
493
|
+
const excludedSet = new Set(excluded);
|
|
494
|
+
return {
|
|
495
|
+
...response,
|
|
496
|
+
results: response.results.filter((result) => !excludedSet.has(result.media_type))
|
|
497
|
+
};
|
|
319
498
|
}
|
|
320
499
|
};
|
|
500
|
+
/** Build query parameters for movie search. */
|
|
321
501
|
function movieSearchQuery(transport, query, options) {
|
|
322
502
|
return withListDefaults(transport.defaults, {
|
|
323
503
|
include_adult: options.includeAdult,
|
|
@@ -329,6 +509,7 @@ function movieSearchQuery(transport, query, options) {
|
|
|
329
509
|
year: options.year
|
|
330
510
|
});
|
|
331
511
|
}
|
|
512
|
+
/** Build query parameters for TV search. */
|
|
332
513
|
function tvSearchQuery(transport, query, options) {
|
|
333
514
|
return withLanguage(transport.defaults, {
|
|
334
515
|
first_air_date_year: options.firstAirDateYear,
|
|
@@ -339,6 +520,7 @@ function tvSearchQuery(transport, query, options) {
|
|
|
339
520
|
year: options.year
|
|
340
521
|
});
|
|
341
522
|
}
|
|
523
|
+
/** Build query parameters for person and multi-search. */
|
|
342
524
|
function commonSearchQuery(transport, query, options) {
|
|
343
525
|
return withLanguage(transport.defaults, {
|
|
344
526
|
include_adult: options.includeAdult,
|
|
@@ -349,6 +531,11 @@ function commonSearchQuery(transport, query, options) {
|
|
|
349
531
|
}
|
|
350
532
|
//#endregion
|
|
351
533
|
//#region src/resources/trending.ts
|
|
534
|
+
/**
|
|
535
|
+
* Access TMDB trending content (movies, TV, people) for day/week windows.
|
|
536
|
+
*
|
|
537
|
+
* Obtained via `client.trending`.
|
|
538
|
+
*/
|
|
352
539
|
var TrendingResource = class {
|
|
353
540
|
#transport;
|
|
354
541
|
constructor(transport) {
|
|
@@ -369,6 +556,11 @@ var TrendingResource = class {
|
|
|
369
556
|
};
|
|
370
557
|
//#endregion
|
|
371
558
|
//#region src/resources/tv.ts
|
|
559
|
+
/**
|
|
560
|
+
* Access TMDB TV show endpoints (popular, top-rated, details, seasons, etc.).
|
|
561
|
+
*
|
|
562
|
+
* Obtained via `client.tv`.
|
|
563
|
+
*/
|
|
372
564
|
var TVResource = class {
|
|
373
565
|
#transport;
|
|
374
566
|
constructor(transport) {
|
|
@@ -417,6 +609,7 @@ var TVResource = class {
|
|
|
417
609
|
}) });
|
|
418
610
|
}
|
|
419
611
|
};
|
|
612
|
+
/** Build query parameters for TV list endpoints. */
|
|
420
613
|
function tvListQuery(transport, options) {
|
|
421
614
|
return withLanguage(transport.defaults, {
|
|
422
615
|
language: options.language,
|
|
@@ -441,31 +634,190 @@ var TMDBClient = class {
|
|
|
441
634
|
search;
|
|
442
635
|
trending;
|
|
443
636
|
tv;
|
|
444
|
-
#
|
|
637
|
+
#transport;
|
|
445
638
|
constructor(options) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
this
|
|
453
|
-
this.
|
|
454
|
-
this.
|
|
455
|
-
|
|
639
|
+
const { transport, images, ...httpOptions } = options;
|
|
640
|
+
const fallbackTransform = images?.transform;
|
|
641
|
+
const builtInClient = transport === void 0 ? new TMDBHttpClient({
|
|
642
|
+
...httpOptions,
|
|
643
|
+
images
|
|
644
|
+
}) : void 0;
|
|
645
|
+
this.#transport = builtInClient ?? transport;
|
|
646
|
+
this.configuration = new ConfigurationResource(this.#transport);
|
|
647
|
+
this.genres = new GenresResource(this.#transport);
|
|
648
|
+
this.images = new ImagesHelper(this.#transport, builtInClient ? builtInClient.imageTransform : fallbackTransform);
|
|
649
|
+
this.movies = new MoviesResource(this.#transport);
|
|
650
|
+
this.people = new PeopleResource(this.#transport);
|
|
651
|
+
this.search = new SearchResource(this.#transport);
|
|
652
|
+
this.trending = new TrendingResource(this.#transport);
|
|
653
|
+
this.tv = new TVResource(this.#transport);
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Make a raw GET request to any TMDB endpoint.
|
|
657
|
+
*
|
|
658
|
+
* This is an escape hatch for endpoints not covered by the typed resource
|
|
659
|
+
* helpers. The `path` should be relative to the API base URL (e.g.
|
|
660
|
+
* `/movie/{id}/credits`). Response is parsed as JSON and cast to `T`.
|
|
661
|
+
*
|
|
662
|
+
* @param path - API path relative to the base URL (e.g. `/movie/123`)
|
|
663
|
+
* @param options - Optional request-level overrides (headers, query, signal)
|
|
664
|
+
* @returns Parsed JSON response typed as `T`
|
|
665
|
+
*/
|
|
456
666
|
request(path, options) {
|
|
457
|
-
return this.#
|
|
667
|
+
return this.#transport.get(path, options);
|
|
458
668
|
}
|
|
459
669
|
};
|
|
670
|
+
/**
|
|
671
|
+
* Create a new {@link TMDBClient} instance.
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* ```ts
|
|
675
|
+
* const tmdb = createTMDB({ accessToken: 'YOUR_TOKEN' })
|
|
676
|
+
* const { results } = await tmdb.search.movies('Inception')
|
|
677
|
+
* ```
|
|
678
|
+
*
|
|
679
|
+
* @param options - Authentication and configuration options
|
|
680
|
+
* @returns A configured TMDBClient ready to make API calls
|
|
681
|
+
*/
|
|
460
682
|
function createTMDB(options) {
|
|
461
683
|
return new TMDBClient(options);
|
|
462
684
|
}
|
|
463
685
|
//#endregion
|
|
686
|
+
//#region src/search-guards.ts
|
|
687
|
+
function isMovieSearchResult(result) {
|
|
688
|
+
return isObjectWithMediaType(result, "movie");
|
|
689
|
+
}
|
|
690
|
+
function isTVSearchResult(result) {
|
|
691
|
+
return isObjectWithMediaType(result, "tv");
|
|
692
|
+
}
|
|
693
|
+
function isPersonSearchResult(result) {
|
|
694
|
+
return isObjectWithMediaType(result, "person");
|
|
695
|
+
}
|
|
696
|
+
function isMovie(result) {
|
|
697
|
+
return isObjectWithMediaType(result, "movie");
|
|
698
|
+
}
|
|
699
|
+
function isTV(result) {
|
|
700
|
+
return isObjectWithMediaType(result, "tv");
|
|
701
|
+
}
|
|
702
|
+
function isPerson(result) {
|
|
703
|
+
return isObjectWithMediaType(result, "person");
|
|
704
|
+
}
|
|
705
|
+
/** Internal helper: check if a value is an object with a matching `media_type` field. */
|
|
706
|
+
function isObjectWithMediaType(value, mediaType) {
|
|
707
|
+
return typeof value === "object" && value !== null && "media_type" in value && value.media_type === mediaType;
|
|
708
|
+
}
|
|
709
|
+
//#endregion
|
|
710
|
+
//#region src/types.ts
|
|
711
|
+
/**
|
|
712
|
+
* Public set of image sizes TMDB currently documents. The SDK does not
|
|
713
|
+
* pin to a static copy of the TMDB docs — TMDB has added new sizes in
|
|
714
|
+
* the past (e.g. `h632` for profile) without notice — but exposing the
|
|
715
|
+
* canonical list lets callers:
|
|
716
|
+
*
|
|
717
|
+
* - iterate over the set to build a size picker
|
|
718
|
+
* - validate user-supplied sizes with `isKnownBackdropSize(...)` etc.
|
|
719
|
+
* - type a variable as a "known" size without giving up the
|
|
720
|
+
* `(string & {})` escape hatch for forward-compatibility
|
|
721
|
+
*
|
|
722
|
+
* If TMDB publishes a new size, the SDK will still accept it at runtime
|
|
723
|
+
* (every helper is `(string & {})` permissive), but you will need to
|
|
724
|
+
* add the literal to these constants to get autocomplete on it.
|
|
725
|
+
*/
|
|
726
|
+
const KNOWN_BACKDROP_SIZES = Object.freeze([
|
|
727
|
+
"w300",
|
|
728
|
+
"w780",
|
|
729
|
+
"w1280",
|
|
730
|
+
"original"
|
|
731
|
+
]);
|
|
732
|
+
const KNOWN_LOGO_SIZES = Object.freeze([
|
|
733
|
+
"w45",
|
|
734
|
+
"w92",
|
|
735
|
+
"w154",
|
|
736
|
+
"w185",
|
|
737
|
+
"w300",
|
|
738
|
+
"w500",
|
|
739
|
+
"original"
|
|
740
|
+
]);
|
|
741
|
+
const KNOWN_POSTER_SIZES = Object.freeze([
|
|
742
|
+
"w92",
|
|
743
|
+
"w154",
|
|
744
|
+
"w185",
|
|
745
|
+
"w342",
|
|
746
|
+
"w500",
|
|
747
|
+
"w780",
|
|
748
|
+
"original"
|
|
749
|
+
]);
|
|
750
|
+
const KNOWN_PROFILE_SIZES = Object.freeze([
|
|
751
|
+
"w45",
|
|
752
|
+
"w185",
|
|
753
|
+
"h632",
|
|
754
|
+
"original"
|
|
755
|
+
]);
|
|
756
|
+
const KNOWN_STILL_SIZES = Object.freeze([
|
|
757
|
+
"w92",
|
|
758
|
+
"w185",
|
|
759
|
+
"w300",
|
|
760
|
+
"original"
|
|
761
|
+
]);
|
|
762
|
+
/** Union of every documented image size. */
|
|
763
|
+
const KNOWN_IMAGE_SIZES = Object.freeze([
|
|
764
|
+
...KNOWN_BACKDROP_SIZES,
|
|
765
|
+
...KNOWN_LOGO_SIZES,
|
|
766
|
+
...KNOWN_POSTER_SIZES,
|
|
767
|
+
...KNOWN_PROFILE_SIZES,
|
|
768
|
+
...KNOWN_STILL_SIZES
|
|
769
|
+
]);
|
|
770
|
+
/** O(1) membership check backed by a Set. */
|
|
771
|
+
const KNOWN_BACKDROP_SET = new Set(KNOWN_BACKDROP_SIZES);
|
|
772
|
+
const KNOWN_LOGO_SET = new Set(KNOWN_LOGO_SIZES);
|
|
773
|
+
const KNOWN_POSTER_SET = new Set(KNOWN_POSTER_SIZES);
|
|
774
|
+
const KNOWN_PROFILE_SET = new Set(KNOWN_PROFILE_SIZES);
|
|
775
|
+
const KNOWN_STILL_SET = new Set(KNOWN_STILL_SIZES);
|
|
776
|
+
const KNOWN_IMAGE_SET = new Set(KNOWN_IMAGE_SIZES);
|
|
777
|
+
function isKnownBackdropSize(value) {
|
|
778
|
+
return KNOWN_BACKDROP_SET.has(value);
|
|
779
|
+
}
|
|
780
|
+
function isKnownLogoSize(value) {
|
|
781
|
+
return KNOWN_LOGO_SET.has(value);
|
|
782
|
+
}
|
|
783
|
+
function isKnownPosterSize(value) {
|
|
784
|
+
return KNOWN_POSTER_SET.has(value);
|
|
785
|
+
}
|
|
786
|
+
function isKnownProfileSize(value) {
|
|
787
|
+
return KNOWN_PROFILE_SET.has(value);
|
|
788
|
+
}
|
|
789
|
+
function isKnownStillSize(value) {
|
|
790
|
+
return KNOWN_STILL_SET.has(value);
|
|
791
|
+
}
|
|
792
|
+
/** True if `value` is in any documented size set. */
|
|
793
|
+
function isKnownImageSize(value) {
|
|
794
|
+
return KNOWN_IMAGE_SET.has(value);
|
|
795
|
+
}
|
|
796
|
+
//#endregion
|
|
797
|
+
exports.KNOWN_BACKDROP_SIZES = KNOWN_BACKDROP_SIZES;
|
|
798
|
+
exports.KNOWN_IMAGE_SIZES = KNOWN_IMAGE_SIZES;
|
|
799
|
+
exports.KNOWN_LOGO_SIZES = KNOWN_LOGO_SIZES;
|
|
800
|
+
exports.KNOWN_POSTER_SIZES = KNOWN_POSTER_SIZES;
|
|
801
|
+
exports.KNOWN_PROFILE_SIZES = KNOWN_PROFILE_SIZES;
|
|
802
|
+
exports.KNOWN_STILL_SIZES = KNOWN_STILL_SIZES;
|
|
464
803
|
exports.TMDBClient = TMDBClient;
|
|
465
804
|
exports.TMDBError = TMDBError;
|
|
805
|
+
exports.TMDBRateLimitError = TMDBRateLimitError;
|
|
466
806
|
exports.TMDBRequestError = TMDBRequestError;
|
|
467
807
|
exports.TMDBResponseError = TMDBResponseError;
|
|
468
808
|
exports.buildImageUrl = buildImageUrl;
|
|
469
809
|
exports.createTMDB = createTMDB;
|
|
810
|
+
exports.isKnownBackdropSize = isKnownBackdropSize;
|
|
811
|
+
exports.isKnownImageSize = isKnownImageSize;
|
|
812
|
+
exports.isKnownLogoSize = isKnownLogoSize;
|
|
813
|
+
exports.isKnownPosterSize = isKnownPosterSize;
|
|
814
|
+
exports.isKnownProfileSize = isKnownProfileSize;
|
|
815
|
+
exports.isKnownStillSize = isKnownStillSize;
|
|
816
|
+
exports.isMovie = isMovie;
|
|
817
|
+
exports.isMovieSearchResult = isMovieSearchResult;
|
|
818
|
+
exports.isPerson = isPerson;
|
|
819
|
+
exports.isPersonSearchResult = isPersonSearchResult;
|
|
820
|
+
exports.isTV = isTV;
|
|
821
|
+
exports.isTVSearchResult = isTVSearchResult;
|
|
470
822
|
|
|
471
823
|
//# sourceMappingURL=index.cjs.map
|