mirror of
https://github.com/tcgdex/cards-database.git
synced 2025-04-22 02:42:09 +00:00
feature: Implement new Server infrastructure with GraphQL. (#132)
* Added new compiler to db Signed-off-by: Avior <florian.bouillon@delta-wings.net> * Add compiled DB to artifacts Signed-off-by: Avior <florian.bouillon@delta-wings.net> * Fixed space error Signed-off-by: Avior <florian.bouillon@delta-wings.net> * Fixed? Signed-off-by: Avior <florian.bouillon@delta-wings.net> * Update node.js.yml * Update node.js.yml * Made change so the db is no longer dependent on the SDK Signed-off-by: Avior <florian.bouillon@delta-wings.net> * f Signed-off-by: Avior <florian.bouillon@delta-wings.net> * Fixed artifact Signed-off-by: Avior <florian.bouillon@delta-wings.net> * U Signed-off-by: Avior <florian.bouillon@delta-wings.net> * \Changed folder Signed-off-by: Avior <florian.bouillon@delta-wings.net> * Fixede? Signed-off-by: Avior <florian.bouillon@delta-wings.net> * Try with everything * saved the file ;) * ignore compiler * Fixed prebuild being run again * Fixed public folder Signed-off-by: Avior <github@avior.me> * fixed graphql file Signed-off-by: Avior <github@avior.me> * fixed? Signed-off-by: Avior <github@avior.me> * Check tree because life is potato Signed-off-by: Avior <github@avior.me> * this is harder Signed-off-by: Avior <github@avior.me> * f Signed-off-by: Avior <github@avior.me> * Fixed? Signed-off-by: Avior <github@avior.me> * r Signed-off-by: Avior <github@avior.me> * fd Signed-off-by: Avior <github@avior.me> * added back context Signed-off-by: Avior <github@avior.me> * ah Signed-off-by: Avior <github@avior.me> * AAH Signed-off-by: Avior <github@avior.me> * AAAH Signed-off-by: Avior <github@avior.me> * ffffffffffffffffff Signed-off-by: Avior <github@avior.me> * fix: Changed the default builder Signed-off-by: Avior <github@avior.me> * Removed useless tree function Signed-off-by: Avior <github@avior.me>
This commit is contained in:
parent
c991193f4b
commit
c4b4449fd4
22
.dockerignore
Normal file
22
.dockerignore
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
dist/
|
||||||
|
*.asc
|
||||||
|
|
||||||
|
/data/**/*.js
|
||||||
|
|
||||||
|
|
||||||
|
# production
|
||||||
|
/server/dist
|
||||||
|
/server/generated
|
58
.github/workflows/build.yml
vendored
Normal file
58
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
# Publish semver tags as releases.
|
||||||
|
tags: [ 'v*.*.*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Use docker.io for Docker Hub if empty
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
# github.repository as <account>/<repo>
|
||||||
|
IMAGE_NAME: tcgdex/server
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
# Login against a Docker registry except on PR
|
||||||
|
# https://github.com/docker/login-action
|
||||||
|
- name: Log into registry ${{ env.REGISTRY }}
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Extract metadata (tags, labels) for Docker
|
||||||
|
# https://github.com/docker/metadata-action
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
|
# Build and push Docker image with Buildx (don't push on PR)
|
||||||
|
# https://github.com/docker/build-push-action
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
48
.github/workflows/node.js.yml
vendored
48
.github/workflows/node.js.yml
vendored
@ -1,48 +0,0 @@
|
|||||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
||||||
|
|
||||||
name: Compile
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
paths:
|
|
||||||
- 'data/**'
|
|
||||||
- 'meta/**'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'data/**'
|
|
||||||
- 'meta/**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: Compilation Test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Setup NodeJS
|
|
||||||
uses: actions/setup-node@v2.4.1
|
|
||||||
with:
|
|
||||||
node-version: 16.x
|
|
||||||
|
|
||||||
- name: Install packages
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Build the database
|
|
||||||
run: npm run compile
|
|
||||||
|
|
||||||
push:
|
|
||||||
name: Launch the compiler
|
|
||||||
needs: test
|
|
||||||
if: ${{ github.event_name == 'push' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Compiler Dispatch
|
|
||||||
uses: peter-evans/repository-dispatch@v1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.PAC }}
|
|
||||||
repository: tcgdex/compiler
|
|
||||||
event-type: card-database-update
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
*.js
|
*.js
|
||||||
|
|
||||||
node_modules/
|
node_modules/
|
||||||
|
dist/
|
||||||
|
44
Dockerfile
Normal file
44
Dockerfile
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
FROM node:alpine as BUILD_IMAGE
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ADD package.json package-lock.json ./
|
||||||
|
ADD server/package.json server/package-lock.json ./server/
|
||||||
|
|
||||||
|
# install dependencies
|
||||||
|
RUN npm ci && \
|
||||||
|
cd server && \
|
||||||
|
npm ci
|
||||||
|
|
||||||
|
# Add project files
|
||||||
|
ADD . .
|
||||||
|
|
||||||
|
# build
|
||||||
|
RUN npm run compile && \
|
||||||
|
cd server && \
|
||||||
|
npm run compile && \
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# remove dev dependencies
|
||||||
|
RUN npm prune --production && \
|
||||||
|
cd server && \
|
||||||
|
npm prune --production
|
||||||
|
|
||||||
|
# go to another VM
|
||||||
|
FROM node:alpine
|
||||||
|
|
||||||
|
# go to folder
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# copy from build image
|
||||||
|
COPY --from=BUILD_IMAGE /app/server/generated ./generated
|
||||||
|
COPY --from=BUILD_IMAGE /app/server/node_modules ./node_modules
|
||||||
|
COPY --from=BUILD_IMAGE /app/server/dist ./dist
|
||||||
|
COPY --from=BUILD_IMAGE /app/server/public ./public
|
||||||
|
COPY --from=BUILD_IMAGE /app/server/package.json ./package.json
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# run it !
|
||||||
|
CMD ["npm", "run", "start"]
|
@ -59,6 +59,12 @@ You want to ask something or you just want to discuss anything from an idea/impr
|
|||||||
|
|
||||||
Well you can join us on [Discord](https://discord.gg/NehYTAhsZE) !
|
Well you can join us on [Discord](https://discord.gg/NehYTAhsZE) !
|
||||||
|
|
||||||
|
## Project Architecture
|
||||||
|
|
||||||
|
- `/data`: Contains Cards Data architectured around `Series/Set/localId` folders
|
||||||
|
- `/meta`: Contains misc informations for the project (more details in the README.md in `/meta`)
|
||||||
|
- `/server`: The server that allow to communicate with the database
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
See [CONTRIBUTING.md](./CONTRIBUTING.md)
|
See [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||||
|
277
meta/definitions/api.d.ts
vendored
Normal file
277
meta/definitions/api.d.ts
vendored
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
/**
|
||||||
|
* This file define how the API is architectured for the Compiler and the Javascript/Typescript SDK
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* /series endpoint
|
||||||
|
*/
|
||||||
|
export interface SerieResume {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
logo?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* /series/:id endpoint
|
||||||
|
*/
|
||||||
|
export interface Serie extends SerieResume {
|
||||||
|
sets: Array<SetResume>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface variants {
|
||||||
|
normal?: boolean;
|
||||||
|
reverse?: boolean;
|
||||||
|
holo?: boolean;
|
||||||
|
firstEdition?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SetResume {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
logo?: string;
|
||||||
|
symbol?: string;
|
||||||
|
cardCount: {
|
||||||
|
/**
|
||||||
|
* total of number of cards
|
||||||
|
*/
|
||||||
|
total: number;
|
||||||
|
/**
|
||||||
|
* number of cards officialy (on the bottom of each cards)
|
||||||
|
*/
|
||||||
|
official: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* /sets/:id
|
||||||
|
*/
|
||||||
|
export interface Set extends SetResume {
|
||||||
|
serie: SerieResume;
|
||||||
|
tcgOnline?: string;
|
||||||
|
variants?: variants;
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
legal: {
|
||||||
|
/**
|
||||||
|
* Ability to play in standard tournaments
|
||||||
|
*/
|
||||||
|
standard: boolean;
|
||||||
|
/**
|
||||||
|
* Ability to play in expanded tournaments
|
||||||
|
*/
|
||||||
|
expanded: boolean;
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
cards: Array<CardResume>;
|
||||||
|
}
|
||||||
|
export interface CardResume {
|
||||||
|
id: string;
|
||||||
|
localId: string;
|
||||||
|
/**
|
||||||
|
* Card Name (Including the suffix if next to card name)
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
image?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* /cards/:id
|
||||||
|
* /sets/:set/:localId
|
||||||
|
*/
|
||||||
|
export interface Card extends CardResume {
|
||||||
|
/**
|
||||||
|
* Card illustrator
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
rarity: string;
|
||||||
|
/**
|
||||||
|
* Card Category
|
||||||
|
*
|
||||||
|
* - Pokemon
|
||||||
|
* - Trainer
|
||||||
|
* - Energy
|
||||||
|
*/
|
||||||
|
category: string;
|
||||||
|
/**
|
||||||
|
* Card Variants (Override Set Variants)
|
||||||
|
*/
|
||||||
|
variants?: variants;
|
||||||
|
/**
|
||||||
|
* Card Set
|
||||||
|
*/
|
||||||
|
set: SetResume;
|
||||||
|
/**
|
||||||
|
* Pokemon only elements
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Pokemon Pokedex ID
|
||||||
|
*/
|
||||||
|
dexId?: Array<number>;
|
||||||
|
/**
|
||||||
|
* Pokemon HP
|
||||||
|
*/
|
||||||
|
hp?: number;
|
||||||
|
/**
|
||||||
|
* Pokemon Types
|
||||||
|
* ex for multiple https://www.tcgdex.net/database/ex/ex13/17
|
||||||
|
*/
|
||||||
|
types?: Array<string>;
|
||||||
|
/**
|
||||||
|
* Pokemon Sub Evolution
|
||||||
|
*/
|
||||||
|
evolveFrom?: string;
|
||||||
|
/**
|
||||||
|
* Pokemon Weight
|
||||||
|
*/
|
||||||
|
weight?: string;
|
||||||
|
/**
|
||||||
|
* Pokemon Description
|
||||||
|
*/
|
||||||
|
description?: string;
|
||||||
|
/**
|
||||||
|
* Level of the Pokemon
|
||||||
|
*
|
||||||
|
* NOTE: can be equal to 'X' when the pokemon is a LEVEL-UP one
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
suffix?: string;
|
||||||
|
/**
|
||||||
|
* Pokemon Held Item
|
||||||
|
*
|
||||||
|
* ex https://www.tcgdex.net/database/dp/dp2/75
|
||||||
|
*/
|
||||||
|
item?: {
|
||||||
|
name: string;
|
||||||
|
effect: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Pokemon Abilities
|
||||||
|
*
|
||||||
|
* multi abilities ex https://www.tcgdex.net/database/ex/ex15/10
|
||||||
|
*/
|
||||||
|
abilities?: Array<{
|
||||||
|
type: string;
|
||||||
|
name: string;
|
||||||
|
effect: string;
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Pokemon Attacks
|
||||||
|
*/
|
||||||
|
attacks?: Array<{
|
||||||
|
cost?: Array<string>;
|
||||||
|
name: string;
|
||||||
|
effect?: string;
|
||||||
|
damage?: string | number;
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Pokemon Weaknesses
|
||||||
|
*/
|
||||||
|
weaknesses?: Array<{
|
||||||
|
type: string;
|
||||||
|
value?: string;
|
||||||
|
}>;
|
||||||
|
resistances?: Array<{
|
||||||
|
type: string;
|
||||||
|
value?: string;
|
||||||
|
}>;
|
||||||
|
retreat?: number;
|
||||||
|
effect?: string;
|
||||||
|
trainerType?: string;
|
||||||
|
energyType?: string;
|
||||||
|
/**
|
||||||
|
* Define the rotation mark on cards >= Sword & Shield
|
||||||
|
*/
|
||||||
|
regulationMark?: string;
|
||||||
|
/**
|
||||||
|
* Card ability to be played in official tournaments
|
||||||
|
*
|
||||||
|
* Note: all cards are avaialable to play in unlimited tournaments
|
||||||
|
*/
|
||||||
|
legal: {
|
||||||
|
/**
|
||||||
|
* Ability to play in standard tournaments
|
||||||
|
*/
|
||||||
|
standard: boolean;
|
||||||
|
/**
|
||||||
|
* Ability to play in expanded tournaments
|
||||||
|
*/
|
||||||
|
expanded: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String Endpoint List
|
||||||
|
*/
|
||||||
|
export declare type StringEndpointList = Array<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StringEndpoint
|
||||||
|
*/
|
||||||
|
export interface StringEndpoint {
|
||||||
|
name: string;
|
||||||
|
cards: Array<CardResume>;
|
||||||
|
}
|
167
meta/definitions/graphql.gql
Normal file
167
meta/definitions/graphql.gql
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
##################
|
||||||
|
# Global #
|
||||||
|
##################
|
||||||
|
|
||||||
|
# Locale Directive ex: {sets @locale(fr)}
|
||||||
|
directive @locale (
|
||||||
|
lang: String!
|
||||||
|
) on FIELD
|
||||||
|
|
||||||
|
# Queries to use on the DB
|
||||||
|
type Query {
|
||||||
|
cards(filters: CardsFilters, pagination: Pagination): [Card]
|
||||||
|
sets: [Set]
|
||||||
|
series: [Serie]
|
||||||
|
card(
|
||||||
|
id: ID!,
|
||||||
|
set: String
|
||||||
|
): Card
|
||||||
|
set(
|
||||||
|
id: ID!
|
||||||
|
): Set
|
||||||
|
serie(
|
||||||
|
id: ID!
|
||||||
|
): Serie
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pagination input
|
||||||
|
input Pagination {
|
||||||
|
page: Float!
|
||||||
|
count: Float!
|
||||||
|
}
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Card #
|
||||||
|
##################
|
||||||
|
|
||||||
|
# Filters to be used with the Card query
|
||||||
|
input CardsFilters {
|
||||||
|
category: String
|
||||||
|
description: String
|
||||||
|
energyType: String
|
||||||
|
evolveFrom: String
|
||||||
|
hp: Float
|
||||||
|
id: ID
|
||||||
|
localId: String
|
||||||
|
dexId: Float
|
||||||
|
illustrator: String
|
||||||
|
image: String
|
||||||
|
level: Float
|
||||||
|
levelId: String
|
||||||
|
name: String
|
||||||
|
rarity: String
|
||||||
|
regulationMark: String
|
||||||
|
stage: String
|
||||||
|
suffix: String
|
||||||
|
trainerType: String
|
||||||
|
retreat: Float
|
||||||
|
}
|
||||||
|
|
||||||
|
type Card {
|
||||||
|
abilities: [AbilitiesListItem]
|
||||||
|
attacks: [AttacksListItem]
|
||||||
|
category: String!
|
||||||
|
description: String
|
||||||
|
dexId: [Float]
|
||||||
|
energyType: String
|
||||||
|
evolveFrom: String
|
||||||
|
hp: Float
|
||||||
|
id: String!
|
||||||
|
illustrator: String
|
||||||
|
image: String
|
||||||
|
item: Item
|
||||||
|
legal: Legal!
|
||||||
|
level: Float
|
||||||
|
localId: String!
|
||||||
|
name: String!
|
||||||
|
rarity: String!
|
||||||
|
regulationMark: String
|
||||||
|
resistances: [[WeakResListItem]]
|
||||||
|
retreat: Float
|
||||||
|
set: Set!
|
||||||
|
stage: String
|
||||||
|
suffix: String
|
||||||
|
trainerType: String
|
||||||
|
types: [String]
|
||||||
|
variants: Variants
|
||||||
|
weaknesses: [[WeakResListItem]]
|
||||||
|
}
|
||||||
|
|
||||||
|
type AbilitiesListItem {
|
||||||
|
effect: String
|
||||||
|
name: String
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttacksListItem {
|
||||||
|
cost: [String]
|
||||||
|
damage: String
|
||||||
|
effect: String
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Item {
|
||||||
|
effect: String!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Legal {
|
||||||
|
expanded: Boolean
|
||||||
|
standard: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type WeakResListItem {
|
||||||
|
type: String!
|
||||||
|
value: String
|
||||||
|
}
|
||||||
|
|
||||||
|
type Variants {
|
||||||
|
firstEdition: Boolean!
|
||||||
|
holo: Boolean!
|
||||||
|
normal: Boolean!
|
||||||
|
reverse: Boolean!
|
||||||
|
}
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Set #
|
||||||
|
##################
|
||||||
|
|
||||||
|
type Set {
|
||||||
|
cardCount: CardCount!
|
||||||
|
cards: [Card]!
|
||||||
|
id: String!
|
||||||
|
logo: String
|
||||||
|
name: String!
|
||||||
|
symbol: String
|
||||||
|
serie: Serie!
|
||||||
|
}
|
||||||
|
|
||||||
|
type CardCount {
|
||||||
|
firstEd: Float
|
||||||
|
holo: Float
|
||||||
|
normal: Float
|
||||||
|
official: Float!
|
||||||
|
reverse: Float
|
||||||
|
total: Float!
|
||||||
|
}
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Serie #
|
||||||
|
##################
|
||||||
|
|
||||||
|
type Serie {
|
||||||
|
id: String!
|
||||||
|
logo: String
|
||||||
|
name: String!
|
||||||
|
sets: [Set]!
|
||||||
|
}
|
||||||
|
|
||||||
|
##################
|
||||||
|
# StringEndpoint #
|
||||||
|
##################
|
||||||
|
|
||||||
|
type StringEndpoint {
|
||||||
|
cards: [Card]!
|
||||||
|
name: String!
|
||||||
|
}
|
316
package-lock.json
generated
316
package-lock.json
generated
@ -1,17 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "@tcgdex/db",
|
"name": "cards-database",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@tcgdex/db",
|
|
||||||
"version": "2.0.0",
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@dzeio/object-util": "^1.2.0",
|
||||||
|
"@tcgdex/sdk": "^2.4.4",
|
||||||
|
"@types/glob": "^7.1.4",
|
||||||
"@types/jscodeshift": "^0.11.2",
|
"@types/jscodeshift": "^0.11.2",
|
||||||
"@types/node": "^16.11.6",
|
"@types/node": "^16.11.6",
|
||||||
|
"@types/node-fetch": "2.5.12",
|
||||||
|
"glob": "^7.1.7",
|
||||||
"jscodeshift": "^0.13.0",
|
"jscodeshift": "^0.13.0",
|
||||||
|
"node-fetch": "2.6.5",
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.4.0",
|
||||||
"typescript": "^4.4.4"
|
"typescript": "^4.4.4"
|
||||||
}
|
}
|
||||||
@ -1788,6 +1791,25 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@dzeio/object-util": {
|
||||||
|
"version": "1.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dzeio/object-util/-/object-util-1.4.2.tgz",
|
||||||
|
"integrity": "sha512-dqBQo9aUNUi9XVAMJl3dZgA+wKAQgehdvmMX+04X29ovWCYcWuXvq+8HEzckQXMPQ/xYw+D/fV2S96QYaHzX1g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tcgdex/sdk": {
|
||||||
|
"version": "2.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tcgdex/sdk/-/sdk-2.4.5.tgz",
|
||||||
|
"integrity": "sha512-NOzMffhUdRdlrMrd6PCQ7A/bbwlkY9DiMbR8O+JWjRKg/q/E2IXKewYRCFzFvCXe/pEs+gbYj/Jhta49OzYxHg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"isomorphic-unfetch": "^3.1.0",
|
||||||
|
"unfetch": "^4.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tsconfig/node10": {
|
"node_modules/@tsconfig/node10": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
|
||||||
@ -1812,6 +1834,16 @@
|
|||||||
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
|
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/glob": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/minimatch": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/jscodeshift": {
|
"node_modules/@types/jscodeshift": {
|
||||||
"version": "0.11.2",
|
"version": "0.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/jscodeshift/-/jscodeshift-0.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/jscodeshift/-/jscodeshift-0.11.2.tgz",
|
||||||
@ -1822,12 +1854,28 @@
|
|||||||
"recast": "^0.20.3"
|
"recast": "^0.20.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/minimatch": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "16.11.6",
|
"version": "16.11.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz",
|
||||||
"integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==",
|
"integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node-fetch": {
|
||||||
|
"version": "2.5.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz",
|
||||||
|
"integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"form-data": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.5.0",
|
"version": "8.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
|
||||||
@ -1924,6 +1972,12 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/atob": {
|
"node_modules/atob": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||||
@ -2215,6 +2269,18 @@
|
|||||||
"node": ">=0.1.90"
|
"node": ">=0.1.90"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/commondir": {
|
"node_modules/commondir": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
@ -2395,6 +2461,15 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/diff": {
|
"node_modules/diff": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
@ -2571,6 +2646,20 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fragment-cache": {
|
"node_modules/fragment-cache": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
|
||||||
@ -2628,9 +2717,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "7.1.6",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
@ -2885,6 +2974,16 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/isomorphic-unfetch": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^2.6.1",
|
||||||
|
"unfetch": "^4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@ -3091,6 +3190,27 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.50.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz",
|
||||||
|
"integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz",
|
||||||
|
"integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.50.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
@ -3224,6 +3344,18 @@
|
|||||||
"node": ">= 0.10.5"
|
"node": ">= 0.10.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.6.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
|
||||||
|
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-modules-regexp": {
|
"node_modules/node-modules-regexp": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
|
||||||
@ -3990,6 +4122,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/ts-node": {
|
"node_modules/ts-node": {
|
||||||
"version": "10.4.0",
|
"version": "10.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz",
|
||||||
@ -4050,6 +4188,12 @@
|
|||||||
"node": ">=4.2.0"
|
"node": ">=4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unfetch": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/unicode-canonical-property-names-ecmascript": {
|
"node_modules/unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
|
||||||
@ -4173,6 +4317,22 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wrappy": {
|
"node_modules/wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
@ -5465,6 +5625,22 @@
|
|||||||
"@cspotcode/source-map-consumer": "0.8.0"
|
"@cspotcode/source-map-consumer": "0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@dzeio/object-util": {
|
||||||
|
"version": "1.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dzeio/object-util/-/object-util-1.4.2.tgz",
|
||||||
|
"integrity": "sha512-dqBQo9aUNUi9XVAMJl3dZgA+wKAQgehdvmMX+04X29ovWCYcWuXvq+8HEzckQXMPQ/xYw+D/fV2S96QYaHzX1g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@tcgdex/sdk": {
|
||||||
|
"version": "2.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tcgdex/sdk/-/sdk-2.4.5.tgz",
|
||||||
|
"integrity": "sha512-NOzMffhUdRdlrMrd6PCQ7A/bbwlkY9DiMbR8O+JWjRKg/q/E2IXKewYRCFzFvCXe/pEs+gbYj/Jhta49OzYxHg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"isomorphic-unfetch": "^3.1.0",
|
||||||
|
"unfetch": "^4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@tsconfig/node10": {
|
"@tsconfig/node10": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
|
||||||
@ -5489,6 +5665,16 @@
|
|||||||
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
|
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/glob": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/minimatch": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/jscodeshift": {
|
"@types/jscodeshift": {
|
||||||
"version": "0.11.2",
|
"version": "0.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/jscodeshift/-/jscodeshift-0.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/jscodeshift/-/jscodeshift-0.11.2.tgz",
|
||||||
@ -5499,12 +5685,28 @@
|
|||||||
"recast": "^0.20.3"
|
"recast": "^0.20.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/minimatch": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "16.11.6",
|
"version": "16.11.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz",
|
||||||
"integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==",
|
"integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/node-fetch": {
|
||||||
|
"version": "2.5.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz",
|
||||||
|
"integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"form-data": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "8.5.0",
|
"version": "8.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
|
||||||
@ -5571,6 +5773,12 @@
|
|||||||
"tslib": "^2.0.1"
|
"tslib": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"atob": {
|
"atob": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||||
@ -5803,6 +6011,15 @@
|
|||||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"commondir": {
|
"commondir": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
@ -5954,6 +6171,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"diff": {
|
"diff": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
@ -6086,6 +6309,17 @@
|
|||||||
"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
|
"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"form-data": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fragment-cache": {
|
"fragment-cache": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
|
||||||
@ -6131,9 +6365,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.6",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
@ -6329,6 +6563,16 @@
|
|||||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"isomorphic-unfetch": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"node-fetch": "^2.6.1",
|
||||||
|
"unfetch": "^4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@ -6491,6 +6735,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.50.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz",
|
||||||
|
"integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz",
|
||||||
|
"integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.50.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
@ -6604,6 +6863,15 @@
|
|||||||
"minimatch": "^3.0.2"
|
"minimatch": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-fetch": {
|
||||||
|
"version": "2.6.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
|
||||||
|
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node-modules-regexp": {
|
"node-modules-regexp": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
|
||||||
@ -7218,6 +7486,12 @@
|
|||||||
"repeat-string": "^1.6.1"
|
"repeat-string": "^1.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"version": "10.4.0",
|
"version": "10.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz",
|
||||||
@ -7250,6 +7524,12 @@
|
|||||||
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
|
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"unfetch": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"unicode-canonical-property-names-ecmascript": {
|
"unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
|
||||||
@ -7346,6 +7626,22 @@
|
|||||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
|
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
10
package.json
10
package.json
@ -1,18 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "@tcgdex/db",
|
"private": true,
|
||||||
"version": "2.0.0",
|
|
||||||
"description": "Database of pokemon TCG",
|
|
||||||
"repository": "https://github.com/tcgdex/cards-database.git",
|
|
||||||
"author": "Avior <florian@avior.me>",
|
|
||||||
"license": "MIT",
|
|
||||||
"private": false,
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"compile": "tsc --project tsconfig.json"
|
"compile": "tsc --project tsconfig.json"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jscodeshift": "^0.11.2",
|
|
||||||
"@types/node": "^16.11.6",
|
"@types/node": "^16.11.6",
|
||||||
"jscodeshift": "^0.13.0",
|
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.4.0",
|
||||||
"typescript": "^4.4.4"
|
"typescript": "^4.4.4"
|
||||||
}
|
}
|
||||||
|
6
server/.gitignore
vendored
Normal file
6
server/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/node_modules
|
||||||
|
/dist
|
||||||
|
/generated
|
||||||
|
/public/**/graphql.gql
|
||||||
|
/public/**/api.d.ts
|
||||||
|
/public/**/openapi.yaml
|
5
server/README.md
Normal file
5
server/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# TCGdex/server
|
||||||
|
|
||||||
|
The server running the TCGdex API.
|
||||||
|
|
||||||
|
More informations at https://github.com/tcgdex/cards-database#pokemon-tcgdex
|
7
server/compiler/compilerInterfaces.d.ts
vendored
Normal file
7
server/compiler/compilerInterfaces.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
export interface Endpoint<Index extends {} = {}, Item extends {} = {}, SubItem extends {} = {}, C = undefined> {
|
||||||
|
index(common: C): Promise<Index | undefined>
|
||||||
|
item(common: C): Promise<Record<string, Item> | undefined>
|
||||||
|
sub?(common: C, item: string): Promise<Record<string, SubItem> | undefined>
|
||||||
|
common?(): Promise<C>
|
||||||
|
}
|
30
server/compiler/endpoints/cards.ts
Normal file
30
server/compiler/endpoints/cards.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Card as CardSingle, CardResume } from '../../../meta/definitions/api'
|
||||||
|
import { Card, Languages } from '../../../interfaces'
|
||||||
|
import { Endpoint } from '../compilerInterfaces'
|
||||||
|
import { cardToCardSimple, cardToCardSingle, getCards } from '../utils/cardUtil'
|
||||||
|
|
||||||
|
type CardList = Array<CardResume>
|
||||||
|
|
||||||
|
export default class implements Endpoint<CardList, CardSingle, Record<string, unknown>, Array<[string, Card]>> {
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private lang: keyof Languages
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async index(common: Array<[string, Card]>): Promise<CardList> {
|
||||||
|
return Promise.all(common.map((c) => cardToCardSimple(c[0], c[1], this.lang)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public async item(common: Array<[string, Card]>): Promise<Record<string, CardSingle>> {
|
||||||
|
const items: Record<string, CardSingle> = {}
|
||||||
|
for await (const card of common) {
|
||||||
|
items[`${card[1].set.id}-${card[0]}`] = await cardToCardSingle(card[0], card[1], this.lang)
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
public async common(): Promise<Array<[string, Card]>> {
|
||||||
|
return getCards(this.lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
33
server/compiler/endpoints/series.ts
Normal file
33
server/compiler/endpoints/series.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Serie as SerieSingle, SerieResume } from '../../../meta/definitions/api'
|
||||||
|
import { Languages, Serie } from '../../../interfaces'
|
||||||
|
import { Endpoint } from '../compilerInterfaces'
|
||||||
|
import { getSeries, serieToSerieSimple, serieToSerieSingle } from '../utils/serieUtil'
|
||||||
|
|
||||||
|
type SerieList = Array<SerieResume>
|
||||||
|
|
||||||
|
export default class implements Endpoint<SerieList, SerieSingle, Record<string, any>, Array<Serie>> {
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private lang: keyof Languages
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async index(common: Array<Serie>): Promise<Array<SerieResume>> {
|
||||||
|
return Promise.all(common.map((s) => serieToSerieSimple(s, this.lang)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public async item(common: Array<Serie>): Promise<Record<string, SerieSingle>> {
|
||||||
|
const items: Record<string, SerieSingle> = {}
|
||||||
|
for await (const val of common) {
|
||||||
|
const gen = await serieToSerieSingle(val, this.lang)
|
||||||
|
const name = val.name[this.lang] as string
|
||||||
|
items[name] = gen
|
||||||
|
items[val.id] = gen
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
public async common(): Promise<Array<Serie>> {
|
||||||
|
return getSeries(this.lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
57
server/compiler/endpoints/sets.ts
Normal file
57
server/compiler/endpoints/sets.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { Set as SetSingle, Card as CardSingle, SetResume } from '../../../meta/definitions/api'
|
||||||
|
import { getSets, isSetAvailable, setToSetSimple, setToSetSingle } from '../utils/setUtil'
|
||||||
|
import { Languages, Set } from '../../../interfaces'
|
||||||
|
import { Endpoint } from '../compilerInterfaces'
|
||||||
|
import { cardToCardSingle, getCards } from '../utils/cardUtil'
|
||||||
|
|
||||||
|
type SetList = Array<SetResume>
|
||||||
|
|
||||||
|
export default class implements Endpoint<SetList, SetSingle, CardSingle, Array<Set>> {
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private lang: keyof Languages
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async index(common: Array<Set>): Promise<SetList> {
|
||||||
|
const sets = common
|
||||||
|
.sort((a, b) => a.releaseDate > b.releaseDate ? 1 : -1)
|
||||||
|
|
||||||
|
const tmp: SetList = await Promise.all(sets.map((el) => setToSetSimple(el, this.lang)))
|
||||||
|
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
public async item(common: Array<Set>): Promise<Record<string, SetSingle>> {
|
||||||
|
const sets = await Promise.all(common
|
||||||
|
.map((set) => setToSetSingle(set, this.lang)))
|
||||||
|
const res: Record<string, SetSingle> = {}
|
||||||
|
|
||||||
|
for (const set of sets) {
|
||||||
|
res[set.name] = set
|
||||||
|
res[set.id] = set
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
public async common(): Promise<Array<Set>> {
|
||||||
|
return getSets(undefined, this.lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sub(common: Array<Set>, item: string): Promise<Record<string, CardSingle> | undefined> {
|
||||||
|
const set = common.find((s) => s.name[this.lang] === item || s.id === item)
|
||||||
|
|
||||||
|
if (!set || !isSetAvailable(set, this.lang)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const lit = await getCards(this.lang, set)
|
||||||
|
const list: Record<string, CardSingle> = {}
|
||||||
|
for await (const card of lit) {
|
||||||
|
list[card[0]] = await cardToCardSingle(card[0], card[1], this.lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
server/compiler/index.ts
Normal file
63
server/compiler/index.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/* eslint-disable max-statements */
|
||||||
|
import { Endpoint } from './compilerInterfaces'
|
||||||
|
import { promises as fs } from 'fs'
|
||||||
|
import { fetchRemoteFile } from './utils/util'
|
||||||
|
|
||||||
|
const LANGS = ['en', 'fr', 'es', 'it', 'pt', 'de']
|
||||||
|
|
||||||
|
const DIST_FOLDER = './generated'
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
const paths = (await fs.readdir('./compiler/endpoints')).filter((p) => p.endsWith('.ts'))
|
||||||
|
|
||||||
|
console.log('Prefetching pictures')
|
||||||
|
await fetchRemoteFile('https://assets.tcgdex.net/datas.json')
|
||||||
|
|
||||||
|
// Delete dist folder to be sure to have a clean base
|
||||||
|
try {
|
||||||
|
await fs.rm(DIST_FOLDER, {recursive: true})
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
|
||||||
|
console.log('Let\'s GO !')
|
||||||
|
|
||||||
|
// Process each languages
|
||||||
|
for await (const lang of LANGS) {
|
||||||
|
console.log('Processing', lang)
|
||||||
|
// loop through """endpoints"""
|
||||||
|
for await (const file of paths) {
|
||||||
|
|
||||||
|
// final folder path
|
||||||
|
const folder = `${DIST_FOLDER}/${lang}`
|
||||||
|
|
||||||
|
// Make the folder
|
||||||
|
await fs.mkdir(folder, {recursive: true})
|
||||||
|
|
||||||
|
// Import the """Endpoint"""
|
||||||
|
const Ep = (await import(`./endpoints/${file}`)).default
|
||||||
|
|
||||||
|
const endpoint = new Ep(lang) as Endpoint
|
||||||
|
|
||||||
|
console.log(file, 'Running Common')
|
||||||
|
let common: any | null = null
|
||||||
|
|
||||||
|
if (endpoint.common) {
|
||||||
|
common = await endpoint.common()
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(file, 'Running Item')
|
||||||
|
const item = await endpoint.item(common)
|
||||||
|
|
||||||
|
// Write to file
|
||||||
|
await fs.writeFile(`${folder}/${file.replace('.ts', '')}.json`, JSON.stringify(item))
|
||||||
|
|
||||||
|
console.log(file, 'Finished Item')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally copy definitions files to the public folder :D
|
||||||
|
for await (const file of await fs.readdir('../meta/definitions')) {
|
||||||
|
await fs.copyFile('../meta/definitions/' + file, './public/v2/' + file)
|
||||||
|
}
|
||||||
|
|
||||||
|
})()
|
153
server/compiler/utils/cardUtil.ts
Normal file
153
server/compiler/utils/cardUtil.ts
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/* eslint-disable sort-keys */
|
||||||
|
import { setToSetSimple } from './setUtil'
|
||||||
|
import { cardIsLegal, DB_PATH, fetchRemoteFile, smartGlob } from './util'
|
||||||
|
import { Set, SupportedLanguages, Card, Types } from '../../../interfaces'
|
||||||
|
import { Card as CardSingle, CardResume } from '../../../meta/definitions/api'
|
||||||
|
import translate from './translationUtil'
|
||||||
|
|
||||||
|
export async function getCardPictures(cardId: string, card: Card, lang: SupportedLanguages): Promise<string | undefined> {
|
||||||
|
try {
|
||||||
|
const file = await fetchRemoteFile('https://assets.tcgdex.net/datas.json')
|
||||||
|
const fileExists = Boolean(file[lang]?.[card.set.serie.id]?.[card.set.id]?.[cardId])
|
||||||
|
if (fileExists) {
|
||||||
|
return `https://assets.tcgdex.net/${lang}/${card.set.serie.id}/${card.set.id}/${cardId}`
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function cardToCardSimple(id: string, card: Card, lang: SupportedLanguages): Promise<CardResume> {
|
||||||
|
const cardName = card.name[lang]
|
||||||
|
if (!cardName) {
|
||||||
|
throw new Error(`Card (${card.set.id}-${id}) has no name in (${lang})`)
|
||||||
|
}
|
||||||
|
const img = await getCardPictures(id, card, lang)
|
||||||
|
return {
|
||||||
|
id: `${card.set.id}-${id}`,
|
||||||
|
image: img,
|
||||||
|
localId: id,
|
||||||
|
name: cardName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-lines-per-function
|
||||||
|
export async function cardToCardSingle(localId: string, card: Card, lang: SupportedLanguages): Promise<CardSingle> {
|
||||||
|
const image = await getCardPictures(localId, card, lang)
|
||||||
|
|
||||||
|
if (!card.name[lang]) {
|
||||||
|
throw new Error(`Card (${localId}) dont exist in (${lang})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
category: translate('category', card.category, lang) as any,
|
||||||
|
id: `${card.set.id}-${localId}`,
|
||||||
|
illustrator: card.illustrator,
|
||||||
|
image,
|
||||||
|
localId,
|
||||||
|
name: card.name[lang] as string,
|
||||||
|
|
||||||
|
rarity: translate('rarity', card.rarity, lang) as any,
|
||||||
|
set: await setToSetSimple(card.set, lang),
|
||||||
|
variants: {
|
||||||
|
firstEdition: typeof card.variants?.firstEdition === 'boolean' ? card.variants.firstEdition : false,
|
||||||
|
holo: typeof card.variants?.holo === 'boolean' ? card.variants.holo : true,
|
||||||
|
normal: typeof card.variants?.normal === 'boolean' ? card.variants.normal : true,
|
||||||
|
reverse: typeof card.variants?.reverse === 'boolean' ? card.variants.reverse : true
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
dexId: card.dexId,
|
||||||
|
hp: card.hp,
|
||||||
|
types: card.types?.map((t) => translate('types', t, lang)) as Array<Types>,
|
||||||
|
evolveFrom: card.evolveFrom && card.evolveFrom[lang],
|
||||||
|
weight: card.weight,
|
||||||
|
description: card.description ? card.description[lang] as string : undefined,
|
||||||
|
level: card.level,
|
||||||
|
stage: translate('stage', card.stage, lang) as any,
|
||||||
|
suffix: translate('suffix', card.suffix, lang) as any,
|
||||||
|
item: card.item ? {
|
||||||
|
name: card.item.name[lang] as string,
|
||||||
|
effect: card.item.effect[lang] as string
|
||||||
|
} : undefined,
|
||||||
|
|
||||||
|
abilities: card.abilities?.map((el) => ({
|
||||||
|
type: translate('abilityType', el.type, lang) as any,
|
||||||
|
name: el.name[lang] as string,
|
||||||
|
effect: el.effect[lang] as string
|
||||||
|
})),
|
||||||
|
|
||||||
|
attacks: card.attacks?.map((el) => ({
|
||||||
|
cost: el.cost?.map((t) => translate('types', t, lang)) as Array<Types>,
|
||||||
|
name: el.name[lang] as string,
|
||||||
|
effect: el.effect ? el.effect[lang] : undefined,
|
||||||
|
damage: el.damage
|
||||||
|
})),
|
||||||
|
weaknesses: card.weaknesses?.map((el) => ({
|
||||||
|
type: translate('types', el.type, lang) as Types,
|
||||||
|
value: el.value
|
||||||
|
})),
|
||||||
|
|
||||||
|
resistances: card.resistances?.map((el) => ({
|
||||||
|
type: translate('types', el.type, lang) as Types,
|
||||||
|
value: el.value
|
||||||
|
})),
|
||||||
|
|
||||||
|
retreat: card.retreat,
|
||||||
|
|
||||||
|
effect: card.effect ? card.effect[lang] : undefined,
|
||||||
|
|
||||||
|
trainerType: translate('trainerType', card.trainerType, lang) as any,
|
||||||
|
energyType: translate('energyType', card.energyType, lang) as any,
|
||||||
|
regulationMark: card.regulationMark,
|
||||||
|
|
||||||
|
legal: {
|
||||||
|
standard: cardIsLegal('standard', card, localId),
|
||||||
|
expanded: cardIsLegal('expanded', card, localId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param setName the setname of the card
|
||||||
|
* @param id the local id of the card
|
||||||
|
* @returns [the local id, the Card object]
|
||||||
|
*/
|
||||||
|
export async function getCard(serie: string, setName: string, id: string): Promise<Card> {
|
||||||
|
return (await import(`../../${DB_PATH}/data/${serie}/${setName}/${id}.js`)).default
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCards(lang: SupportedLanguages, set?: Set): Promise<Array<[string, Card]>> {
|
||||||
|
const cards = await smartGlob(`${DB_PATH}/data/${(set && set.serie.name.en) ?? '*'}/${(set && set.name.en) ?? '*'}/*.js`)
|
||||||
|
const list: Array<[string, Card]> = []
|
||||||
|
for (const path of cards) {
|
||||||
|
const id = path.substring(path.lastIndexOf('/') + 1, path.lastIndexOf('.'))
|
||||||
|
const setName = (set && set.name.en) ?? (() => {
|
||||||
|
const part1 = path.substr(0, path.lastIndexOf(id) - 1)
|
||||||
|
return part1.substr(part1.lastIndexOf('/') + 1)
|
||||||
|
})()
|
||||||
|
const serieName = (set && set.serie.name.en) ?? (() => {
|
||||||
|
const part1 = path.substr(0, path.lastIndexOf(setName) - 1)
|
||||||
|
return part1.substr(part1.lastIndexOf('/') + 1)
|
||||||
|
})()
|
||||||
|
// console.log(path, id, setName)
|
||||||
|
const c = await getCard(serieName, setName, id)
|
||||||
|
if (!c.name[lang]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list.push([id, c])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by id when possible
|
||||||
|
return list.sort(([a], [b]) => {
|
||||||
|
const ra = parseInt(a, 10)
|
||||||
|
const rb = parseInt(b, 10)
|
||||||
|
if (!isNaN(ra) && !isNaN(rb)) {
|
||||||
|
return ra - rb
|
||||||
|
}
|
||||||
|
return a >= b ? 1 : -1
|
||||||
|
})
|
||||||
|
}
|
66
server/compiler/utils/serieUtil.ts
Normal file
66
server/compiler/utils/serieUtil.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { DB_PATH, smartGlob } from './util'
|
||||||
|
import { setToSetSimple, getSets } from './setUtil'
|
||||||
|
import { Serie, SupportedLanguages, Set } from '../../../interfaces'
|
||||||
|
import { Serie as SerieSingle, SerieResume } from '../../../meta/definitions/api'
|
||||||
|
|
||||||
|
export async function getSerie(name: string): Promise<Serie> {
|
||||||
|
return (await import(`../../${DB_PATH}/data/${name}.js`)).default
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isSerieAvailable(serie: Serie, lang: SupportedLanguages): Promise<boolean> {
|
||||||
|
if (!serie.name[lang]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const sets = await getSets(serie.name.en, lang)
|
||||||
|
return sets.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSeries(lang: SupportedLanguages): Promise<Array<Serie>> {
|
||||||
|
let series: Array<Serie> = (await Promise.all((await smartGlob(`${DB_PATH}/data/*.js`))
|
||||||
|
// Find Serie's name
|
||||||
|
.map((it) => it.substring(it.lastIndexOf('/') + 1, it.length - 3))
|
||||||
|
// Fetch the Serie
|
||||||
|
.map((it) => getSerie(it))))
|
||||||
|
// Filter the serie if no name's exists in the selected lang
|
||||||
|
.filter((serie) => Boolean(serie.name[lang]))
|
||||||
|
|
||||||
|
// Filter available series
|
||||||
|
const isAvailable = await Promise.all(series.map((serie) => isSerieAvailable(serie, lang)))
|
||||||
|
series = series.filter((_, index) => isAvailable[index])
|
||||||
|
|
||||||
|
// Sort series by the first set release date
|
||||||
|
const tmp: Array<[Serie, Set | undefined]> = await Promise.all(series.map(async (it) => [
|
||||||
|
it,
|
||||||
|
(await getSets(it.name.en, lang))
|
||||||
|
.reduce<Set | undefined>((p, c) => p ? p.releaseDate < c.releaseDate ? p : c : c, undefined) as Set
|
||||||
|
] as [Serie, Set]))
|
||||||
|
|
||||||
|
return tmp.sort((a, b) => (a[1] ? a[1].releaseDate : '0') > (b[1] ? b[1].releaseDate : '0') ? 1 : -1).map((it) => it[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function serieToSerieSimple(serie: Serie, lang: SupportedLanguages): Promise<SerieResume> {
|
||||||
|
const setsTmp = await getSets(serie.name.en, lang)
|
||||||
|
const sets = await Promise.all(setsTmp
|
||||||
|
.sort((a, b) => a.releaseDate > b.releaseDate ? 1 : -1)
|
||||||
|
.map((el) => setToSetSimple(el, lang)))
|
||||||
|
const logo = sets.find((set) => set.logo)?.logo
|
||||||
|
return {
|
||||||
|
id: serie.id,
|
||||||
|
logo,
|
||||||
|
name: serie.name[lang] as string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function serieToSerieSingle(serie: Serie, lang: SupportedLanguages): Promise<SerieSingle> {
|
||||||
|
const setsTmp = await getSets(serie.name.en, lang)
|
||||||
|
const sets = await Promise.all(setsTmp
|
||||||
|
.sort((a, b) => a.releaseDate > b.releaseDate ? 1 : -1)
|
||||||
|
.map((el) => setToSetSimple(el, lang)))
|
||||||
|
const logo = sets.find((set) => set.logo)?.logo
|
||||||
|
return {
|
||||||
|
id: serie.id,
|
||||||
|
logo,
|
||||||
|
name: serie.name[lang] as string,
|
||||||
|
sets
|
||||||
|
}
|
||||||
|
}
|
104
server/compiler/utils/setUtil.ts
Normal file
104
server/compiler/utils/setUtil.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { Set, SupportedLanguages } from '../../../interfaces'
|
||||||
|
import { DB_PATH, fetchRemoteFile, setIsLegal, smartGlob } from './util'
|
||||||
|
import { cardToCardSimple, getCards } from './cardUtil'
|
||||||
|
import { SetResume, Set as SetSingle } from '../../../meta/definitions/api'
|
||||||
|
|
||||||
|
interface t {
|
||||||
|
[key: string]: Set
|
||||||
|
}
|
||||||
|
|
||||||
|
const setCache: t = {}
|
||||||
|
|
||||||
|
export function isSetAvailable(set: Set, lang: SupportedLanguages): boolean {
|
||||||
|
return lang in set.name && lang in set.serie.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the set
|
||||||
|
* @param name the name of the set (don't include.js/.ts)
|
||||||
|
*/
|
||||||
|
export async function getSet(name: string, serie = '*'): Promise<Set> {
|
||||||
|
if (!setCache[name]) {
|
||||||
|
try {
|
||||||
|
const [path] = await smartGlob(`${DB_PATH}/data/${serie}/${name}.js`)
|
||||||
|
setCache[name] = (await import('../../' + path)).default
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
console.error(`Error trying to import importing (${`db/data/${serie}/${name}.js`})`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return setCache[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dont use cache as it wont necessary have them all
|
||||||
|
export async function getSets(serie = '*', lang: SupportedLanguages): Promise<Array<Set>> {
|
||||||
|
// list sets names
|
||||||
|
const rawSets = (await smartGlob(`${DB_PATH}/data/${serie}/*.js`)).map((set) => set.substring(set.lastIndexOf('/') + 1, set.lastIndexOf('.')))
|
||||||
|
// Fetch sets
|
||||||
|
const sets = (await Promise.all(rawSets.map((set) => getSet(set, serie))))
|
||||||
|
// Filter sets
|
||||||
|
.filter((set) => isSetAvailable(set, lang))
|
||||||
|
// Sort sets by release date
|
||||||
|
.sort((a, b) => a.releaseDate > b.releaseDate ? 1 : -1)
|
||||||
|
return sets
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSetPictures(set: Set, lang: SupportedLanguages): Promise<[string | undefined, string | undefined]> {
|
||||||
|
try {
|
||||||
|
const file = await fetchRemoteFile('https://assets.tcgdex.net/datas.json')
|
||||||
|
const logoExists = file[lang]?.[set.serie.id]?.[set.id]?.logo ? `https://assets.tcgdex.net/${lang}/${set.serie.id}/${set.id}/logo` : undefined
|
||||||
|
const symbolExists = file.univ?.[set.serie.id]?.[set.id]?.symbol ? `https://assets.tcgdex.net/univ/${set.serie.id}/${set.id}/symbol` : undefined
|
||||||
|
return [
|
||||||
|
logoExists,
|
||||||
|
symbolExists
|
||||||
|
]
|
||||||
|
} catch {
|
||||||
|
return [undefined, undefined]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setToSetSimple(set: Set, lang: SupportedLanguages): Promise<SetResume> {
|
||||||
|
const cards = await getCards(lang, set)
|
||||||
|
const pics = await getSetPictures(set, lang)
|
||||||
|
return {
|
||||||
|
cardCount: {
|
||||||
|
official: set.cardCount.official,
|
||||||
|
total: Math.max(set.cardCount.official, cards.length)
|
||||||
|
},
|
||||||
|
id: set.id,
|
||||||
|
logo: pics[0],
|
||||||
|
name: set.name[lang] as string,
|
||||||
|
symbol: pics[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setToSetSingle(set: Set, lang: SupportedLanguages): Promise<SetSingle> {
|
||||||
|
const cards = await getCards(lang, set)
|
||||||
|
const pics = await getSetPictures(set, lang)
|
||||||
|
return {
|
||||||
|
cardCount: {
|
||||||
|
firstEd: cards.reduce((count, card) => count + (card[1].variants?.firstEdition ? 1 : 0), 0),
|
||||||
|
holo: cards.reduce((count, card) => count + (card[1].variants?.holo ? 1 : 0), 0),
|
||||||
|
normal: cards.reduce((count, card) => count + (card[1].variants?.normal ? 1 : 0), 0),
|
||||||
|
official: set.cardCount.official,
|
||||||
|
reverse: cards.reduce((count, card) => count + (card[1].variants?.reverse ? 1 : 0), 0),
|
||||||
|
total: Math.max(set.cardCount.official, cards.length)
|
||||||
|
},
|
||||||
|
cards: await Promise.all(cards.map(([id, card]) => cardToCardSimple(id, card, lang))),
|
||||||
|
id: set.id,
|
||||||
|
legal: {
|
||||||
|
expanded: setIsLegal('expanded', set),
|
||||||
|
standard: setIsLegal('standard', set)
|
||||||
|
},
|
||||||
|
logo: pics[0],
|
||||||
|
name: set.name[lang] as string,
|
||||||
|
releaseDate: set.releaseDate,
|
||||||
|
serie: {
|
||||||
|
id: set.serie.id,
|
||||||
|
name: set.serie.name[lang] as string
|
||||||
|
},
|
||||||
|
symbol: pics[1],
|
||||||
|
tcgOnline: set.tcgOnline
|
||||||
|
}
|
||||||
|
}
|
31
server/compiler/utils/translationUtil.ts
Normal file
31
server/compiler/utils/translationUtil.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { SupportedLanguages } from '../../../interfaces'
|
||||||
|
import es from '../../../meta/translations/es.json'
|
||||||
|
import it from '../../../meta/translations/it.json'
|
||||||
|
import pt from '../../../meta/translations/pt.json'
|
||||||
|
import de from '../../../meta/translations/de.json'
|
||||||
|
import fr from '../../../meta/translations/fr.json'
|
||||||
|
|
||||||
|
type translatable = 'types' | 'rarity' | 'stage' | 'category' | 'suffix' | 'abilityType' | 'trainerType' | 'energyType'
|
||||||
|
|
||||||
|
const translations: Record<string, Record<translatable, Record<string, string>>> = {
|
||||||
|
es,
|
||||||
|
fr,
|
||||||
|
it,
|
||||||
|
pt,
|
||||||
|
de
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function translate(item: translatable, key: string | undefined, lang: SupportedLanguages): string | undefined {
|
||||||
|
if (!key) {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
// Temporary trenslations are in english while they are being worked on
|
||||||
|
if (lang === 'en' || !Object.keys(translations).includes(lang)) {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
const res = translations[lang]?.[item]?.[key]
|
||||||
|
if (!res) {
|
||||||
|
throw new Error(`Could not find translation for ${lang}.${item}.${key}`)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
59
server/compiler/utils/util.ts
Normal file
59
server/compiler/utils/util.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { Card, Set } from '../../../interfaces'
|
||||||
|
import glob from 'glob'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
import * as legals from '../../../meta/legals'
|
||||||
|
|
||||||
|
interface fileCacheInterface {
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DB_PATH = "../../"
|
||||||
|
|
||||||
|
const fileCache: fileCacheInterface = {}
|
||||||
|
|
||||||
|
export async function fetchRemoteFile<T = any>(url: string): Promise<T> {
|
||||||
|
if (!fileCache[url]) {
|
||||||
|
const resp = await fetch(url, {
|
||||||
|
timeout: 60 * 1000
|
||||||
|
})
|
||||||
|
fileCache[url] = resp.json()
|
||||||
|
}
|
||||||
|
return fileCache[url]
|
||||||
|
}
|
||||||
|
|
||||||
|
const globCache: Record<string, Array<string>> = {}
|
||||||
|
|
||||||
|
export async function smartGlob(query: string): Promise<Array<string>> {
|
||||||
|
if (!globCache[query]) {
|
||||||
|
globCache[query] = await new Promise((res) => {
|
||||||
|
glob(query, (_, matches) => res(matches))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return globCache[query]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cardIsLegal(type: 'standard' | 'expanded', card: Card, localId: string): boolean {
|
||||||
|
const legal = legals[type]
|
||||||
|
if (
|
||||||
|
legal.includes.series.includes(card.set.serie.id) ||
|
||||||
|
legal.includes.sets.includes(card.set.id) ||
|
||||||
|
card.regulationMark && legal.includes.regulationMark.includes(card.regulationMark)
|
||||||
|
) {
|
||||||
|
return !(
|
||||||
|
legal.excludes.sets.includes(card.set.id) ||
|
||||||
|
legal.excludes.cards.includes(`${card.set.id}-${localId}`)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setIsLegal(type: 'standard' | 'expanded', set: Set): boolean {
|
||||||
|
const legal = legals[type]
|
||||||
|
if (
|
||||||
|
legal.includes.series.includes(set.serie.id) ||
|
||||||
|
legal.includes.sets.includes(set.id)
|
||||||
|
) {
|
||||||
|
return !legal.excludes.sets.includes(set.id)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
2840
server/package-lock.json
generated
Normal file
2840
server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
server/package.json
Normal file
38
server/package.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "@tcgdex/server",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"compile": "ts-node compiler/index.ts",
|
||||||
|
"dev": "ts-node-dev -T src/index.ts",
|
||||||
|
"build": "tsc --project tsconfig.json",
|
||||||
|
"start": "node dist/index.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@dzeio/config": "^1.1.4",
|
||||||
|
"@dzeio/object-util": "^1.4.2",
|
||||||
|
"@tcgdex/sdk": "^2.4.5",
|
||||||
|
"apicache": "^1.6.3",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-graphql": "^0.12.0",
|
||||||
|
"graphql": "^15.7.0",
|
||||||
|
"js2xmlparser": "^4.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/apicache": "^1.6.0",
|
||||||
|
"@types/express": "^4.17.13",
|
||||||
|
"@types/glob": "^7.2.0",
|
||||||
|
"@types/node": "^16.11.6",
|
||||||
|
"@types/node-fetch": "^2.5.12",
|
||||||
|
"fs-extra": "^10.0.0",
|
||||||
|
"glob": "^7.2.0",
|
||||||
|
"node-fetch": "^2.6.6",
|
||||||
|
"ts-node": "^10.4.0",
|
||||||
|
"ts-node-dev": "^1.1.8",
|
||||||
|
"typescript": "^4.4.4"
|
||||||
|
}
|
||||||
|
}
|
BIN
server/public/favicon.ico
Normal file
BIN
server/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
0
server/public/v2/.gitkeep
Normal file
0
server/public/v2/.gitkeep
Normal file
112
server/src/V2/Components/Card.ts
Normal file
112
server/src/V2/Components/Card.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { objectLoop } from '@dzeio/object-util'
|
||||||
|
import { Card as SDKCard, CardResume, SupportedLanguages } from '@tcgdex/sdk'
|
||||||
|
import Set from './Set'
|
||||||
|
import { Pagination } from '../../interfaces'
|
||||||
|
|
||||||
|
type LocalCard = Omit<SDKCard, 'set'> & {set: () => Set}
|
||||||
|
|
||||||
|
interface variants {
|
||||||
|
normal?: boolean;
|
||||||
|
reverse?: boolean;
|
||||||
|
holo?: boolean;
|
||||||
|
firstEdition?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Card implements LocalCard {
|
||||||
|
illustrator?: string | undefined
|
||||||
|
rarity!: string
|
||||||
|
category!: string
|
||||||
|
variants?: variants | undefined
|
||||||
|
dexId?: number[] | undefined
|
||||||
|
hp?: number | undefined
|
||||||
|
types?: string[] | undefined
|
||||||
|
evolveFrom?: string | undefined
|
||||||
|
weight?: string | undefined
|
||||||
|
description?: string | undefined
|
||||||
|
level?: string | number | undefined
|
||||||
|
stage?: string | undefined
|
||||||
|
suffix?: string | undefined
|
||||||
|
item?: { name: string; effect: string } | undefined
|
||||||
|
abilities?: { type: string; name: string; effect: string }[] | undefined
|
||||||
|
attacks?: { cost?: string[] | undefined; name: string; effect?: string | undefined; damage?: string | number | undefined }[] | undefined
|
||||||
|
weaknesses?: { type: string; value?: string | undefined }[] | undefined
|
||||||
|
resistances?: { type: string; value?: string | undefined }[] | undefined
|
||||||
|
retreat?: number | undefined
|
||||||
|
effect?: string | undefined
|
||||||
|
trainerType?: string | undefined
|
||||||
|
energyType?: string | undefined
|
||||||
|
regulationMark?: string | undefined
|
||||||
|
legal!: { standard: boolean; expanded: boolean }
|
||||||
|
id!: string
|
||||||
|
localId!: string
|
||||||
|
name!: string
|
||||||
|
image?: string | undefined
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private lang: SupportedLanguages,
|
||||||
|
private card: SDKCard
|
||||||
|
) {
|
||||||
|
objectLoop(card, (it, key) => {
|
||||||
|
if (key === 'set') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this[key as 'id'] = it
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public set(): Set {
|
||||||
|
return Set.findOne(this.lang, {id: this.card.set.id}) as Set
|
||||||
|
}
|
||||||
|
|
||||||
|
public static find(lang: SupportedLanguages, params: Partial<Record<keyof SDKCard, any>> = {}, pagination?: Pagination) {
|
||||||
|
let list : Array<SDKCard> = (require(`../../../generated/${lang}/cards.json`) as Array<SDKCard>)
|
||||||
|
.filter((c) => objectLoop(params, (it, key) => {
|
||||||
|
if (typeof it === "string") {
|
||||||
|
return c[key as 'localId'].toLowerCase().includes(it.toLowerCase())
|
||||||
|
}
|
||||||
|
return c[key as 'localId'].includes(it)
|
||||||
|
}))
|
||||||
|
if (pagination) {
|
||||||
|
list = list
|
||||||
|
.splice(pagination.count * pagination.page - 1, pagination.count)
|
||||||
|
}
|
||||||
|
return list.map((it) => new Card(lang, it))
|
||||||
|
}
|
||||||
|
|
||||||
|
public static raw(lang: SupportedLanguages): Array<SDKCard> {
|
||||||
|
return require(`../../generated/${lang}/cards.json`)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static findOne(lang: SupportedLanguages, params: Partial<Record<keyof SDKCard, any>> = {}) {
|
||||||
|
const res = (require(`../../generated/${lang}/cards.json`) as Array<SDKCard>).find((c) => {
|
||||||
|
return objectLoop(params, (it, key) => {
|
||||||
|
if (key === 'set') {
|
||||||
|
return c['set'].id.includes(it) || c['set'].name.includes(it)
|
||||||
|
}
|
||||||
|
if (typeof it === "string") {
|
||||||
|
return c[key as 'localId'].toLowerCase().includes(it.toLowerCase())
|
||||||
|
}
|
||||||
|
return c[key as 'localId'].includes(it)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (!res) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return new Card(lang, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
public resume(): CardResume {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
localId: this.localId,
|
||||||
|
name: this.name,
|
||||||
|
image: this.image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public full(): SDKCard {
|
||||||
|
return this.card
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
server/src/V2/Components/Serie.ts
Normal file
66
server/src/V2/Components/Serie.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { objectLoop } from '@dzeio/object-util'
|
||||||
|
import { Serie as SDKSerie, SerieResume, SupportedLanguages } from '@tcgdex/sdk'
|
||||||
|
import Set from './Set'
|
||||||
|
import { Pagination } from '../../interfaces'
|
||||||
|
|
||||||
|
type LocalSerie = Omit<SDKSerie, 'sets'> & {sets: () => Array<Set>}
|
||||||
|
|
||||||
|
export default class Serie implements LocalSerie {
|
||||||
|
|
||||||
|
id!: string
|
||||||
|
name!: string
|
||||||
|
logo?: string | undefined
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private lang: SupportedLanguages,
|
||||||
|
private serie: SDKSerie
|
||||||
|
) {
|
||||||
|
objectLoop(serie, (it, key) => {
|
||||||
|
if (key === 'sets') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this[key as 'id'] = it
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public sets(): Array<Set> {
|
||||||
|
return this.serie.sets.map((s) => Set.findOne(this.lang, {id: s.id}) as Set)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static find(lang: SupportedLanguages, params: Partial<Record<keyof SDKSerie, any>> = {}, pagination?: Pagination) {
|
||||||
|
let list = (require(`../../generated/${lang}/series.json`) as Array<SDKSerie>)
|
||||||
|
.filter((c) => objectLoop(params, (it, key) => {
|
||||||
|
return c[key as 'id'].includes(it)
|
||||||
|
}))
|
||||||
|
if (pagination) {
|
||||||
|
list = list
|
||||||
|
.splice(pagination.count * pagination.page - 1, pagination.count)
|
||||||
|
}
|
||||||
|
return list.map((it) => new Serie(lang, it))
|
||||||
|
}
|
||||||
|
|
||||||
|
public static findOne(lang: SupportedLanguages, params: Partial<Record<keyof Serie, any>> = {}): Serie | undefined {
|
||||||
|
const res = (require(`../../../generated/${lang}/series.json`) as Array<SDKSerie>)
|
||||||
|
.find((c) => {
|
||||||
|
return objectLoop(params, (it, key) => {
|
||||||
|
return c[key as 'id'].includes(it)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (!res) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return new Serie(lang, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
public resume(): SerieResume {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
name: this.name,
|
||||||
|
logo: this.logo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public full(): SDKSerie {
|
||||||
|
return this.serie
|
||||||
|
}
|
||||||
|
}
|
89
server/src/V2/Components/Set.ts
Normal file
89
server/src/V2/Components/Set.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { objectLoop } from '@dzeio/object-util'
|
||||||
|
import { Set as SDKSet, SetResume, SupportedLanguages } from '@tcgdex/sdk'
|
||||||
|
import Card from './Card'
|
||||||
|
import { Pagination } from '../../interfaces'
|
||||||
|
import Serie from './Serie'
|
||||||
|
|
||||||
|
interface variants {
|
||||||
|
normal?: boolean;
|
||||||
|
reverse?: boolean;
|
||||||
|
holo?: boolean;
|
||||||
|
firstEdition?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalSet = {serie: () => Serie, cards: () => Array<Card>} & Omit<SDKSet, 'serie' | 'cards'>
|
||||||
|
|
||||||
|
export default class Set implements LocalSet {
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private lang: SupportedLanguages,
|
||||||
|
private set: SDKSet
|
||||||
|
) {
|
||||||
|
objectLoop(set, (it, key) => {
|
||||||
|
if (key === 'serie' || key === 'cards') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this[key as 'id'] = it
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tcgOnline?: string | undefined
|
||||||
|
variants?: variants | undefined
|
||||||
|
releaseDate!: string
|
||||||
|
legal!: { standard: boolean; expanded: boolean }
|
||||||
|
cardCount!: { total: number; official: number; normal: number; reverse: number; holo: number; firstEd?: number | undefined }
|
||||||
|
id!: string
|
||||||
|
name!: string
|
||||||
|
logo?: string | undefined
|
||||||
|
symbol?: string | undefined
|
||||||
|
|
||||||
|
public serie(): Serie {
|
||||||
|
return Serie.findOne(this.lang, {id: this.set.serie.id}) as Serie
|
||||||
|
}
|
||||||
|
|
||||||
|
public cards(): Array<Card> {
|
||||||
|
return this.set.cards.map((s) => Card.findOne(this.lang, {id: s.id}) as Card)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static find(lang: SupportedLanguages, params: Partial<Record<keyof SDKSet, any>> = {}, pagination?: Pagination) {
|
||||||
|
let list = (require(`../../../generated/${lang}/sets.json`) as Array<SDKSet>)
|
||||||
|
.filter((c) => objectLoop(params, (it, key) => {
|
||||||
|
return c[key as 'id'].includes(it)
|
||||||
|
}))
|
||||||
|
if (pagination) {
|
||||||
|
list = list
|
||||||
|
.splice(pagination.count * pagination.page - 1, pagination.count)
|
||||||
|
}
|
||||||
|
return list.map((it) => new Set(lang, it))
|
||||||
|
}
|
||||||
|
|
||||||
|
public static findOne(lang: SupportedLanguages, params: Partial<Record<keyof Set, any>> = {}) {
|
||||||
|
const res = (require(`../../../generated/${lang}/sets.json`) as Array<SDKSet>).find((c) => {
|
||||||
|
return objectLoop(params, (it, key) => {
|
||||||
|
return c[key as 'id'].includes(it)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (!res) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return new Set(lang, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
public resume(): SetResume {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
name: this.name,
|
||||||
|
logo: this.logo,
|
||||||
|
symbol: this.symbol,
|
||||||
|
cardCount: {
|
||||||
|
total: this.cardCount.total,
|
||||||
|
official: this.cardCount.official
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public full(): SDKSet {
|
||||||
|
return this.set
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
212
server/src/V2/endpoints/jsonEndpoints.ts
Normal file
212
server/src/V2/endpoints/jsonEndpoints.ts
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
import { objectKeys, objectSize } from '@dzeio/object-util'
|
||||||
|
import { Card as SDKCard } from '@tcgdex/sdk'
|
||||||
|
import Card from '../Components/Card'
|
||||||
|
import Serie from '../Components/Serie'
|
||||||
|
import Set from '../Components/Set'
|
||||||
|
import express from 'express'
|
||||||
|
import apicache from 'apicache'
|
||||||
|
import { betterSorter, checkLanguage, sendError, unique } from '../../util'
|
||||||
|
|
||||||
|
const server = express.Router()
|
||||||
|
|
||||||
|
const endpointToField: Record<string, keyof SDKCard> = {
|
||||||
|
"categories": 'category',
|
||||||
|
'energy-types': 'energyType',
|
||||||
|
"hp": 'hp',
|
||||||
|
'illustrators': 'illustrator',
|
||||||
|
"rarities": 'rarity',
|
||||||
|
'regulation-marks': 'regulationMark',
|
||||||
|
"retreats": 'retreat',
|
||||||
|
"stages": "stage",
|
||||||
|
"suffixes": "suffix",
|
||||||
|
"trainer-types": "trainerType",
|
||||||
|
|
||||||
|
// fields that need special care
|
||||||
|
'dex-ids': 'dexId',
|
||||||
|
"sets": "set",
|
||||||
|
"types": "types",
|
||||||
|
"variants": "variants",
|
||||||
|
}
|
||||||
|
|
||||||
|
// server
|
||||||
|
// .get('/cache/performance', (req, res) => {
|
||||||
|
// res.json(apicache.getPerformance())
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // add route to display cache index
|
||||||
|
// .get('/cache/index', (req, res) => {
|
||||||
|
// res.json(apicache.getIndex())
|
||||||
|
// })
|
||||||
|
|
||||||
|
server
|
||||||
|
.use(apicache.middleware('1 day', undefined, {}))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listing Endpoint
|
||||||
|
* ex: /v2/en/cards
|
||||||
|
*/
|
||||||
|
.get('/:lang/:endpoint', (req, res): void => {
|
||||||
|
let { lang, endpoint } = req.params
|
||||||
|
|
||||||
|
if (endpoint.endsWith('.json')) {
|
||||||
|
endpoint = endpoint.replace('.json', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkLanguage(lang)) {
|
||||||
|
return sendError('LanguageNotFoundError', res, lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: any
|
||||||
|
|
||||||
|
switch (endpoint) {
|
||||||
|
case 'cards':
|
||||||
|
// to be quicker we directly return the raw file
|
||||||
|
if (objectSize(req.query) === 0) {
|
||||||
|
result = Card.raw(lang)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
result = Card
|
||||||
|
.find(lang, req.query)
|
||||||
|
.map((c) => c.resume())
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'sets':
|
||||||
|
result = Set
|
||||||
|
.find(lang, req.query)
|
||||||
|
.map((c) => c.resume())
|
||||||
|
break
|
||||||
|
case 'series':
|
||||||
|
result = Serie
|
||||||
|
.find(lang, req.query)
|
||||||
|
.map((c) => c.resume())
|
||||||
|
break
|
||||||
|
case 'categories':
|
||||||
|
case "energy-types":
|
||||||
|
case "hp":
|
||||||
|
case "illustrators":
|
||||||
|
case "rarities":
|
||||||
|
case "regulation-marks":
|
||||||
|
case "retreats":
|
||||||
|
case "series":
|
||||||
|
case "stages":
|
||||||
|
case "suffixes":
|
||||||
|
case "trainer-types":
|
||||||
|
result = unique(
|
||||||
|
Card.raw(lang)
|
||||||
|
.map((c) => c[endpointToField[endpoint]] as string)
|
||||||
|
.filter((c) => c)
|
||||||
|
).sort(betterSorter)
|
||||||
|
break
|
||||||
|
case "types":
|
||||||
|
case "dex-ids":
|
||||||
|
result = unique(
|
||||||
|
Card.raw(lang)
|
||||||
|
.map((c) => c[endpointToField[endpoint]] as Array<string>)
|
||||||
|
.filter((c) => c)
|
||||||
|
.reduce((p, c) => [...p, ...c], [] as Array<string>)
|
||||||
|
).sort(betterSorter)
|
||||||
|
break
|
||||||
|
case "variants":
|
||||||
|
result = unique(
|
||||||
|
Card.raw(lang)
|
||||||
|
.map((c) => objectKeys(c.variants ?? {}) as Array<string>)
|
||||||
|
.filter((c) => c)
|
||||||
|
.reduce((p, c) => [...p, ...c], [] as Array<string>)
|
||||||
|
).sort()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
sendError('EndpointNotFoundError', res, endpoint)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
sendError('NotFoundError', res)
|
||||||
|
}
|
||||||
|
res.json(result)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listing Endpoint
|
||||||
|
* ex: /v2/en/cards/base1-1
|
||||||
|
*/
|
||||||
|
.get('/:lang/:endpoint/:id', (req, res) => {
|
||||||
|
let { id, lang, endpoint } = req.params
|
||||||
|
|
||||||
|
if (id.endsWith('.json')) {
|
||||||
|
id = id.replace('.json', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
id = id.toLowerCase()
|
||||||
|
|
||||||
|
if (!checkLanguage(lang)) {
|
||||||
|
return sendError('LanguageNotFoundError', res, lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: any | undefined
|
||||||
|
switch (endpoint) {
|
||||||
|
case 'cards':
|
||||||
|
result = Card.findOne(lang, {id})?.full()
|
||||||
|
if (!result) {
|
||||||
|
result = Card.findOne(lang, {name: id})?.full()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'sets':
|
||||||
|
result = Set.findOne(lang, {id})?.full()
|
||||||
|
if (!result) {
|
||||||
|
result = Set.findOne(lang, {name: id})?.full()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'series':
|
||||||
|
result = Serie.findOne(lang, {id})?.full()
|
||||||
|
if (!result) {
|
||||||
|
result = Serie.findOne(lang, {name: id})?.full()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
result = Card.find(lang, {[endpointToField[endpoint]]: id})
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
return res.status(404).send({error: "Endpoint or id not found"})
|
||||||
|
}
|
||||||
|
return res.send(result)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sub id Endpoint (for the set endpoint only currently)
|
||||||
|
* ex: /v2/en/sets/base1/1
|
||||||
|
*/
|
||||||
|
.get('/:lang/:endpoint/:id/:subid', (req, res) => {
|
||||||
|
let { id, lang, endpoint, subid } = req.params
|
||||||
|
|
||||||
|
if (subid.endsWith('.json')) {
|
||||||
|
subid = subid.replace('.json', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
id = id.toLowerCase()
|
||||||
|
subid = subid.toLowerCase()
|
||||||
|
|
||||||
|
if (!checkLanguage(lang)) {
|
||||||
|
return sendError('LanguageNotFoundError', res, lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: any | undefined
|
||||||
|
|
||||||
|
switch (endpoint) {
|
||||||
|
case 'sets':
|
||||||
|
result = Card
|
||||||
|
.findOne(lang, {localId: subid, set: id})?.full()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
return sendError('NotFoundError', res)
|
||||||
|
}
|
||||||
|
return res.send(result)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default server
|
22
server/src/V2/graphql/index.ts
Normal file
22
server/src/V2/graphql/index.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import { graphqlHTTP } from 'express-graphql'
|
||||||
|
import { buildSchema } from 'graphql'
|
||||||
|
import resolver from './resolver'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
// Init Express Router
|
||||||
|
const router = express.Router()
|
||||||
|
/**
|
||||||
|
* Drawbacks
|
||||||
|
* Attack.damage is a string instead of possibly being a number or a string
|
||||||
|
*/
|
||||||
|
const schema = buildSchema(fs.readFileSync('./public/v2/graphql.gql').toString())
|
||||||
|
|
||||||
|
// Add graphql to the route
|
||||||
|
router.use(graphqlHTTP({
|
||||||
|
schema,
|
||||||
|
rootValue: resolver,
|
||||||
|
graphiql: true
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default router
|
49
server/src/V2/graphql/resolver.ts
Normal file
49
server/src/V2/graphql/resolver.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import Card from '../Components/Card'
|
||||||
|
import { Options } from '../../interfaces'
|
||||||
|
import Serie from '../Components/Serie'
|
||||||
|
import Set from '../Components/Set'
|
||||||
|
import { SupportedLanguages } from '@tcgdex/sdk'
|
||||||
|
import { checkLanguage } from '../../util'
|
||||||
|
|
||||||
|
const middleware = <Q extends Record<string, any> = Record<string, any>>(fn: (lang: SupportedLanguages, query: Q) => any) => (
|
||||||
|
data: Q,
|
||||||
|
_: any,
|
||||||
|
e: any
|
||||||
|
) => {
|
||||||
|
let lang = e?.fieldNodes?.[0]?.directives?.[0]?.arguments?.[0]?.value?.value
|
||||||
|
if (!lang) {
|
||||||
|
lang = 'en'
|
||||||
|
}
|
||||||
|
if (!checkLanguage(lang)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return fn(lang, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Cards Endpoints
|
||||||
|
cards: middleware<Options<keyof Card['card']>>((lang, query) => {
|
||||||
|
return Card.find(lang, query.filters ?? {}, query.pagination)
|
||||||
|
}),
|
||||||
|
card: middleware<{set?: string, id: string}>((lang, query) => {
|
||||||
|
const toSearch = query.set ? 'localId' : 'id'
|
||||||
|
return Card.findOne(lang, {[toSearch]: query.id})
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Set Endpoints
|
||||||
|
set: middleware<{id: string}>((lang, query) => {
|
||||||
|
return Set.findOne(lang, {id: query.id}) ?? Set.findOne(lang, {name: query.id})
|
||||||
|
}),
|
||||||
|
sets: middleware<Options<keyof Set['set']>>((lang, query) => {
|
||||||
|
return Set.find(lang, query.filters ?? {}, query.pagination)
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Serie Endpoints
|
||||||
|
serie: middleware<{id: string}>((lang, query) => {
|
||||||
|
return Serie.findOne(lang, {id: query.id}) ?? Serie.findOne(lang, {name: query.id})
|
||||||
|
}),
|
||||||
|
series: middleware<Options<keyof Serie['serie']>>((lang, query) => {
|
||||||
|
return Serie.find(lang, query.filters ?? {}, query.pagination)
|
||||||
|
}),
|
||||||
|
|
||||||
|
};
|
35
server/src/index.ts
Normal file
35
server/src/index.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import graphql from './V2/graphql'
|
||||||
|
import jsonEndpoints from './V2/endpoints/jsonEndpoints'
|
||||||
|
|
||||||
|
// Current API version
|
||||||
|
const VERSION = 2
|
||||||
|
|
||||||
|
// Init Express server
|
||||||
|
const server = express()
|
||||||
|
|
||||||
|
// Set global headers
|
||||||
|
server.use((_, res, next) => {
|
||||||
|
res
|
||||||
|
.setHeader('Access-Control-Allow-Origin', '*')
|
||||||
|
.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS')
|
||||||
|
.setHeader('Access-Control-Allow-Headers', 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range')
|
||||||
|
.setHeader('Access-Control-Expose-Headers', 'Content-Length,Content-Range')
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
server.get('/', (_, res) => {
|
||||||
|
res.redirect('https://www.tcgdex.net/docs?ref=api.tcgdex.net')
|
||||||
|
})
|
||||||
|
|
||||||
|
server.use(express.static('./public'))
|
||||||
|
|
||||||
|
// Setup GraphQL
|
||||||
|
server.use(`/v${VERSION}/graphql`, graphql)
|
||||||
|
|
||||||
|
// Setup JSON endpoints
|
||||||
|
server.use(`/v${VERSION}`, jsonEndpoints)
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
server.listen(3000)
|
||||||
|
console.log(`🚀 Server ready at localhost:3000`);
|
11
server/src/interfaces.d.ts
vendored
Normal file
11
server/src/interfaces.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SupportedLanguages } from '@tcgdex/sdk'
|
||||||
|
|
||||||
|
export interface Pagination {
|
||||||
|
page: number
|
||||||
|
count: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Options<T> {
|
||||||
|
pagination?: Pagination
|
||||||
|
filters?: Partial<Record<T, any>>
|
||||||
|
}
|
57
server/src/util.ts
Normal file
57
server/src/util.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { SupportedLanguages } from '@tcgdex/sdk'
|
||||||
|
import { Response } from 'express'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
export function checkLanguage(str: string): str is SupportedLanguages {
|
||||||
|
return ['en', 'fr', 'es', 'it', 'pt', 'de'].includes(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unique(arr: Array<string>): Array<string> {
|
||||||
|
return arr.reduce((p, c) => p.includes(c) ? p : [...p, c], [] as Array<string>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendError(error: 'UnknownError' | 'NotFoundError' | 'LanguageNotFoundError' | 'EndpointNotFoundError', res: Response, v?: any) {
|
||||||
|
let message = ''
|
||||||
|
let status = 404
|
||||||
|
switch (error) {
|
||||||
|
case 'LanguageNotFoundError':
|
||||||
|
message = `Language not found (${v})`
|
||||||
|
break
|
||||||
|
case 'EndpointNotFoundError':
|
||||||
|
message = `Endpoint not found (${v})`
|
||||||
|
break
|
||||||
|
case 'NotFoundError':
|
||||||
|
message = 'The resource you are searching does not exists'
|
||||||
|
break
|
||||||
|
case 'UnknownError':
|
||||||
|
default:
|
||||||
|
message = `an unknown error occured (${v})`
|
||||||
|
status = 500
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res.status(status).json({
|
||||||
|
message
|
||||||
|
}).end()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function betterSorter(a: string, b: string) {
|
||||||
|
const ra = parseInt(a, 10)
|
||||||
|
const rb = parseInt(b, 10)
|
||||||
|
if (!isNaN(ra) && !isNaN(rb)) {
|
||||||
|
return ra - rb
|
||||||
|
}
|
||||||
|
return a >= b ? 1 : -1
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tree(path: string, padding = 0) {
|
||||||
|
const folder = fs.readdirSync(path)
|
||||||
|
for (const file of folder) {
|
||||||
|
const filePath = path + '/' + file
|
||||||
|
console.log(filePath.padStart(padding, '-'))
|
||||||
|
try {
|
||||||
|
fs.lstatSync(filePath).isDirectory()
|
||||||
|
tree(filePath)
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
7
server/tsconfig.json
Normal file
7
server/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "./node_modules/@dzeio/config/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
"include": ["data", "interfaces.d.ts", "meta"],
|
"include": ["data", "interfaces.d.ts"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user