tmdb-lumosovich 0.7.0 → 0.8.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
@@ -6,7 +6,7 @@
6
6
 
7
7
  A small, typed Node.js wrapper for the TMDB API, including IMDb ID lookup through TMDB.
8
8
 
9
- **Lumosovich** is being built incrementally toward full TMDB v3 API coverage. It currently supports external-ID lookup, search, discovery, trending, movie and TV series catalogs and metadata, reviews, keywords, networks, collections, companies, configuration, movie, TV, and person details, and TV seasons, episodes, and episode groups.
9
+ **Lumosovich** is being built incrementally toward full TMDB v3 API coverage. It currently supports external-ID lookup, search, discovery, trending, movie and TV series catalogs and metadata, reviews, keywords, networks, collections, companies, configuration, people and credit resources, and TV seasons, episodes, and episode groups.
10
10
 
11
11
  The name comes from **Lumos** — a spell for bringing light — with a little Russian-style `-ovich` treatment.
12
12
 
@@ -14,11 +14,11 @@ Light up the movie database.
14
14
 
15
15
  ## Current status
16
16
 
17
- Install [tmdb-lumosovich on npm](https://www.npmjs.com/package/tmdb-lumosovich), with source and issues on [GitHub](https://github.com/namipsg/lumosovich). Version `0.7.0` covers **109 of 152 TMDB v3 operations**, including all 48 TV operations, measured against the official specification pinned on September 27, 2026. This is an early `0.x` library; full API coverage is still in progress. See the [coverage baseline](https://github.com/namipsg/lumosovich/blob/main/spec/README.md) for how coverage is checked.
17
+ Install [tmdb-lumosovich on npm](https://www.npmjs.com/package/tmdb-lumosovich), with source and issues on [GitHub](https://github.com/namipsg/lumosovich). Version `0.8.0` covers **121 of 152 TMDB v3 operations**, including all TV, person, and credit operations, measured against the official specification pinned on September 27, 2026. This is an early `0.x` library; full API coverage is still in progress. See the [coverage baseline](https://github.com/namipsg/lumosovich/blob/main/spec/README.md) for how coverage is checked.
18
18
 
19
- Version `0.7.0` moves the npm package from `lumosovich` to `tmdb-lumosovich`; the public `Lumosovich` class and TMDB API coverage are unchanged. Previously published `lumosovich` versions remain installable, but new projects should use `tmdb-lumosovich`. The read-only live smoke test exercises all 99 public read operations. Account-state and rating methods require a TMDB session and are verified by offline request contracts.
19
+ The package moved from `lumosovich` to `tmdb-lumosovich` in version `0.7.1`; the public `Lumosovich` class kept its name. Previously published `lumosovich` versions remain installable, but new projects should use `tmdb-lumosovich`. The read-only live smoke test exercises all 111 public read operations. Account-state and rating methods require a TMDB session and are verified by offline request contracts.
20
20
 
21
- The release passes 180 offline tests and TypeScript declaration checks. See the [changelog](https://github.com/namipsg/lumosovich/blob/main/CHANGELOG.md) for earlier releases.
21
+ The release passes 196 offline tests and TypeScript declaration checks. See the [changelog](https://github.com/namipsg/lumosovich/blob/main/CHANGELOG.md) for earlier releases.
22
22
 
23
23
  ## Installation
24
24
 
@@ -348,6 +348,30 @@ const group = groups.results[0]
348
348
 
349
349
  `changeList()` reports changed series IDs. Episode-group IDs are strings supplied by TMDB. The methods return TMDB's original response fields and preserve empty collections.
350
350
 
351
+ ## People and credits
352
+
353
+ Use numeric TMDB person IDs for people resources. Credit details use the string `credit_id` returned in a person's cast or crew record.
354
+
355
+ ```js
356
+ const personId = 31;
357
+ const popularPeople = await tmdb.people.popular({ page: 1 });
358
+ const movieCredits = await tmdb.people.movieCredits(personId);
359
+ const tvCredits = await tmdb.people.tvCredits(personId);
360
+ const combinedCredits = await tmdb.people.combinedCredits(personId);
361
+ const creditId = movieCredits.cast[0]?.credit_id;
362
+ const credit = creditId ? await tmdb.credits.get(creditId) : undefined;
363
+
364
+ const externalIds = await tmdb.people.externalIds(personId);
365
+ const portraits = await tmdb.people.images(personId);
366
+ const tagged = await tmdb.people.taggedImages(personId, { page: 1 });
367
+ const translations = await tmdb.people.translations(personId);
368
+ const latestPerson = await tmdb.people.latest();
369
+ const changedPeople = await tmdb.people.changeList({ page: 1 });
370
+ const changes = await tmdb.people.changes(personId);
371
+ ```
372
+
373
+ The three credit collections return `cast` and `crew` arrays with TMDB's original fields; `combinedCredits()` includes both movie and TV roles. `taggedImages()` has paginated image records, including episode stills, while `images()` has portrait profiles. TMDB can report `page: 0` for tagged images even when page 1 is requested. `changeList()` and `changes()` accept optional `startDate`, `endDate`, and `page`; dates use `YYYY-MM-DD`. `popular()` and `taggedImages()` accept a positive page. Credit details, popular people, and the three credit collections support a `language` override. `latest()` can be incomplete while a new person record is being edited on TMDB.
374
+
351
375
  ## Image URLs
352
376
 
353
377
  TMDB detail responses contain image paths. Build a safe public image URL with:
@@ -435,12 +459,12 @@ TMDB_API_KEY=... npm run test:live
435
459
  ```
436
460
 
437
461
  The credential is read from the environment, never from a tracked file. This checks
438
- all 99 public read operations. Account-state and rating methods require a TMDB
462
+ all 111 public read operations. Account-state and rating methods require a TMDB
439
463
  session and are covered by offline contracts; the live test makes read-only requests.
440
464
 
441
465
  ## Roadmap
442
466
 
443
- People, lists, authentication, and the remaining TMDB resources will expand coverage in future releases. The [coverage ledger](https://github.com/namipsg/lumosovich/blob/main/spec/coverage.json) tracks implemented and remaining operations.
467
+ Lists, authentication, and the remaining TMDB resources will expand coverage in future releases. The [coverage ledger](https://github.com/namipsg/lumosovich/blob/main/spec/coverage.json) tracks implemented and remaining operations.
444
468
 
445
469
  The goal is to cover the TMDB API while keeping the library:
446
470
 
@@ -1,4 +1,5 @@
1
1
  import type { TmdbId } from './types.js';
2
+ import type { PersonResourceMethods } from './person-types.js';
2
3
  import type { MovieMetadataMethods } from './metadata-types.js';
3
4
  import type { MovieCatalogMethods } from './catalog-types.js';
4
5
  import type { TvCatalogMethods, TvMetadataMethods } from './tv-types.js';
@@ -87,6 +88,7 @@ export interface TvContentRatingsResponse {
87
88
  }
88
89
  export interface PersonCredit {
89
90
  id: TmdbId;
91
+ credit_id?: string;
90
92
  media_type?: 'movie' | 'tv';
91
93
  title?: string;
92
94
  name?: string;
@@ -251,6 +253,6 @@ export interface TvMethods extends TvMetadataMethods, TvCatalogMethods, TvSeries
251
253
  get(seriesId: TmdbId, seasonNumber: number, episodeNumber: number, options?: EpisodeDetailOptions): Promise<TvEpisodeDetails>;
252
254
  };
253
255
  }
254
- export interface PersonMethods {
256
+ export interface PersonMethods extends PersonResourceMethods {
255
257
  get(id: TmdbId, options?: PersonDetailOptions): Promise<PersonDetails>;
256
258
  }
package/dist/details.js CHANGED
@@ -5,6 +5,7 @@ import { createTvMetadataMethods } from './tv-metadata.js';
5
5
  import { createTvSeriesResourceMethods, createTvEpisodeGroupMethods } from './tv-series-resources.js';
6
6
  import { createTvSeasonResourceMethods } from './tv-season-resources.js';
7
7
  import { createTvEpisodeResourceMethods } from './tv-episode-resources.js';
8
+ import { createPersonResourceMethods } from './person-resources.js';
8
9
  import { positiveId, slotNumber } from './ids.js';
9
10
  import { validateDetailResponse, validateEpisodeResponse, validateSeasonResponse, } from './validation.js';
10
11
  const MOVIE_APPEND = [
@@ -104,6 +105,7 @@ export function createTvMethods(http) {
104
105
  }
105
106
  export function createPersonMethods(http) {
106
107
  return {
108
+ ...createPersonResourceMethods(http),
107
109
  async get(id, options = {}) {
108
110
  const validId = positiveId(id);
109
111
  const payload = await http.get(`person/${validId}`, detailParameters(options, PERSON_APPEND));
package/dist/index.cjs CHANGED
@@ -915,6 +915,132 @@ function createTvEpisodeResourceMethods(http) {
915
915
  };
916
916
  }
917
917
 
918
+ // src/person-validation.ts
919
+ var positiveId2 = (value) => Number.isSafeInteger(value) && Number(value) > 0;
920
+ var count2 = (value) => Number.isSafeInteger(value) && Number(value) >= 0;
921
+ function validatePersonCredits(value, id) {
922
+ return validateIdShape(value, id, {
923
+ cast: metadataChecks.identities,
924
+ crew: metadataChecks.identities
925
+ });
926
+ }
927
+ function validatePopularPeople(value) {
928
+ const page3 = validateSearchPage(value);
929
+ if (!page3.results.every((person) => isRecord(person) && typeof person.name === "string")) {
930
+ invalidResponse();
931
+ }
932
+ return page3;
933
+ }
934
+ function validateTaggedImages(value, id) {
935
+ if (!isRecord(value) || value.id !== id || !count2(value.page) || !count2(value.total_pages) || !count2(value.total_results) || !Array.isArray(value.results) || !value.results.every((image) => isRecord(image) && typeof image.id === "string" && !!image.id && typeof image.file_path === "string" && typeof image.media_type === "string" && !!image.media_type && typeof image.image_type === "string" && isRecord(image.media) && positiveId2(image.media.id))) {
936
+ invalidResponse();
937
+ }
938
+ return value;
939
+ }
940
+ function validateCreditDetails(value, id) {
941
+ if (!isRecord(value) || value.id !== id || typeof value.credit_type !== "string" || !["movie", "tv"].includes(String(value.media_type)) || !isRecord(value.person) || !positiveId2(value.person.id) || typeof value.person.name !== "string" || !isRecord(value.media) || !positiveId2(value.media.id)) {
942
+ invalidResponse();
943
+ }
944
+ return value;
945
+ }
946
+
947
+ // src/person-resources.ts
948
+ function createPersonResourceMethods(http) {
949
+ return {
950
+ async changeList(options = {}) {
951
+ return validateSearchPage(await http.get(
952
+ "person/changes",
953
+ changeParameters(options),
954
+ { includeLanguage: false }
955
+ ));
956
+ },
957
+ async changes(id, options = {}) {
958
+ const validId = positiveId(id);
959
+ return validateMovieChanges(await http.get(
960
+ `person/${validId}/changes`,
961
+ changeParameters(options),
962
+ { includeLanguage: false }
963
+ ));
964
+ },
965
+ async combinedCredits(id, options = {}) {
966
+ const validId = positiveId(id);
967
+ return validatePersonCredits(await http.get(`person/${validId}/combined_credits`, {
968
+ language: options.language
969
+ }), validId);
970
+ },
971
+ async externalIds(id) {
972
+ const validId = positiveId(id);
973
+ return validateIdShape(await http.get(
974
+ `person/${validId}/external_ids`,
975
+ {},
976
+ { includeLanguage: false }
977
+ ), validId, { imdb_id: metadataChecks.externalId, wikidata_id: metadataChecks.externalId });
978
+ },
979
+ async images(id) {
980
+ const validId = positiveId(id);
981
+ return validateIdShape(await http.get(
982
+ `person/${validId}/images`,
983
+ {},
984
+ { includeLanguage: false }
985
+ ), validId, { profiles: metadataChecks.images });
986
+ },
987
+ async latest() {
988
+ return validateLatestDetail(await http.get("person/latest", {}, { includeLanguage: false }), "name");
989
+ },
990
+ async movieCredits(id, options = {}) {
991
+ const validId = positiveId(id);
992
+ return validatePersonCredits(await http.get(`person/${validId}/movie_credits`, {
993
+ language: options.language
994
+ }), validId);
995
+ },
996
+ async popular(options = {}) {
997
+ if (options.page !== void 0 && (!Number.isSafeInteger(options.page) || options.page < 1)) {
998
+ throw new TypeError("page must be a positive integer");
999
+ }
1000
+ return validatePopularPeople(await http.get("person/popular", {
1001
+ language: options.language,
1002
+ page: options.page
1003
+ }));
1004
+ },
1005
+ async taggedImages(id, options = {}) {
1006
+ const validId = positiveId(id);
1007
+ if (options.page !== void 0 && (!Number.isSafeInteger(options.page) || options.page < 1)) {
1008
+ throw new TypeError("page must be a positive integer");
1009
+ }
1010
+ return validateTaggedImages(await http.get(
1011
+ `person/${validId}/tagged_images`,
1012
+ { page: options.page },
1013
+ { includeLanguage: false }
1014
+ ), validId);
1015
+ },
1016
+ async translations(id) {
1017
+ const validId = positiveId(id);
1018
+ return validateIdShape(await http.get(
1019
+ `person/${validId}/translations`,
1020
+ {},
1021
+ { includeLanguage: false }
1022
+ ), validId, { translations: metadataChecks.translations });
1023
+ },
1024
+ async tvCredits(id, options = {}) {
1025
+ const validId = positiveId(id);
1026
+ return validatePersonCredits(await http.get(`person/${validId}/tv_credits`, {
1027
+ language: options.language
1028
+ }), validId);
1029
+ }
1030
+ };
1031
+ }
1032
+ function createCreditMethods(http) {
1033
+ return {
1034
+ async get(creditId, options = {}) {
1035
+ const id = pathId(creditId, "credit ID");
1036
+ return validateCreditDetails(await http.get(
1037
+ `credit/${encodeURIComponent(id)}`,
1038
+ { language: options.language }
1039
+ ), id);
1040
+ }
1041
+ };
1042
+ }
1043
+
918
1044
  // src/details.ts
919
1045
  var MOVIE_APPEND = [
920
1046
  "credits",
@@ -1024,6 +1150,7 @@ function createTvMethods(http) {
1024
1150
  }
1025
1151
  function createPersonMethods(http) {
1026
1152
  return {
1153
+ ...createPersonResourceMethods(http),
1027
1154
  async get(id, options = {}) {
1028
1155
  const validId = positiveId(id);
1029
1156
  const payload = await http.get(
@@ -1582,6 +1709,7 @@ var Lumosovich = class {
1582
1709
  networks;
1583
1710
  tv;
1584
1711
  people;
1712
+ credits;
1585
1713
  images;
1586
1714
  configuration;
1587
1715
  certifications;
@@ -1623,6 +1751,7 @@ var Lumosovich = class {
1623
1751
  this.networks = createNetworkMethods(http);
1624
1752
  this.tv = createTvMethods(http);
1625
1753
  this.people = createPersonMethods(http);
1754
+ this.credits = createCreditMethods(http);
1626
1755
  this.images = createImageMethods();
1627
1756
  this.configuration = createConfigurationMethods(http);
1628
1757
  this.certifications = createCertificationMethods(http);
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { CreditMethods } from './person-types.js';
1
2
  import type { CertificationMethods, ConfigurationMethods, GenreMethods } from './reference-types.js';
2
3
  import type { ExternalIdSource, FindOptions, FindResponse, LumosovichOptions, SearchMethods } from './types.js';
3
4
  import type { MovieMethods, PersonMethods, TvMethods } from './detail-types.js';
@@ -6,6 +7,7 @@ import type { CollectionMethods, CompanyMethods } from './metadata-types.js';
6
7
  import type { DiscoverMethods, KeywordMethods, NetworkMethods, ReviewMethods, TrendingMethods } from './catalog-types.js';
7
8
  export { TmdbError } from './errors.js';
8
9
  export type { TmdbErrorCode } from './errors.js';
10
+ export type { CreditDetails, CreditMethods, PersonChangeListOptions, PersonChangeListPage, PersonChangesOptions, PersonChangesResponse, PersonCreditsResponse, PersonExternalIdsResponse, PersonImagesResponse, PersonPopularHit, PersonPopularPage, PersonResourceMethods, PersonTaggedImage, PersonTaggedImagesPage, PersonTranslationsResponse, } from './person-types.js';
9
11
  export type { ImageMethods, ImageSize } from './images.js';
10
12
  export type { DiscoverMethods, DiscoverCommonOptions, KeywordDetails, KeywordMethods, KeywordMoviesPage, MovieAccountStates, MovieCatalogMethods, MovieCatalogOptions, MovieCatalogPage, MovieChangeListOptions, MovieChangeListPage, MovieDiscoverOptions, MovieListHit, MovieListPage, NetworkAlternativeNamesResponse, NetworkDetails, NetworkImagesResponse, NetworkMethods, RatingResponse, RatingSession, ReviewDetails, ReviewMethods, ReviewPage, ReviewSummary, TrendingMethods, TrendingWindow, TvDiscoverOptions, } from './catalog-types.js';
11
13
  export type { Certification, CertificationMethods, CertificationsResponse, ConfigurationMethods, ConfigurationResponse, Country, CountryTimezones, DepartmentJobs, GenreMethods, GenresResponse, ImageConfiguration, Language, LanguageOptions, } from './reference-types.js';
@@ -30,6 +32,7 @@ export declare class Lumosovich {
30
32
  readonly networks: NetworkMethods;
31
33
  readonly tv: TvMethods;
32
34
  readonly people: PersonMethods;
35
+ readonly credits: CreditMethods;
33
36
  readonly images: ImageMethods;
34
37
  readonly configuration: ConfigurationMethods;
35
38
  readonly certifications: CertificationMethods;
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { createCollectionMethods, createCompanyMethods } from './metadata.js';
5
5
  import { createDiscoverMethods } from './discover.js';
6
6
  import { createKeywordMethods, createNetworkMethods, createReviewMethods, createTrendingMethods, } from './explore.js';
7
7
  import { createSearchMethods } from './search.js';
8
+ import { createCreditMethods } from './person-resources.js';
8
9
  import { createCertificationMethods, createConfigurationMethods, createGenreMethods } from './reference.js';
9
10
  import { validateFindResponse } from './validation.js';
10
11
  export { TmdbError } from './errors.js';
@@ -22,6 +23,7 @@ export class Lumosovich {
22
23
  networks;
23
24
  tv;
24
25
  people;
26
+ credits;
25
27
  images;
26
28
  configuration;
27
29
  certifications;
@@ -55,6 +57,7 @@ export class Lumosovich {
55
57
  this.networks = createNetworkMethods(http);
56
58
  this.tv = createTvMethods(http);
57
59
  this.people = createPersonMethods(http);
60
+ this.credits = createCreditMethods(http);
58
61
  this.images = createImageMethods();
59
62
  this.configuration = createConfigurationMethods(http);
60
63
  this.certifications = createCertificationMethods(http);
@@ -0,0 +1,4 @@
1
+ import { TmdbHttpClient } from './http.js';
2
+ import type { PersonResourceMethods, CreditMethods } from './person-types.js';
3
+ export declare function createPersonResourceMethods(http: TmdbHttpClient): PersonResourceMethods;
4
+ export declare function createCreditMethods(http: TmdbHttpClient): CreditMethods;
@@ -0,0 +1,72 @@
1
+ import { pathId, positiveId } from './ids.js';
2
+ import { changeParameters } from './metadata.js';
3
+ import { metadataChecks, validateIdShape, validateLatestDetail, validateMovieChanges } from './metadata-validation.js';
4
+ import { validateSearchPage } from './validation.js';
5
+ import { validateCreditDetails, validatePersonCredits, validatePopularPeople, validateTaggedImages } from './person-validation.js';
6
+ export function createPersonResourceMethods(http) {
7
+ return {
8
+ async changeList(options = {}) {
9
+ return validateSearchPage(await http.get('person/changes', changeParameters(options), { includeLanguage: false }));
10
+ },
11
+ async changes(id, options = {}) {
12
+ const validId = positiveId(id);
13
+ return validateMovieChanges(await http.get(`person/${validId}/changes`, changeParameters(options), { includeLanguage: false }));
14
+ },
15
+ async combinedCredits(id, options = {}) {
16
+ const validId = positiveId(id);
17
+ return validatePersonCredits(await http.get(`person/${validId}/combined_credits`, {
18
+ language: options.language,
19
+ }), validId);
20
+ },
21
+ async externalIds(id) {
22
+ const validId = positiveId(id);
23
+ return validateIdShape(await http.get(`person/${validId}/external_ids`, {}, { includeLanguage: false }), validId, { imdb_id: metadataChecks.externalId, wikidata_id: metadataChecks.externalId });
24
+ },
25
+ async images(id) {
26
+ const validId = positiveId(id);
27
+ return validateIdShape(await http.get(`person/${validId}/images`, {}, { includeLanguage: false }), validId, { profiles: metadataChecks.images });
28
+ },
29
+ async latest() {
30
+ return validateLatestDetail(await http.get('person/latest', {}, { includeLanguage: false }), 'name');
31
+ },
32
+ async movieCredits(id, options = {}) {
33
+ const validId = positiveId(id);
34
+ return validatePersonCredits(await http.get(`person/${validId}/movie_credits`, {
35
+ language: options.language,
36
+ }), validId);
37
+ },
38
+ async popular(options = {}) {
39
+ if (options.page !== undefined && (!Number.isSafeInteger(options.page) || options.page < 1)) {
40
+ throw new TypeError('page must be a positive integer');
41
+ }
42
+ return validatePopularPeople(await http.get('person/popular', {
43
+ language: options.language, page: options.page,
44
+ }));
45
+ },
46
+ async taggedImages(id, options = {}) {
47
+ const validId = positiveId(id);
48
+ if (options.page !== undefined && (!Number.isSafeInteger(options.page) || options.page < 1)) {
49
+ throw new TypeError('page must be a positive integer');
50
+ }
51
+ return validateTaggedImages(await http.get(`person/${validId}/tagged_images`, { page: options.page }, { includeLanguage: false }), validId);
52
+ },
53
+ async translations(id) {
54
+ const validId = positiveId(id);
55
+ return validateIdShape(await http.get(`person/${validId}/translations`, {}, { includeLanguage: false }), validId, { translations: metadataChecks.translations });
56
+ },
57
+ async tvCredits(id, options = {}) {
58
+ const validId = positiveId(id);
59
+ return validatePersonCredits(await http.get(`person/${validId}/tv_credits`, {
60
+ language: options.language,
61
+ }), validId);
62
+ },
63
+ };
64
+ }
65
+ export function createCreditMethods(http) {
66
+ return {
67
+ async get(creditId, options = {}) {
68
+ const id = pathId(creditId, 'credit ID');
69
+ return validateCreditDetails(await http.get(`credit/${encodeURIComponent(id)}`, { language: options.language }), id);
70
+ },
71
+ };
72
+ }
@@ -0,0 +1,86 @@
1
+ import type { ImageAsset, PersonCredit, PersonDetails } from './detail-types.js';
2
+ import type { MovieChangeListOptions } from './catalog-types.js';
3
+ import type { MovieChangesOptions, MovieChangesResponse, TranslationsResponse } from './metadata-types.js';
4
+ import type { LanguageOptions } from './reference-types.js';
5
+ import type { PageOptions, SearchPage, TmdbId } from './types.js';
6
+ export type PersonChangeListOptions = MovieChangeListOptions;
7
+ export type PersonChangesOptions = MovieChangesOptions;
8
+ export type PersonChangesResponse = MovieChangesResponse;
9
+ export type PersonTranslationsResponse = TranslationsResponse;
10
+ export type PersonChangeListPage = SearchPage<{
11
+ id: TmdbId;
12
+ adult?: boolean;
13
+ [key: string]: unknown;
14
+ }>;
15
+ export interface PersonPopularHit {
16
+ id: TmdbId;
17
+ name: string;
18
+ known_for?: Array<Record<string, unknown>>;
19
+ profile_path?: string | null;
20
+ [key: string]: unknown;
21
+ }
22
+ export type PersonPopularPage = SearchPage<PersonPopularHit>;
23
+ export interface PersonCreditsResponse {
24
+ id: TmdbId;
25
+ cast: PersonCredit[];
26
+ crew: PersonCredit[];
27
+ [key: string]: unknown;
28
+ }
29
+ export interface PersonExternalIdsResponse {
30
+ id: TmdbId;
31
+ imdb_id?: string | null;
32
+ wikidata_id?: string | null;
33
+ [key: string]: unknown;
34
+ }
35
+ export interface PersonImagesResponse {
36
+ id: TmdbId;
37
+ profiles: ImageAsset[];
38
+ [key: string]: unknown;
39
+ }
40
+ export interface PersonTaggedImage extends ImageAsset {
41
+ id: string;
42
+ image_type: string;
43
+ media_type: string;
44
+ media: Record<string, unknown>;
45
+ }
46
+ export interface PersonTaggedImagesPage {
47
+ id: TmdbId;
48
+ page: number;
49
+ results: PersonTaggedImage[];
50
+ total_pages: number;
51
+ total_results: number;
52
+ [key: string]: unknown;
53
+ }
54
+ export interface CreditDetails {
55
+ id: string;
56
+ credit_type: string;
57
+ media_type: 'movie' | 'tv';
58
+ person: {
59
+ id: TmdbId;
60
+ name: string;
61
+ [key: string]: unknown;
62
+ };
63
+ media: {
64
+ id: TmdbId;
65
+ [key: string]: unknown;
66
+ };
67
+ department?: string;
68
+ job?: string;
69
+ [key: string]: unknown;
70
+ }
71
+ export interface CreditMethods {
72
+ get(creditId: string, options?: LanguageOptions): Promise<CreditDetails>;
73
+ }
74
+ export interface PersonResourceMethods {
75
+ changeList(options?: PersonChangeListOptions): Promise<PersonChangeListPage>;
76
+ changes(id: TmdbId, options?: PersonChangesOptions): Promise<PersonChangesResponse>;
77
+ combinedCredits(id: TmdbId, options?: LanguageOptions): Promise<PersonCreditsResponse>;
78
+ externalIds(id: TmdbId): Promise<PersonExternalIdsResponse>;
79
+ images(id: TmdbId): Promise<PersonImagesResponse>;
80
+ latest(): Promise<PersonDetails>;
81
+ movieCredits(id: TmdbId, options?: LanguageOptions): Promise<PersonCreditsResponse>;
82
+ popular(options?: PageOptions & LanguageOptions): Promise<PersonPopularPage>;
83
+ taggedImages(id: TmdbId, options?: PageOptions): Promise<PersonTaggedImagesPage>;
84
+ translations(id: TmdbId): Promise<PersonTranslationsResponse>;
85
+ tvCredits(id: TmdbId, options?: LanguageOptions): Promise<PersonCreditsResponse>;
86
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { CreditDetails, PersonTaggedImagesPage } from './person-types.js';
2
+ export declare function validatePersonCredits<T>(value: unknown, id: number): T;
3
+ export declare function validatePopularPeople<T>(value: unknown): T;
4
+ export declare function validateTaggedImages(value: unknown, id: number): PersonTaggedImagesPage;
5
+ export declare function validateCreditDetails(value: unknown, id: string): CreditDetails;
@@ -0,0 +1,40 @@
1
+ import { invalidResponse, isRecord, validateSearchPage } from './validation.js';
2
+ import { metadataChecks, validateIdShape } from './metadata-validation.js';
3
+ const positiveId = (value) => Number.isSafeInteger(value) && Number(value) > 0;
4
+ const count = (value) => Number.isSafeInteger(value) && Number(value) >= 0;
5
+ export function validatePersonCredits(value, id) {
6
+ return validateIdShape(value, id, {
7
+ cast: metadataChecks.identities,
8
+ crew: metadataChecks.identities,
9
+ });
10
+ }
11
+ export function validatePopularPeople(value) {
12
+ const page = validateSearchPage(value);
13
+ if (!page.results.every((person) => isRecord(person) && typeof person.name === 'string')) {
14
+ invalidResponse();
15
+ }
16
+ return page;
17
+ }
18
+ export function validateTaggedImages(value, id) {
19
+ if (!isRecord(value) || value.id !== id || !count(value.page) ||
20
+ !count(value.total_pages) || !count(value.total_results) ||
21
+ !Array.isArray(value.results) || !value.results.every((image) => isRecord(image) && typeof image.id === 'string' && !!image.id &&
22
+ typeof image.file_path === 'string' &&
23
+ typeof image.media_type === 'string' && !!image.media_type &&
24
+ typeof image.image_type === 'string' && isRecord(image.media) &&
25
+ positiveId(image.media.id))) {
26
+ invalidResponse();
27
+ }
28
+ return value;
29
+ }
30
+ export function validateCreditDetails(value, id) {
31
+ if (!isRecord(value) || value.id !== id ||
32
+ typeof value.credit_type !== 'string' ||
33
+ !['movie', 'tv'].includes(String(value.media_type)) ||
34
+ !isRecord(value.person) || !positiveId(value.person.id) ||
35
+ typeof value.person.name !== 'string' ||
36
+ !isRecord(value.media) || !positiveId(value.media.id)) {
37
+ invalidResponse();
38
+ }
39
+ return value;
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmdb-lumosovich",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Typed Node.js client for the TMDB API with IMDb ID lookup for movies, TV shows, and people",
5
5
  "keywords": [
6
6
  "tmdb",