mirror of
https://github.com/tcgdex/javascript-sdk.git
synced 2025-04-22 02:42:08 +00:00
feat: Allow a user to filters elements on the API (#275)
This commit is contained in:
parent
f2621890e1
commit
bf54ab3809
@ -17,7 +17,7 @@
|
|||||||
<img src="https://img.shields.io/github/stars/tcgdex/javascript-sdk?style=flat-square" alt="Github stars">
|
<img src="https://img.shields.io/github/stars/tcgdex/javascript-sdk?style=flat-square" alt="Github stars">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/tcgdex/javascript-sdk/actions/workflows/build.yml">
|
<a href="https://github.com/tcgdex/javascript-sdk/actions/workflows/build.yml">
|
||||||
<img src="https://img.shields.io/github/workflow/status/tcgdex/javascript-sdk/Build%20&%20Test?style=flat-square" alt="the TCGdex JAvascript SDK is released under the MIT license." />
|
<img src="https://img.shields.io/github/actions/workflow/status/tcgdex/javascript-sdk/build.yml?style=flat-square" alt="the TCGdex JAvascript SDK is released under the MIT license." />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.gg/NehYTAhsZE">
|
<a href="https://discord.gg/NehYTAhsZE">
|
||||||
<img src="https://img.shields.io/discord/857231041261076491?color=%235865F2&label=Discord&style=flat-square" alt="Discord Link">
|
<img src="https://img.shields.io/discord/857231041261076491?color=%235865F2&label=Discord&style=flat-square" alt="Discord Link">
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
const TCGdex = require("../src/tcgdex").default
|
/// <reference types="jest" />
|
||||||
const fetch = require('node-fetch')
|
|
||||||
|
const { default: TCGdex, Query } = require("../src/tcgdex")
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
|
||||||
const fakeFetch = (response, status = 200) => jest.fn(() =>
|
const fakeFetch = (response, status = 200) => jest.fn(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
@ -8,59 +10,131 @@ const fakeFetch = (response, status = 200) => jest.fn(() =>
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
test('Basic test', async () => {
|
test('Basic test', async () => {
|
||||||
const tcgdex = new TCGdex('en')
|
const tcgdex = new TCGdex('en')
|
||||||
TCGdex.fetch = fakeFetch({ok: true})
|
TCGdex.fetch = fakeFetch({ ok: true })
|
||||||
const res = await tcgdex.fetch('cards', 'basic-test')
|
const res = await tcgdex.fetch('cards', 'basic-test')
|
||||||
expect(res).toEqual({ok: true})
|
expect(res).toEqual({ ok: true })
|
||||||
expect(TCGdex.fetch).toHaveBeenCalledTimes(1)
|
expect(TCGdex.fetch).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Cache test', async () => {
|
|
||||||
const tcgdex = new TCGdex('en')
|
|
||||||
TCGdex.fetch = fakeFetch({ok: 'a'})
|
|
||||||
const res1 = await tcgdex.fetch('cards', 'cache-test')
|
|
||||||
expect(res1).toEqual({ok: 'a'})
|
|
||||||
TCGdex.fetch = fakeFetch({ok: 'b'})
|
|
||||||
const res2 = await tcgdex.fetch('cards', 'cache-test')
|
|
||||||
expect(res2).toEqual({ok: 'a'})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('endpoint errors', async () => {
|
test('endpoint errors', async () => {
|
||||||
const tcgdex = new TCGdex('en')
|
const tcgdex = new TCGdex('en')
|
||||||
TCGdex.fetch = fakeFetch({ok: 'a'})
|
TCGdex.fetch = fakeFetch({ ok: 'a' })
|
||||||
await expect(tcgdex.fetch('non existing endpoint')).rejects.toThrow()
|
await expect(tcgdex.fetch('non existing endpoint')).rejects.toThrow()
|
||||||
await expect(tcgdex.fetch()).rejects.toThrow()
|
await expect(tcgdex.fetch()).rejects.toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('404 test', async () => {
|
test(`404 error`, async () => {
|
||||||
const tcgdex = new TCGdex('en')
|
|
||||||
TCGdex.fetch = fakeFetch(undefined, 404)
|
|
||||||
expect(
|
|
||||||
await tcgdex.fetch('cards', '404-test')
|
|
||||||
).not.toBeDefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('test real endpoints', async () => {
|
|
||||||
const tcgdex = new TCGdex('en')
|
const tcgdex = new TCGdex('en')
|
||||||
TCGdex.fetch = fetch
|
TCGdex.fetch = fetch
|
||||||
const endpoints = [
|
|
||||||
{endpoint: 'fetchCard', params: ['swsh1-1']},
|
|
||||||
{endpoint: 'fetchCard', params: ['1', 'Sword & Shield']},
|
|
||||||
{endpoint: 'fetchCards', params: ['swsh1']},
|
|
||||||
{endpoint: 'fetchCards', params: []},
|
|
||||||
{endpoint: 'fetchSet', params: ['swsh1']},
|
|
||||||
{endpoint: 'fetchSets', params: ['swsh']},
|
|
||||||
{endpoint: 'fetchSets', params: []},
|
|
||||||
{endpoint: 'fetchSeries', params: []},
|
|
||||||
{endpoint: 'fetchSerie', params: ['swsh']},
|
|
||||||
]
|
|
||||||
|
|
||||||
for await (const item of endpoints) {
|
|
||||||
expect(
|
expect(
|
||||||
await tcgdex[item.endpoint](...item.params)
|
await tcgdex.card.get('404-error')
|
||||||
).toBeDefined()
|
).toBeNull()
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(`test getting full set from list`, async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fetch
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await (await tcgdex.set.list())[0].getSet()
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`test getting full serie from list`, async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fetch
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await (await tcgdex.serie.list())[0].getSerie()
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`test getting full card from list`, async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fetch
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await (await tcgdex.card.list())[0].getCard()
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test(`test get set from card`, async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fetch
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await (await tcgdex.card.get('swsh1-136')).getSet()
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`test get serie from set`, async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fetch
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await (await tcgdex.set.get('swsh1')).getSerie()
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`advanced query system`, async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fetch
|
||||||
|
|
||||||
|
expect(
|
||||||
|
(await tcgdex.card.list(
|
||||||
|
Query.create()
|
||||||
|
.equal('name', 'Pikachu')
|
||||||
|
.greaterOrEqualThan('hp', 60)
|
||||||
|
.lesserThan('hp', 70)
|
||||||
|
.contains('localId', '5')
|
||||||
|
.not.contains('localId', 'tg')
|
||||||
|
.not.equal('id', 'cel25-5')
|
||||||
|
.sort('localId', 'ASC')
|
||||||
|
.paginate(3, 2)
|
||||||
|
)).length
|
||||||
|
).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
const endpoints = [
|
||||||
|
{ endpoint: 'card', params: ['swsh1-136'] },
|
||||||
|
{ endpoint: 'set', params: ['swsh1'] },
|
||||||
|
{ endpoint: 'serie', params: ['swsh'] },
|
||||||
|
{ endpoint: 'type', params: ['fire'] },
|
||||||
|
{ endpoint: 'retreat', params: ['1'] },
|
||||||
|
{ endpoint: 'rarity', params: ['common'] },
|
||||||
|
{ endpoint: 'illustrator', params: [''] },
|
||||||
|
{ endpoint: 'hp', params: ['30'] },
|
||||||
|
{ endpoint: 'categorie', params: ['pokemon'] },
|
||||||
|
{ endpoint: 'dexID', params: ['1'] },
|
||||||
|
{ endpoint: 'energyType', params: ['normal'] },
|
||||||
|
{ endpoint: 'regulationMark', params: ['f'] },
|
||||||
|
{ endpoint: 'stage', params: ['basic'] },
|
||||||
|
{ endpoint: 'suffixe', params: ['ex'] },
|
||||||
|
{ endpoint: 'trainerType', params: ['item'] },
|
||||||
|
{ endpoint: 'variant', params: ['normal'] },
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const endpoint of endpoints) {
|
||||||
|
test(`test real ${endpoint.endpoint} endpoint list`, async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fetch
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await (tcgdex[endpoint.endpoint]).list()
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`test real ${endpoint.endpoint} endpoint item`, async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fetch
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await (tcgdex[endpoint.endpoint]).get(endpoint.params[0])
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
54
__tests__/cache.test.js
Normal file
54
__tests__/cache.test.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/// <reference types="jest" />
|
||||||
|
|
||||||
|
const { default: MemoryCache } = require("../src/Psr/SimpleCache/MemoryCache")
|
||||||
|
|
||||||
|
const TCGdex = require("../src/tcgdex").default
|
||||||
|
|
||||||
|
test('that cache store and get one element', async () => {
|
||||||
|
const cache = new MemoryCache()
|
||||||
|
cache.set('a', 'b')
|
||||||
|
expect(cache.get('a')).toBe('b')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('that cache store and get multiple elements', async () => {
|
||||||
|
const cache = new MemoryCache()
|
||||||
|
cache.setMultiple({
|
||||||
|
'a': 'b',
|
||||||
|
'c': 'd'
|
||||||
|
})
|
||||||
|
expect(cache.getMultiple(['a', 'c'])).toStrictEqual({
|
||||||
|
a: 'b',
|
||||||
|
c: 'd'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('cache expiration', async () => {
|
||||||
|
const cache = new MemoryCache()
|
||||||
|
cache.set('a', 'b', 1)
|
||||||
|
// wait 2 secs
|
||||||
|
await new Promise((res) => setTimeout(res, 2000))
|
||||||
|
expect(cache.get('a')).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('cache deletion', async () => {
|
||||||
|
const cache = new MemoryCache()
|
||||||
|
cache.set('a', 'b')
|
||||||
|
expect(cache.get('a')).toBe('b')
|
||||||
|
cache.delete('a')
|
||||||
|
expect(cache.get('a')).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('cache cleared', async () => {
|
||||||
|
const cache = new MemoryCache()
|
||||||
|
cache.set('a', 'b')
|
||||||
|
expect(cache.get('a')).toBe('b')
|
||||||
|
cache.clear()
|
||||||
|
expect(cache.get('a')).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('cache exists', async () => {
|
||||||
|
const cache = new MemoryCache()
|
||||||
|
expect(cache.has('a')).toBe(false)
|
||||||
|
cache.set('a', 'b')
|
||||||
|
expect(cache.has('a')).toBe(true)
|
||||||
|
})
|
66
__tests__/deprecated.test.js
Normal file
66
__tests__/deprecated.test.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
const TCGdex = require("../src/tcgdex").default
|
||||||
|
const fetch = require('node-fetch')
|
||||||
|
|
||||||
|
const fakeFetch = (response, status = 200) => jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
status: status,
|
||||||
|
json: () => Promise.resolve(response),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
test('Basic test', async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fakeFetch({ok: true})
|
||||||
|
const res = await tcgdex.fetch('cards', 'basic-test')
|
||||||
|
expect(res).toEqual({ok: true})
|
||||||
|
expect(TCGdex.fetch).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Cache test', async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fakeFetch({ok: 'a'})
|
||||||
|
const res1 = await tcgdex.fetch('cards', 'cache-test')
|
||||||
|
expect(res1).toEqual({ok: 'a'})
|
||||||
|
TCGdex.fetch = fakeFetch({ok: 'b'})
|
||||||
|
const res2 = await tcgdex.fetch('cards', 'cache-test')
|
||||||
|
expect(res2).toEqual({ok: 'a'})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('endpoint errors', async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fakeFetch({ok: 'a'})
|
||||||
|
await expect(tcgdex.fetch('non existing endpoint')).rejects.toThrow()
|
||||||
|
await expect(tcgdex.fetch()).rejects.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('404 test', async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fakeFetch(undefined, 404)
|
||||||
|
expect(
|
||||||
|
await tcgdex.fetch('cards', '404-test')
|
||||||
|
).not.toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test real endpoints', async () => {
|
||||||
|
const tcgdex = new TCGdex('en')
|
||||||
|
TCGdex.fetch = fetch
|
||||||
|
const endpoints = [
|
||||||
|
{endpoint: 'fetchCard', params: ['swsh1-1']},
|
||||||
|
{endpoint: 'fetchCard', params: ['1', 'Sword & Shield']},
|
||||||
|
{endpoint: 'fetchCards', params: ['swsh1']},
|
||||||
|
{endpoint: 'fetchCards', params: []},
|
||||||
|
{endpoint: 'fetchSet', params: ['swsh1']},
|
||||||
|
{endpoint: 'fetchSets', params: ['swsh']},
|
||||||
|
{endpoint: 'fetchSets', params: []},
|
||||||
|
{endpoint: 'fetchSeries', params: []},
|
||||||
|
{endpoint: 'fetchSerie', params: ['swsh']},
|
||||||
|
]
|
||||||
|
|
||||||
|
for await (const item of endpoints) {
|
||||||
|
expect(
|
||||||
|
await tcgdex[item.endpoint](...item.params)
|
||||||
|
).toBeDefined()
|
||||||
|
}
|
||||||
|
})
|
58
package-lock.json
generated
58
package-lock.json
generated
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@tcgdex/sdk",
|
"name": "@tcgdex/sdk",
|
||||||
"version": "2.5.0",
|
"version": "2.6.0-beta.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@tcgdex/sdk",
|
"name": "@tcgdex/sdk",
|
||||||
"version": "2.5.0",
|
"version": "2.6.0-beta.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"isomorphic-unfetch": "^3",
|
"@dzeio/object-util": "^1",
|
||||||
"unfetch": "^4"
|
"isomorphic-unfetch": "^3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7",
|
"@babel/core": "^7",
|
||||||
@ -1750,11 +1750,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@dzeio/object-util": {
|
"node_modules/@dzeio/object-util": {
|
||||||
"version": "1.4.5",
|
"version": "1.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@dzeio/object-util/-/object-util-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/@dzeio/object-util/-/object-util-1.8.3.tgz",
|
||||||
"integrity": "sha512-V04GE77lipF2qnzMuA+T3blyPVo+ABKQLrmEteerXecA7G+TCisyQKIVMewFvF9qNsJ1LOVTckWW9wnRPyAwoQ==",
|
"integrity": "sha512-/d0ezut7EGrEKedcD8K2Jb2NAMSFfhxNj4rpUBlGzmmakJjJCXAgXvSDLjUwYrgHuabxbxlAn90Wo727MCzWLA=="
|
||||||
"dev": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc": {
|
"node_modules/@eslint/eslintrc": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
@ -2768,10 +2766,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.11.18",
|
"version": "20.12.14",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz",
|
||||||
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
|
"integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node-fetch": {
|
"node_modules/@types/node-fetch": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
@ -8889,6 +8890,12 @@
|
|||||||
"typpy": "^2.3.4"
|
"typpy": "^2.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/unfetch": {
|
"node_modules/unfetch": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
|
||||||
@ -10387,11 +10394,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@dzeio/object-util": {
|
"@dzeio/object-util": {
|
||||||
"version": "1.4.5",
|
"version": "1.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@dzeio/object-util/-/object-util-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/@dzeio/object-util/-/object-util-1.8.3.tgz",
|
||||||
"integrity": "sha512-V04GE77lipF2qnzMuA+T3blyPVo+ABKQLrmEteerXecA7G+TCisyQKIVMewFvF9qNsJ1LOVTckWW9wnRPyAwoQ==",
|
"integrity": "sha512-/d0ezut7EGrEKedcD8K2Jb2NAMSFfhxNj4rpUBlGzmmakJjJCXAgXvSDLjUwYrgHuabxbxlAn90Wo727MCzWLA=="
|
||||||
"dev": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"@eslint/eslintrc": {
|
"@eslint/eslintrc": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
@ -11183,10 +11188,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "18.11.18",
|
"version": "20.12.14",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz",
|
||||||
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
|
"integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"@types/node-fetch": {
|
"@types/node-fetch": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
@ -15647,6 +15655,12 @@
|
|||||||
"typpy": "^2.3.4"
|
"typpy": "^2.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"unfetch": {
|
"unfetch": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@tcgdex/sdk",
|
"name": "@tcgdex/sdk",
|
||||||
"version": "2.5.1",
|
"version": "2.6.0-beta.1",
|
||||||
"main": "./dist/tcgdex.node.js",
|
"main": "./dist/tcgdex.node.js",
|
||||||
"module": "./dist/tcgdex.node.mjs",
|
"module": "./dist/tcgdex.node.mjs",
|
||||||
"types": "./dist/tcgdex.node.d.ts",
|
"types": "./dist/tcgdex.node.d.ts",
|
||||||
@ -53,8 +53,8 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"isomorphic-unfetch": "^3",
|
"@dzeio/object-util": "^1",
|
||||||
"unfetch": "^4"
|
"isomorphic-unfetch": "^3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "node scripts/export-version-number.js",
|
"prebuild": "node scripts/export-version-number.js",
|
||||||
|
38
src/Psr/SimpleCache/CacheAbstract.ts
Normal file
38
src/Psr/SimpleCache/CacheAbstract.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { objectLoop } from '@dzeio/object-util'
|
||||||
|
import type CacheInterface from './CacheInterface'
|
||||||
|
|
||||||
|
export default abstract class CacheAsbract implements CacheInterface {
|
||||||
|
|
||||||
|
public getMultiple<T>(keys: Array<string>, defaultValues?: Array<T> | undefined): Record<string, T> {
|
||||||
|
const res: Record<string, T> = {}
|
||||||
|
for (let idx = 0; idx < keys.length; idx++) {
|
||||||
|
const key = keys[idx] as string
|
||||||
|
const value = this.get(key, defaultValues?.[idx]) as T | undefined
|
||||||
|
if (typeof value === 'undefined') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res[key] = value
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
public setMultiple<T>(values: Record<string, T>, ttl?: number | undefined): boolean {
|
||||||
|
objectLoop(values, (v, k) => {
|
||||||
|
this.set(k, v, ttl)
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteMultiple(keys: Array<string>): boolean {
|
||||||
|
for (const key of keys) {
|
||||||
|
this.delete(key)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract get<T>(key: string, defaultValue?: T): T | undefined
|
||||||
|
public abstract set<T>(key: string, value: T, ttl?: number): boolean
|
||||||
|
public abstract delete(key: string): boolean
|
||||||
|
public abstract clear(): boolean
|
||||||
|
public abstract has(key: string): boolean
|
||||||
|
}
|
109
src/Psr/SimpleCache/CacheInterface.d.ts
vendored
Normal file
109
src/Psr/SimpleCache/CacheInterface.d.ts
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
export default interface CacheInterface {
|
||||||
|
/**
|
||||||
|
* Fetches a value from the cache.
|
||||||
|
*
|
||||||
|
* @param key The unique key of this item in the cache.
|
||||||
|
* @param defaultValue Default value to return if the key does not exist.
|
||||||
|
*
|
||||||
|
* @return T The value of the item from the cache, or $default in case of cache miss.
|
||||||
|
*
|
||||||
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||||
|
* MUST be thrown if the $key string is not a legal value.
|
||||||
|
*/
|
||||||
|
get<T>(key: string, defaultValue?: T): T | undefined
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
|
||||||
|
*
|
||||||
|
* @param key The key of the item to store.
|
||||||
|
* @param value The value of the item to store. Must be serializable.
|
||||||
|
* @param {null|number} ttl The TTL value of this item. If no value is sent and
|
||||||
|
* the driver supports TTL then the library may set a default value
|
||||||
|
* for it or let the driver take care of that.
|
||||||
|
*
|
||||||
|
* @return bool True on success and false on failure.
|
||||||
|
*
|
||||||
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||||
|
* MUST be thrown if the $key string is not a legal value.
|
||||||
|
*/
|
||||||
|
set<T>(key: string, value: T, ttl?: number): boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an item from the cache by its unique key.
|
||||||
|
*
|
||||||
|
* @param key The unique cache key of the item to delete.
|
||||||
|
*
|
||||||
|
* @return True if the item was successfully removed. False if there was an error.
|
||||||
|
*
|
||||||
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||||
|
* MUST be thrown if the $key string is not a legal value.
|
||||||
|
*/
|
||||||
|
delete(key: string): boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wipes clean the entire cache's keys.
|
||||||
|
*
|
||||||
|
* @return boolean True on success and false on failure.
|
||||||
|
*/
|
||||||
|
clear(): boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains multiple cache items by their unique keys.
|
||||||
|
*
|
||||||
|
* @param keys A list of keys that can obtained in a single operation.
|
||||||
|
* @param defaultValues $default Default value to return for keys that do not exist.
|
||||||
|
*
|
||||||
|
* @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
|
||||||
|
*
|
||||||
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||||
|
* MUST be thrown if $keys is neither an array nor a Traversable,
|
||||||
|
* or if any of the $keys are not a legal value.
|
||||||
|
*/
|
||||||
|
getMultiple<T>(keys: Array<string>, defaultValues?: Array<T>): Record<string, T>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persists a set of key => value pairs in the cache, with an optional TTL.
|
||||||
|
*
|
||||||
|
* @param values A list of key => value pairs for a multiple-set operation.
|
||||||
|
* @param ttl Optional. The TTL value of this item. If no value is sent and
|
||||||
|
* the driver supports TTL then the library may set a default value
|
||||||
|
* for it or let the driver take care of that.
|
||||||
|
*
|
||||||
|
* @return bool True on success and false on failure.
|
||||||
|
*
|
||||||
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||||
|
* MUST be thrown if $values is neither an array nor a Traversable,
|
||||||
|
* or if any of the $values are not a legal value.
|
||||||
|
*/
|
||||||
|
setMultiple<T>(values: Record<string, T>, ttl?: number): boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes multiple cache items in a single operation.
|
||||||
|
*
|
||||||
|
* @param keys A list of string-based keys to be deleted.
|
||||||
|
*
|
||||||
|
* @return bool True if the items were successfully removed. False if there was an error.
|
||||||
|
*
|
||||||
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||||
|
* MUST be thrown if $keys is neither an array nor a Traversable,
|
||||||
|
* or if any of the $keys are not a legal value.
|
||||||
|
*/
|
||||||
|
deleteMultiple(keys: Array<string>): boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether an item is present in the cache.
|
||||||
|
*
|
||||||
|
* NOTE: It is recommended that has() is only to be used for cache warming type purposes
|
||||||
|
* and not to be used within your live applications operations for get/set, as this method
|
||||||
|
* is subject to a race condition where your has() will return true and immediately after,
|
||||||
|
* another script can remove it, making the state of your app out of date.
|
||||||
|
*
|
||||||
|
* @param key The cache item key.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||||
|
* MUST be thrown if the $key string is not a legal value.
|
||||||
|
*/
|
||||||
|
has(key: string): boolean
|
||||||
|
}
|
91
src/Psr/SimpleCache/LocalStorageCache.ts
Normal file
91
src/Psr/SimpleCache/LocalStorageCache.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import CacheAsbract from './CacheAbstract'
|
||||||
|
|
||||||
|
interface CacheItem<T> {
|
||||||
|
data: T
|
||||||
|
expire?: number | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache implementation that uses browser storage.
|
||||||
|
*
|
||||||
|
* This class extends `CacheAsbract` and provides a concrete implementation
|
||||||
|
* of the caching interface. It stores cached items in browser storage,
|
||||||
|
* which is suitable for storing small amounts of data.
|
||||||
|
*/
|
||||||
|
export default class BrowserStorageCache extends CacheAsbract {
|
||||||
|
private storage: Storage
|
||||||
|
public constructor(private readonly prefix?: string, session = false) {
|
||||||
|
super()
|
||||||
|
if (session) {
|
||||||
|
this.storage = window.sessionStorage
|
||||||
|
} else {
|
||||||
|
this.storage = window.localStorage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get<T>(key: string, defaultValue?: T | undefined): T | undefined {
|
||||||
|
const raw = this.storage.getItem(this.getFinalKey(key))
|
||||||
|
|
||||||
|
if (!raw) {
|
||||||
|
return defaultValue ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const item: CacheItem<T> = JSON.parse(raw)
|
||||||
|
|
||||||
|
if (item.expire && item.expire < new Date().getTime()) {
|
||||||
|
this.delete(key)
|
||||||
|
return defaultValue ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.data
|
||||||
|
}
|
||||||
|
|
||||||
|
public set<T>(key: string, value: T, ttl?: number | undefined): boolean {
|
||||||
|
let expire = undefined
|
||||||
|
if (ttl) {
|
||||||
|
expire = (new Date()).getTime() + (ttl * 1000)
|
||||||
|
}
|
||||||
|
const data: CacheItem<unknown> = {
|
||||||
|
data: value,
|
||||||
|
expire: expire
|
||||||
|
}
|
||||||
|
this.storage.setItem(this.getFinalKey(key), JSON.stringify(data))
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(key: string): boolean {
|
||||||
|
this.storage.removeItem(this.getFinalKey(key))
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear(): boolean {
|
||||||
|
const keys = this.keys()
|
||||||
|
return this.deleteMultiple(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(key: string): boolean {
|
||||||
|
return !!this.storage.getItem(this.getFinalKey(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
private keys(): Array<string> {
|
||||||
|
const list: Array<string> = []
|
||||||
|
for (let idx = 0; idx < this.storage.length; idx++) {
|
||||||
|
const key = this.storage.key(idx)
|
||||||
|
if (!key || this.prefix && !key?.startsWith(`${this.prefix}/`)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list.push(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFinalKey(key: string): string {
|
||||||
|
if (!this.prefix) {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return `${this.prefix}/${key}`
|
||||||
|
}
|
||||||
|
}
|
58
src/Psr/SimpleCache/MemoryCache.ts
Normal file
58
src/Psr/SimpleCache/MemoryCache.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import CacheAsbract from './CacheAbstract'
|
||||||
|
|
||||||
|
interface CacheItem<T> {
|
||||||
|
data: T
|
||||||
|
expire?: number | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memory cache implementation that stores cached items in memory.
|
||||||
|
* This class extends the abstract `CacheAbstract` and provides a basic in-memory caching mechanism.
|
||||||
|
*
|
||||||
|
* @class MemoryCache
|
||||||
|
*/
|
||||||
|
export default class MemoryCache extends CacheAsbract {
|
||||||
|
private cache: Map<string, CacheItem<unknown>> = new Map()
|
||||||
|
|
||||||
|
public get<T>(key: string, defaultValue?: T | undefined): T | undefined {
|
||||||
|
const item = this.cache.get(key)
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return defaultValue ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.expire && item.expire < new Date().getTime()) {
|
||||||
|
this.delete(key)
|
||||||
|
return defaultValue ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.data as T | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
public set<T>(key: string, value: T, ttl?: number | undefined): boolean {
|
||||||
|
let expire: number | undefined
|
||||||
|
if (ttl) {
|
||||||
|
expire = new Date().getTime() + ttl * 1000
|
||||||
|
}
|
||||||
|
this.cache.set(key, {
|
||||||
|
data: value,
|
||||||
|
expire: expire
|
||||||
|
})
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(key: string): boolean {
|
||||||
|
this.cache.delete(key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear(): boolean {
|
||||||
|
this.cache.clear()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(key: string): boolean {
|
||||||
|
return this.cache.has(key)
|
||||||
|
}
|
||||||
|
}
|
88
src/Query.ts
Normal file
88
src/Query.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
export default class Query {
|
||||||
|
public params: Array<{ key: string, value: string | number | boolean }> = []
|
||||||
|
|
||||||
|
public not: {
|
||||||
|
equal: (key: string, value: string) => Query
|
||||||
|
contains: (key: string, value: string) => Query
|
||||||
|
includes: (key: string, value: string) => Query
|
||||||
|
like: (key: string, value: string) => Query
|
||||||
|
isNull: (key: string) => Query
|
||||||
|
} = {
|
||||||
|
equal: (key: string, value: string) => {
|
||||||
|
this.params.push({ key: key, value: `neq:${value}` })
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
contains: (key: string, value: string) => {
|
||||||
|
this.params.push({ key: key, value: `not:${value}` })
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
includes: (key: string, value: string) => this.not.contains(key, value),
|
||||||
|
like: (key: string, value: string) => this.not.contains(key, value),
|
||||||
|
isNull: (key: string) => {
|
||||||
|
this.params.push({ key: key, value: 'notnull:' })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static create(): Query {
|
||||||
|
return new Query()
|
||||||
|
}
|
||||||
|
|
||||||
|
public includes(key: string, value: string): this {
|
||||||
|
return this.contains(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
public like(key: string, value: string): this {
|
||||||
|
return this.contains(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
public contains(key: string, value: string): this {
|
||||||
|
this.params.push({ key: key, value: value })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public equal(key: string, value: string): this {
|
||||||
|
this.params.push({ key: key, value: `eq:${value}` })
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public sort(key: string, order: 'ASC' | 'DESC'): this {
|
||||||
|
this.params.push({ key: 'sort:field', value: key })
|
||||||
|
this.params.push({ key: 'sort:order', value: order })
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public greaterOrEqualThan(key: string, value: number) {
|
||||||
|
this.params.push({ key: key, value: `gte:${value}` })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public lesserOrEqualThan(key: string, value: number) {
|
||||||
|
this.params.push({ key: key, value: `lte:${value}` })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public greaterThan(key: string, value: number) {
|
||||||
|
this.params.push({ key: key, value: `gt:${value}` })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public lesserThan(key: string, value: number) {
|
||||||
|
this.params.push({ key: key, value: `lt:${value}` })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNull(key: string) {
|
||||||
|
this.params.push({ key: key, value: 'null:' })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public paginate(page: number, itemsPerPage: number): this {
|
||||||
|
this.params.push({ key: 'pagination:page', value: page })
|
||||||
|
this.params.push({ key: 'pagination:itemsPerPage', value: itemsPerPage })
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
import TCGdex from './tcgdex'
|
|
||||||
import { version } from './version'
|
|
||||||
|
|
||||||
export default class Request {
|
|
||||||
|
|
||||||
// 1 hour of TTL by default
|
|
||||||
public static ttl = 1000 * 60 * 60
|
|
||||||
|
|
||||||
private static cache: Record<string, {response: any, time: number}> = {}
|
|
||||||
|
|
||||||
public static async fetch<T>(url: string): Promise<T | undefined> {
|
|
||||||
let request = this.cache[url]
|
|
||||||
const now = new Date().getTime()
|
|
||||||
if (!request || now - request.time > this.ttl) {
|
|
||||||
const unfetch = TCGdex.fetch
|
|
||||||
const resp = await unfetch(url, {
|
|
||||||
headers: {
|
|
||||||
'user-agent': `@tcgdex/javascript-sdk/${version}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (resp.status !== 200) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cache[url] = { response: await resp.json(), time: now }
|
|
||||||
request = this.cache[url]
|
|
||||||
}
|
|
||||||
return request.response
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
26
src/endpoints/Endpoint.ts
Normal file
26
src/endpoints/Endpoint.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import type { Endpoints } from '../interfaces'
|
||||||
|
import Model from '../models/Model'
|
||||||
|
import type Query from '../Query'
|
||||||
|
import type TCGdex from '../tcgdex'
|
||||||
|
|
||||||
|
export default class Endpoint<Item extends Model, List extends Model> {
|
||||||
|
public constructor(
|
||||||
|
protected readonly tcgdex: TCGdex,
|
||||||
|
protected readonly itemModel: new (sdk: TCGdex) => Item,
|
||||||
|
protected readonly listModel: new (sdk: TCGdex) => List,
|
||||||
|
protected readonly endpoint: Endpoints
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public async get(id: string | number): Promise<Item | null> {
|
||||||
|
const res = await this.tcgdex.fetch(this.endpoint as 'cards', id as string)
|
||||||
|
if (!res) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return Model.build(new this.itemModel(this.tcgdex), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async list(query?: Query): Promise<Array<List>> {
|
||||||
|
const res = await this.tcgdex.fetchWithQuery([this.endpoint], query?.params)
|
||||||
|
return (res as Array<object> ?? []).map((it) => Model.build(new this.listModel(this.tcgdex), it))
|
||||||
|
}
|
||||||
|
}
|
24
src/endpoints/SimpleEndpoint.ts
Normal file
24
src/endpoints/SimpleEndpoint.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import type { Endpoints } from '../interfaces'
|
||||||
|
import Model from '../models/Model'
|
||||||
|
import type Query from '../Query'
|
||||||
|
import type TCGdex from '../tcgdex'
|
||||||
|
|
||||||
|
export default class SimpleEndpoint<Item extends Model, List extends string | number> {
|
||||||
|
public constructor(
|
||||||
|
protected readonly tcgdex: TCGdex,
|
||||||
|
protected readonly itemModel: new (sdk: TCGdex) => Item,
|
||||||
|
protected readonly endpoint: Endpoints
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async get(id: string | number): Promise<Item | null> {
|
||||||
|
const res = await this.tcgdex.fetch(this.endpoint as 'cards', id as string)
|
||||||
|
if (!res) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return Model.build(new this.itemModel(this.tcgdex), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async list(query?: Query): Promise<Array<List>> {
|
||||||
|
return await this.tcgdex.fetchWithQuery([this.endpoint], query?.params) ?? []
|
||||||
|
}
|
||||||
|
}
|
9
src/interfaces.ts → src/interfaces.d.ts
vendored
9
src/interfaces.ts → src/interfaces.d.ts
vendored
@ -310,3 +310,12 @@ export interface StringEndpoint {
|
|||||||
name: string
|
name: string
|
||||||
cards: Array<CardResume>
|
cards: Array<CardResume>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Quality = 'low' | 'high'
|
||||||
|
|
||||||
|
export type Extension = 'jpg' | 'webp' | 'png'
|
||||||
|
|
||||||
|
export type Endpoints = 'cards' | 'categories' | 'dex-ids' | 'energy-types' |
|
||||||
|
'hp' | 'illustrators' | 'rarities' | 'regulation-marks' |
|
||||||
|
'retreats' | 'series' | 'sets' | 'stages' | 'suffixes' |
|
||||||
|
'trainer-types' | 'types' | 'variants'
|
198
src/models/Card.ts
Normal file
198
src/models/Card.ts
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
import CardResume from './CardResume'
|
||||||
|
import type { Variants } from './Other'
|
||||||
|
import type TCGdexSet from './Set'
|
||||||
|
import type SetResume from './SetResume'
|
||||||
|
|
||||||
|
// TODO: sort elements by alphabetical order
|
||||||
|
export default class Card extends CardResume {
|
||||||
|
/**
|
||||||
|
* Card illustrator
|
||||||
|
*/
|
||||||
|
public illustrator?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card Rarity
|
||||||
|
*
|
||||||
|
* - None https://www.tcgdex.net/database/sm/smp/SM01
|
||||||
|
* - Common https://www.tcgdex.net/database/xy/xy9/1
|
||||||
|
* - Uncommon https://www.tcgdex.net/database/xy/xy9/2
|
||||||
|
* - Rare https://www.tcgdex.net/database/xy/xy9/3
|
||||||
|
* - Ultra Rare
|
||||||
|
* - Secret Rare
|
||||||
|
*/
|
||||||
|
public rarity!: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card Category
|
||||||
|
*
|
||||||
|
* - Pokemon
|
||||||
|
* - Trainer
|
||||||
|
* - Energy
|
||||||
|
*/
|
||||||
|
public category!: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card Variants (Override Set Variants)
|
||||||
|
*/
|
||||||
|
public variants?: Variants
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card Set
|
||||||
|
*/
|
||||||
|
public set!: SetResume
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon only elements
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Pokedex ID
|
||||||
|
*/
|
||||||
|
public dexId?: Array<number>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon HP
|
||||||
|
*/
|
||||||
|
public hp?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Types
|
||||||
|
* ex for multiple https://www.tcgdex.net/database/ex/ex13/17
|
||||||
|
*/
|
||||||
|
public types?: Array<string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Sub Evolution
|
||||||
|
*/
|
||||||
|
public evolveFrom?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Weight
|
||||||
|
*/
|
||||||
|
public weight?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Description
|
||||||
|
*/
|
||||||
|
public description?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Level of the Pokemon
|
||||||
|
*
|
||||||
|
* NOTE: can be equal to 'X' when the pokemon is a LEVEL-UP one
|
||||||
|
*/
|
||||||
|
public level?: number | string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Stage
|
||||||
|
*
|
||||||
|
* - Basic https://www.tcgdex.net/database/xy/xy9/1
|
||||||
|
* - BREAK https://www.tcgdex.net/database/xy/xy9/18
|
||||||
|
* - LEVEL-UP https://www.tcgdex.net/database/dp/dp1/121
|
||||||
|
* - MEGA https://www.tcgdex.net/database/xy/xy1/2
|
||||||
|
* - RESTORED https://www.tcgdex.net/database/bw/bw5/53
|
||||||
|
* - Stage1 https://www.tcgdex.net/database/xy/xy9/2
|
||||||
|
* - Stage2 https://www.tcgdex.net/database/xy/xy9/3
|
||||||
|
* - VMAX https://www.tcgdex.net/database/swsh/swsh1/50
|
||||||
|
*/
|
||||||
|
public stage?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card Suffix
|
||||||
|
*
|
||||||
|
* - EX https://www.tcgdex.net/database/ex/ex2/94
|
||||||
|
* - GX https://www.tcgdex.net/database/sm/sm12/4
|
||||||
|
* - V https://www.tcgdex.net/database/swsh/swsh1/1
|
||||||
|
* - Legend https://www.tcgdex.net/database/hgss/hgss1/114
|
||||||
|
* - Prime https://www.tcgdex.net/database/hgss/hgss2/85
|
||||||
|
* - SP https://www.tcgdex.net/database/pl/pl1/7
|
||||||
|
* - TAG TEAM-GX https://www.tcgdex.net/database/sm/sm12/226
|
||||||
|
*/
|
||||||
|
public suffix?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Held Item
|
||||||
|
*
|
||||||
|
* ex https://www.tcgdex.net/database/dp/dp2/75
|
||||||
|
*/
|
||||||
|
public item?: {
|
||||||
|
name: string
|
||||||
|
effect: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Abilities
|
||||||
|
*
|
||||||
|
* multi abilities ex https://www.tcgdex.net/database/ex/ex15/10
|
||||||
|
*/
|
||||||
|
public abilities?: Array<{
|
||||||
|
type: string
|
||||||
|
name: string
|
||||||
|
effect: string
|
||||||
|
}>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Attacks
|
||||||
|
*/
|
||||||
|
public attacks?: Array<{
|
||||||
|
cost?: Array<string>
|
||||||
|
name: string
|
||||||
|
effect?: string
|
||||||
|
damage?: string | number
|
||||||
|
}>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Weaknesses
|
||||||
|
*/
|
||||||
|
public weaknesses?: Array<{
|
||||||
|
type: string
|
||||||
|
value?: string
|
||||||
|
}>
|
||||||
|
|
||||||
|
public resistances?: Array<{
|
||||||
|
type: string
|
||||||
|
value?: string
|
||||||
|
}>
|
||||||
|
|
||||||
|
public retreat?: number
|
||||||
|
|
||||||
|
// Trainer/Energy
|
||||||
|
public effect?: string
|
||||||
|
|
||||||
|
// Trainer Only
|
||||||
|
public trainerType?: string
|
||||||
|
|
||||||
|
// Energy Only
|
||||||
|
public energyType?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the rotation mark on cards >= Sword & Shield
|
||||||
|
*/
|
||||||
|
public regulationMark?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card ability to be played in official tournaments
|
||||||
|
*
|
||||||
|
* Note: all cards are avaialable to play in unlimited tournaments
|
||||||
|
*/
|
||||||
|
public legal!: {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ability to play in standard tournaments
|
||||||
|
*/
|
||||||
|
standard: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ability to play in expanded tournaments
|
||||||
|
*/
|
||||||
|
expanded: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async getCard(): Promise<Card> {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSet(): Promise<TCGdexSet> {
|
||||||
|
return (await this.sdk.set.get(this.set.id))!
|
||||||
|
}
|
||||||
|
}
|
47
src/models/CardResume.ts
Normal file
47
src/models/CardResume.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import type { Extension, Quality } from '../interfaces'
|
||||||
|
import type Card from './Card'
|
||||||
|
import Model from './Model'
|
||||||
|
|
||||||
|
export default class CardResume extends Model {
|
||||||
|
/**
|
||||||
|
* Globally unique card ID based on the set ID and the cards ID within the set
|
||||||
|
*/
|
||||||
|
public id!: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card image url without the extension and quality
|
||||||
|
*
|
||||||
|
* @see {@link getImageURL}
|
||||||
|
*/
|
||||||
|
public image?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID indexing this card within its set, usually just its number
|
||||||
|
*/
|
||||||
|
public localId!: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card Name (Including the suffix if next to card name)
|
||||||
|
*/
|
||||||
|
public name!: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the the Card Image full URL
|
||||||
|
*
|
||||||
|
* @param {Quality} quality the quality you want your image to be in
|
||||||
|
* @param {Extension} extension extension you want you image to be
|
||||||
|
* @return the full card URL
|
||||||
|
*/
|
||||||
|
public getImageURL(quality: Quality = 'high', extension: Extension = 'png'): string {
|
||||||
|
return `${this.image}/${quality}.${extension}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full Card
|
||||||
|
*
|
||||||
|
* @return the full card if available
|
||||||
|
*/
|
||||||
|
public async getCard(): Promise<Card> {
|
||||||
|
return (await this.sdk.card.get(this.id))!
|
||||||
|
}
|
||||||
|
}
|
28
src/models/Model.ts
Normal file
28
src/models/Model.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { objectLoop } from '@dzeio/object-util'
|
||||||
|
import type TCGdex from '../tcgdex'
|
||||||
|
|
||||||
|
export default abstract class Model {
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
protected readonly sdk: TCGdex
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* build a model depending on the data given
|
||||||
|
* @param model the model to build
|
||||||
|
* @param data the data to fill it with
|
||||||
|
*/
|
||||||
|
public static build<T extends Model>(model: T, data?: object): T {
|
||||||
|
if (!data) {
|
||||||
|
throw new Error('data is necessary.')
|
||||||
|
}
|
||||||
|
model.fill(data)
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fill(obj: object) {
|
||||||
|
objectLoop(obj, (value, key) => {
|
||||||
|
(this as object)[key] = value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
6
src/models/Other.d.ts
vendored
Normal file
6
src/models/Other.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export interface Variants {
|
||||||
|
normal?: boolean
|
||||||
|
reverse?: boolean
|
||||||
|
holo?: boolean
|
||||||
|
firstEdition?: boolean
|
||||||
|
}
|
21
src/models/Serie.ts
Normal file
21
src/models/Serie.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { objectLoop } from '@dzeio/object-util'
|
||||||
|
import Model from './Model'
|
||||||
|
import SerieResume from './SerieResume'
|
||||||
|
import SetResume from './SetResume'
|
||||||
|
|
||||||
|
export default class Serie extends SerieResume {
|
||||||
|
public sets!: Array<SetResume>
|
||||||
|
|
||||||
|
protected fill(obj: object): void {
|
||||||
|
objectLoop(obj, (value, key) => {
|
||||||
|
switch (key) {
|
||||||
|
case 'sets':
|
||||||
|
this.sets = (value as Array<any>).map((it) => Model.build(new SetResume(this.sdk), it))
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this[key] = value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
24
src/models/SerieResume.ts
Normal file
24
src/models/SerieResume.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import type { Extension } from '../interfaces'
|
||||||
|
import Model from './Model'
|
||||||
|
import type Serie from './Serie'
|
||||||
|
|
||||||
|
export default class SerieResume extends Model {
|
||||||
|
public id!: string
|
||||||
|
public name!: string
|
||||||
|
public logo?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the the Card Image full URL
|
||||||
|
*
|
||||||
|
* @param {Quality} quality the quality you want your image to be in
|
||||||
|
* @param {Extension} extension extension you want you image to be
|
||||||
|
* @return the full card URL
|
||||||
|
*/
|
||||||
|
public getImageURL(extension: Extension = 'png'): string {
|
||||||
|
return `${this.logo}.${extension}`
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSerie(): Promise<Serie> {
|
||||||
|
return (await this.sdk.serie.get(this.id))!
|
||||||
|
}
|
||||||
|
}
|
89
src/models/Set.ts
Normal file
89
src/models/Set.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { objectLoop } from '@dzeio/object-util'
|
||||||
|
import CardResume from './CardResume'
|
||||||
|
import Model from './Model'
|
||||||
|
import type { Variants } from './Other'
|
||||||
|
import type SerieResume from './SerieResume'
|
||||||
|
|
||||||
|
// biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
|
||||||
|
export default class Set extends Model {
|
||||||
|
public id!: string
|
||||||
|
public name!: string
|
||||||
|
public logo?: string
|
||||||
|
public symbol?: string
|
||||||
|
public serie!: SerieResume
|
||||||
|
public tcgOnline?: string
|
||||||
|
public variants?: Variants
|
||||||
|
|
||||||
|
public releaseDate!: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Designate if the set is usable in tournaments
|
||||||
|
*
|
||||||
|
* Note: this is specific to the set and if a
|
||||||
|
* card is banned from the set it will still be true
|
||||||
|
*/
|
||||||
|
public legal!: {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ability to play in standard tournaments
|
||||||
|
*/
|
||||||
|
standard: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ability to play in expanded tournaments
|
||||||
|
*/
|
||||||
|
expanded: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
public cardCount!: {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* total of number of cards
|
||||||
|
*/
|
||||||
|
total: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* number of cards officialy (on the bottom of each cards)
|
||||||
|
*/
|
||||||
|
official: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* number of cards having a normal version
|
||||||
|
*/
|
||||||
|
normal: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* number of cards having an reverse version
|
||||||
|
*/
|
||||||
|
reverse: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* number of cards having an holo version
|
||||||
|
*/
|
||||||
|
holo: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of possible cards
|
||||||
|
*/
|
||||||
|
firstEd?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
public cards!: Array<CardResume>
|
||||||
|
|
||||||
|
public async getSerie() {
|
||||||
|
return this.sdk.serie.get(this.serie.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fill(obj: object): void {
|
||||||
|
objectLoop(obj, (value, key) => {
|
||||||
|
switch (key) {
|
||||||
|
case 'cards':
|
||||||
|
this.cards = (value as Array<any>).map((it) => Model.build(new CardResume(this.sdk), it))
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this[key] = value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
25
src/models/SetResume.ts
Normal file
25
src/models/SetResume.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import Model from './Model'
|
||||||
|
import type TCGdexSet from './Set'
|
||||||
|
|
||||||
|
export default class SetResume extends Model {
|
||||||
|
public id!: string
|
||||||
|
public name!: string
|
||||||
|
public logo?: string
|
||||||
|
public symbol?: string
|
||||||
|
public cardCount!: {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* total of number of cards
|
||||||
|
*/
|
||||||
|
total: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* number of cards officialy (on the bottom of each cards)
|
||||||
|
*/
|
||||||
|
official: number
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSet(): Promise<TCGdexSet> {
|
||||||
|
return (await this.sdk.set.get(this.id))!
|
||||||
|
}
|
||||||
|
}
|
21
src/models/StringEndpoint.ts
Normal file
21
src/models/StringEndpoint.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { objectLoop } from '@dzeio/object-util'
|
||||||
|
import CardResume from './CardResume'
|
||||||
|
import Model from './Model'
|
||||||
|
|
||||||
|
export default class StringEndpoint extends Model {
|
||||||
|
public name!: string
|
||||||
|
public cards!: Array<CardResume>
|
||||||
|
|
||||||
|
protected fill(obj: object): void {
|
||||||
|
objectLoop(obj, (value, key) => {
|
||||||
|
switch (key) {
|
||||||
|
case 'cards':
|
||||||
|
this.cards = (value as Array<any>).map((it) => Model.build(new CardResume(this.sdk), it))
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this[key] = value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import unfetch from 'unfetch'
|
|
||||||
import TCGdex from './tcgdex'
|
import TCGdex from './tcgdex'
|
||||||
|
|
||||||
TCGdex.fetch = window.fetch ?? unfetch as any
|
TCGdex.fetch = window.fetch
|
||||||
|
|
||||||
export default TCGdex
|
export default TCGdex
|
||||||
|
245
src/tcgdex.ts
245
src/tcgdex.ts
@ -1,24 +1,144 @@
|
|||||||
import RequestWrapper from './Request'
|
import type CacheInterface from './Psr/SimpleCache/CacheInterface'
|
||||||
import { Serie, Set, Card, CardResume, SerieList, SetList, SupportedLanguages, StringEndpoint } from './interfaces'
|
import LocalStorageCache from './Psr/SimpleCache/LocalStorageCache'
|
||||||
type Endpoint = 'cards' | 'categories' | 'dex-ids' | 'energy-types' | 'hp' | 'illustrators' | 'rarities' | 'regulation-marks' | 'retreats' | 'series' | 'sets' | 'stages' | 'suffixes' | 'trainer-types' | 'types' | 'variants'
|
import MemoryCache from './Psr/SimpleCache/MemoryCache'
|
||||||
|
import Query from './Query'
|
||||||
|
import Endpoint from './endpoints/Endpoint'
|
||||||
|
import SimpleEndpoint from './endpoints/SimpleEndpoint'
|
||||||
|
import type {
|
||||||
|
Card,
|
||||||
|
CardResume,
|
||||||
|
Endpoints,
|
||||||
|
Serie,
|
||||||
|
SerieList,
|
||||||
|
SetList,
|
||||||
|
StringEndpoint,
|
||||||
|
SupportedLanguages,
|
||||||
|
Set as TCGdexSet
|
||||||
|
} from './interfaces'
|
||||||
|
import CardModel from './models/Card'
|
||||||
|
import CardResumeModel from './models/CardResume'
|
||||||
|
import SerieModel from './models/Serie'
|
||||||
|
import SerieResume from './models/SerieResume'
|
||||||
|
import SetModel from './models/Set'
|
||||||
|
import SetResumeModel from './models/SetResume'
|
||||||
|
import StringEndpointModel from './models/StringEndpoint'
|
||||||
|
import { ENDPOINTS, detectContext } from './utils'
|
||||||
|
import { version } from './version'
|
||||||
|
|
||||||
const ENDPOINTS: Array<Endpoint> = ['cards', 'categories', 'dex-ids', 'energy-types', 'hp', 'illustrators', 'rarities', 'regulation-marks', 'retreats', 'series', 'sets', 'stages', 'suffixes', 'trainer-types', 'types', 'variants']
|
|
||||||
const BASE_URL = 'https://api.tcgdex.net/v2'
|
|
||||||
export default class TCGdex {
|
export default class TCGdex {
|
||||||
|
|
||||||
public static fetch: typeof fetch
|
/**
|
||||||
|
* How the remote data is going to be fetched
|
||||||
|
*/
|
||||||
|
public static fetch: typeof fetch = fetch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated to change the lang use `this.lang`
|
* @deprecated to change the lang use {@link TCGdex.getLang} and {@link TCGdex.setLang}
|
||||||
*/
|
*/
|
||||||
public static defaultLang: SupportedLanguages = 'en'
|
public static defaultLang: SupportedLanguages = 'en'
|
||||||
|
|
||||||
public constructor(public lang?: SupportedLanguages) {}
|
/**
|
||||||
|
* the previously hidden caching system used by TCGdex to not kill the API
|
||||||
|
*/
|
||||||
|
public cache: CacheInterface =
|
||||||
|
detectContext() === 'browser' ? new LocalStorageCache('tcgdex-cache') : new MemoryCache()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the default cache TTL, only subsequent requests will have their ttl changed
|
||||||
|
*/
|
||||||
|
public cacheTTL = 60 * 60
|
||||||
|
|
||||||
|
public readonly card = new Endpoint(this, CardModel, CardResumeModel, 'cards')
|
||||||
|
public readonly set = new Endpoint(this, SetModel, SetResumeModel, 'sets')
|
||||||
|
public readonly serie = new Endpoint(this, SerieModel, SerieResume, 'series')
|
||||||
|
|
||||||
|
public readonly type = new SimpleEndpoint(this, StringEndpointModel, 'types')
|
||||||
|
public readonly retreat = new SimpleEndpoint(this, StringEndpointModel, 'retreats')
|
||||||
|
public readonly rarity = new SimpleEndpoint(this, StringEndpointModel, 'rarities')
|
||||||
|
public readonly illustrator = new SimpleEndpoint(this, StringEndpointModel, 'illustrators')
|
||||||
|
public readonly hp = new SimpleEndpoint(this, StringEndpointModel, 'hp')
|
||||||
|
public readonly categorie = new SimpleEndpoint(this, StringEndpointModel, 'categories')
|
||||||
|
public readonly dexID = new SimpleEndpoint(this, StringEndpointModel, 'dex-ids')
|
||||||
|
public readonly energyType = new SimpleEndpoint(this, StringEndpointModel, 'energy-types')
|
||||||
|
public readonly regulationMark = new SimpleEndpoint(this, StringEndpointModel, 'regulation-marks')
|
||||||
|
public readonly stage = new SimpleEndpoint(this, StringEndpointModel, 'stages')
|
||||||
|
public readonly suffixe = new SimpleEndpoint(this, StringEndpointModel, 'suffixes')
|
||||||
|
public readonly trainerType = new SimpleEndpoint(this, StringEndpointModel, 'trainer-types')
|
||||||
|
public readonly variant = new SimpleEndpoint(this, StringEndpointModel, 'variants')
|
||||||
|
|
||||||
|
private lang: SupportedLanguages = 'en'
|
||||||
|
private endpointURL = 'https://api.tcgdex.net/v2'
|
||||||
|
|
||||||
|
public constructor(lang: SupportedLanguages = 'en') {
|
||||||
|
this.setLang(lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use the constructor parameter or {@link TCGdex.setLang} when in an instance
|
||||||
|
*/
|
||||||
|
public static setDefaultLang(lang: SupportedLanguages) {
|
||||||
|
TCGdex.defaultLang = lang
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link TCGdex.setLang} when in an instance
|
||||||
|
*/
|
||||||
|
public static getDefaultLang(): SupportedLanguages {
|
||||||
|
return TCGdex.defaultLang
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the endpoint URL
|
||||||
|
* ex: `https://api.tcgdex.net/v2`
|
||||||
|
* @param endpoint the url
|
||||||
|
*/
|
||||||
|
public setEndpoint(endpoint: string) {
|
||||||
|
this.endpointURL = endpoint
|
||||||
|
}
|
||||||
|
public getEndpoint(): string {
|
||||||
|
return this.endpointURL
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set the current cache methodology
|
||||||
|
* @param cache the cache to use
|
||||||
|
*/
|
||||||
|
public setCache(cache: CacheInterface) {
|
||||||
|
this.cache = cache
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the current cache methodology
|
||||||
|
* @param cache the cache to use
|
||||||
|
*/
|
||||||
|
public getCache(): CacheInterface {
|
||||||
|
return this.cache
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the endpoint URL
|
||||||
|
* ex: `https://api.tcgdex.net/v2`
|
||||||
|
* @param endpoint the url
|
||||||
|
*/
|
||||||
|
public setCacheTTL(seconds: number) {
|
||||||
|
this.cacheTTL = seconds
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* get the current useed cache ttl in seconds
|
||||||
|
* @returns the cache ttl in seconds
|
||||||
|
*/
|
||||||
|
public getCacheTTL(): number {
|
||||||
|
return this.cacheTTL
|
||||||
|
}
|
||||||
|
|
||||||
public getLang(): SupportedLanguages {
|
public getLang(): SupportedLanguages {
|
||||||
return this.lang ?? TCGdex.defaultLang ?? 'en'
|
return this.lang ?? TCGdex.defaultLang ?? 'en'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setLang(lang: SupportedLanguages) {
|
||||||
|
this.lang = lang
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut to easily fetch a card using both it's global id and it's local ID
|
* Shortcut to easily fetch a card using both it's global id and it's local ID
|
||||||
* @param id the card global/local ID
|
* @param id the card global/local ID
|
||||||
@ -47,7 +167,7 @@ export default class TCGdex {
|
|||||||
/**
|
/**
|
||||||
* @deprecated use `this.fetch('sets', set)`
|
* @deprecated use `this.fetch('sets', set)`
|
||||||
*/
|
*/
|
||||||
public async fetchSet(set: string): Promise<Set | undefined> {
|
public async fetchSet(set: string): Promise<TCGdexSet | undefined> {
|
||||||
return this.fetch('sets', set)
|
return this.fetch('sets', set)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +224,7 @@ export default class TCGdex {
|
|||||||
* @param endpoint_0 'sets'
|
* @param endpoint_0 'sets'
|
||||||
* @param endpoint_1 {string} the set name or ID
|
* @param endpoint_1 {string} the set name or ID
|
||||||
*/
|
*/
|
||||||
public async fetch(...endpoint: ['sets', string]): Promise<Set | undefined>
|
public async fetch(...endpoint: ['sets', string]): Promise<TCGdexSet | undefined>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch every sets
|
* Fetch every sets
|
||||||
@ -148,7 +268,7 @@ export default class TCGdex {
|
|||||||
* @param endpoint_1 {string} (Optionnal) some details to go from the index file to the item file (mostly the ID/name)
|
* @param endpoint_1 {string} (Optionnal) some details to go from the index file to the item file (mostly the ID/name)
|
||||||
* @param endpoint_2 {string} (Optionnal) only for sets the card local ID to fetch the card through the set
|
* @param endpoint_2 {string} (Optionnal) only for sets the card local ID to fetch the card through the set
|
||||||
*/
|
*/
|
||||||
public async fetch(...endpoint: Array<Endpoint | string>): Promise<any | undefined> {
|
public async fetch<T = object>(...endpoint: Array<Endpoints | string>): Promise<T | undefined> {
|
||||||
if (endpoint.length === 0) {
|
if (endpoint.length === 0) {
|
||||||
throw new Error('endpoint to fetch is empty!')
|
throw new Error('endpoint to fetch is empty!')
|
||||||
}
|
}
|
||||||
@ -157,29 +277,112 @@ export default class TCGdex {
|
|||||||
if (!ENDPOINTS.includes(baseEndpoint)) {
|
if (!ENDPOINTS.includes(baseEndpoint)) {
|
||||||
throw new Error(`unknown endpoint to fetch! (${baseEndpoint})`)
|
throw new Error(`unknown endpoint to fetch! (${baseEndpoint})`)
|
||||||
}
|
}
|
||||||
return this.makeRequest(baseEndpoint, ...endpoint)
|
return this.actualFetch<T>(this.getFullURL([baseEndpoint, ...endpoint]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to make the request and normalize the whole path
|
* @param endpoint the endpoint to fetch
|
||||||
|
* @param query the query
|
||||||
*/
|
*/
|
||||||
private makeRequest<T = any>(...url: Array<string | number>) {
|
public async fetchWithQuery<T = object>(
|
||||||
|
endpoint: [Endpoints, ...Array<string>],
|
||||||
|
query?: Array<{ key: string, value: string | number | boolean }>
|
||||||
|
): Promise<T | undefined> {
|
||||||
|
if (endpoint.length === 0) {
|
||||||
|
throw new Error('endpoint to fetch is empty!')
|
||||||
|
}
|
||||||
|
const baseEndpoint = endpoint[0].toLowerCase() as Endpoints
|
||||||
|
if (!ENDPOINTS.includes(baseEndpoint)) {
|
||||||
|
throw new Error(`unknown endpoint to fetch! (${baseEndpoint})`)
|
||||||
|
}
|
||||||
|
return this.actualFetch<T>(this.getFullURL(endpoint, query))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* format the final URL
|
||||||
|
*/
|
||||||
|
private getFullURL(
|
||||||
|
url: Array<string | number>,
|
||||||
|
searchParams?: Array<{ key: string, value: string | number | boolean }>
|
||||||
|
): string {
|
||||||
// Normalize path
|
// Normalize path
|
||||||
const path = url.map((subPath) => encodeURI(
|
let path = url.map(this.encode).join('/')
|
||||||
subPath
|
|
||||||
|
// handle the Search Params
|
||||||
|
if (searchParams) {
|
||||||
|
path += '?'
|
||||||
|
for (let idx = 0; idx < searchParams.length; idx++) {
|
||||||
|
const param = searchParams[idx]
|
||||||
|
if (idx !== 0) {
|
||||||
|
path += '&'
|
||||||
|
}
|
||||||
|
path += `${this.encode(param.key)}=${this.encode(param.value)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return with the endpoint and all the shit
|
||||||
|
return `${this.getEndpoint()}/${this.getLang()}/${path}`
|
||||||
|
}
|
||||||
|
|
||||||
|
private async actualFetch<T = object>(path: string): Promise<T | undefined> {
|
||||||
|
// get and return the cached value if available
|
||||||
|
const cached = this.cache.get(path)
|
||||||
|
if (cached) {
|
||||||
|
return cached as T
|
||||||
|
}
|
||||||
|
|
||||||
|
// the actual Fetch :D
|
||||||
|
const resp = await TCGdex.fetch(path, {
|
||||||
|
headers: {
|
||||||
|
'user-agent': `@tcgdex/javascript-sdk/${version}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// throw if a server-side error is occured
|
||||||
|
if (resp.status >= 500) {
|
||||||
|
try {
|
||||||
|
const json = JSON.stringify(await resp.json())
|
||||||
|
throw new Error(json)
|
||||||
|
} catch {
|
||||||
|
throw new Error('TCGdex Server responded with an invalid error :(')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// response is not valid :O
|
||||||
|
if (resp.status !== 200) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse, put to cache and return
|
||||||
|
const json = await resp.json()
|
||||||
|
this.cache.set(path, json, this.cacheTTL)
|
||||||
|
return json as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encode a string to be used in an url
|
||||||
|
* @param str the string to encode to URL
|
||||||
|
* @returns the encoded string
|
||||||
|
*/
|
||||||
|
private encode(str: string | number | boolean): string {
|
||||||
|
return encodeURI(
|
||||||
|
str
|
||||||
// Transform numbers to string
|
// Transform numbers to string
|
||||||
.toString()
|
.toString()
|
||||||
// replace this special character with an escaped one
|
// replace this special character with an escaped one
|
||||||
.replace('?', '%3F')
|
.replace('?', '%3F')
|
||||||
// normalize the string
|
// normalize the string
|
||||||
.normalize('NFC')
|
.normalize('NFC')
|
||||||
// remove some special chars by nothing
|
// remove some special chars
|
||||||
// eslint-disable-next-line no-misleading-character-class
|
// eslint-disable-next-line no-misleading-character-class
|
||||||
.replace(/["'\u0300-\u036f]/gu, '')
|
.replace(/["'\u0300-\u036f]/gu, '')
|
||||||
)).join('/')
|
)
|
||||||
return RequestWrapper.fetch<T>(`${BASE_URL}/${this.getLang()}/${path}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from './interfaces'
|
|
||||||
|
export * from './models/Card'
|
||||||
|
export {
|
||||||
|
Query
|
||||||
|
}
|
||||||
|
|
||||||
|
20
src/utils.ts
Normal file
20
src/utils.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { Endpoints } from './interfaces'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* detect the current running context ofthe program
|
||||||
|
*/
|
||||||
|
export function detectContext(): 'browser' | 'server' {
|
||||||
|
try {
|
||||||
|
const isBrowser = !!window
|
||||||
|
return isBrowser ? 'browser' : 'server'
|
||||||
|
} catch {
|
||||||
|
return 'server'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ENDPOINTS: Array<Endpoints> = [
|
||||||
|
'cards', 'categories', 'dex-ids', 'energy-types',
|
||||||
|
'hp', 'illustrators', 'rarities', 'regulation-marks',
|
||||||
|
'retreats', 'series', 'sets', 'stages', 'suffixes',
|
||||||
|
'trainer-types', 'types', 'variants'
|
||||||
|
] as const
|
@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"extends": "./node_modules/@dzeio/config/tsconfig.base",
|
"extends": "./node_modules/@dzeio/config/tsconfig.base",
|
||||||
"include": ["./src/tcgdex.node.ts"],
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2015",
|
"outDir": "dist",
|
||||||
"rootDir": "./src",
|
"strictNullChecks": true
|
||||||
}
|
},
|
||||||
|
"exclude": [
|
||||||
|
"__tests__"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user