diff --git a/.changeset/hungry-ghosts-dream.md b/.changeset/hungry-ghosts-dream.md
new file mode 100644
index 0000000..b4a9195
--- /dev/null
+++ b/.changeset/hungry-ghosts-dream.md
@@ -0,0 +1,5 @@
+---
+'@untidy/thetvdb': minor
+---
+
+feat: temporarily remove signal, possibly due to a memory leak.
diff --git a/.changeset/three-hounds-train.md b/.changeset/three-hounds-train.md
new file mode 100644
index 0000000..5035bf2
--- /dev/null
+++ b/.changeset/three-hounds-train.md
@@ -0,0 +1,5 @@
+---
+'@untidy/thetvdb': minor
+---
+
+feat: support `/inspiration/types` and `/genders` endpoints
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 8eb7b8a..6709bcf 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -4,7 +4,7 @@
"features": {
"ghcr.io/devcontainers/features/node:1": {
"nodeGypDependencies": true,
- "version": "18.18.2",
+ "version": "20.9.0",
"nvmVersion": "latest"
}
},
diff --git a/docs/api/index.md b/docs/api/index.md
index 44e9d2a..79be17d 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -17,10 +17,9 @@ future updates.
## Constructor
-| params | type | Required | Description |
-| ------- | -------- | :------: | ------------------------------------------------- |
-| token | `string` | Yes | Your TVDB API token. |
-| timeout | `number` | optional | Request timeout in milliseconds. `Default: 5000`. |
+| params | type | Required | Description |
+| ------ | -------- | :------: | -------------------- |
+| token | `string` | Yes | Your TVDB API token. |
::: danger WARNING
@@ -35,53 +34,3 @@ import { TheTVDBExtended } from '@untidy/thetvdb';
new TheTVDBExtended('your token');
```
-
-### Set token and timeout
-
-```js
-import { TheTVDBExtended } from '@untidy/thetvdb';
-
-new TheTVDBExtended('your token', 10000);
-```
-
-## Advanced
-
-### getTime
-
-This method retrieves the custom request timeout value in milliseconds.
-
-| type | Description |
-| -------- | ----------------------------------------------- |
-| `number` | The custom request timeout value currently set. |
-
-::: details Example
-
-```js
-import { TheTVDB } from '@untidy/thetvdb';
-
-const client = new TheTVDB('your token');
-
-client.getTime();
-```
-
-:::
-
-### setTime
-
-This method allows setting a custom request timeout value in milliseconds.
-
-| param | type | Required | Description |
-| ------- | -------- | :------: | ---------------------------------------------- |
-| timeout | `number` | Yes | The new request timeout value in milliseconds. |
-
-::: details Example
-
-```js
-import { TheTVDBExtended } from '@untidy/thetvdb';
-
-const client = new TheTVDBExtended('your token');
-
-client.setTime(10000);
-```
-
-:::
diff --git a/docs/api/thetvdb-extended.md b/docs/api/thetvdb-extended.md
index 408c403..70bb897 100644
--- a/docs/api/thetvdb-extended.md
+++ b/docs/api/thetvdb-extended.md
@@ -94,6 +94,22 @@ This method returns a list of active entity types records and does not require a
await client.getEntities();
```
+## getGenders
+
+This method returns a list of genders records and does not require any parameters.
+
+### Supported endpoint
+
+| method | endpoint |
+| ------------------------------- | ---------- |
+| | `/genders` |
+
+### List of genre records
+
+```js
+await client.getGenders();
+```
+
## getGenres
This method returns a list of genre records and does not require any parameters.
@@ -110,6 +126,22 @@ This method returns a list of genre records and does not require any parameters.
await client.getGenres();
```
+## getInspirationTypes
+
+This method returns a list of inspiration types records and does not require any parameters.
+
+### Supported endpoint
+
+| method | endpoint |
+| ------------------------------- | -------------------- |
+| | `/inspiration/types` |
+
+### List of genre records
+
+```js
+await client.getInspirationTypes();
+```
+
## getLanguages
This method returns a list of language records and does not require any parameters.
diff --git a/docs/guide/supported-endpoints.md b/docs/guide/supported-endpoints.md
index 5ca3849..7d9619a 100644
--- a/docs/guide/supported-endpoints.md
+++ b/docs/guide/supported-endpoints.md
@@ -33,10 +33,10 @@ List of endpoints from [TheTVDB API V4](https://thetvdb.github.io/v4-api/).
| `/episodes/{id}` | :white_check_mark: |
| `/episodes/{id}/extended` | :white_check_mark: |
| `/episodes/{id}/translations/{language}` | :interrobang: |
-| `/genders` | :interrobang: |
+| `/genders` | :white_check_mark: |
| `/genres` | :white_check_mark: |
| `/genres/{id}` | :interrobang: |
-| `/inspiration/types` | :interrobang: |
+| `/inspiration/types` | :white_check_mark: |
| `/languages` | :white_check_mark: |
| `/lists` | :interrobang: |
| `/lists/{id}` | :interrobang: |
diff --git a/package.json b/package.json
index 618393e..8c2ecb3 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
"vitepress": "1.0.0-rc.24"
},
"engines": {
- "node": "^18.0.0 || ^20.0.0",
+ "node": "^18.16.1 || ^20.0.0",
"pnpm": ">=8"
},
"repository": {
diff --git a/src/core.ts b/src/core.ts
index cbff33a..1eb7015 100644
--- a/src/core.ts
+++ b/src/core.ts
@@ -1,44 +1,20 @@
-import { clearTimeout, setTimeout } from 'node:timers';
import { URL } from 'node:url';
export abstract class Base {
private readonly _token;
protected readonly api = 'https://api4.thetvdb.com';
- private _timeout: number;
- constructor(token: string, timeout = 5000) {
+ constructor(token: string) {
this.validateInput(token, 'Token is required');
- this.validateTimeout(timeout);
this._token = token;
- this._timeout = timeout;
- }
-
- public getTime(): number {
- return this._timeout;
- }
-
- public setTime(value: number): void {
- this.validateTimeout(value);
- this._timeout = value;
}
protected async fetcher(url: string | URL): Promise {
- const controller = new AbortController();
- const { signal } = controller;
- const timeout = setTimeout(() => {
- controller.abort();
- }, this._timeout);
-
- try {
- const response = await fetch(url, {
- headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this._token}` },
- signal,
- });
+ const response = await fetch(url, {
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this._token}` },
+ });
- return (await response.json()) as T;
- } finally {
- clearTimeout(timeout);
- }
+ return (await response.json()) as T;
}
protected validateInput(input: string, message: string): void {
@@ -47,15 +23,6 @@ export abstract class Base {
}
}
- protected validateTimeout(input: number): void {
- if (typeof input !== 'number' || isNaN(input)) {
- throw new TypeError('timeout must be of type number');
- }
- if (input <= 0) {
- throw new RangeError('timeout must be a positive number');
- }
- }
-
protected createURL(path: string): URL {
return new URL(path, this.api);
}
diff --git a/src/extended.ts b/src/extended.ts
index 0679b68..c93f92f 100644
--- a/src/extended.ts
+++ b/src/extended.ts
@@ -96,10 +96,18 @@ interface Shared {
name: string;
}
+interface Inspiration extends Shared {
+ description: string;
+ reference_name: string;
+ url: string;
+}
+
type GetContentRatings = Data;
type GetCountries = Data;
type GetEntities = Data;
+type GetGenders = Data;
type GetGenres = Data;
+type GetInspirationTypes = Data;
type GetLanguages = Data;
type GetUpdates = DataLink;
type GetArtworkStatuses = Data;
@@ -107,37 +115,39 @@ type GetArtworkTypes = Data;
export class TheTVDBExtended extends Base {
public async getArtworkStatuses(): Promise {
- const endpoint = this.api + '/v4/artwork/statuses';
- return await this.fetcher(endpoint);
+ return await this.fetcher(this.api + '/v4/artwork/statuses');
}
public async getArtworkTypes(): Promise {
- const endpoint = this.api + '/v4/artwork/types';
- return await this.fetcher(endpoint);
+ return await this.fetcher(this.api + '/v4/artwork/types');
}
public async getContentRatings(): Promise {
- const endpoint = this.api + '/v4/content/ratings';
- return await this.fetcher(endpoint);
+ return await this.fetcher(this.api + '/v4/content/ratings');
}
public async getCountries(): Promise {
- const endpoint = this.api + '/v4/countries';
- return await this.fetcher(endpoint);
+ return await this.fetcher(this.api + '/v4/countries');
}
public async getEntities(): Promise {
- return await this.fetcher(this.api + '/v4/entities')
+ return await this.fetcher(this.api + '/v4/entities');
+ }
+
+ public async getGenders(): Promise {
+ return await this.fetcher(this.api + '/v4/genders');
}
public async getGenres(): Promise {
- const endpoint = this.api + '/v4/genres';
- return await this.fetcher(endpoint);
+ return await this.fetcher(this.api + '/v4/genres');
+ }
+
+ public async getInspirationTypes(): Promise {
+ return await this.fetcher(this.api + '/v4/inspiration/types');
}
public async getLanguages(): Promise {
- const endpoint = this.api + '/v4/languages';
- return await this.fetcher(endpoint);
+ return await this.fetcher(this.api + '/v4/languages');
}
public async getUpdates(options: updateO): Promise {
diff --git a/tests/core.test.ts b/tests/core.test.ts
index 335bde3..6c4835b 100644
--- a/tests/core.test.ts
+++ b/tests/core.test.ts
@@ -16,61 +16,8 @@ describe('constructor token validation tests', () => {
expect(() => new Base(1234)).toThrow('Token is required');
});
- it('throws error when non-string timeout is provided', () => {
- // @ts-expect-error: Cannot create an instance of an abstract class.
- expect(() => new Base('fake token', 'lala')).toThrow('timeout must be of type number');
- });
-
- it('throws error when the timeout provided is 0', () => {
- // @ts-expect-error: Cannot create an instance of an abstract class.
- expect(() => new Base('fake token', 0)).toThrow('timeout must be a positive number');
- });
-
it('creates an instance when a valid token is provided', () => {
// @ts-expect-error: Cannot create an instance of an abstract class.
expect(() => new Base('fake token')).not.toThrow();
});
-
- it('returns the default custom timeout', () => {
- // @ts-expect-error: Cannot create an instance of an abstract class.
- const client = new Base('fake token');
- expect(client.getTime()).toBe(5000);
- });
-
- it('returns the custom timeout set by the user', () => {
- // @ts-expect-error: Cannot create an instance of an abstract class.
- const client = new Base('fake token', 500);
- expect(client.getTime()).toBe(500);
- });
-
- it('returns the updated custom timeout after it is modified', () => {
- // @ts-expect-error: Cannot create an instance of an abstract class.
- const client = new Base('fake token', 1000);
- expect(client.getTime()).toBe(1000);
-
- client.setTime(400);
- expect(client.getTime()).toBe(400);
- });
-
- it('sets the default timeout and allows updating it later', () => {
- // @ts-expect-error: Cannot create an instance of an abstract class.
- const client = new Base('fake token');
- expect(client.getTime()).toBe(5000);
-
- client.setTime(100);
- expect(client.getTime()).toBe(100);
- });
-});
-
-describe('abort controller', () => {
- it('aborts the request when the timeout is reached', async () => {
- const abortSpy = jest.spyOn(global.AbortController.prototype, 'abort');
- // @ts-expect-error: Cannot create an instance of an abstract class.
- const client = new Base('fake token', 1);
-
- await expect(async () => await client.fetcher('https://delay.com/delay')).rejects.toThrow(
- 'This operation was aborted'
- );
- expect(abortSpy).toHaveBeenCalled();
- });
});
diff --git a/tests/extended.test.ts b/tests/extended.test.ts
index 6e651d4..1f4b272 100644
--- a/tests/extended.test.ts
+++ b/tests/extended.test.ts
@@ -61,6 +61,26 @@ describe('getGenres()', () => {
});
});
+describe('getGenders()', () => {
+ test('returns a successful response', async () => {
+ const { data } = await client.getGenders();
+ expect(Array.isArray(data)).toBe(true);
+ expect(data[0]?.name).toBe('Male');
+ expect(data[1]?.id).toBe(2);
+ });
+});
+
+describe('getInspirationTypes()', () => {
+ test('returns a successful response', async () => {
+ const { data } = await client.getInspirationTypes();
+ expect(Array.isArray(data)).toBe(true);
+ expect(data).toHaveLength(2);
+ expect(data[0]?.name).toBe('Historical Event');
+ expect(data[1]?.id).toBe(2);
+ expect(data[1]?.reference_name).toBe('Goodreads');
+ });
+});
+
describe('getLanguages()', () => {
test('returns a successful response', async () => {
const { status, data } = await client.getLanguages();
diff --git a/tests/mocks/handlers.ts b/tests/mocks/handlers.ts
index f477032..45da0af 100644
--- a/tests/mocks/handlers.ts
+++ b/tests/mocks/handlers.ts
@@ -26,7 +26,9 @@ import {
filterSerie,
filterSerieS,
filterSerieY,
+ genders,
genres,
+ inspirationTypes,
languages,
movie,
movieE,
@@ -55,53 +57,57 @@ import {
export const handlers: HttpHandler[] = [
http.get('https://api4.thetvdb.com/v4/awards/categories/:id/extended', () => {
- return HttpResponse.json(awardsCategoryIdExtended)
+ return HttpResponse.json(awardsCategoryIdExtended);
}),
http.get('https://api4.thetvdb.com/v4/awards/categories/:id', () => {
- return HttpResponse.json(awardsCategoryId)
+ return HttpResponse.json(awardsCategoryId);
}),
http.get('https://api4.thetvdb.com/v4/awards/:id/extended', () => {
- return HttpResponse.json(awardsIdExtended)
+ return HttpResponse.json(awardsIdExtended);
}),
http.get('https://api4.thetvdb.com/v4/awards/:id', () => {
- return HttpResponse.json(awardsId)
-
+ return HttpResponse.json(awardsId);
}),
http.get('https://api4.thetvdb.com/v4/awards', () => {
- return HttpResponse.json(awards)
+ return HttpResponse.json(awards);
}),
// @ts-expect-error: DefaultBodyType doesn't expect undefined
http.get('https://api4.thetvdb.com/v4/companies', ({ request }) => {
if (request.url === 'https://api4.thetvdb.com/v4/companies?page=94') {
- return HttpResponse.json(companiesPage)
+ return HttpResponse.json(companiesPage);
} else {
- return HttpResponse.json(companies)
+ return HttpResponse.json(companies);
}
}),
// @ts-expect-error: DefaultBodyType doesn't expect undefined
http.get('https://api4.thetvdb.com/v4/companies/:path', ({ request }) => {
switch (request.url) {
case 'https://api4.thetvdb.com/v4/companies/types':
- return HttpResponse.json(companiesTypes)
+ return HttpResponse.json(companiesTypes);
default:
- return HttpResponse.json(companyId)
+ return HttpResponse.json(companyId);
}
}),
http.get('https://api4.thetvdb.com/v4/content/ratings', () => {
- return HttpResponse.json(contentRatings)
+ return HttpResponse.json(contentRatings);
}),
http.get('https://api4.thetvdb.com/v4/countries', () => {
- return HttpResponse.json(countries)
-
+ return HttpResponse.json(countries);
}),
http.get('https://api4.thetvdb.com/v4/entities', () => {
- return HttpResponse.json(entities)
+ return HttpResponse.json(entities);
+ }),
+ http.get('https://api4.thetvdb.com/v4/genders', () => {
+ return HttpResponse.json(genders);
}),
http.get('https://api4.thetvdb.com/v4/genres', () => {
- return HttpResponse.json(genres)
+ return HttpResponse.json(genres);
+ }),
+ http.get('https://api4.thetvdb.com/v4/inspiration/types', () => {
+ return HttpResponse.json(inspirationTypes);
}),
http.get('https://api4.thetvdb.com/v4/languages', () => {
- return HttpResponse.json(languages)
+ return HttpResponse.json(languages);
}),
// @ts-expect-error: DefaultBodyType doesn't expect undefined
http.get('https://api4.thetvdb.com/v4/updates', ({ request }) => {
@@ -112,7 +118,7 @@ export const handlers: HttpHandler[] = [
}
}),
http.get('https://api4.thetvdb.com/v4/artwork/:id/extended', () => {
- return HttpResponse.json(artworkExtended)
+ return HttpResponse.json(artworkExtended);
}),
// @ts-expect-error: DefaultBodyType doesn't expect undefined
http.get('https://api4.thetvdb.com/v4/artwork/:id', ({ request }) => {
@@ -126,7 +132,7 @@ export const handlers: HttpHandler[] = [
}
}),
http.get('https://api4.thetvdb.com/v4/characters/:id', () => {
- return HttpResponse.json(character)
+ return HttpResponse.json(character);
}),
// @ts-expect-error: DefaultBodyType doesn't expect undefined
http.get('https://api4.thetvdb.com/v4/episodes/:id/extended', ({ request }) => {
@@ -137,8 +143,7 @@ export const handlers: HttpHandler[] = [
}
}),
http.get('https://api4.thetvdb.com/v4/episodes/:id', () => {
- return HttpResponse.json(episodes)
-
+ return HttpResponse.json(episodes);
}),
// @ts-expect-error: DefaultBodyType doesn't expect undefined
http.get('https://api4.thetvdb.com/v4/people/:id/extended', ({ request }) => {
@@ -149,8 +154,7 @@ export const handlers: HttpHandler[] = [
}
}),
http.get('https://api4.thetvdb.com/v4/people/:id', () => {
- return HttpResponse.json(people)
-
+ return HttpResponse.json(people);
}),
// @ts-expect-error: DefaultBodyType doesn't expect undefined
http.get('https://api4.thetvdb.com/v4/search', ({ request }) => {
@@ -188,8 +192,7 @@ export const handlers: HttpHandler[] = [
}
}),
http.get('https://api4.thetvdb.com/v4/movies/:id', () => {
- return HttpResponse.json(movie)
-
+ return HttpResponse.json(movie);
}),
// @ts-expect-error: DefaultBodyType doesn't expect undefined
http.get('https://api4.thetvdb.com/v4/seasons/:id/extended', ({ request }) => {
@@ -201,8 +204,7 @@ export const handlers: HttpHandler[] = [
}
}),
http.get('https://api4.thetvdb.com/v4/seasons/:id', () => {
- return HttpResponse.json(season)
-
+ return HttpResponse.json(season);
}),
// @ts-expect-error: DefaultBodyType doesn't expect undefined
http.get('https://api4.thetvdb.com/v4/series/filter', ({ request }) => {
@@ -233,6 +235,6 @@ export const handlers: HttpHandler[] = [
}
}),
http.get('https://api4.thetvdb.com/v4/series/:id', () => {
- return HttpResponse.json(series)
+ return HttpResponse.json(series);
}),
];
diff --git a/tests/mocks/response.ts b/tests/mocks/response.ts
index 99e29c7..0b7bd8a 100644
--- a/tests/mocks/response.ts
+++ b/tests/mocks/response.ts
@@ -114,7 +114,7 @@ const entities = {
status: 'success',
data: [
{
- name: "series",
+ name: 'series',
},
],
};
@@ -180,6 +180,20 @@ const filterSerie = {
data: [{ name: 'Made In Hollywood' }],
};
+// https://api4.thetvdb.com/v4/genders
+const genders = {
+ data: [
+ {
+ id: 1,
+ name: 'Male',
+ },
+ {
+ id: 2,
+ name: 'Female',
+ },
+ ],
+};
+
// https://api4.thetvdb.com/v4/genres
const genres = {
status: 'success',
@@ -193,6 +207,22 @@ const genres = {
],
};
+// https://api4.thetvdb.com/v4/inspiration/types
+const inspirationTypes = {
+ data: [
+ {
+ id: 1,
+ name: 'Historical Event',
+ reference_name: 'Wikipedia',
+ },
+ {
+ id: 2,
+ name: 'Book Series',
+ reference_name: 'Goodreads',
+ },
+ ],
+};
+
// https://api4.thetvdb.com/v4/languages
const languages = {
status: 'success',
@@ -469,7 +499,9 @@ export {
filterSerie,
filterSerieS,
filterSerieY,
+ genders,
genres,
+ inspirationTypes,
languages,
movie,
movieE,
@@ -493,6 +525,5 @@ export {
seriesET,
seriesETS,
updates,
- updatesFull
+ updatesFull,
};
-