Showing preview only (1,331K chars total). Download the full file or copy to clipboard to get everything.
Repository: 5e-bits/5e-srd-api
Branch: main
Commit: 03745614bfd9
Files: 567
Total size: 1.2 MB
Directory structure:
gitextract_5nuvmdzy/
├── .dockerignore
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── actions/
│ │ └── validate-pr-title/
│ │ └── action.yml
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.yml
│ ├── codeql-analysis.yml
│ ├── lint-pr.yml
│ ├── release-please.yml
│ └── release.yml
├── .gitignore
├── .nvmrc
├── .prettierrc
├── .redocly.yaml
├── CHANGELOG.md
├── Dockerfile
├── LICENSE.md
├── README.md
├── app.json
├── docker-compose.yml
├── eslint.config.js
├── heroku.yml
├── nodemon.json
├── openapi-to-postman.json
├── package.json
├── release-please-config.json
├── src/
│ ├── controllers/
│ │ ├── api/
│ │ │ ├── 2014/
│ │ │ │ ├── abilityScoreController.ts
│ │ │ │ ├── alignmentController.ts
│ │ │ │ ├── backgroundController.ts
│ │ │ │ ├── classController.ts
│ │ │ │ ├── conditionController.ts
│ │ │ │ ├── damageTypeController.ts
│ │ │ │ ├── equipmentCategoryController.ts
│ │ │ │ ├── equipmentController.ts
│ │ │ │ ├── featController.ts
│ │ │ │ ├── featureController.ts
│ │ │ │ ├── languageController.ts
│ │ │ │ ├── magicItemController.ts
│ │ │ │ ├── magicSchoolController.ts
│ │ │ │ ├── monsterController.ts
│ │ │ │ ├── proficiencyController.ts
│ │ │ │ ├── raceController.ts
│ │ │ │ ├── ruleController.ts
│ │ │ │ ├── ruleSectionController.ts
│ │ │ │ ├── skillController.ts
│ │ │ │ ├── spellController.ts
│ │ │ │ ├── subclassController.ts
│ │ │ │ ├── subraceController.ts
│ │ │ │ ├── traitController.ts
│ │ │ │ └── weaponPropertyController.ts
│ │ │ ├── 2024/
│ │ │ │ ├── abilityScoreController.ts
│ │ │ │ ├── alignmentController.ts
│ │ │ │ ├── backgroundController.ts
│ │ │ │ ├── conditionController.ts
│ │ │ │ ├── damageTypeController.ts
│ │ │ │ ├── equipmentCategoryController.ts
│ │ │ │ ├── equipmentController.ts
│ │ │ │ ├── featController.ts
│ │ │ │ ├── languageController.ts
│ │ │ │ ├── magicItemController.ts
│ │ │ │ ├── magicSchoolController.ts
│ │ │ │ ├── proficiencyController.ts
│ │ │ │ ├── skillController.ts
│ │ │ │ ├── speciesController.ts
│ │ │ │ ├── subclassController.ts
│ │ │ │ ├── subspeciesController.ts
│ │ │ │ ├── traitController.ts
│ │ │ │ ├── weaponMasteryPropertyController.ts
│ │ │ │ └── weaponPropertyController.ts
│ │ │ ├── imageController.ts
│ │ │ ├── v2014Controller.ts
│ │ │ └── v2024Controller.ts
│ │ ├── apiController.ts
│ │ ├── docsController.ts
│ │ └── simpleController.ts
│ ├── css/
│ │ └── custom.css
│ ├── graphql/
│ │ ├── 2014/
│ │ │ ├── common/
│ │ │ │ ├── choiceTypes.ts
│ │ │ │ ├── equipmentTypes.ts
│ │ │ │ ├── interfaces.ts
│ │ │ │ └── unions.ts
│ │ │ ├── resolvers/
│ │ │ │ ├── abilityScore/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── alignment/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── background/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── class/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── condition/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── damageType/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── equipment/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── equipmentCategory/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── feat/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── feature/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── language/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── level/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── magicItem/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── magicSchool/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── monster/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── proficiency/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── race/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── rule/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── ruleSection/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── skill/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── spell/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── subclass/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── subrace/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── trait/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ └── weaponProperty/
│ │ │ │ ├── args.ts
│ │ │ │ └── resolver.ts
│ │ │ ├── types/
│ │ │ │ ├── backgroundTypes.ts
│ │ │ │ ├── featureTypes.ts
│ │ │ │ ├── monsterTypes.ts
│ │ │ │ ├── proficiencyTypes.ts
│ │ │ │ ├── startingEquipment/
│ │ │ │ │ ├── choice.ts
│ │ │ │ │ ├── common.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── optionSet.ts
│ │ │ │ ├── subclassTypes.ts
│ │ │ │ └── traitTypes.ts
│ │ │ └── utils/
│ │ │ ├── helpers.ts
│ │ │ ├── resolvers.ts
│ │ │ └── startingEquipmentResolver.ts
│ │ ├── 2024/
│ │ │ ├── common/
│ │ │ │ ├── choiceTypes.ts
│ │ │ │ ├── equipmentTypes.ts
│ │ │ │ ├── interfaces.ts
│ │ │ │ ├── resolver.ts
│ │ │ │ └── unions.ts
│ │ │ ├── resolvers/
│ │ │ │ ├── abilityScore/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── alignment/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── background/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── condition/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── damageType/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── equipment/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── equipmentCategory/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── feat/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── language/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── magicItem/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── magicSchool/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── proficiency/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── skill/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── species/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── subclass/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── subspecies/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── trait/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ ├── weaponMasteryProperty/
│ │ │ │ │ ├── args.ts
│ │ │ │ │ └── resolver.ts
│ │ │ │ └── weaponProperty/
│ │ │ │ ├── args.ts
│ │ │ │ └── resolver.ts
│ │ │ ├── types/
│ │ │ │ └── backgroundEquipment/
│ │ │ │ ├── choice.ts
│ │ │ │ ├── common.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── optionSet.ts
│ │ │ └── utils/
│ │ │ ├── backgroundEquipmentResolver.ts
│ │ │ ├── choiceResolvers.ts
│ │ │ └── resolvers.ts
│ │ ├── common/
│ │ │ ├── args.ts
│ │ │ ├── choiceTypes.ts
│ │ │ ├── enums.ts
│ │ │ ├── inputs.ts
│ │ │ └── types.ts
│ │ └── utils/
│ │ └── resolvers.ts
│ ├── middleware/
│ │ ├── apolloServer.ts
│ │ ├── bugsnag.ts
│ │ └── errorHandler.ts
│ ├── models/
│ │ ├── 2014/
│ │ │ ├── abilityScore.ts
│ │ │ ├── alignment.ts
│ │ │ ├── background.ts
│ │ │ ├── class.ts
│ │ │ ├── collection.ts
│ │ │ ├── condition.ts
│ │ │ ├── damageType.ts
│ │ │ ├── equipment.ts
│ │ │ ├── equipmentCategory.ts
│ │ │ ├── feat.ts
│ │ │ ├── feature.ts
│ │ │ ├── language.ts
│ │ │ ├── level.ts
│ │ │ ├── magicItem.ts
│ │ │ ├── magicSchool.ts
│ │ │ ├── monster.ts
│ │ │ ├── proficiency.ts
│ │ │ ├── race.ts
│ │ │ ├── rule.ts
│ │ │ ├── ruleSection.ts
│ │ │ ├── skill.ts
│ │ │ ├── spell.ts
│ │ │ ├── subclass.ts
│ │ │ ├── subrace.ts
│ │ │ ├── trait.ts
│ │ │ └── weaponProperty.ts
│ │ ├── 2024/
│ │ │ ├── abilityScore.ts
│ │ │ ├── alignment.ts
│ │ │ ├── background.ts
│ │ │ ├── collection.ts
│ │ │ ├── condition.ts
│ │ │ ├── damageType.ts
│ │ │ ├── equipment.ts
│ │ │ ├── equipmentCategory.ts
│ │ │ ├── feat.ts
│ │ │ ├── language.ts
│ │ │ ├── magicItem.ts
│ │ │ ├── magicSchool.ts
│ │ │ ├── proficiency.ts
│ │ │ ├── skill.ts
│ │ │ ├── species.ts
│ │ │ ├── subclass.ts
│ │ │ ├── subspecies.ts
│ │ │ ├── trait.ts
│ │ │ ├── weaponMasteryProperty.ts
│ │ │ └── weaponProperty.ts
│ │ └── common/
│ │ ├── apiReference.ts
│ │ ├── areaOfEffect.ts
│ │ ├── choice.ts
│ │ ├── damage.ts
│ │ └── difficultyClass.ts
│ ├── public/
│ │ └── index.html
│ ├── routes/
│ │ ├── api/
│ │ │ ├── 2014/
│ │ │ │ ├── abilityScores.ts
│ │ │ │ ├── alignments.ts
│ │ │ │ ├── backgrounds.ts
│ │ │ │ ├── classes.ts
│ │ │ │ ├── conditions.ts
│ │ │ │ ├── damageTypes.ts
│ │ │ │ ├── equipment.ts
│ │ │ │ ├── equipmentCategories.ts
│ │ │ │ ├── feats.ts
│ │ │ │ ├── features.ts
│ │ │ │ ├── images.ts
│ │ │ │ ├── languages.ts
│ │ │ │ ├── magicItems.ts
│ │ │ │ ├── magicSchools.ts
│ │ │ │ ├── monsters.ts
│ │ │ │ ├── proficiencies.ts
│ │ │ │ ├── races.ts
│ │ │ │ ├── ruleSections.ts
│ │ │ │ ├── rules.ts
│ │ │ │ ├── skills.ts
│ │ │ │ ├── spells.ts
│ │ │ │ ├── subclasses.ts
│ │ │ │ ├── subraces.ts
│ │ │ │ ├── traits.ts
│ │ │ │ └── weaponProperties.ts
│ │ │ ├── 2014.ts
│ │ │ ├── 2024/
│ │ │ │ ├── abilityScores.ts
│ │ │ │ ├── alignments.ts
│ │ │ │ ├── backgrounds.ts
│ │ │ │ ├── conditions.ts
│ │ │ │ ├── damageTypes.ts
│ │ │ │ ├── equipment.ts
│ │ │ │ ├── equipmentCategories.ts
│ │ │ │ ├── feats.ts
│ │ │ │ ├── languages.ts
│ │ │ │ ├── magicItems.ts
│ │ │ │ ├── magicSchools.ts
│ │ │ │ ├── proficiencies.ts
│ │ │ │ ├── skills.ts
│ │ │ │ ├── species.ts
│ │ │ │ ├── subclasses.ts
│ │ │ │ ├── subspecies.ts
│ │ │ │ ├── traits.ts
│ │ │ │ ├── weaponMasteryProperties.ts
│ │ │ │ └── weaponProperty.ts
│ │ │ ├── 2024.ts
│ │ │ └── images.ts
│ │ └── api.ts
│ ├── schemas/
│ │ └── schemas.ts
│ ├── server.ts
│ ├── start.ts
│ ├── swagger/
│ │ ├── README.md
│ │ ├── parameters/
│ │ │ ├── 2014/
│ │ │ │ ├── combined.yml
│ │ │ │ ├── path/
│ │ │ │ │ ├── ability-scores.yml
│ │ │ │ │ ├── alignments.yml
│ │ │ │ │ ├── backgrounds.yml
│ │ │ │ │ ├── classes.yml
│ │ │ │ │ ├── common.yml
│ │ │ │ │ ├── conditions.yml
│ │ │ │ │ ├── damage-types.yml
│ │ │ │ │ ├── equipment.yml
│ │ │ │ │ ├── features.yml
│ │ │ │ │ ├── languages.yml
│ │ │ │ │ ├── magic-schools.yml
│ │ │ │ │ ├── monsters.yml
│ │ │ │ │ ├── proficiencies.yml
│ │ │ │ │ ├── races.yml
│ │ │ │ │ ├── rule-sections.yml
│ │ │ │ │ ├── rules.yml
│ │ │ │ │ ├── skills.yml
│ │ │ │ │ ├── spells.yml
│ │ │ │ │ ├── subclasses.yml
│ │ │ │ │ ├── subraces.yml
│ │ │ │ │ ├── traits.yml
│ │ │ │ │ └── weapon-properties.yml
│ │ │ │ └── query/
│ │ │ │ ├── classes.yml
│ │ │ │ ├── monsters.yml
│ │ │ │ └── spells.yml
│ │ │ └── 2024/
│ │ │ └── .keepme
│ │ ├── paths/
│ │ │ ├── 2014/
│ │ │ │ ├── ability-scores.yml
│ │ │ │ ├── alignments.yml
│ │ │ │ ├── backgrounds.yml
│ │ │ │ ├── classes.yml
│ │ │ │ ├── combined.yml
│ │ │ │ ├── common.yml
│ │ │ │ ├── conditions.yml
│ │ │ │ ├── damage-types.yml
│ │ │ │ ├── equipment-categories.yml
│ │ │ │ ├── equipment.yml
│ │ │ │ ├── feats.yml
│ │ │ │ ├── features.yml
│ │ │ │ ├── languages.yml
│ │ │ │ ├── magic-items.yml
│ │ │ │ ├── magic-schools.yml
│ │ │ │ ├── monsters.yml
│ │ │ │ ├── proficiencies.yml
│ │ │ │ ├── races.yml
│ │ │ │ ├── rule-sections.yml
│ │ │ │ ├── rules.yml
│ │ │ │ ├── skills.yml
│ │ │ │ ├── spells.yml
│ │ │ │ ├── subclasses.yml
│ │ │ │ ├── subraces.yml
│ │ │ │ ├── traits.yml
│ │ │ │ └── weapon-properties.yml
│ │ │ └── 2024/
│ │ │ └── .keepme
│ │ ├── schemas/
│ │ │ ├── 2014/
│ │ │ │ ├── ability-scores.yml
│ │ │ │ ├── alignments.yml
│ │ │ │ ├── armor.yml
│ │ │ │ ├── backgrounds.yml
│ │ │ │ ├── classes.yml
│ │ │ │ ├── combined.yml
│ │ │ │ ├── common.yml
│ │ │ │ ├── equipment.yml
│ │ │ │ ├── feats.yml
│ │ │ │ ├── features.yml
│ │ │ │ ├── game-mechanics.yml
│ │ │ │ ├── language.yml
│ │ │ │ ├── monsters-common.yml
│ │ │ │ ├── monsters.yml
│ │ │ │ ├── multiclassing.yml
│ │ │ │ ├── proficiencies.yml
│ │ │ │ ├── races.yml
│ │ │ │ ├── rules.yml
│ │ │ │ ├── skills.yml
│ │ │ │ ├── spell.yml
│ │ │ │ ├── spellcasting.yml
│ │ │ │ ├── subclass.yml
│ │ │ │ ├── subrace.yml
│ │ │ │ ├── traits.yml
│ │ │ │ └── weapon.yml
│ │ │ └── 2024/
│ │ │ └── .keepme
│ │ └── swagger.yml
│ ├── tests/
│ │ ├── controllers/
│ │ │ ├── api/
│ │ │ │ ├── 2014/
│ │ │ │ │ ├── abilityScoreController.test.ts
│ │ │ │ │ ├── alignmentController.test.ts
│ │ │ │ │ ├── backgroundController.test.ts
│ │ │ │ │ ├── classController.test.ts
│ │ │ │ │ ├── conditionController.test.ts
│ │ │ │ │ ├── damageTypeController.test.ts
│ │ │ │ │ ├── equipmentCategoryController.test.ts
│ │ │ │ │ ├── equipmentController.test.ts
│ │ │ │ │ ├── featController.test.ts
│ │ │ │ │ ├── featureController.test.ts
│ │ │ │ │ ├── languageController.test.ts
│ │ │ │ │ ├── magicItemController.test.ts
│ │ │ │ │ ├── magicSchoolController.test.ts
│ │ │ │ │ ├── monsterController.test.ts
│ │ │ │ │ ├── proficiencyController.test.ts
│ │ │ │ │ ├── raceController.test.ts
│ │ │ │ │ ├── ruleSectionController.test.ts
│ │ │ │ │ ├── rulesController.test.ts
│ │ │ │ │ ├── skillController.test.ts
│ │ │ │ │ ├── spellController.test.ts
│ │ │ │ │ ├── subclassController.test.ts
│ │ │ │ │ ├── subraceController.test.ts
│ │ │ │ │ ├── traitController.test.ts
│ │ │ │ │ └── weaponPropertyController.test.ts
│ │ │ │ ├── 2024/
│ │ │ │ │ ├── BackgroundController.test.ts
│ │ │ │ │ ├── FeatController.test.ts
│ │ │ │ │ ├── MagicItemController.test.ts
│ │ │ │ │ ├── ProficiencyController.test.ts
│ │ │ │ │ ├── SubclassController.test.ts
│ │ │ │ │ ├── abilityScoreController.test.ts
│ │ │ │ │ ├── alignmentController.test.ts
│ │ │ │ │ ├── conditionController.test.ts
│ │ │ │ │ ├── damageTypeController.test.ts
│ │ │ │ │ ├── equipmentCategoryController.test.ts
│ │ │ │ │ ├── equipmentController.test.ts
│ │ │ │ │ ├── languageController.test.ts
│ │ │ │ │ ├── magicSchoolController.test.ts
│ │ │ │ │ ├── skillController.test.ts
│ │ │ │ │ ├── speciesController.test.ts
│ │ │ │ │ ├── subspeciesController.test.ts
│ │ │ │ │ ├── traitController.test.ts
│ │ │ │ │ ├── weaponMasteryPropertyController.test.ts
│ │ │ │ │ └── weaponPropertyController.test.ts
│ │ │ │ ├── v2014Controller.test.ts
│ │ │ │ └── v2024Controller.test.ts
│ │ │ ├── apiController.test.ts
│ │ │ ├── globalSetup.ts
│ │ │ └── simpleController.test.ts
│ │ ├── factories/
│ │ │ ├── 2014/
│ │ │ │ ├── abilityScore.factory.ts
│ │ │ │ ├── alignment.factory.ts
│ │ │ │ ├── background.factory.ts
│ │ │ │ ├── class.factory.ts
│ │ │ │ ├── collection.factory.ts
│ │ │ │ ├── common.factory.ts
│ │ │ │ ├── condition.factory.ts
│ │ │ │ ├── damageType.factory.ts
│ │ │ │ ├── equipment.factory.ts
│ │ │ │ ├── equipmentCategory.factory.ts
│ │ │ │ ├── feat.factory.ts
│ │ │ │ ├── feature.factory.ts
│ │ │ │ ├── language.factory.ts
│ │ │ │ ├── level.factory.ts
│ │ │ │ ├── magicItem.factory.ts
│ │ │ │ ├── magicSchool.factory.ts
│ │ │ │ ├── monster.factory.ts
│ │ │ │ ├── proficiency.factory.ts
│ │ │ │ ├── race.factory.ts
│ │ │ │ ├── rule.factory.ts
│ │ │ │ ├── ruleSection.factory.ts
│ │ │ │ ├── skill.factory.ts
│ │ │ │ ├── spell.factory.ts
│ │ │ │ ├── subclass.factory.ts
│ │ │ │ ├── subrace.factory.ts
│ │ │ │ ├── trait.factory.ts
│ │ │ │ └── weaponProperty.factory.ts
│ │ │ └── 2024/
│ │ │ ├── abilityScore.factory.ts
│ │ │ ├── alignment.factory.ts
│ │ │ ├── background.factory.ts
│ │ │ ├── collection.factory.ts
│ │ │ ├── common.factory.ts
│ │ │ ├── condition.factory.ts
│ │ │ ├── damageType.factory.ts
│ │ │ ├── equipment.factory.ts
│ │ │ ├── equipmentCategory.factory.ts
│ │ │ ├── feat.factory.ts
│ │ │ ├── language.factory.ts
│ │ │ ├── magicItem.factory.ts
│ │ │ ├── magicSchool.factory.ts
│ │ │ ├── proficiency.factory.ts
│ │ │ ├── skill.factory.ts
│ │ │ ├── species.factory.ts
│ │ │ ├── subclass.factory.ts
│ │ │ ├── subspecies.factory.ts
│ │ │ ├── trait.factory.ts
│ │ │ ├── weaponMasteryProperty.factory.ts
│ │ │ └── weaponProperty.factory.ts
│ │ ├── integration/
│ │ │ ├── api/
│ │ │ │ ├── 2014/
│ │ │ │ │ ├── abilityScores.itest.ts
│ │ │ │ │ ├── classes.itest.ts
│ │ │ │ │ ├── conditions.itest.ts
│ │ │ │ │ ├── damageTypes.itest.ts
│ │ │ │ │ ├── equipment.itest.ts
│ │ │ │ │ ├── equipmentCategories.itest.ts
│ │ │ │ │ ├── feats.itest.ts
│ │ │ │ │ ├── features.itest.ts
│ │ │ │ │ ├── languages.itest.ts
│ │ │ │ │ ├── magicItems.itest.ts
│ │ │ │ │ ├── magicSchools.itest.ts
│ │ │ │ │ ├── monsters.itest.ts
│ │ │ │ │ ├── proficiencies.itest.ts
│ │ │ │ │ ├── races.itest.ts
│ │ │ │ │ ├── ruleSections.itest.ts
│ │ │ │ │ ├── rules.itest.ts
│ │ │ │ │ ├── skills.itest.ts
│ │ │ │ │ ├── spells.itest.ts
│ │ │ │ │ ├── subclasses.itest.ts
│ │ │ │ │ ├── subraces.itest.ts
│ │ │ │ │ ├── traits.itest.ts
│ │ │ │ │ └── weaponProperties.itest.ts
│ │ │ │ ├── 2024/
│ │ │ │ │ ├── abilityScores.itest.ts
│ │ │ │ │ ├── alignment.itest.ts
│ │ │ │ │ ├── background.itest.ts
│ │ │ │ │ ├── condition.itest.ts
│ │ │ │ │ ├── damageType.itest.ts
│ │ │ │ │ ├── equipment.itest.ts
│ │ │ │ │ ├── equipmentCategories.itest.ts
│ │ │ │ │ ├── feat.itest.ts
│ │ │ │ │ ├── language.itest.ts
│ │ │ │ │ ├── magicItem.itest.ts
│ │ │ │ │ ├── magicSchool.itest.ts
│ │ │ │ │ ├── proficiency.itest.ts
│ │ │ │ │ ├── skills.itest.ts
│ │ │ │ │ ├── species.itest.ts
│ │ │ │ │ ├── subclass.itest.ts
│ │ │ │ │ ├── subspecies.itest.ts
│ │ │ │ │ ├── traits.itest.ts
│ │ │ │ │ ├── weaponMasteryProperty.itest.ts
│ │ │ │ │ └── weaponProperty.itest.ts
│ │ │ │ ├── abilityScores.itest.ts
│ │ │ │ ├── classes.itest.ts
│ │ │ │ ├── conditions.itest.ts
│ │ │ │ ├── damageTypes.itest.ts
│ │ │ │ ├── equipment.itest.ts
│ │ │ │ ├── equipmentCategories.itest.ts
│ │ │ │ ├── feats.itest.ts
│ │ │ │ ├── features.itest.ts
│ │ │ │ ├── languages.itest.ts
│ │ │ │ ├── magicItems.itest.ts
│ │ │ │ ├── magicSchools.itest.ts
│ │ │ │ ├── monsters.itest.ts
│ │ │ │ ├── proficiencies.itest.ts
│ │ │ │ ├── races.itest.ts
│ │ │ │ ├── ruleSections.itest.ts
│ │ │ │ ├── rules.itest.ts
│ │ │ │ ├── skills.itest.ts
│ │ │ │ ├── spells.itest.ts
│ │ │ │ ├── subclasses.itest.ts
│ │ │ │ ├── subraces.itest.ts
│ │ │ │ ├── traits.itest.ts
│ │ │ │ └── weaponProperties.itest.ts
│ │ │ └── server.itest.ts
│ │ ├── support/
│ │ │ ├── db.ts
│ │ │ ├── index.ts
│ │ │ ├── requestHelpers.ts
│ │ │ └── types.d.ts
│ │ ├── util/
│ │ │ └── data.test.ts
│ │ └── vitest.setup.ts
│ └── util/
│ ├── RedisClient.ts
│ ├── awsS3Client.ts
│ ├── data.ts
│ ├── environmentVariables.ts
│ ├── index.ts
│ ├── modelOptions.ts
│ ├── prewarmCache.ts
│ └── regex.ts
├── tsconfig.json
├── vitest.config.integration.ts
└── vitest.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
/node_modules/
dist
# Git / VCS
.git/
# Editor / IDE
.vscode/
# Environment / Secrets
.env
# Logs
*.log
npm-debug.log*
# OS generated files
.DS_Store
================================================
FILE: .github/CODEOWNERS
================================================
# Default owner for all files
* @bagelbits
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [5e-bits]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: bagelbits
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## What does this do?
\<It's not clear if I don't update this text with relevant info\>
## How was it tested?
\<It's not clear if I don't update this text with relevant info\>
## Is there a Github issue this is resolving?
\<It's not clear if I don't update this text with relevant info\>
## Was any impacted documentation updated to reflect this change?
\<It's not clear if I don't update this text with relevant info\>
## Here's a fun image for your troubles
\<Add a fun image here\>
================================================
FILE: .github/actions/validate-pr-title/action.yml
================================================
name: 'Validate PR Title'
description: 'Validates that PR titles follow conventional commit format with custom types'
inputs:
title:
description: 'PR title to validate'
required: true
outputs:
valid:
description: 'Whether the title is valid'
value: ${{ steps.validate.outputs.valid }}
runs:
using: 'composite'
steps:
- name: Validate PR title
id: validate
shell: bash
run: |
title="${{ inputs.title }}"
# Define allowed types
allowed_types="feat|fix|docs|style|refactor|perf|test|build|ci|chore|deps"
# Check if title matches conventional commit format
if echo "$title" | grep -qE "^($allowed_types)(\(.+\))?!?: .+"; then
echo "valid=true" >> $GITHUB_OUTPUT
echo "✅ PR title is valid: $title"
else
echo "valid=false" >> $GITHUB_OUTPUT
echo "❌ PR title is invalid: $title"
echo ""
echo "Expected format: <type>[optional scope]: <description>"
echo ""
echo "Allowed types: $allowed_types"
echo ""
echo "Examples:"
echo " feat: add new feature"
echo " fix: resolve bug"
echo " deps: update dependencies"
echo " chore: update CI configuration"
exit 1
fi
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: 'npm' # See documentation for possible values
directory: '/' # Location of package manifests
schedule:
interval: 'weekly'
commit-message:
prefix: 'chore'
include: 'scope'
- package-ecosystem: 'github-actions'
# Workflow files stored in the default location of `.github/workflows`. (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.)
directory: '/'
schedule:
interval: 'weekly'
commit-message:
prefix: 'chore'
include: 'scope'
================================================
FILE: .github/workflows/ci.yml
================================================
# 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: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
jobs:
lint:
name: Run linter
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # @v6.0.0
- name: Use Node.js 22.x
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # @v6.0.0
with:
node-version: 22.x
- run: npm install
- name: Lint Code
run: npm run lint
- name: Validate OpenAPI Spec
run: npm run validate-swagger
unit:
name: Run tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # @v6.0.0
- name: Use Node.js 22.x
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # @v6.0.0
with:
node-version: 22.x
- run: npm install
- run: npm run test:unit
integration:
name: Run Integration tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # @v6.0.0
- run: npm run test:integration:local
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: 'CodeQL'
on:
push:
branches: [main]
pull_request:
# The branches below must be a subset of the branches above
branches: [main]
schedule:
- cron: '15 6 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ['javascript']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v4
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
================================================
FILE: .github/workflows/lint-pr.yml
================================================
name: "Lint PR"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
main:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Validate PR title
uses: ./.github/actions/validate-pr-title
with:
title: ${{ github.event.pull_request.title }}
================================================
FILE: .github/workflows/release-please.yml
================================================
name: Release Please
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
version: ${{ steps.release.outputs.version }}
steps:
- name: Generate Deploy Bot token
id: generate-token
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.DEPLOYMENT_APP_ID }}
private-key: ${{ secrets.DEPLOYMENT_APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Release Please
id: release
uses: googleapis/release-please-action@v4
with:
token: ${{ steps.generate-token.outputs.token }}
release-type: node
config-file: ./release-please-config.json
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
release:
types: [published]
workflow_dispatch:
repository_dispatch:
env:
REGISTRY: ghcr.io
jobs:
build-and-publish:
name: Build and Publish Release Assets
runs-on: ubuntu-latest
if: github.repository == '5e-bits/5e-srd-api'
permissions:
contents: write
packages: write
steps:
- name: Determine Release Tag
id: tag
run: |
if [ "${{ github.event_name }}" = "release" ]; then
echo "tag=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
else
# For workflow_dispatch, get the latest release tag
LATEST_TAG=$(gh api repos/${{ github.repository }}/releases/latest --jq .tag_name)
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ steps.tag.outputs.tag }}
- name: Use Node.js 22.x
uses: actions/setup-node@v6
with:
node-version: 22.x
- name: Install Dependencies
run: npm ci
- name: Build Artifacts
run: |
npm run bundle-swagger
npm run gen-postman
- name: Upload Release Assets
if: github.event_name == 'release'
run: |
gh release upload ${{ github.event.release.tag_name }} \
./dist/openapi.yml \
./dist/openapi.json \
./dist/collection.postman.json
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
container-release:
name: Container Release
runs-on: ubuntu-latest
if: github.repository == '5e-bits/5e-srd-api'
env:
IMAGE_NAME: ${{ github.repository }}
permissions:
contents: read
packages: write
steps:
- name: Determine Release Tag
id: tag
run: |
if [ "${{ github.event_name }}" = "release" ]; then
echo "tag=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
else
# For workflow_dispatch, get the latest release tag
LATEST_TAG=$(gh api repos/${{ github.repository }}/releases/latest --jq .tag_name)
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout repository
uses: actions/checkout@v6
with:
ref: ${{ steps.tag.outputs.tag }}
- name: Log in to the Container registry
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }},${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
labels: version=${{ steps.tag.outputs.tag }}
heroku-deploy:
name: Deploy to Heroku
runs-on: ubuntu-latest
if: github.repository == '5e-bits/5e-srd-api'
needs: [build-and-publish, container-release]
steps:
- name: Determine Release Tag
id: tag
run: |
if [ "${{ github.event_name }}" = "release" ]; then
echo "tag=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
else
# For workflow_dispatch, get the latest release tag
LATEST_TAG=$(gh api repos/${{ github.repository }}/releases/latest --jq .tag_name)
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ steps.tag.outputs.tag }}
- name: Install Heroku CLI
run: curl https://cli-assets.heroku.com/install.sh | sh
- name: Deploy to Heroku
uses: akhileshns/heroku-deploy@v3.14.15
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: "dnd-5e-srd-api"
heroku_email: "cdurianward@gmail.com"
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
.env
# JetBrains config files
.idea
dist
dist/**
# Portman creates some temporary files here during conversion.
tmp/**
collection.postman.json
# Docker
docker-compose.override.yml
# Claude
.claude
================================================
FILE: .nvmrc
================================================
lts/jod
================================================
FILE: .prettierrc
================================================
{
"printWidth": 100,
"singleQuote": true,
"trailingComma": "none",
"tabWidth": 2,
"semi": false,
"quoteProps": "as-needed"
}
================================================
FILE: .redocly.yaml
================================================
extends:
- recommended
rules:
operation-operationId: off
operation-4xx-response: off
# no-invalid-media-type-examples: off
================================================
FILE: CHANGELOG.md
================================================
# [5.1.0](https://github.com/5e-bits/5e-srd-api/compare/v5.0.0...v5.1.0) (2025-09-15)
### Features
* **2024:** Add equipment and categories ([#815](https://github.com/5e-bits/5e-srd-api/issues/815)) ([81dae46](https://github.com/5e-bits/5e-srd-api/commit/81dae461faa031b93d9c0edf86a458f7c1f3f2c6))
# [5.0.0](https://github.com/5e-bits/5e-srd-api/compare/v4.2.1...v5.0.0) (2025-09-04)
* refactor(race/subrace)!: remove redundant data ([#825](https://github.com/5e-bits/5e-srd-api/issues/825)) ([043fc16](https://github.com/5e-bits/5e-srd-api/commit/043fc160c5f6d41d88a18d8cac11f66f4e2a55d9))
### BREAKING CHANGES
* dropped the `race.starting_proficiencies`,
`race.starting_proficiency_options`, `subrace.starting_proficiencies`,
`subrace.language_options`, and `subrace.languages` properties of all
races and subraces in the database. Clients can instead find this data
on the corresponding traits linked to each race or subrace.
## How was it tested?
I ran the database + API project locally with Docker and called the
endpoints of the various classes and subclasses. I also ran the unit and
integration tests in the API project.
## Is there a Github issue this is resolving?
https://github.com/5e-bits/5e-database/issues/874
## Was any impacted documentation updated to reflect this change?
I touched every reference of the properties in the API project. I took a
look at the docs project, but couldn't fully find my way around the
project to give a clear indication on if anything needed to change.
## Here's a fun image for your troubles
My players once sold an iron pot to well known business woman and secret
member of the Zhentarim and convinced her that it was a magic pot that
can restore spoiled food. They even sneaked into her house to cast
purify food and drink on it to make sure she believed them.

## [5.4.0](https://github.com/5e-bits/5e-srd-api/compare/v5.3.0...v5.4.0) (2026-04-01)
### Features
* **2024:** Add magic items ([#1044](https://github.com/5e-bits/5e-srd-api/issues/1044)) ([8686fd0](https://github.com/5e-bits/5e-srd-api/commit/8686fd0ca89358cd3a256353985ec73fd9ab5d4f))
* **2024:** Add Species, Subspecies, and Traits ([#1038](https://github.com/5e-bits/5e-srd-api/issues/1038)) ([0bcde32](https://github.com/5e-bits/5e-srd-api/commit/0bcde324e7228b85fd40d324aec0d865287a4872))
* **2024:** Add subclasses ([#1043](https://github.com/5e-bits/5e-srd-api/issues/1043)) ([29ae4fa](https://github.com/5e-bits/5e-srd-api/commit/29ae4fa8f4efae0eb29bc9d6523e5c48839fa0c7))
## [5.3.0](https://github.com/5e-bits/5e-srd-api/compare/v5.2.4...v5.3.0) (2026-03-10)
### Features
* **2024:** Add Backgrounds, Feats, and Proficiencies for 2024 ([#1018](https://github.com/5e-bits/5e-srd-api/issues/1018)) ([b584a84](https://github.com/5e-bits/5e-srd-api/commit/b584a8428ab079b7e889b54f28eeb33a0347cea5))
## [5.2.4](https://github.com/5e-bits/5e-srd-api/compare/v5.2.3...v5.2.4) (2025-12-11)
### Bug Fixes
* **ci:** Use app token ([#956](https://github.com/5e-bits/5e-srd-api/issues/956)) ([5abffd8](https://github.com/5e-bits/5e-srd-api/commit/5abffd830dd00dbd5bb55dd96c1627d002fed95d))
## [5.2.3](https://github.com/5e-bits/5e-srd-api/compare/v5.2.2...v5.2.3) (2025-12-11)
### Bug Fixes
* **ci:** Release is now automatic on release PR merge ([#954](https://github.com/5e-bits/5e-srd-api/issues/954)) ([d8a798d](https://github.com/5e-bits/5e-srd-api/commit/d8a798d3bb3552c6ba5a3248cda80923013ac6b9))
## [5.2.2](https://github.com/5e-bits/5e-srd-api/compare/v5.2.1...v5.2.2) (2025-12-04)
### Bug Fixes
* Add Spell field resolver to Subclass Resolver ([#952](https://github.com/5e-bits/5e-srd-api/issues/952)) ([c15ef5d](https://github.com/5e-bits/5e-srd-api/commit/c15ef5da7fa3271cd25d67908d7f16d064930113))
## [5.2.1](https://github.com/5e-bits/5e-srd-api/compare/v5.2.0...v5.2.1) (2025-12-01)
### Bug Fixes
* missing subclass features ([#946](https://github.com/5e-bits/5e-srd-api/issues/946)) ([8170142](https://github.com/5e-bits/5e-srd-api/commit/8170142dfd9cea306773d1942e21aab52d5901d6))
## [5.2.0](https://github.com/5e-bits/5e-srd-api/compare/v5.1.0...v5.2.0) (2025-10-24)
### Features
* **release:** Use release please ([#911](https://github.com/5e-bits/5e-srd-api/issues/911)) ([a8b50dd](https://github.com/5e-bits/5e-srd-api/commit/a8b50dd9256ecf0e5be8517625be839be6e6976e))
### Bug Fixes
* **dependabot:** use build instead of deps ([#920](https://github.com/5e-bits/5e-srd-api/issues/920)) ([52a439d](https://github.com/5e-bits/5e-srd-api/commit/52a439da3855b62291007ff37bfd99650e37c7cb))
## [4.2.1](https://github.com/5e-bits/5e-srd-api/compare/v4.2.0...v4.2.1) (2025-06-23)
### Bug Fixes
* **races:** Language options is optional for graphql ([#807](https://github.com/5e-bits/5e-srd-api/issues/807)) ([2715e0f](https://github.com/5e-bits/5e-srd-api/commit/2715e0f457cc1404b870e4284b53e2ca227a8355))
# [4.2.0](https://github.com/5e-bits/5e-srd-api/compare/v4.1.1...v4.2.0) (2025-06-13)
### Features
* **2024:** Add a bunch of easy endpoints to 2024 ([#800](https://github.com/5e-bits/5e-srd-api/issues/800)) ([2b4871d](https://github.com/5e-bits/5e-srd-api/commit/2b4871da07bea5f8e9d9e907e37e8c4124254cea))
## [4.1.1](https://github.com/5e-bits/5e-srd-api/compare/v4.1.0...v4.1.1) (2025-06-11)
### Bug Fixes
* **graphql:** Spell DC now resolves to Ability Score ([#798](https://github.com/5e-bits/5e-srd-api/issues/798)) ([ddb5c26](https://github.com/5e-bits/5e-srd-api/commit/ddb5c26e7dc1794534bd364bcfa20386793dd35d))
# [4.1.0](https://github.com/5e-bits/5e-srd-api/compare/v4.0.3...v4.1.0) (2025-06-11)
### Features
* **graphql:** Sets up /graphql/2024 endpoint and abstracts shared code ([#790](https://github.com/5e-bits/5e-srd-api/issues/790)) ([acf7780](https://github.com/5e-bits/5e-srd-api/commit/acf7780e301cf1fb2a166eeefd177a8f4225af3b))
## [4.0.3](https://github.com/5e-bits/5e-srd-api/compare/v4.0.2...v4.0.3) (2025-06-05)
### Bug Fixes
* **graphql:** Fix the endpoint for deprecated graphql endpoint ([#791](https://github.com/5e-bits/5e-srd-api/issues/791)) ([2139389](https://github.com/5e-bits/5e-srd-api/commit/2139389d91bfb264acb1929e36a3ebd9ea5b19c2))
## [4.0.2](https://github.com/5e-bits/5e-srd-api/compare/v4.0.1...v4.0.2) (2025-06-02)
### Bug Fixes
* **prettier:** Run prettier against everything ([#780](https://github.com/5e-bits/5e-srd-api/issues/780)) ([01905b2](https://github.com/5e-bits/5e-srd-api/commit/01905b2be462990966e7790b2897aebb1cbe578a))
## [4.0.1](https://github.com/5e-bits/5e-srd-api/compare/v4.0.0...v4.0.1) (2025-06-02)
### Bug Fixes
* **lint:** Fix simple linting rules ([#778](https://github.com/5e-bits/5e-srd-api/issues/778)) ([3c6cc95](https://github.com/5e-bits/5e-srd-api/commit/3c6cc95753f3f485a58f2e2dfe93cebd1de614d2))
# [4.0.0](https://github.com/5e-bits/5e-srd-api/compare/v3.24.0...v4.0.0) (2025-06-01)
* feat(graphql)!: Migrate to typegraphql ([#752](https://github.com/5e-bits/5e-srd-api/issues/752)) ([6bb9e75](https://github.com/5e-bits/5e-srd-api/commit/6bb9e755ea0e35aebe8f2f3bdae0362fa77694e6))
### BREAKING CHANGES
* Some fields are now different but more consistent
across the graphql endpoints.
## What does this do?
* Completely rewrites our entire GraphQL endpoint using `type-graphql`
and `zod`
* Fixes migration errors from the typegoose migration
## How was it tested?
Locally, there are numerous side-by-side comparisons with production.
But it is possible I missed something.
## Is there a Github issue this is resolving?
Nope. I'm just insane.
## Was any impacted documentation updated to reflect this change?
Luckily, GraphQL introspection is self-documenting.
## Here's a fun image for your troubles

# [3.24.0](https://github.com/5e-bits/5e-srd-api/compare/v3.23.7...v3.24.0) (2025-05-30)
### Features
* **class:** Add `level` query for class spells for filtering spell level ([#776](https://github.com/5e-bits/5e-srd-api/issues/776)) ([0c41457](https://github.com/5e-bits/5e-srd-api/commit/0c414571ea409c37c15d1b82d1da86844cf18ba9))
## [3.23.7](https://github.com/5e-bits/5e-srd-api/compare/v3.23.6...v3.23.7) (2025-05-08)
### Bug Fixes
* **deploy:** Undo everything from before ([c3c84d1](https://github.com/5e-bits/5e-srd-api/commit/c3c84d1b20ce930ed1afb558c26955a049ca9560))
## [3.23.6](https://github.com/5e-bits/5e-srd-api/compare/v3.23.5...v3.23.6) (2025-05-08)
### Bug Fixes
* **deploy:** Let's try this one more time ([5c5c1cf](https://github.com/5e-bits/5e-srd-api/commit/5c5c1cfa774826837114014e7dbdf9e150e865d3))
## [3.23.5](https://github.com/5e-bits/5e-srd-api/compare/v3.23.4...v3.23.5) (2025-05-08)
### Bug Fixes
* **deploy:** Let's try that again. Now using deploy bot as author ([3f5dfa0](https://github.com/5e-bits/5e-srd-api/commit/3f5dfa04c60144c82086829039edb73a8bc9055e))
## [3.23.4](https://github.com/5e-bits/5e-srd-api/compare/v3.23.3...v3.23.4) (2025-05-08)
### Bug Fixes
* **deploy:** Use deploy bot for authoring commits ([1f62894](https://github.com/5e-bits/5e-srd-api/commit/1f628949cd218d5939dd8b6f68c702f2355761ba))
## [3.23.3](https://github.com/5e-bits/5e-srd-api/compare/v3.23.2...v3.23.3) (2025-05-06)
### Bug Fixes
* **class:** showSpellsForClassAndLevel now gives the spells available at that class level ([#758](https://github.com/5e-bits/5e-srd-api/issues/758)) ([22b1b35](https://github.com/5e-bits/5e-srd-api/commit/22b1b351e7355bcdde04c5fd2fee0c967fb4f2f5))
## [3.23.2](https://github.com/5e-bits/5e-srd-api/compare/v3.23.1...v3.23.2) (2025-05-04)
### Bug Fixes
* **desc:** Fix models to match data reality for desc ([#751](https://github.com/5e-bits/5e-srd-api/issues/751)) ([6bcd610](https://github.com/5e-bits/5e-srd-api/commit/6bcd610df4cccc57d79e8ff3b075fee356c34f04))
## [3.23.1](https://github.com/5e-bits/5e-srd-api/compare/v3.23.0...v3.23.1) (2025-05-04)
### Bug Fixes
* **images:** Fix key for regex ([6f8a99d](https://github.com/5e-bits/5e-srd-api/commit/6f8a99da050ff4ce0c57bfc5377fcdf57b42f60c))
# [3.23.0](https://github.com/5e-bits/5e-srd-api/compare/v3.22.0...v3.23.0) (2025-05-04)
### Features
* **images:** Add /api/images endpoint and use fetch for image fetching ([#750](https://github.com/5e-bits/5e-srd-api/issues/750)) ([fec63b7](https://github.com/5e-bits/5e-srd-api/commit/fec63b78ac075ca8c093896f6bd6c8519ac9870b))
# [3.22.0](https://github.com/5e-bits/5e-srd-api/compare/v3.21.0...v3.22.0) (2025-04-28)
### Features
* **node:** Bump to Node 22 ([#742](https://github.com/5e-bits/5e-srd-api/issues/742)) ([8c27177](https://github.com/5e-bits/5e-srd-api/commit/8c271775661473764295d71bc681a31bda6dd01c))
# [3.21.0](https://github.com/5e-bits/5e-srd-api/compare/v3.20.1...v3.21.0) (2025-04-27)
### Features
* **release:** Convert semver release to use App instead of PAT ([#736](https://github.com/5e-bits/5e-srd-api/issues/736)) ([aad1a29](https://github.com/5e-bits/5e-srd-api/commit/aad1a29c459bed41daa73af09c4d64db9dcab770))
## [3.20.1](https://github.com/5e-bits/5e-srd-api/compare/v3.20.0...v3.20.1) (2025-04-27)
### Bug Fixes
* :bug: Resolve full `Option` objects in `subrace.language_options` resolver ([#735](https://github.com/5e-bits/5e-srd-api/issues/735)) ([ef37127](https://github.com/5e-bits/5e-srd-api/commit/ef37127a68c0303c51d62d21835baa595b742435))
# [3.20.0](https://github.com/5e-bits/5e-srd-api/compare/v3.19.0...v3.20.0) (2025-04-25)
### Bug Fixes
* **release:** Give workflow write permissions ([6c50c47](https://github.com/5e-bits/5e-srd-api/commit/6c50c47f0a599967c8e0ac6cea271452cbf696f9))
* **release:** Set token on checkout ([158dc35](https://github.com/5e-bits/5e-srd-api/commit/158dc35bde5efb687e7c937c038e7ba54e9bc352))
* **release:** Use PAT instead of normal GITHUB_TOKEN ([d518a8d](https://github.com/5e-bits/5e-srd-api/commit/d518a8d8eb1cca5ea399fb3d3ce97c7bd0fba618))
### Features
* **semver:** Updates changelog and npm version on release ([#733](https://github.com/5e-bits/5e-srd-api/issues/733)) ([40f60f3](https://github.com/5e-bits/5e-srd-api/commit/40f60f39ea1ed10d1b2b27f7e53c3d0fe6a7a9be))
# Changelog
## 2020-01-12
- Make GET queries case insensitive for `name` where supported.
- Fix Home link to work when you're on the Docs page
## 2020-01-11
- 100% Test coverage between unit and integration tests
- Overloaded routes will be removed and moved onto routes that make sense.
- General cleanup of the code base and breakup to make testing easier.
## 2020-01-09
- Add in Docker Compose
## 2020-01-08
- Add Prettier for auto formatting
- Add in 404 support to stop timeouts
- Add Heroku badge
- Add in Jest testing framework
- Add Bugsnag for error reporting
## 2020-01-06
- Update current docs to match new database changes
## 2020-01-05
- Fix race and subrace routes (#33)
## 2020-01-04
- Converted number indices to string indices based off of name
- Added a Contribute link for the API
- Move changes to Changelog
- Fixed the navbar for the Docs to use the same partial
## 2020-01-02
- All of the database changes made in the last few months have finally landed
- Replaced Slack chat with <a href="https://discord.gg/TQuYTv7">Discord</a>
- Added some HTTPS support
## 2018-01-07
- The Database and API are now OPEN SOURCE! Find it on my <a href="http://github.com/bagelbits">github</a>
- Updated changes from DevinOchman's <a href="https://github.com/adrpadua/5e-database/pull/3">pull request</a>: New Races, Subraces, Traits
## 2017-04-17
- Created Slack chat group, which you can access <a href="http://dnd-5e-api-slack.herokuapp.com/">here</a>. A place to ask questions and make suggestions etc.
- Updated "url" members of every object to have 'www.' to avoid CORS errors
================================================
FILE: Dockerfile
================================================
# ---- Builder Stage ----
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json ./
# Copy the package-lock.json that was freshly generated on your host
COPY package-lock.json ./
# Clean existing node_modules just in case of Docker layer caching weirdness.
# Then run `npm ci` which is generally recommended for CI/Docker if you have a package-lock.json.
RUN rm -rf node_modules
RUN npm ci
# Copy the rest of the application source code
# .dockerignore will handle exclusions like node_modules, dist, etc.
COPY . .
# Build the application
# This uses tsconfig.json to output to ./dist
RUN npm run build
# ---- Final Stage ----
FROM node:22-alpine
WORKDIR /app
# Copy package.json and lock file (good practice)
COPY package.json ./
COPY package-lock.json* ./
# Copy node_modules from builder stage - this includes all dependencies with scripts run
COPY --from=builder /app/node_modules ./node_modules/
# Set environment to production AFTER dependencies are in place
ENV NODE_ENV=production
# Copy built application from builder stage
COPY --from=builder /app/dist ./dist/
# Copy entire source tree (needed for tests and potentially other runtime file access)
COPY --from=builder /app/src ./src/
# Copy config files needed for tests/runtime
COPY --from=builder /app/vitest.config*.ts ./
COPY --from=builder /app/tsconfig.json ./
# # Add non-root user for security
# RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# # Change ownership of app directory to the non-root user AFTER user creation
# RUN chown -R appuser:appgroup /app
# USER appuser
# Expose port (replace 3000 if different)
EXPOSE 3000
# Start the main process.
CMD ["node", "--experimental-specifier-resolution=node", "dist/src/start.js"]
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) [2018-2020] [Adrian Padua, Christopher Ward]
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# 5e-srd-api
[](https://github.com/5e-bits/5e-srd-api/actions/workflows/ci.yml)
[](https://discord.gg/TQuYTv7)


REST API to access [D&D 5th Edition SRD API](https://www.dnd5eapi.co/)
Talk to us [on Discord!](https://discord.gg/TQuYTv7)
## How to Run
Make sure you have the latest version of the database:
```shell
docker compose pull
```
Then run it with docker-compose:
```shell
docker compose up --build
```
### M1/M2/M3 Macs
The command above pulls the latest image of the database from ghcr.io, which only targets the amd64 platform. If you are running on a different platform (like a Mac with Apple Silicon), you will need to build the image yourself. See the [5e-database](https://github.com/5e-bits/5e-database#how-to-run) repo for additional details.
```shell
cd ../
git clone https://github.com/5e-bits/5e-database.git
```
Then back over here in the 5e-srd-api repo, in the file `docker-compose.yml`, you can replace the line `image: bagelbits/5e-database` with `build: ../5e-database` (or whatever you named the custom db image).
## Making API Requests
Make API requests by using the root address:
`http://localhost:3000/api/2014`
You should get a response with the available endpoints for the root:
```json
{
"ability-scores": "/api/2014/ability-scores",
"classes": "/api/2014/classes",
"conditions": "/api/2014/conditions",
"damage-types": "/api/2014/damage-types",
"equipment-categories": "/api/2014/equipment-categories",
"equipment": "/api/2014/equipment",
"features": "/api/2014/features",
"languages": "/api/2014/languages",
"magic-schools": "/api/2014/magic-schools",
"monsters": "/api/2014/monsters",
"proficiencies": "/api/2014/proficiencies",
"races": "/api/2014/races",
"skills": "/api/2014/skills",
"spells": "/api/2014/spells",
"subclasses": "/api/2014/subclasses",
"subraces": "/api/2014/subraces",
"traits": "/api/2014/traits",
"weapon-properties": "/api/2014/weapon-properties"
}
```
### Versioning
The API is versioned by release years of the SRD. Currently only `/api/2014` is available. The next version will be `/api/2024`.
## Working with a local image of 5e Database
If you are working on a feature which requires changes to both this repo, _and_ the 5e-database repo, it is useful to know how to connect the former to the latter for testing purposes. A simple process for doing so is as follows:
1. In the file `docker-compose.yml`, you can replace the line `image: bagelbits/5e-database` with `build: [relativePathToDatabaseRepo]`. Make sure not to commit this change, as it is intended for local testing only.
2. Run your branch of 5e-srd-api using the method outlined in the above section of this readme file. So long as there are no transient errors, the API should build successfully, and your changes to both repos should be noticeable.
## Working with image resources from s3
The API uses s3 to store image files for monsters. The image files live in a bucket called `dnd-5e-api-images` under the `/monsters` folder.
To test locally, you can [use `localstack` to mock s3](https://docs.localstack.cloud/user-guide/aws/s3/). To do so, you will first need to install `localstack`,
`awscli`, and `awslocal`. You can then run the following commands to configure and start the localstack container:
```shell
export AWS_CONFIG_ENV=localstack_dev
localstack start
awslocal s3api create-bucket --bucket dnd-5e-api-images
awslocal s3 cp aboleth.png s3://dnd-5e-api-images/monsters/
npm run dev
```
Request the image by navigating to an image URL in a browser, or via HTTP request:
```shell
curl http://localhost:3000/api/2014/monsters/aboleth.png --output downloaded-aboleth.png
```
When interacting with the image you should see logs in the terminal where you started localstack. You can also use [localstack's webui](https://app.localstack.cloud/dashboard) to view the bucket and
contents.
## Data Issues
If you see anything wrong with the data itself, please open an issue or PR over [here.](https://github.com/5e-bits/5e-database/)
## Running Tests
### Unit Tests
You can run unit tests locally by using the command: `npm run test:unit`
### Integration Tests
Integration tests need to be ran in the API docker container for them to function properly.
In order to run integration tests locally you can use the command: `npm run test:integration:local`
## Documentation
Public facing API documentation lives [here.](https://www.dnd5eapi.co/docs)
The [docs repository](https://github.com/5e-bits/docs) contains the source for the public facing API documentation. It uses
[Docusaurus](https://docusaurus.io/) to generate the site from a bundled OpenAPI spec.
More details on working with the OpenAPI spec can be found in the [`src/swagger`](src/swagger/) directory's [README](src/swagger/README.md). The most up-to-date bundled OpenAPI specs themselves are included in [the latest release](https://github.com/5e-bits/5e-srd-api/releases/latest) in both [JSON](https://github.com/5e-bits/5e-srd-api/releases/latest/download/openapi.json) and [YAML](https://github.com/5e-bits/5e-srd-api/releases/latest/download/openapi.yml) formats, which can be used to generate your own documentation, clients, etc.
A [Postman collection](https://github.com/5e-bits/5e-srd-api/releases/latest/download/collection.postman.json) can also be found in the latest release. This can be imported into [the Postman HTTP client](https://www.postman.com/) to execute test requests against production & local deployments of the API.
## Contributing
- Fork this repository
- Create a new branch for your work
- Push up any changes to your branch, and open a pull request. Don't feel it needs to be perfect — incomplete work is totally fine. We'd love to help get it ready for merging.
## Code of Conduct
The Code of Conduct for this repo can be found [here.](https://github.com/5e-bits/5e-srd-api/wiki#code-of-conduct)
## Contributors
<a href="https://github.com/5e-bits/5e-srd-api/graphs/contributors">
<img src="https://contrib.rocks/image?repo=5e-bits/5e-srd-api" />
</a>
================================================
FILE: app.json
================================================
{
"name": "5e-srd-api",
"scripts": {},
"env": {
"AWS_ACCESS_KEY_ID": {
"required": true
},
"AWS_SECRET_ACCESS_KEY": {
"required": true
},
"BUGSNAG_API_KEY": {
"required": true
},
"GRAPHQL_API_KEY": {
"required": true
},
"MONGODB_URI": {
"required": true
},
"NODE_ENV": {
"required": true
},
"REALM_API_KEY": {
"required": true
},
"REALM_APP_ID": {
"required": true
}
},
"formation": {
"web": {
"quantity": 1
}
},
"addons": ["bugsnag:tauron", "heroku-redis:mini"],
"buildpacks": [
{
"url": "heroku/nodejs"
}
],
"stack": "container"
}
================================================
FILE: docker-compose.yml
================================================
services:
db:
image: ghcr.io/5e-bits/5e-database:latest
# build: ../5e-database
ports:
- '27017:27017'
cache:
image: redis:6.2.5
ports:
- '6379:6379'
api:
environment:
MONGODB_URI: mongodb://db/5e-database
REDIS_URL: redis://cache:6379
build: .
ports:
- '3000:3000'
depends_on:
- db
- cache
================================================
FILE: eslint.config.js
================================================
import eslint from '@eslint/js'
import stylistic from '@stylistic/eslint-plugin'
import eslintConfigPrettier from 'eslint-config-prettier'
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'
import { importX } from 'eslint-plugin-import-x'
import globals from 'globals'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{
name: 'base-ignore',
ignores: ['**/coverage/**', '**/dist/**', '**/node_modules/**']
},
eslint.configs.recommended,
// Main TypeScript and JS linting configuration
{
name: 'typescript-and-imports-x',
files: ['**/*.ts', '**/*.js'],
extends: [
...tseslint.configs.recommended,
importX.flatConfigs.recommended,
importX.flatConfigs.typescript
],
plugins: {
'@stylistic': stylistic,
'import-x': importX
},
languageOptions: {
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname
},
globals: {
...globals.node,
...globals.browser,
...globals.jquery
}
},
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/strict-boolean-expressions': 'error',
'import-x/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
['parent', 'sibling', 'index'],
'object',
'type'
],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true },
pathGroups: [{ pattern: '@/**', group: 'internal', position: 'after' }],
pathGroupsExcludedImportTypes: ['builtin', 'external', 'object', 'type']
}
],
'import-x/no-duplicates': 'error',
'import-x/no-named-as-default': 'off',
'import-x/no-named-as-default-member': 'off'
},
settings: {
'import-x/resolver-next': [
createTypeScriptImportResolver({
alwaysTryTypes: true,
project: './tsconfig.json'
})
]
}
},
eslintConfigPrettier // MUST BE LAST
)
================================================
FILE: heroku.yml
================================================
build:
docker:
web: Dockerfile
run:
web: node dist/src/start.js
================================================
FILE: nodemon.json
================================================
{
"verbose": false,
"ignore": [
"*.test.*",
"dist/*",
"node_modules/*",
"collection.postman.json",
"tmp/*"
],
"ext": "js,ts,ejs,json,yml"
}
================================================
FILE: openapi-to-postman.json
================================================
{
"folderStrategy": "Tags",
"includeAuthInfoInExample": true,
"optimizeConversion": true,
"stackLimit": 50,
"requestParametersResolution": "Example",
"exampleParametersResolution": "Example"
}
================================================
FILE: package.json
================================================
{
"name": "dnd-5e-srd-api",
"version": "5.4.0",
"engines": {
"node": "22.x",
"npm": ">=10.8.0"
},
"type": "module",
"private": true,
"scripts": {
"build:ts": "tsc && tsc-alias",
"build": "npm-run-all --sequential clean bundle-swagger build:ts copy-assets",
"clean": "rimraf dist/*",
"copy-assets": "mkdir -p dist && cp -R src/css dist/src && cp -R src/public dist/src",
"start": "npm run build && node dist/src/start.js",
"dev": "nodemon npm run start",
"lint": "eslint . --config eslint.config.js",
"lint:fix": "eslint . --config eslint.config.js --fix",
"validate-swagger": "redocly lint src/swagger/swagger.yml",
"bundle-swagger": "npm run validate-swagger && redocly bundle --output dist/openapi.yml src/swagger/swagger.yml && redocly bundle --output dist/openapi.json src/swagger/swagger.yml",
"gen-postman": "npm run bundle-swagger && openapi2postmanv2 -s dist/openapi.yml -o dist/collection.postman.json -c openapi-to-postman.json",
"test": "npm run test:unit && npm run test:integration:local",
"test:unit": "vitest run",
"test:integration": "vitest run --config ./vitest.config.integration.ts",
"test:integration:local": "docker compose pull && docker compose build && docker compose run --use-aliases api npm run test:integration"
},
"dependencies": {
"@apollo/server": "^5.5.0",
"@as-integrations/express5": "^1.1.2",
"@aws-sdk/client-s3": "^3.994.0",
"@bugsnag/js": "^8.8.1",
"@bugsnag/plugin-express": "^8.8.0",
"@graphql-tools/schema": "^10.0.31",
"@redocly/cli": "^2.25.4",
"@typegoose/typegoose": "^13.2.1",
"body-parser": "^2.2.2",
"class-validator": "^0.15.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.6",
"debug": "^4.4.3",
"ejs": "^5.0.2",
"escape-string-regexp": "^5.0.0",
"express": "^5.1.0",
"express-rate-limit": "^8.2.2",
"graphql": "^16.11.0",
"graphql-depth-limit": "^1.1.0",
"mongoose": "~9.2.3",
"morgan": "^1.10.1",
"node-fetch": "^3.1.1",
"redis": "^5.10.0",
"reflect-metadata": "^0.2.2",
"serve-favicon": "^2.5.1",
"type-graphql": "^2.0.0-rc.2",
"zod": "^4.3.6"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@faker-js/faker": "^10.3.0",
"@stylistic/eslint-plugin": "^5.7.1",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.5",
"@types/fs-extra": "^11.0.4",
"@types/graphql-depth-limit": "^1.1.3",
"@types/morgan": "^1.9.10",
"@types/node": "^25.5.2",
"@types/shelljs": "^0.10.0",
"@types/supertest": "^7.2.0",
"@typescript-eslint/eslint-plugin": "^8.57.2",
"@typescript-eslint/parser": "^8.58.2",
"eslint": "^10.2.0",
"eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-ejs": "^0.0.2",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-prettier": "^5.5.5",
"fishery": "^2.3.1",
"fs-extra": "^11.3.1",
"globals": "^17.0.0",
"mongodb-memory-server": "^11.0.1",
"node-mocks-http": "^1.17.2",
"nodemon": "^3.1.10",
"npm-run-all": "^4.1.5",
"openapi-to-postmanv2": "^5.3.5",
"prettier": "^3.8.2",
"rimraf": "^6.1.3",
"shelljs": "^0.10.0",
"supertest": "^7.2.2",
"ts-node": "^10.9.2",
"tsc-alias": "^1.8.16",
"typedoc": "^0.28.16",
"typescript": "^5.9.3",
"typescript-eslint": "^8.58.0",
"vitest": "^4.1.2",
"yaml": "^2.8.2"
}
}
================================================
FILE: release-please-config.json
================================================
{
"release-type": "node",
"packages": {
".": {
"release-type": "node",
"package-name": "5e-srd-api",
"changelog-sections": [
{
"type": "feat",
"section": "Features"
},
{
"type": "fix",
"section": "Bug Fixes"
},
{
"type": "deps",
"section": "Dependencies",
"hidden": false
}
]
}
}
}
================================================
FILE: src/controllers/api/2014/abilityScoreController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import AbilityScoreModel from '@/models/2014/abilityScore'
export default new SimpleController(AbilityScoreModel)
================================================
FILE: src/controllers/api/2014/alignmentController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import AlignmentModel from '@/models/2014/alignment'
export default new SimpleController(AlignmentModel)
================================================
FILE: src/controllers/api/2014/backgroundController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Background from '@/models/2014/background'
export default new SimpleController(Background)
================================================
FILE: src/controllers/api/2014/classController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import SimpleController from '@/controllers/simpleController'
import Class from '@/models/2014/class'
import Feature from '@/models/2014/feature'
import Level from '@/models/2014/level'
import Proficiency from '@/models/2014/proficiency'
import Spell from '@/models/2014/spell'
import Subclass from '@/models/2014/subclass'
import {
ClassLevelsQuerySchema,
LevelParamsSchema,
ShowParamsSchema,
SpellIndexQuerySchema
} from '@/schemas/schemas'
import { escapeRegExp, ResourceList } from '@/util'
const simpleController = new SimpleController(Class)
interface ShowLevelsForClassQuery {
'class.url': string
$or: Record<string, null | Record<string, RegExp>>[]
}
export const index = async (req: Request, res: Response, next: NextFunction) =>
simpleController.index(req, res, next)
export const show = async (req: Request, res: Response, next: NextFunction) =>
simpleController.show(req, res, next)
export const showLevelsForClass = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path and query parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
const validatedQuery = ClassLevelsQuerySchema.safeParse(req.query)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
if (!validatedQuery.success) {
return res
.status(400)
.json({ error: 'Invalid query parameters', details: validatedQuery.error.issues })
}
const { index } = validatedParams.data
const { subclass } = validatedQuery.data
const searchQueries: ShowLevelsForClassQuery = {
'class.url': '/api/2014/classes/' + index,
$or: [{ subclass: null }]
}
if (subclass !== undefined) {
searchQueries.$or.push({
'subclass.url': { $regex: new RegExp(escapeRegExp(subclass), 'i') }
})
}
const data = await Level.find(searchQueries).sort({ level: 'asc' })
if (data !== null && data !== undefined && data.length > 0) {
return res.status(200).json(data)
} else {
return res.status(404).json({ error: 'Not found' })
}
} catch (err) {
next(err)
}
}
export const showLevelForClass = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = LevelParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index, level } = validatedParams.data
const urlString = '/api/2014/classes/' + index + '/levels/' + level
const data = await Level.findOne({ url: urlString })
if (!data) return next()
return res.status(200).json(data)
} catch (err) {
next(err)
}
}
export const showMulticlassingForClass = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const urlString = '/api/2014/classes/' + req.params.index
const data = await Class.findOne({ url: urlString })
return res.status(200).json(data?.multi_classing)
} catch (err) {
next(err)
}
}
export const showSubclassesForClass = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2014/classes/' + index
const data = await Subclass.find({ 'class.url': urlString })
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ url: 'asc', level: 'asc' })
if (data !== null && data !== undefined && data.length > 0) {
return res.status(200).json(ResourceList(data))
} else {
return res.status(404).json({ error: 'Not found' })
}
} catch (err) {
next(err)
}
}
export const showStartingEquipmentForClass = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const data = await Class.findOne({ index: req.params.index })
return res.status(200).json({
starting_equipment: data?.starting_equipment,
starting_equipment_options: data?.starting_equipment_options
})
} catch (err) {
next(err)
}
}
export const showSpellcastingForClass = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const data = await Class.findOne({ index: index })
if (
data !== null &&
data !== undefined &&
data.spellcasting !== null &&
data.spellcasting !== undefined
) {
return res.status(200).json(data.spellcasting)
} else {
return res.status(404).json({ error: 'Not found' })
}
} catch (err) {
next(err)
}
}
export const showSpellsForClass = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedParams = ShowParamsSchema.safeParse(req.params)
const validatedQuery = SpellIndexQuerySchema.safeParse(req.query)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
if (!validatedQuery.success) {
return res
.status(400)
.json({ error: 'Invalid query parameters', details: validatedQuery.error.issues })
}
const { index } = validatedParams.data
const { level } = validatedQuery.data
// Check if class exists first
const classExists = await Class.findOne({ index }).lean()
if (!classExists) {
return res.status(404).json({ error: 'Not found' })
}
const urlString = '/api/2014/classes/' + index
const findQuery: { 'classes.url': string; level?: { $in: number[] } } = {
'classes.url': urlString
}
if (level !== undefined) {
findQuery.level = { $in: level.map(Number) }
}
const data = await Spell.find(findQuery)
.select({ index: 1, level: 1, name: 1, url: 1, _id: 0 })
.sort({ level: 'asc', url: 'asc' })
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
export const showSpellsForClassAndLevel = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
// Validate path parameters
const validatedParams = LevelParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index, level: classLevel } = validatedParams.data
// Find the level data for the class
const levelData = await Level.findOne({ 'class.index': index, level: classLevel }).lean()
let maxSpellLevel = -1 // Default to -1 indicating no spellcasting ability found
if (levelData?.spellcasting) {
maxSpellLevel = 0 // Has spellcasting, so at least cantrips (level 0) might be available
const spellcasting = levelData.spellcasting
for (let i = 9; i >= 1; i--) {
// Check if the spell slot exists and is greater than 0
const spellSlotKey = `spell_slots_level_${i}` as keyof typeof spellcasting
if (spellcasting[spellSlotKey] != null && spellcasting[spellSlotKey]! > 0) {
maxSpellLevel = i
break // Found the highest level
}
}
}
if (maxSpellLevel < 0) {
return res.status(200).json(ResourceList([]))
}
const urlString = '/api/2014/classes/' + index
// Find spells for the class with level <= maxSpellLevel
const spellData = await Spell.find({
'classes.url': urlString,
level: { $lte: maxSpellLevel, $gte: 0 } // Query uses maxSpellLevel
})
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
.lean() // Use lean for performance as we only read data
return res.status(200).json(ResourceList(spellData))
} catch (err) {
next(err)
}
}
export const showFeaturesForClass = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
// Check if class exists first
const classExists = await Class.findOne({ index }).lean()
if (!classExists) {
return res.status(404).json({ error: 'Not found' })
}
const urlString = '/api/2014/classes/' + index
const data = await Feature.find({
'class.url': urlString
})
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ level: 'asc', url: 'asc' })
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
export const showFeaturesForClassAndLevel = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
// Validate path parameters
const validatedParams = LevelParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index, level } = validatedParams.data
const urlString = '/api/2014/classes/' + index
const data = await Feature.find({
'class.url': urlString,
level
})
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ level: 'asc', url: 'asc' })
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
export const showProficienciesForClass = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
// Check if class exists first
const classExists = await Class.findOne({ index }).lean()
if (!classExists) {
return res.status(404).json({ error: 'Not found' })
}
const urlString = '/api/2014/classes/' + index
const data = await Proficiency.find({ 'classes.url': urlString })
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2014/conditionController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import ConditionModel from '@/models/2014/condition'
export default new SimpleController(ConditionModel)
================================================
FILE: src/controllers/api/2014/damageTypeController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import DamageType from '@/models/2014/damageType'
export default new SimpleController(DamageType)
================================================
FILE: src/controllers/api/2014/equipmentCategoryController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import EquipmentCategory from '@/models/2014/equipmentCategory'
export default new SimpleController(EquipmentCategory)
================================================
FILE: src/controllers/api/2014/equipmentController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Equipment from '@/models/2014/equipment'
export default new SimpleController(Equipment)
================================================
FILE: src/controllers/api/2014/featController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Feat from '@/models/2014/feat'
export default new SimpleController(Feat)
================================================
FILE: src/controllers/api/2014/featureController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Feature from '@/models/2014/feature'
export default new SimpleController(Feature)
================================================
FILE: src/controllers/api/2014/languageController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Language from '@/models/2014/language'
export default new SimpleController(Language)
================================================
FILE: src/controllers/api/2014/magicItemController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import MagicItem from '@/models/2014/magicItem'
import { NameQuerySchema, ShowParamsSchema } from '@/schemas/schemas'
import { escapeRegExp, redisClient, ResourceList } from '@/util'
interface IndexQuery {
name?: { $regex: RegExp }
}
export const index = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate query parameters
const validatedQuery = NameQuerySchema.safeParse(req.query)
if (!validatedQuery.success) {
return res
.status(400)
.json({ error: 'Invalid query parameters', details: validatedQuery.error.issues })
}
const { name } = validatedQuery.data
const searchQueries: IndexQuery = {}
if (name !== undefined) {
searchQueries.name = { $regex: new RegExp(escapeRegExp(name), 'i') }
}
const redisKey = req.originalUrl
const data = await redisClient.get(redisKey)
if (data !== null && data !== undefined && data !== '') {
res.status(200).json(JSON.parse(data))
} else {
const data = await MagicItem.find(searchQueries)
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
const jsonData = ResourceList(data)
redisClient.set(redisKey, JSON.stringify(jsonData))
return res.status(200).json(jsonData)
}
} catch (err) {
next(err)
}
}
export const show = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const data = await MagicItem.findOne({ index: index })
if (data === null || data === undefined) return next()
return res.status(200).json(data)
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2014/magicSchoolController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import MagicSchool from '@/models/2014/magicSchool'
export default new SimpleController(MagicSchool)
================================================
FILE: src/controllers/api/2014/monsterController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import Monster from '@/models/2014/monster'
import { MonsterIndexQuerySchema, ShowParamsSchema } from '@/schemas/schemas'
import { escapeRegExp, redisClient, ResourceList } from '@/util'
interface IndexQuery {
name?: { $regex: RegExp }
challenge_rating?: { $in: number[] }
}
export const index = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate query parameters
const validatedQuery = MonsterIndexQuerySchema.safeParse(req.query)
if (!validatedQuery.success) {
return res
.status(400)
.json({ error: 'Invalid query parameters', details: validatedQuery.error.issues })
}
const { name, challenge_rating } = validatedQuery.data
const searchQueries: IndexQuery = {}
if (name !== undefined) {
searchQueries.name = { $regex: new RegExp(escapeRegExp(name), 'i') }
}
if (challenge_rating !== undefined && challenge_rating.length > 0) {
searchQueries.challenge_rating = { $in: challenge_rating }
}
const redisKey = req.originalUrl
const data = await redisClient.get(redisKey)
if (data !== null) {
res.status(200).json(JSON.parse(data))
} else {
const data = await Monster.find(searchQueries)
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
const jsonData = ResourceList(data)
redisClient.set(redisKey, JSON.stringify(jsonData))
return res.status(200).json(jsonData)
}
} catch (err) {
next(err)
}
}
export const show = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const data = await Monster.findOne({ index: index })
if (!data) return next()
return res.status(200).json(data)
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2014/proficiencyController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Proficiency from '@/models/2014/proficiency'
export default new SimpleController(Proficiency)
================================================
FILE: src/controllers/api/2014/raceController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import SimpleController from '@/controllers/simpleController'
import Proficiency from '@/models/2014/proficiency'
import Race from '@/models/2014/race'
import Subrace from '@/models/2014/subrace'
import Trait from '@/models/2014/trait'
import { ShowParamsSchema } from '@/schemas/schemas'
import { ResourceList } from '@/util/data'
const simpleController = new SimpleController(Race)
export const index = async (req: Request, res: Response, next: NextFunction) =>
simpleController.index(req, res, next)
export const show = async (req: Request, res: Response, next: NextFunction) =>
simpleController.show(req, res, next)
export const showSubracesForRace = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2014/races/' + index
const data = await Subrace.find({ 'race.url': urlString }).select({
index: 1,
name: 1,
url: 1,
_id: 0
})
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
export const showTraitsForRace = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2014/races/' + index
const data = await Trait.find({ 'races.url': urlString }).select({
index: 1,
name: 1,
url: 1,
_id: 0
})
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
export const showProficienciesForRace = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2014/races/' + index
const data = await Proficiency.find({ 'races.url': urlString })
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2014/ruleController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import Rule from '@/models/2014/rule'
import { NameDescQuerySchema, ShowParamsSchema } from '@/schemas/schemas'
import { escapeRegExp, redisClient, ResourceList } from '@/util'
interface IndexQuery {
name?: { $regex: RegExp }
desc?: { $regex: RegExp }
}
export const index = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate query parameters
const validatedQuery = NameDescQuerySchema.safeParse(req.query)
if (!validatedQuery.success) {
return res
.status(400)
.json({ error: 'Invalid query parameters', details: validatedQuery.error.issues })
}
const { name, desc } = validatedQuery.data
const searchQueries: IndexQuery = {}
if (name !== undefined) {
searchQueries.name = { $regex: new RegExp(escapeRegExp(name), 'i') }
}
if (desc !== undefined) {
searchQueries.desc = { $regex: new RegExp(escapeRegExp(desc), 'i') }
}
const redisKey = req.originalUrl
const data = await redisClient.get(redisKey)
if (data !== null) {
res.status(200).json(JSON.parse(data))
} else {
const data = await Rule.find(searchQueries)
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
const jsonData = ResourceList(data)
redisClient.set(redisKey, JSON.stringify(jsonData))
return res.status(200).json(jsonData)
}
} catch (err) {
next(err)
}
}
export const show = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const data = await Rule.findOne({ index: index })
if (!data) return next()
return res.status(200).json(data)
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2014/ruleSectionController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import RuleSection from '@/models/2014/ruleSection'
import { NameDescQuerySchema, ShowParamsSchema } from '@/schemas/schemas'
import { escapeRegExp, redisClient, ResourceList } from '@/util'
interface IndexQuery {
name?: { $regex: RegExp }
desc?: { $regex: RegExp }
}
export const index = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate query parameters
const validatedQuery = NameDescQuerySchema.safeParse(req.query)
if (!validatedQuery.success) {
return res
.status(400)
.json({ error: 'Invalid query parameters', details: validatedQuery.error.issues })
}
const { name, desc } = validatedQuery.data
const searchQueries: IndexQuery = {}
if (name !== undefined) {
searchQueries.name = { $regex: new RegExp(escapeRegExp(name), 'i') }
}
if (desc !== undefined) {
searchQueries.desc = { $regex: new RegExp(escapeRegExp(desc), 'i') }
}
const redisKey = req.originalUrl
const data = await redisClient.get(redisKey)
if (data !== null) {
res.status(200).json(JSON.parse(data))
} else {
const data = await RuleSection.find(searchQueries)
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
const jsonData = ResourceList(data)
redisClient.set(redisKey, JSON.stringify(jsonData))
return res.status(200).json(jsonData)
}
} catch (err) {
next(err)
}
}
export const show = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const data = await RuleSection.findOne({ index: index })
if (!data) return next()
return res.status(200).json(data)
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2014/skillController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Skill from '@/models/2014/skill'
export default new SimpleController(Skill)
================================================
FILE: src/controllers/api/2014/spellController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import Spell from '@/models/2014/spell'
import { ShowParamsSchema, SpellIndexQuerySchema } from '@/schemas/schemas'
import { escapeRegExp, redisClient, ResourceList } from '@/util'
interface IndexQuery {
name?: { $regex: RegExp }
level?: { $in: number[] }
'school.name'?: { $in: RegExp[] }
}
export const index = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedQuery = SpellIndexQuerySchema.safeParse(req.query)
if (!validatedQuery.success) {
return res
.status(400)
.json({ error: 'Invalid query parameters', details: validatedQuery.error.issues })
}
const { name, level, school } = validatedQuery.data
const searchQueries: IndexQuery = {}
if (name !== undefined) {
searchQueries.name = { $regex: new RegExp(escapeRegExp(name), 'i') }
}
if (level !== undefined) {
searchQueries.level = { $in: level.map(Number) }
}
if (school !== undefined) {
const schoolRegex = school.map((s) => new RegExp(escapeRegExp(s), 'i'))
searchQueries['school.name'] = { $in: schoolRegex }
}
const redisKey = req.originalUrl
const data = await redisClient.get(redisKey)
if (data !== null) {
res.status(200).json(JSON.parse(data))
} else {
const data = await Spell.find(searchQueries)
.select({ index: 1, level: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
const jsonData = ResourceList(data)
redisClient.set(redisKey, JSON.stringify(jsonData))
return res.status(200).json(jsonData)
}
} catch (err) {
next(err)
}
}
export const show = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const data = await Spell.findOne({ index: index })
if (!data) return next()
return res.status(200).json(data)
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2014/subclassController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import SimpleController from '@/controllers/simpleController'
import Feature from '@/models/2014/feature'
import Level from '@/models/2014/level'
import Subclass from '@/models/2014/subclass'
import { LevelParamsSchema, ShowParamsSchema } from '@/schemas/schemas'
import { ResourceList } from '@/util/data'
const simpleController = new SimpleController(Subclass)
export const index = async (req: Request, res: Response, next: NextFunction) =>
simpleController.index(req, res, next)
export const show = async (req: Request, res: Response, next: NextFunction) =>
simpleController.show(req, res, next)
export const showLevelsForSubclass = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2014/subclasses/' + index
const data = await Level.find({ 'subclass.url': urlString }).sort({ level: 'asc' })
return res.status(200).json(data)
} catch (err) {
next(err)
}
}
export const showLevelForSubclass = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedParams = LevelParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index, level } = validatedParams.data
const urlString = '/api/2014/subclasses/' + index + '/levels/' + level
const data = await Level.findOne({ url: urlString })
if (!data) return next()
return res.status(200).json(data)
} catch (err) {
next(err)
}
}
export const showFeaturesForSubclass = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2014/subclasses/' + index
const data = await Feature.find({
'subclass.url': urlString
})
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ level: 'asc', url: 'asc' })
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
export const showFeaturesForSubclassAndLevel = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const validatedParams = LevelParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index, level } = validatedParams.data
const urlString = '/api/2014/subclasses/' + index
const data = await Feature.find({
level: level,
'subclass.url': urlString
})
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ level: 'asc', url: 'asc' })
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2014/subraceController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import SimpleController from '@/controllers/simpleController'
import Proficiency from '@/models/2014/proficiency'
import Subrace from '@/models/2014/subrace'
import Trait from '@/models/2014/trait'
import { ShowParamsSchema } from '@/schemas/schemas'
import { ResourceList } from '@/util/data'
const simpleController = new SimpleController(Subrace)
export const index = async (req: Request, res: Response, next: NextFunction) =>
simpleController.index(req, res, next)
export const show = async (req: Request, res: Response, next: NextFunction) =>
simpleController.show(req, res, next)
export const showTraitsForSubrace = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2014/subraces/' + index
const data = await Trait.find({ 'subraces.url': urlString }).select({
index: 1,
name: 1,
url: 1,
_id: 0
})
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
export const showProficienciesForSubrace = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2014/subraces/' + index
const data = await Proficiency.find({ 'races.url': urlString })
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2014/traitController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Trait from '@/models/2014/trait'
export default new SimpleController(Trait)
================================================
FILE: src/controllers/api/2014/weaponPropertyController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import WeaponProperty from '@/models/2014/weaponProperty'
export default new SimpleController(WeaponProperty)
================================================
FILE: src/controllers/api/2024/abilityScoreController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import AbilityScoreModel from '@/models/2024/abilityScore'
export default new SimpleController(AbilityScoreModel)
================================================
FILE: src/controllers/api/2024/alignmentController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import AlignmentModel from '@/models/2024/alignment'
export default new SimpleController(AlignmentModel)
================================================
FILE: src/controllers/api/2024/backgroundController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import BackgroundModel from '@/models/2024/background'
export default new SimpleController(BackgroundModel)
================================================
FILE: src/controllers/api/2024/conditionController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import ConditionModel from '@/models/2024/condition'
export default new SimpleController(ConditionModel)
================================================
FILE: src/controllers/api/2024/damageTypeController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import DamageTypeModel from '@/models/2024/damageType'
export default new SimpleController(DamageTypeModel)
================================================
FILE: src/controllers/api/2024/equipmentCategoryController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import EquipmentCategory from '@/models/2024/equipmentCategory'
export default new SimpleController(EquipmentCategory)
================================================
FILE: src/controllers/api/2024/equipmentController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Equipment from '@/models/2024/equipment'
export default new SimpleController(Equipment)
================================================
FILE: src/controllers/api/2024/featController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import FeatModel from '@/models/2024/feat'
export default new SimpleController(FeatModel)
================================================
FILE: src/controllers/api/2024/languageController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import LanguageModel from '@/models/2024/language'
export default new SimpleController(LanguageModel)
================================================
FILE: src/controllers/api/2024/magicItemController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import MagicItemModel from '@/models/2024/magicItem'
export default new SimpleController(MagicItemModel)
================================================
FILE: src/controllers/api/2024/magicSchoolController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import MagicSchoolModel from '@/models/2024/magicSchool'
export default new SimpleController(MagicSchoolModel)
================================================
FILE: src/controllers/api/2024/proficiencyController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import ProficiencyModel from '@/models/2024/proficiency'
export default new SimpleController(ProficiencyModel)
================================================
FILE: src/controllers/api/2024/skillController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Skill from '@/models/2024/skill'
export default new SimpleController(Skill)
================================================
FILE: src/controllers/api/2024/speciesController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import SimpleController from '@/controllers/simpleController'
import Species2024Model from '@/models/2024/species'
import Subspecies2024Model from '@/models/2024/subspecies'
import Trait2024Model from '@/models/2024/trait'
import { ShowParamsSchema } from '@/schemas/schemas'
import { ResourceList } from '@/util/data'
const simpleController = new SimpleController(Species2024Model)
export const index = async (req: Request, res: Response, next: NextFunction) =>
simpleController.index(req, res, next)
export const show = async (req: Request, res: Response, next: NextFunction) =>
simpleController.show(req, res, next)
export const showSubspeciesForSpecies = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2024/species/' + index
const data = await Subspecies2024Model.find({ 'species.url': urlString }).select({
index: 1,
name: 1,
url: 1,
_id: 0
})
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
export const showTraitsForSpecies = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2024/species/' + index
const data = await Trait2024Model.find({ 'species.url': urlString }).select({
index: 1,
name: 1,
url: 1,
_id: 0
})
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2024/subclassController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import SubclassModel from '@/models/2024/subclass'
export default new SimpleController(SubclassModel)
================================================
FILE: src/controllers/api/2024/subspeciesController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import SimpleController from '@/controllers/simpleController'
import Subspecies2024Model from '@/models/2024/subspecies'
import Trait2024Model from '@/models/2024/trait'
import { ShowParamsSchema } from '@/schemas/schemas'
import { ResourceList } from '@/util/data'
const simpleController = new SimpleController(Subspecies2024Model)
export const index = async (req: Request, res: Response, next: NextFunction) =>
simpleController.index(req, res, next)
export const show = async (req: Request, res: Response, next: NextFunction) =>
simpleController.show(req, res, next)
export const showTraitsForSubspecies = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data
const urlString = '/api/2024/subspecies/' + index
const data = await Trait2024Model.find({ 'subspecies.url': urlString }).select({
index: 1,
name: 1,
url: 1,
_id: 0
})
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/2024/traitController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import Trait2024Model from '@/models/2024/trait'
export default new SimpleController(Trait2024Model)
================================================
FILE: src/controllers/api/2024/weaponMasteryPropertyController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import WeaponMasteryPropertyModel from '@/models/2024/weaponMasteryProperty'
export default new SimpleController(WeaponMasteryPropertyModel)
================================================
FILE: src/controllers/api/2024/weaponPropertyController.ts
================================================
import SimpleController from '@/controllers/simpleController'
import WeaponPropertyModel from '@/models/2024/weaponProperty'
export default new SimpleController(WeaponPropertyModel)
================================================
FILE: src/controllers/api/imageController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import { awsRegion } from '@/util/environmentVariables'
const BUCKET_NAME = 'dnd-5e-api-images'
const AWS_REGION = awsRegion || 'us-east-1'
const show = async (req: Request, res: Response, next: NextFunction) => {
let key: string | undefined
try {
key = req.url.slice(1)
if (!key || !/^[a-zA-Z0-9/._-]+$/.test(key)) {
return res.status(400).send('Invalid image path')
}
const publicUrl = `https://${BUCKET_NAME}.s3.${AWS_REGION}.amazonaws.com/${key}`
const s3Response = await fetch(publicUrl)
if (!s3Response.ok) {
return res.status(s3Response.status).send(await s3Response.text())
}
const contentTypeFromHeader = s3Response.headers.get('content-type')
const actualContentType =
contentTypeFromHeader != null ? contentTypeFromHeader : 'application/octet-stream'
res.setHeader('Content-Type', actualContentType)
const contentLength = s3Response.headers.get('content-length')
if (typeof contentLength === 'string' && contentLength) {
res.setHeader('Content-Length', contentLength)
}
if (s3Response.body) {
const reader = s3Response.body.getReader()
while (true) {
const { done, value } = await reader.read()
if (done) break
res.write(value)
}
res.end()
} else {
throw new Error('Response body from S3 was null')
}
} catch (err: any) {
if (err !== null) {
res.status(404).end('File Not Found')
} else {
console.error('Error fetching image from S3:', err)
next(err)
}
}
}
export default {
show
}
================================================
FILE: src/controllers/api/v2014Controller.ts
================================================
import { NextFunction, Request, Response } from 'express'
import Collection from '@/models/2014/collection'
export const index = async (req: Request, res: Response, next: NextFunction) => {
try {
const data = await Collection.find({})
.select({ index: 1, _id: 0 })
.sort({ index: 'asc' })
.exec()
const apiIndex: Record<string, string> = {}
data.forEach((item) => {
if (item.index === 'levels') return
apiIndex[item.index] = `/api/2014/${item.index}`
})
return res.status(200).json(apiIndex)
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/api/v2024Controller.ts
================================================
import { NextFunction, Request, Response } from 'express'
import Collection from '@/models/2024/collection'
export const index = async (req: Request, res: Response, next: NextFunction) => {
try {
const data = await Collection.find({})
.select({ index: 1, _id: 0 })
.sort({ index: 'asc' })
.exec()
const apiIndex: Record<string, string> = {}
data.forEach((item) => {
if (item.index === 'levels') return
apiIndex[item.index] = `/api/2024/${item.index}`
})
return res.status(200).json(apiIndex)
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/apiController.ts
================================================
import { NextFunction, Request, Response } from 'express'
import Collection from '@/models/2014/collection'
export default async (req: Request, res: Response, next: NextFunction) => {
try {
const collections = await Collection.find({})
const colName = req.path.split('/')[1]
const colRequested = collections.find((col) => col.index === colName)
if (colRequested === undefined && colName !== '') {
res.sendStatus(404)
return
}
const queryString = req.originalUrl.split('?')?.[1]
const redirectUrl = '/api/2014' + req.path
const urlWithQuery = queryString === undefined ? redirectUrl : redirectUrl + '?' + queryString
res.redirect(301, urlWithQuery)
} catch (err) {
next(err)
}
}
================================================
FILE: src/controllers/docsController.ts
================================================
import { Request, Response } from 'express'
export default (req: Request, res: Response) => {
res.redirect('https://5e-bits.github.io/docs')
}
================================================
FILE: src/controllers/simpleController.ts
================================================
import { ReturnModelType } from '@typegoose/typegoose'
import { NextFunction, Request, Response } from 'express'
import { NameQuerySchema, ShowParamsSchema } from '@/schemas/schemas'
import { ResourceList } from '@/util/data'
import { escapeRegExp } from '@/util/regex'
interface IndexQuery {
name?: { $regex: RegExp }
}
class SimpleController {
Schema: ReturnModelType<any>
constructor(Schema: ReturnModelType<any>) {
this.Schema = Schema
}
async index(req: Request, res: Response, next: NextFunction) {
try {
// Validate query parameters
const validatedQuery = NameQuerySchema.safeParse(req.query)
if (!validatedQuery.success) {
// Handle validation errors - customize error response as needed
return res
.status(400)
.json({ error: 'Invalid query parameters', details: validatedQuery.error.issues })
}
const { name } = validatedQuery.data
const searchQueries: IndexQuery = {}
if (name !== undefined) {
// Use validated name
searchQueries.name = { $regex: new RegExp(escapeRegExp(name), 'i') }
}
const data = await this.Schema.find(searchQueries)
.select({ index: 1, name: 1, url: 1, _id: 0 })
.sort({ index: 'asc' })
.exec()
return res.status(200).json(ResourceList(data))
} catch (err) {
next(err)
}
}
async show(req: Request, res: Response, next: NextFunction) {
try {
// Validate path parameters
const validatedParams = ShowParamsSchema.safeParse(req.params)
if (!validatedParams.success) {
// Handle validation errors
return res
.status(400)
.json({ error: 'Invalid path parameters', details: validatedParams.error.issues })
}
const { index } = validatedParams.data // Use validated index
// Use validated index in the query
const data = await this.Schema.findOne({ index })
if (data === null) return next()
res.status(200).json(data)
} catch (err) {
next(err)
}
}
}
export default SimpleController
================================================
FILE: src/css/custom.css
================================================
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary-color: #d81921;
--primary-dark: #c62828;
--text-color: #263238;
--bg-dark: #21201e;
--bg-light: #f5f5f5;
--link-color: #1b95e0;
--spacing: 1rem;
}
body {
font-family: 'Helvetica', sans-serif;
color: var(--text-color);
line-height: 1.6;
padding-top: 3.5rem;
}
/* Typography */
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0 0 var(--spacing) 0;
font-weight: 900;
}
a {
color: var(--link-color);
text-decoration: none;
}
/* Navigation */
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
background: var(--primary-color);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
z-index: 1000;
height: 3.5rem;
overflow: hidden;
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--spacing);
}
.nav-links {
display: flex;
justify-content: flex-end;
list-style: none;
}
.nav-links li a {
display: block;
padding: 1rem;
color: white;
transition: background-color 0.3s;
}
.nav-links li.active a,
.nav-links li a:hover {
background-color: var(--primary-dark);
}
/* Header */
.header {
background-color: var(--bg-dark);
padding: 4rem 1rem;
text-align: center;
}
.header h1 {
color: white;
margin: 0 auto;
max-width: 800px;
font-size: 3.5rem;
line-height: 1.2;
}
.header h2 {
color: white;
margin: 0.5rem auto 0;
max-width: 800px;
font-weight: 300;
font-size: 1.5rem;
opacity: 0.9;
}
/* CTA Section */
.cta {
background-color: var(--bg-light);
padding: 3rem 1rem;
text-align: center;
font-size: 1.3rem;
font-weight: 300;
}
/* Interactive Section */
.content {
max-width: 1200px;
margin: 0 auto;
padding: 2rem var(--spacing);
}
.interactive-section {
max-width: 800px;
margin: 0 auto;
}
.api-input {
display: flex;
margin: 1rem 0;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}
.api-prefix {
padding: 0.5rem 1rem;
background: var(--bg-light);
border-right: 1px solid #ddd;
white-space: nowrap;
}
#interactive {
flex: 1;
padding: 0.5rem;
border: none;
outline: none;
}
.api-input button {
padding: 0.5rem 1.5rem;
background: var(--link-color);
color: white;
border: none;
cursor: pointer;
transition: background-color 0.3s;
}
.api-input button:hover {
background-color: #1576b3;
}
.hints {
font-size: 0.9rem;
margin: 1rem 0;
}
.output {
background: var(--bg-light);
padding: 1rem;
border-radius: 4px;
overflow: auto;
max-height: 400px;
white-space: pre-wrap;
word-break: break-word;
}
/* Responsive Design */
@media (max-width: 768px) {
.nav-links {
display: none;
}
.menu-toggle {
display: block;
}
.api-input {
flex-direction: column;
}
.api-prefix {
border-right: none;
border-bottom: 1px solid #ddd;
}
}
.menu-toggle {
display: none;
background: none;
border: none;
padding: 1rem;
cursor: pointer;
}
.menu-toggle span {
display: block;
width: 25px;
height: 3px;
background: white;
margin: 4px 0;
transition: 0.3s;
}
================================================
FILE: src/graphql/2014/common/choiceTypes.ts
================================================
import { Field, Int, ObjectType } from 'type-graphql'
import { AbilityScore } from '@/models/2014/abilityScore'
import { Language } from '@/models/2014/language'
import { Proficiency } from '@/models/2014/proficiency'
import { ProficiencyChoiceItem } from './unions'
// --- Language Choice Types ---
@ObjectType({ description: 'Represents a reference to a language within a choice option set.' })
export class LanguageChoiceOption {
@Field(() => String, { description: 'The type of this option (e.g., "reference").' })
option_type!: string
@Field(() => Language, { description: 'The resolved Language object.' })
item!: Language
}
@ObjectType({ description: 'Represents a set of language options for a choice.' })
export class LanguageChoiceOptionSet {
@Field(() => String, {
description: 'The type of the option set (e.g., resource_list, options_array).'
})
option_set_type!: string
@Field(() => [LanguageChoiceOption], {
description: 'The list of language options available.'
})
options!: LanguageChoiceOption[]
}
@ObjectType({ description: 'Represents a choice from a list of languages.' })
export class LanguageChoice {
@Field(() => Int, { description: 'The number of languages to choose from this list.' })
choose!: number
@Field(() => String, { description: 'The type of choice (e.g., languages).' })
type!: string
@Field(() => LanguageChoiceOptionSet, { description: 'The set of language options available.' })
from!: LanguageChoiceOptionSet
}
// --- Proficiency Choice Types ---
@ObjectType({
description:
'Represents a reference to a Proficiency or nested ProficiencyChoice within a choice option set.'
})
export class ProficiencyChoiceOption {
@Field(() => String, { description: 'The type of this option (e.g., "reference", "choice").' })
option_type!: string
@Field(() => ProficiencyChoiceItem, {
description: 'The resolved Proficiency object or nested ProficiencyChoice.'
})
item!: Proficiency | ProficiencyChoice
}
@ObjectType({ description: 'Represents a set of Proficiency options for a choice.' })
export class ProficiencyChoiceOptionSet {
@Field(() => String, {
description: 'The type of the option set (e.g., resource_list, options_array).'
})
option_set_type!: string
@Field(() => [ProficiencyChoiceOption], {
description: 'The list of Proficiency options available.'
})
options!: ProficiencyChoiceOption[]
}
@ObjectType({
description: 'Represents a choice from a list of Proficiencies or nested ProficiencyChoices.'
})
export class ProficiencyChoice {
@Field(() => Int, { description: 'The number of Proficiencies to choose from this list.' })
choose!: number
@Field(() => String, { description: 'The type of choice (e.g., proficiencies).' })
type!: string
@Field(() => ProficiencyChoiceOptionSet, {
description: 'The set of Proficiency options available.'
})
from!: ProficiencyChoiceOptionSet
@Field(() => String, { nullable: true, description: 'Description of the choice.' })
desc?: string
}
// --- Prerequisite Choice Types ---
@ObjectType({ description: 'A single prerequisite option' })
export class PrerequisiteChoiceOption {
@Field(() => String, { description: 'The type of option.' })
option_type!: string
@Field(() => AbilityScore, { description: 'The ability score required.' })
ability_score!: AbilityScore
@Field(() => Int, { description: 'The minimum score required.' })
minimum_score!: number
}
@ObjectType({ description: 'A set of prerequisite options to choose from' })
export class PrerequisiteChoiceOptionSet {
@Field(() => String, { description: 'The type of option set.' })
option_set_type!: string
@Field(() => [PrerequisiteChoiceOption], { description: 'The available options.' })
options!: PrerequisiteChoiceOption[]
}
@ObjectType({ description: 'A choice of prerequisites for multi-classing' })
export class PrerequisiteChoice {
@Field(() => Int, { description: 'Number of prerequisites to choose.' })
choose!: number
@Field(() => String, { description: 'Type of prerequisites to choose from.' })
type!: string
@Field(() => PrerequisiteChoiceOptionSet, { description: 'The options to choose from.' })
from!: PrerequisiteChoiceOptionSet
@Field(() => String, { nullable: true, description: 'Description of the prerequisite choice.' })
desc?: string
}
// --- Ability Score Bonus Choice Types ---
@ObjectType({ description: 'A single ability score bonus option' })
export class AbilityScoreBonusChoiceOption {
@Field(() => String, { description: 'The type of option.' })
option_type!: string
@Field(() => AbilityScore, { description: 'The ability score to increase.' })
ability_score!: AbilityScore
@Field(() => Int, { description: 'The amount to increase the ability score by.' })
bonus!: number
}
@ObjectType({ description: 'A set of ability score bonus options to choose from' })
export class AbilityScoreBonusChoiceOptionSet {
@Field(() => String, { description: 'The type of option set.' })
option_set_type!: string
@Field(() => [AbilityScoreBonusChoiceOption], { description: 'The available options.' })
options!: AbilityScoreBonusChoiceOption[]
}
@ObjectType({ description: 'A choice of ability score bonuses for a race' })
export class AbilityScoreBonusChoice {
@Field(() => Int, { description: 'Number of ability score bonuses to choose.' })
choose!: number
@Field(() => String, { description: 'Type of ability score bonuses to choose from.' })
type!: string
@Field(() => AbilityScoreBonusChoiceOptionSet, { description: 'The options to choose from.' })
from!: AbilityScoreBonusChoiceOptionSet
@Field(() => String, {
nullable: true,
description: 'Description of the ability score bonus choice.'
})
desc?: string
}
================================================
FILE: src/graphql/2014/common/equipmentTypes.ts
================================================
import { Field, Int, ObjectType } from 'type-graphql'
import { ArmorClass, Content, Equipment, Range, Speed, ThrowRange } from '@/models/2014/equipment'
import { WeaponProperty } from '@/models/2014/weaponProperty'
import { APIReference } from '@/models/common/apiReference'
import { Damage } from '@/models/common/damage'
import { IEquipment } from './interfaces'
@ObjectType({ description: 'Represents Armor equipment', implements: IEquipment })
export class Armor extends Equipment {
@Field(() => String, { description: 'Category of armor (e.g., Light, Medium, Heavy).' })
declare armor_category: string
@Field(() => ArmorClass, { description: 'Armor export Class details for this armor.' })
declare armor_class: ArmorClass
@Field(() => Int, {
nullable: true,
description: 'Minimum Strength score required to use this armor effectively.'
})
declare str_minimum?: number
@Field(() => Boolean, {
nullable: true,
description: 'Whether wearing the armor imposes disadvantage on Stealth checks.'
})
declare stealth_disadvantage?: boolean
}
@ObjectType({ description: 'Represents Weapon equipment', implements: IEquipment })
export class Weapon extends Equipment {
@Field(() => String, { description: 'Category of weapon (e.g., Simple, Martial).' })
declare weapon_category: string
@Field(() => String, { description: 'Range classification of weapon (e.g., Melee, Ranged).' })
declare weapon_range: string
@Field(() => String, { description: 'Range category for weapons (e.g., Melee, Ranged).' })
declare category_range: string
@Field(() => Damage, { nullable: true, description: 'Primary damage dealt by the weapon.' })
declare damage?: Damage
@Field(() => Damage, {
nullable: true,
description: 'Damage dealt when using the weapon with two hands.'
})
declare two_handed_damage?: Damage
@Field(() => Range, { nullable: true, description: 'Weapon range details.' })
declare range?: Range
@Field(() => ThrowRange, { nullable: true, description: 'Range when the weapon is thrown.' })
declare throw_range?: ThrowRange
@Field(() => [WeaponProperty], { nullable: true, description: 'Properties of the weapon.' })
declare properties?: APIReference[] // Resolved externally
}
@ObjectType({ description: 'Represents Tool equipment', implements: IEquipment })
export class Tool extends Equipment {
@Field(() => String, { description: "Category of tool (e.g., Artisan's Tools, Gaming Set)." })
declare tool_category: string
}
@ObjectType({ description: 'Represents Gear equipment (general purpose)', implements: IEquipment })
export class Gear extends Equipment {}
@ObjectType({
description: "Represents Gear that contains other items (e.g., Explorer's Pack)",
implements: IEquipment
})
export class Pack extends Gear {
@Field(() => [Content], { nullable: true, description: 'Items contained within the pack.' })
declare contents?: Content[]
}
@ObjectType({ description: 'Represents Ammunition equipment', implements: IEquipment })
export class Ammunition extends Gear {
@Field(() => Int, { description: 'Quantity of ammunition in the bundle.' })
declare quantity: number
}
@ObjectType({ description: 'Represents Vehicle equipment', implements: IEquipment })
export class Vehicle extends Equipment {
@Field(() => String, { description: 'Category of vehicle (e.g., Ship, Land).' })
declare vehicle_category: string
@Field(() => Speed, { nullable: true, description: 'Movement speed of the vehicle.' })
declare speed?: Speed
@Field(() => String, { nullable: true, description: 'Carrying capacity of the vehicle.' })
declare capacity?: string
}
================================================
FILE: src/graphql/2014/common/interfaces.ts
================================================
import { Field, Float, InterfaceType } from 'type-graphql'
import { Cost } from '@/models/2014/equipment'
@InterfaceType({
description: 'Common fields shared by all types of equipment and magic items.'
})
export abstract class IEquipment {
@Field(() => String, { description: 'The unique identifier for this equipment.' })
index!: string
@Field(() => String, { description: 'The name of the equipment.' })
name!: string
@Field(() => Cost, { description: 'Cost of the equipment in coinage.' })
cost!: Cost
@Field(() => Float, { nullable: true, description: 'Weight of the equipment in pounds.' })
weight?: number
@Field(() => [String], { nullable: true, description: 'Description of the equipment.' })
desc?: string[]
}
================================================
FILE: src/graphql/2014/common/unions.ts
================================================
import { createUnionType } from 'type-graphql'
import { ProficiencyChoice } from '@/graphql/2014/common/choiceTypes'
import { MagicItem } from '@/models/2014/magicItem'
import { Proficiency } from '@/models/2014/proficiency'
import { Ammunition, Armor, Gear, Pack, Tool, Vehicle, Weapon } from './equipmentTypes'
function resolveEquipmentType(
value: any
):
| typeof Armor
| typeof Weapon
| typeof Tool
| typeof Gear
| typeof Pack
| typeof Ammunition
| typeof Vehicle
| null {
if ('armor_class' in value) {
return Armor
}
if ('weapon_category' in value || 'weapon_range' in value) {
return Weapon
}
if ('tool_category' in value) {
return Tool
}
if ('vehicle_category' in value) {
return Vehicle
}
if ('contents' in value) {
return Pack
}
if (value.gear_category?.index === 'ammunition') {
return Ammunition
}
if ('gear_category' in value) {
return Gear
}
return null
}
export const EquipmentOrMagicItem = createUnionType({
name: 'EquipmentOrMagicItem',
types: () => {
return [Armor, Weapon, Tool, Gear, Pack, Ammunition, Vehicle, MagicItem] as const
},
resolveType: (value) => {
if ('rarity' in value) {
return MagicItem
}
const equipmentType = resolveEquipmentType(value)
if (equipmentType) {
return equipmentType
}
console.warn('Could not resolve type for EquipmentOrMagicItem:', value)
throw new Error('Could not resolve type for EquipmentOrMagicItem')
}
})
export const AnyEquipment = createUnionType({
name: 'AnyEquipment',
types: () => {
return [Armor, Weapon, Tool, Gear, Pack, Ammunition, Vehicle] as const
},
resolveType: (value) => {
const equipmentType = resolveEquipmentType(value)
if (equipmentType) {
return equipmentType
}
console.warn('Could not resolve type for AnyEquipment:', value)
return Gear
}
})
export const ProficiencyChoiceItem = createUnionType({
name: 'ProficiencyChoiceItem',
types: () => [Proficiency, ProficiencyChoice] as const,
resolveType: (value) => {
if (typeof value === 'object' && 'choose' in value && 'type' in value && 'from' in value) {
return ProficiencyChoice
}
return Proficiency
}
})
================================================
FILE: src/graphql/2014/resolvers/abilityScore/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum AbilityScoreOrderField {
NAME = 'name',
FULL_NAME = 'full_name'
}
export const ABILITY_SCORE_SORT_FIELD_MAP: Record<AbilityScoreOrderField, string> = {
[AbilityScoreOrderField.NAME]: 'name',
[AbilityScoreOrderField.FULL_NAME]: 'full_name'
}
registerEnumType(AbilityScoreOrderField, {
name: 'AbilityScoreOrderField',
description: 'Fields to sort Ability Scores by'
})
@InputType()
export class AbilityScoreOrder implements BaseOrderInterface<AbilityScoreOrderField> {
@Field(() => AbilityScoreOrderField)
by!: AbilityScoreOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => AbilityScoreOrder, { nullable: true })
then_by?: AbilityScoreOrder
}
export const AbilityScoreOrderSchema: z.ZodType<AbilityScoreOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(AbilityScoreOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: AbilityScoreOrderSchema.optional()
})
)
export const AbilityScoreArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
full_name: z.string().optional(),
order: AbilityScoreOrderSchema.optional()
})
export const AbilityScoreIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class AbilityScoreArgs extends BaseFilterArgs {
@Field(() => String, {
nullable: true,
description: 'Filter by ability score full name (case-insensitive, partial match)'
})
full_name?: string
@Field(() => AbilityScoreOrder, {
nullable: true,
description: 'Specify sorting order for ability scores.'
})
order?: AbilityScoreOrder
}
================================================
FILE: src/graphql/2014/resolvers/abilityScore/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { buildSortPipeline } from '@/graphql/common/args'
import { resolveMultipleReferences } from '@/graphql/utils/resolvers'
import AbilityScoreModel, { AbilityScore } from '@/models/2014/abilityScore'
import SkillModel, { Skill } from '@/models/2014/skill'
import { escapeRegExp } from '@/util'
import {
ABILITY_SCORE_SORT_FIELD_MAP,
AbilityScoreArgs,
AbilityScoreArgsSchema,
AbilityScoreIndexArgsSchema,
AbilityScoreOrderField
} from './args'
@Resolver(AbilityScore)
export class AbilityScoreResolver {
@Query(() => [AbilityScore], {
description: 'Gets all ability scores, optionally filtered by name and sorted.'
})
async abilityScores(
@Args(() => AbilityScoreArgs) args: AbilityScoreArgs
): Promise<AbilityScore[]> {
const validatedArgs = AbilityScoreArgsSchema.parse(args)
const query = AbilityScoreModel.find()
const filters: Record<string, any>[] = []
if (validatedArgs.name != null && validatedArgs.name !== '') {
filters.push({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
if (validatedArgs.full_name != null && validatedArgs.full_name !== '') {
filters.push({
full_name: { $regex: new RegExp(escapeRegExp(validatedArgs.full_name), 'i') }
})
}
if (filters.length > 0) {
query.where({ $and: filters })
}
const sortQuery = buildSortPipeline<AbilityScoreOrderField>({
order: validatedArgs.order,
sortFieldMap: ABILITY_SCORE_SORT_FIELD_MAP,
defaultSortField: AbilityScoreOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => AbilityScore, {
nullable: true,
description: 'Gets a single ability score by index.'
})
async abilityScore(@Arg('index', () => String) indexInput: string): Promise<AbilityScore | null> {
const { index } = AbilityScoreIndexArgsSchema.parse({ index: indexInput })
return AbilityScoreModel.findOne({ index }).lean()
}
@FieldResolver(() => [Skill])
async skills(@Root() abilityScore: AbilityScore): Promise<Skill[]> {
return resolveMultipleReferences(abilityScore.skills, SkillModel)
}
}
================================================
FILE: src/graphql/2014/resolvers/alignment/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum AlignmentOrderField {
NAME = 'name'
}
export const ALIGNMENT_SORT_FIELD_MAP: Record<AlignmentOrderField, string> = {
[AlignmentOrderField.NAME]: 'name'
}
registerEnumType(AlignmentOrderField, {
name: 'AlignmentOrderField',
description: 'Fields to sort Alignments by'
})
@InputType()
export class AlignmentOrder implements BaseOrderInterface<AlignmentOrderField> {
@Field(() => AlignmentOrderField)
by!: AlignmentOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => AlignmentOrder, { nullable: true })
then_by?: AlignmentOrder
}
export const AlignmentOrderSchema: z.ZodType<AlignmentOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(AlignmentOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: AlignmentOrderSchema.optional()
})
)
export const AlignmentArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
order: AlignmentOrderSchema.optional()
})
export const AlignmentIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class AlignmentArgs extends BaseFilterArgs {
@Field(() => AlignmentOrder, {
nullable: true,
description: 'Specify sorting order for alignments.'
})
order?: AlignmentOrder
}
================================================
FILE: src/graphql/2014/resolvers/alignment/resolver.ts
================================================
import { Arg, Args, Query, Resolver } from 'type-graphql'
import { buildSortPipeline } from '@/graphql/common/args'
import AlignmentModel, { Alignment } from '@/models/2014/alignment'
import { escapeRegExp } from '@/util'
import {
ALIGNMENT_SORT_FIELD_MAP,
AlignmentArgs,
AlignmentArgsSchema,
AlignmentIndexArgsSchema,
AlignmentOrderField
} from './args'
@Resolver(Alignment)
export class AlignmentResolver {
@Query(() => [Alignment], {
description: 'Gets all alignments, optionally filtered by name and sorted.'
})
async alignments(@Args(() => AlignmentArgs) args: AlignmentArgs): Promise<Alignment[]> {
const validatedArgs = AlignmentArgsSchema.parse(args)
const query = AlignmentModel.find()
if (validatedArgs.name != null && validatedArgs.name !== '') {
query.where({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
const sortQuery = buildSortPipeline<AlignmentOrderField>({
order: validatedArgs.order,
sortFieldMap: ALIGNMENT_SORT_FIELD_MAP,
defaultSortField: AlignmentOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => Alignment, { nullable: true, description: 'Gets a single alignment by index.' })
async alignment(@Arg('index', () => String) indexInput: string): Promise<Alignment | null> {
const { index } = AlignmentIndexArgsSchema.parse({ index: indexInput })
return AlignmentModel.findOne({ index }).lean()
}
}
================================================
FILE: src/graphql/2014/resolvers/background/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum BackgroundOrderField {
NAME = 'name'
}
export const BACKGROUND_SORT_FIELD_MAP: Record<BackgroundOrderField, string> = {
[BackgroundOrderField.NAME]: 'name'
}
registerEnumType(BackgroundOrderField, {
name: 'BackgroundOrderField',
description: 'Fields to sort Backgrounds by'
})
@InputType()
export class BackgroundOrder implements BaseOrderInterface<BackgroundOrderField> {
@Field(() => BackgroundOrderField)
by!: BackgroundOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => BackgroundOrder, { nullable: true })
then_by?: BackgroundOrder
}
export const BackgroundOrderSchema: z.ZodType<BackgroundOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(BackgroundOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: BackgroundOrderSchema.optional()
})
)
export const BackgroundArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
order: BackgroundOrderSchema.optional()
})
export const BackgroundIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class BackgroundArgs extends BaseFilterArgs {
@Field(() => BackgroundOrder, {
nullable: true,
description: 'Specify sorting order for backgrounds.'
})
order?: BackgroundOrder
}
================================================
FILE: src/graphql/2014/resolvers/background/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { LanguageChoice } from '@/graphql/2014/common/choiceTypes'
import {
IdealChoice,
IdealOption as ResolvedIdealOption
} from '@/graphql/2014/types/backgroundTypes'
import { StartingEquipmentChoice } from '@/graphql/2014/types/startingEquipment'
import { resolveLanguageChoice } from '@/graphql/2014/utils/resolvers'
import { resolveStartingEquipmentChoices } from '@/graphql/2014/utils/startingEquipmentResolver'
import { buildSortPipeline } from '@/graphql/common/args'
import { StringChoice } from '@/graphql/common/choiceTypes'
import {
resolveMultipleReferences,
resolveSingleReference,
resolveStringChoice
} from '@/graphql/utils/resolvers'
import AlignmentModel, { Alignment } from '@/models/2014/alignment'
import BackgroundModel, { Background, EquipmentRef } from '@/models/2014/background'
import EquipmentModel, { Equipment } from '@/models/2014/equipment'
import ProficiencyModel, { Proficiency } from '@/models/2014/proficiency'
import { Choice, IdealOption, OptionsArrayOptionSet } from '@/models/common/choice'
import { escapeRegExp } from '@/util'
import {
BACKGROUND_SORT_FIELD_MAP,
BackgroundArgs,
BackgroundArgsSchema,
BackgroundIndexArgsSchema,
BackgroundOrderField
} from './args'
@Resolver(Background)
export class BackgroundResolver {
@Query(() => [Background], {
description: 'Gets all backgrounds, optionally filtered by name and sorted by name.'
})
async backgrounds(@Args(() => BackgroundArgs) args: BackgroundArgs): Promise<Background[]> {
const validatedArgs = BackgroundArgsSchema.parse(args)
const query = BackgroundModel.find()
if (validatedArgs.name != null && validatedArgs.name !== '') {
query.where({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
const sortQuery = buildSortPipeline<BackgroundOrderField>({
order: validatedArgs.order,
sortFieldMap: BACKGROUND_SORT_FIELD_MAP,
defaultSortField: BackgroundOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => Background, { nullable: true, description: 'Gets a single background by index.' })
async background(@Arg('index', () => String) indexInput: string): Promise<Background | null> {
const { index } = BackgroundIndexArgsSchema.parse({ index: indexInput })
return BackgroundModel.findOne({ index }).lean()
}
@FieldResolver(() => [Proficiency], { nullable: true })
async starting_proficiencies(@Root() background: Background): Promise<Proficiency[]> {
return resolveMultipleReferences(background.starting_proficiencies, ProficiencyModel)
}
@FieldResolver(() => StringChoice, {
nullable: true,
description: 'Resolves the flaws choice for the background.'
})
async flaws(@Root() background: Background): Promise<StringChoice | null> {
return resolveStringChoice(background.flaws as Choice)
}
@FieldResolver(() => StringChoice, {
nullable: true,
description: 'Resolves the bonds choice for the background.'
})
async bonds(@Root() background: Background): Promise<StringChoice | null> {
return resolveStringChoice(background.bonds as Choice)
}
@FieldResolver(() => StringChoice, {
nullable: true,
description: 'Resolves the personality traits choice for the background.'
})
async personality_traits(@Root() background: Background): Promise<StringChoice | null> {
return resolveStringChoice(background.personality_traits as Choice)
}
@FieldResolver(() => IdealChoice, {
nullable: true,
description: 'Resolves the ideals choice for the background.'
})
async ideals(@Root() background: Background): Promise<IdealChoice> {
const choiceData = background.ideals as Choice
const optionSet = choiceData.from as OptionsArrayOptionSet
const resolvedIdealOptions: ResolvedIdealOption[] = []
if (Array.isArray(optionSet.options)) {
for (const option of optionSet.options) {
const idealOption = option as IdealOption
const resolvedAlignments = (await resolveMultipleReferences(
idealOption.alignments,
AlignmentModel
)) as Alignment[]
resolvedIdealOptions.push({
option_type: idealOption.option_type,
desc: idealOption.desc,
alignments: resolvedAlignments
})
}
}
return {
choose: choiceData.choose,
type: choiceData.type,
from: {
option_set_type: optionSet.option_set_type,
options: resolvedIdealOptions
}
}
}
@FieldResolver(() => LanguageChoice, {
nullable: true,
description: 'Resolves the language choices for the background.'
})
async language_options(@Root() background: Background): Promise<LanguageChoice | null> {
return resolveLanguageChoice(background.language_options as Choice)
}
@FieldResolver(() => [StartingEquipmentChoice], {
nullable: true,
description: 'Resolves starting equipment choices for the background.'
})
async starting_equipment_options(
@Root() background: Background
): Promise<StartingEquipmentChoice[] | null> {
return resolveStartingEquipmentChoices(background.starting_equipment_options)
}
}
@Resolver(EquipmentRef)
export class EquipmentRefResolver {
@FieldResolver(() => Equipment)
async equipment(@Root() equipmentRef: EquipmentRef): Promise<Equipment | null> {
return resolveSingleReference(equipmentRef.equipment, EquipmentModel)
}
}
================================================
FILE: src/graphql/2014/resolvers/class/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
import { NumberFilterInput, NumberFilterInputSchema } from '@/graphql/common/inputs'
export enum ClassOrderField {
NAME = 'name',
HIT_DIE = 'hit_die'
}
export const CLASS_SORT_FIELD_MAP: Record<ClassOrderField, string> = {
[ClassOrderField.NAME]: 'name',
[ClassOrderField.HIT_DIE]: 'hit_die'
}
registerEnumType(ClassOrderField, {
name: 'ClassOrderField',
description: 'Fields to sort Classes by'
})
@InputType()
export class ClassOrder implements BaseOrderInterface<ClassOrderField> {
@Field(() => ClassOrderField)
by!: ClassOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => ClassOrder, { nullable: true })
then_by?: ClassOrder
}
export const ClassOrderSchema: z.ZodType<ClassOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(ClassOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: ClassOrderSchema.optional()
})
)
export const ClassArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
hit_die: NumberFilterInputSchema.optional(),
order: ClassOrderSchema.optional()
})
export const ClassIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class ClassArgs extends BaseFilterArgs {
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by hit die size. Allows exact match, list of values, or a range.'
})
hit_die?: NumberFilterInput
@Field(() => ClassOrder, {
nullable: true,
description: 'Specify sorting order for classes. Allows nested sorting.'
})
order?: ClassOrder
}
================================================
FILE: src/graphql/2014/resolvers/class/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import {
PrerequisiteChoice,
PrerequisiteChoiceOption,
PrerequisiteChoiceOptionSet,
ProficiencyChoice
} from '@/graphql/2014/common/choiceTypes'
import { AnyEquipment } from '@/graphql/2014/common/unions'
import { StartingEquipmentChoice } from '@/graphql/2014/types/startingEquipment'
import { resolveProficiencyChoiceArray } from '@/graphql/2014/utils/resolvers'
import { resolveStartingEquipmentChoices } from '@/graphql/2014/utils/startingEquipmentResolver'
import { buildSortPipeline } from '@/graphql/common/args'
import { buildMongoQueryFromNumberFilter } from '@/graphql/common/inputs'
import { resolveMultipleReferences, resolveSingleReference } from '@/graphql/utils/resolvers'
import AbilityScoreModel, { AbilityScore } from '@/models/2014/abilityScore'
import ClassModel, {
Class,
ClassEquipment,
MultiClassing,
MultiClassingPrereq
} from '@/models/2014/class'
import EquipmentModel from '@/models/2014/equipment'
import LevelModel, { Level } from '@/models/2014/level'
import ProficiencyModel, { Proficiency } from '@/models/2014/proficiency'
import SpellModel, { Spell } from '@/models/2014/spell'
import SubclassModel, { Subclass } from '@/models/2014/subclass'
import { APIReference } from '@/models/common/apiReference'
import { Choice, OptionsArrayOptionSet, ScorePrerequisiteOption } from '@/models/common/choice'
import { escapeRegExp } from '@/util'
import {
CLASS_SORT_FIELD_MAP,
ClassArgs,
ClassArgsSchema,
ClassIndexArgsSchema,
ClassOrderField
} from './args'
@Resolver(Class)
export class ClassResolver {
@Query(() => [Class], {
description: 'Gets all classes, optionally filtering by name or hit die and sorted.'
})
async classes(@Args(() => ClassArgs) args: ClassArgs): Promise<Class[]> {
const validatedArgs = ClassArgsSchema.parse(args)
const query = ClassModel.find()
const filters: any[] = []
if (validatedArgs.name != null && validatedArgs.name !== '') {
filters.push({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
if (validatedArgs.hit_die) {
const hitDieQuery = buildMongoQueryFromNumberFilter(validatedArgs.hit_die)
if (hitDieQuery) {
filters.push({ hit_die: hitDieQuery })
}
}
if (filters.length > 0) {
query.where({ $and: filters })
}
const sortQuery = buildSortPipeline<ClassOrderField>({
order: validatedArgs.order,
sortFieldMap: CLASS_SORT_FIELD_MAP,
defaultSortField: ClassOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => Class, { nullable: true, description: 'Gets a single class by its index.' })
async class(@Arg('index', () => String) indexInput: string): Promise<Class | null> {
const { index } = ClassIndexArgsSchema.parse({ index: indexInput })
return ClassModel.findOne({ index }).lean()
}
@FieldResolver(() => [Level])
async class_levels(@Root() classData: Class): Promise<Level[]> {
return LevelModel.find({
'class.index': classData.index,
subclass: { $exists: false }
})
.sort({ level: 1 })
.lean()
}
@FieldResolver(() => [Proficiency])
async proficiencies(@Root() classData: Class): Promise<APIReference[]> {
return resolveMultipleReferences(classData.proficiencies, ProficiencyModel)
}
@FieldResolver(() => [AbilityScore])
async saving_throws(@Root() classData: Class): Promise<APIReference[]> {
return resolveMultipleReferences(classData.saving_throws, AbilityScoreModel)
}
@FieldResolver(() => [Subclass])
async subclasses(@Root() classData: Class): Promise<APIReference[]> {
return resolveMultipleReferences(classData.subclasses, SubclassModel)
}
@FieldResolver(() => [Spell])
async spells(@Root() classData: Class): Promise<Spell[]> {
return SpellModel.find({ 'classes.index': classData.index }).sort({ level: 1, name: 1 }).lean()
}
@FieldResolver(() => [ProficiencyChoice])
async proficiency_choices(@Root() classData: Class): Promise<ProficiencyChoice[]> {
return resolveProficiencyChoiceArray(classData.proficiency_choices)
}
@FieldResolver(() => [StartingEquipmentChoice], {
nullable: true,
description: 'Resolves starting equipment choices for the class.'
})
async starting_equipment_options(
@Root() classData: Class
): Promise<StartingEquipmentChoice[] | null> {
return resolveStartingEquipmentChoices(classData.starting_equipment_options)
}
}
@Resolver(MultiClassing)
export class MultiClassingResolver {
@FieldResolver(() => [Proficiency])
async proficiencies(@Root() multiClassing: MultiClassing): Promise<APIReference[]> {
return resolveMultipleReferences(multiClassing.proficiencies, ProficiencyModel)
}
@FieldResolver(() => [ProficiencyChoice])
async proficiency_choices(@Root() multiClassing: MultiClassing): Promise<ProficiencyChoice[]> {
return resolveProficiencyChoiceArray(multiClassing.proficiency_choices)
}
@FieldResolver(() => PrerequisiteChoice)
async prerequisite_options(
@Root() multiClassing: MultiClassing
): Promise<PrerequisiteChoice | null> {
return resolvePrerequisiteChoice(multiClassing.prerequisite_options)
}
}
@Resolver(MultiClassingPrereq)
export class MultiClassingPrereqResolver {
@FieldResolver(() => AbilityScore)
async ability_score(@Root() prerequisite: MultiClassingPrereq): Promise<APIReference | null> {
return resolveSingleReference(prerequisite.ability_score, AbilityScoreModel)
}
}
@Resolver(ClassEquipment)
export class ClassEquipmentResolver {
@FieldResolver(() => AnyEquipment, { nullable: true })
async equipment(@Root() classEquipment: ClassEquipment): Promise<typeof AnyEquipment | null> {
return resolveSingleReference(classEquipment.equipment, EquipmentModel)
}
}
async function resolvePrerequisiteChoice(
choiceData: Choice | undefined | null
): Promise<PrerequisiteChoice | null> {
if (!choiceData || !choiceData.type || typeof choiceData.choose !== 'number') {
return null
}
const gqlEmbeddedOptions: PrerequisiteChoiceOption[] = []
const optionsArraySet = choiceData.from as OptionsArrayOptionSet
for (const opt of optionsArraySet.options) {
if (opt.option_type === 'score_prerequisite') {
const scoreOpt = opt as ScorePrerequisiteOption
const abilityScore = await resolveSingleReference(scoreOpt.ability_score, AbilityScoreModel)
if (abilityScore != null) {
gqlEmbeddedOptions.push({
option_type: scoreOpt.option_type,
ability_score: abilityScore as AbilityScore,
minimum_score: scoreOpt.minimum_score
})
}
}
}
if (gqlEmbeddedOptions.length === 0 && optionsArraySet.options.length > 0) {
return null
}
const gqlOptionSet: PrerequisiteChoiceOptionSet = {
option_set_type: choiceData.from.option_set_type,
options: gqlEmbeddedOptions
}
return {
choose: choiceData.choose,
type: choiceData.type,
desc: choiceData.desc,
from: gqlOptionSet
}
}
================================================
FILE: src/graphql/2014/resolvers/condition/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum ConditionOrderField {
NAME = 'name'
}
export const CONDITION_SORT_FIELD_MAP: Record<ConditionOrderField, string> = {
[ConditionOrderField.NAME]: 'name'
}
registerEnumType(ConditionOrderField, {
name: 'ConditionOrderField',
description: 'Fields to sort Conditions by'
})
@InputType()
export class ConditionOrder implements BaseOrderInterface<ConditionOrderField> {
@Field(() => ConditionOrderField)
by!: ConditionOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => ConditionOrder, { nullable: true })
then_by?: ConditionOrder
}
export const ConditionOrderSchema: z.ZodType<ConditionOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(ConditionOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: ConditionOrderSchema.optional()
})
)
export const ConditionArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
order: ConditionOrderSchema.optional()
})
export const ConditionIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class ConditionArgs extends BaseFilterArgs {
@Field(() => ConditionOrder, {
nullable: true,
description: 'Specify sorting order for conditions.'
})
order?: ConditionOrder
}
================================================
FILE: src/graphql/2014/resolvers/condition/resolver.ts
================================================
import { Arg, Args, Query, Resolver } from 'type-graphql'
import { buildSortPipeline } from '@/graphql/common/args'
import ConditionModel, { Condition } from '@/models/2014/condition'
import { escapeRegExp } from '@/util'
import {
CONDITION_SORT_FIELD_MAP,
ConditionArgs,
ConditionArgsSchema,
ConditionIndexArgsSchema,
ConditionOrderField
} from './args'
@Resolver(Condition)
export class ConditionResolver {
@Query(() => [Condition], {
description: 'Gets all conditions, optionally filtered by name and sorted by name.'
})
async conditions(@Args(() => ConditionArgs) args: ConditionArgs): Promise<Condition[]> {
const validatedArgs = ConditionArgsSchema.parse(args)
const query = ConditionModel.find()
if (validatedArgs.name != null && validatedArgs.name !== '') {
query.where({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
const sortQuery = buildSortPipeline<ConditionOrderField>({
order: validatedArgs.order,
sortFieldMap: CONDITION_SORT_FIELD_MAP,
defaultSortField: ConditionOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => Condition, { nullable: true, description: 'Gets a single condition by index.' })
async condition(@Arg('index', () => String) indexInput: string): Promise<Condition | null> {
const { index } = ConditionIndexArgsSchema.parse({ index: indexInput })
return ConditionModel.findOne({ index }).lean()
}
}
================================================
FILE: src/graphql/2014/resolvers/damageType/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum DamageTypeOrderField {
NAME = 'name'
}
export const DAMAGE_TYPE_SORT_FIELD_MAP: Record<DamageTypeOrderField, string> = {
[DamageTypeOrderField.NAME]: 'name'
}
registerEnumType(DamageTypeOrderField, {
name: 'DamageTypeOrderField',
description: 'Fields to sort Damage Types by'
})
@InputType()
export class DamageTypeOrder implements BaseOrderInterface<DamageTypeOrderField> {
@Field(() => DamageTypeOrderField)
by!: DamageTypeOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => DamageTypeOrder, { nullable: true })
then_by?: DamageTypeOrder
}
export const DamageTypeOrderSchema: z.ZodType<DamageTypeOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(DamageTypeOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: DamageTypeOrderSchema.optional()
})
)
export const DamageTypeArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
order: DamageTypeOrderSchema.optional()
})
export const DamageTypeIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class DamageTypeArgs extends BaseFilterArgs {
@Field(() => DamageTypeOrder, {
nullable: true,
description: 'Specify sorting order for damage types.'
})
order?: DamageTypeOrder
}
================================================
FILE: src/graphql/2014/resolvers/damageType/resolver.ts
================================================
import { Arg, Args, Query, Resolver } from 'type-graphql'
import { buildSortPipeline } from '@/graphql/common/args'
import DamageTypeModel, { DamageType } from '@/models/2014/damageType'
import { escapeRegExp } from '@/util'
import {
DAMAGE_TYPE_SORT_FIELD_MAP,
DamageTypeArgs,
DamageTypeArgsSchema,
DamageTypeIndexArgsSchema,
DamageTypeOrderField
} from './args'
@Resolver(DamageType)
export class DamageTypeResolver {
@Query(() => [DamageType], {
description: 'Gets all damage types, optionally filtered by name and sorted by name.'
})
async damageTypes(@Args(() => DamageTypeArgs) args: DamageTypeArgs): Promise<DamageType[]> {
const validatedArgs = DamageTypeArgsSchema.parse(args)
const query = DamageTypeModel.find()
if (validatedArgs.name != null && validatedArgs.name !== '') {
query.where({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
const sortQuery = buildSortPipeline<DamageTypeOrderField>({
order: validatedArgs.order,
sortFieldMap: DAMAGE_TYPE_SORT_FIELD_MAP,
defaultSortField: DamageTypeOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => DamageType, { nullable: true, description: 'Gets a single damage type by index.' })
async damageType(@Arg('index', () => String) indexInput: string): Promise<DamageType | null> {
const { index } = DamageTypeIndexArgsSchema.parse({ index: indexInput })
return DamageTypeModel.findOne({ index }).lean()
}
}
================================================
FILE: src/graphql/2014/resolvers/equipment/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum EquipmentOrderField {
NAME = 'name',
WEIGHT = 'weight',
COST_QUANTITY = 'cost_quantity'
}
export const EQUIPMENT_SORT_FIELD_MAP: Record<EquipmentOrderField, string> = {
[EquipmentOrderField.NAME]: 'name',
[EquipmentOrderField.WEIGHT]: 'weight',
[EquipmentOrderField.COST_QUANTITY]: 'cost.quantity'
}
registerEnumType(EquipmentOrderField, {
name: 'EquipmentOrderField',
description: 'Fields to sort Equipment by'
})
@InputType()
export class EquipmentOrder implements BaseOrderInterface<EquipmentOrderField> {
@Field(() => EquipmentOrderField)
by!: EquipmentOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => EquipmentOrder, { nullable: true })
then_by?: EquipmentOrder
}
export const EquipmentOrderSchema: z.ZodType<EquipmentOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(EquipmentOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: EquipmentOrderSchema.optional() // Simplified
})
)
export const EquipmentArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
equipment_category: z.array(z.string()).optional(),
order: EquipmentOrderSchema.optional()
})
export const EquipmentIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class EquipmentArgs extends BaseFilterArgs {
@Field(() => [String], {
nullable: true,
description: 'Filter by one or more equipment category indices (e.g., ["weapon", "armor"])'
})
equipment_category?: string[]
@Field(() => EquipmentOrder, {
nullable: true,
description: 'Specify sorting order for equipment.'
})
order?: EquipmentOrder
}
================================================
FILE: src/graphql/2014/resolvers/equipment/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { AnyEquipment } from '@/graphql/2014/common/unions'
import { buildSortPipeline } from '@/graphql/common/args'
import { resolveMultipleReferences, resolveSingleReference } from '@/graphql/utils/resolvers'
import EquipmentModel, { Content, Equipment } from '@/models/2014/equipment'
import WeaponPropertyModel, { WeaponProperty } from '@/models/2014/weaponProperty'
import { APIReference } from '@/models/common/apiReference'
import { escapeRegExp } from '@/util'
import {
EQUIPMENT_SORT_FIELD_MAP,
EquipmentArgs,
EquipmentArgsSchema,
EquipmentIndexArgsSchema,
EquipmentOrderField
} from './args'
@Resolver(Equipment)
export class EquipmentResolver {
@Query(() => [AnyEquipment], {
description: 'Gets all equipment, optionally filtered and sorted.'
})
async equipments(
@Args(() => EquipmentArgs) args: EquipmentArgs
): Promise<Array<typeof AnyEquipment>> {
const validatedArgs = EquipmentArgsSchema.parse(args)
const query = EquipmentModel.find()
const filters: any[] = []
if (validatedArgs.name != null && validatedArgs.name !== '') {
filters.push({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
if (validatedArgs.equipment_category && validatedArgs.equipment_category.length > 0) {
filters.push({ 'equipment_category.index': { $in: validatedArgs.equipment_category } })
}
if (filters.length > 0) {
query.where({ $and: filters })
}
const sortQuery = buildSortPipeline<EquipmentOrderField>({
order: validatedArgs.order,
sortFieldMap: EQUIPMENT_SORT_FIELD_MAP,
defaultSortField: EquipmentOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => AnyEquipment, {
nullable: true,
description: 'Gets a single piece of equipment by its index.'
})
async equipment(
@Arg('index', () => String) indexInput: string
): Promise<typeof AnyEquipment | null> {
const { index } = EquipmentIndexArgsSchema.parse({ index: indexInput })
return EquipmentModel.findOne({ index }).lean()
}
@FieldResolver(() => [WeaponProperty], { nullable: true })
async properties(@Root() equipment: Equipment): Promise<WeaponProperty[] | null> {
if (!equipment.properties) return null
return resolveMultipleReferences(equipment.properties, WeaponPropertyModel)
}
}
@Resolver(Content)
export class ContentFieldResolver {
@FieldResolver(() => AnyEquipment, {
nullable: true,
description: 'Resolves the APIReference to the actual Equipment.'
})
async item(@Root() content: Content): Promise<typeof AnyEquipment | null> {
const itemRef: APIReference = content.item
if (!itemRef?.index) return null
return resolveSingleReference(itemRef, EquipmentModel)
}
}
================================================
FILE: src/graphql/2014/resolvers/equipmentCategory/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum EquipmentCategoryOrderField {
NAME = 'name'
}
export const EQUIPMENT_CATEGORY_SORT_FIELD_MAP: Record<EquipmentCategoryOrderField, string> = {
[EquipmentCategoryOrderField.NAME]: 'name'
}
registerEnumType(EquipmentCategoryOrderField, {
name: 'EquipmentCategoryOrderField',
description: 'Fields to sort Equipment Categories by'
})
@InputType()
export class EquipmentCategoryOrder implements BaseOrderInterface<EquipmentCategoryOrderField> {
@Field(() => EquipmentCategoryOrderField)
by!: EquipmentCategoryOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => EquipmentCategoryOrder, { nullable: true })
then_by?: EquipmentCategoryOrder
}
export const EquipmentCategoryOrderSchema: z.ZodType<EquipmentCategoryOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(EquipmentCategoryOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: EquipmentCategoryOrderSchema.optional()
})
)
export const EquipmentCategoryArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
order: EquipmentCategoryOrderSchema.optional()
})
export const EquipmentCategoryIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class EquipmentCategoryArgs extends BaseFilterArgs {
@Field(() => EquipmentCategoryOrder, {
nullable: true,
description: 'Specify sorting order for equipment categories.'
})
order?: EquipmentCategoryOrder
}
================================================
FILE: src/graphql/2014/resolvers/equipmentCategory/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { EquipmentOrMagicItem } from '@/graphql/2014/common/unions'
import { buildSortPipeline } from '@/graphql/common/args'
import EquipmentModel, { Equipment } from '@/models/2014/equipment'
import EquipmentCategoryModel, { EquipmentCategory } from '@/models/2014/equipmentCategory'
import MagicItemModel, { MagicItem } from '@/models/2014/magicItem'
import { escapeRegExp } from '@/util'
import {
EQUIPMENT_CATEGORY_SORT_FIELD_MAP,
EquipmentCategoryArgs,
EquipmentCategoryArgsSchema,
EquipmentCategoryIndexArgsSchema,
EquipmentCategoryOrderField
} from './args'
@Resolver(EquipmentCategory)
export class EquipmentCategoryResolver {
@Query(() => [EquipmentCategory], {
description: 'Gets all equipment categories, optionally filtered by name and sorted by name.'
})
async equipmentCategories(
@Args(() => EquipmentCategoryArgs) args: EquipmentCategoryArgs
): Promise<EquipmentCategory[]> {
const validatedArgs = EquipmentCategoryArgsSchema.parse(args)
const query = EquipmentCategoryModel.find()
if (validatedArgs.name != null && validatedArgs.name !== '') {
query.where({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
const sortQuery = buildSortPipeline<EquipmentCategoryOrderField>({
order: validatedArgs.order,
sortFieldMap: EQUIPMENT_CATEGORY_SORT_FIELD_MAP,
defaultSortField: EquipmentCategoryOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => EquipmentCategory, {
nullable: true,
description: 'Gets a single equipment category by index.'
})
async equipmentCategory(
@Arg('index', () => String) indexInput: string
): Promise<EquipmentCategory | null> {
const { index } = EquipmentCategoryIndexArgsSchema.parse({ index: indexInput })
return EquipmentCategoryModel.findOne({ index }).lean()
}
@FieldResolver(() => [EquipmentOrMagicItem])
async equipment(
@Root() equipmentCategory: EquipmentCategory
): Promise<(Equipment | MagicItem)[]> {
if (equipmentCategory.equipment.length === 0) {
return []
}
const equipmentIndices = equipmentCategory.equipment.map((ref) => ref.index)
// Fetch both Equipment and MagicItems matching the indices
const [equipments, magicItems] = await Promise.all([
EquipmentModel.find({ index: { $in: equipmentIndices } }).lean(),
MagicItemModel.find({ index: { $in: equipmentIndices } }).lean()
])
return [...equipments, ...magicItems]
}
}
================================================
FILE: src/graphql/2014/resolvers/feat/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum FeatOrderField {
NAME = 'name'
}
export const FEAT_SORT_FIELD_MAP: Record<FeatOrderField, string> = {
[FeatOrderField.NAME]: 'name'
}
registerEnumType(FeatOrderField, {
name: 'FeatOrderField',
description: 'Fields to sort Feats by'
})
@InputType()
export class FeatOrder implements BaseOrderInterface<FeatOrderField> {
@Field(() => FeatOrderField)
by!: FeatOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => FeatOrder, { nullable: true })
then_by?: FeatOrder
}
export const FeatOrderSchema: z.ZodType<FeatOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(FeatOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: FeatOrderSchema.optional()
})
)
export const FeatArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
order: FeatOrderSchema.optional()
})
export const FeatIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class FeatArgs extends BaseFilterArgs {
@Field(() => FeatOrder, {
nullable: true,
description: 'Specify sorting order for feats.'
})
order?: FeatOrder
}
================================================
FILE: src/graphql/2014/resolvers/feat/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { buildSortPipeline } from '@/graphql/common/args'
import { resolveSingleReference } from '@/graphql/utils/resolvers'
import AbilityScoreModel, { AbilityScore } from '@/models/2014/abilityScore'
import FeatModel, { Feat, Prerequisite } from '@/models/2014/feat'
import { escapeRegExp } from '@/util'
import {
FEAT_SORT_FIELD_MAP,
FeatArgs,
FeatArgsSchema,
FeatIndexArgsSchema,
FeatOrderField
} from './args'
@Resolver(Feat)
export class FeatResolver {
@Query(() => [Feat], {
description: 'Gets all feats, optionally filtered by name and sorted by name.'
})
async feats(@Args(() => FeatArgs) args: FeatArgs): Promise<Feat[]> {
const validatedArgs = FeatArgsSchema.parse(args)
const query = FeatModel.find()
if (validatedArgs.name != null && validatedArgs.name !== '') {
query.where({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
const sortQuery = buildSortPipeline<FeatOrderField>({
order: validatedArgs.order,
sortFieldMap: FEAT_SORT_FIELD_MAP,
defaultSortField: FeatOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => Feat, { nullable: true, description: 'Gets a single feat by index.' })
async feat(@Arg('index', () => String) indexInput: string): Promise<Feat | null> {
const { index } = FeatIndexArgsSchema.parse({ index: indexInput })
return FeatModel.findOne({ index }).lean()
}
}
@Resolver(Prerequisite)
export class PrerequisiteResolver {
@FieldResolver(() => AbilityScore, { nullable: true })
async ability_score(@Root() prerequisite: Prerequisite): Promise<AbilityScore | null> {
return resolveSingleReference(prerequisite.ability_score, AbilityScoreModel)
}
}
================================================
FILE: src/graphql/2014/resolvers/feature/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
import { NumberFilterInput, NumberFilterInputSchema } from '@/graphql/common/inputs'
export enum FeatureOrderField {
NAME = 'name',
LEVEL = 'level',
CLASS = 'class',
SUBCLASS = 'subclass'
}
export const FEATURE_SORT_FIELD_MAP: Record<FeatureOrderField, string> = {
[FeatureOrderField.NAME]: 'name',
[FeatureOrderField.LEVEL]: 'level',
[FeatureOrderField.CLASS]: 'class.name',
[FeatureOrderField.SUBCLASS]: 'subclass.name'
}
registerEnumType(FeatureOrderField, {
name: 'FeatureOrderField',
description: 'Fields to sort Features by'
})
@InputType()
export class FeatureOrder implements BaseOrderInterface<FeatureOrderField> {
@Field(() => FeatureOrderField)
by!: FeatureOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => FeatureOrder, { nullable: true })
then_by?: FeatureOrder
}
export const FeatureOrderSchema: z.ZodType<FeatureOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(FeatureOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: FeatureOrderSchema.optional()
})
)
export const FeatureArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
level: NumberFilterInputSchema.optional(),
class: z.array(z.string()).optional(),
subclass: z.array(z.string()).optional(),
order: FeatureOrderSchema.optional()
})
export const FeatureIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class FeatureArgs extends BaseFilterArgs {
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by level. Allows exact match, list, or range.'
})
level?: NumberFilterInput
@Field(() => [String], {
nullable: true,
description: 'Filter by one or more associated class indices'
})
class?: string[]
@Field(() => [String], {
nullable: true,
description: 'Filter by one or more associated subclass indices'
})
subclass?: string[]
@Field(() => FeatureOrder, {
nullable: true,
description: 'Specify sorting order for features.'
})
order?: FeatureOrder
}
================================================
FILE: src/graphql/2014/resolvers/feature/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { FeaturePrerequisiteUnion } from '@/graphql/2014/types/featureTypes'
import { buildSortPipeline } from '@/graphql/common/args'
import { buildMongoQueryFromNumberFilter } from '@/graphql/common/inputs'
import { resolveMultipleReferences, resolveSingleReference } from '@/graphql/utils/resolvers'
import ClassModel, { Class } from '@/models/2014/class'
import FeatureModel, {
Feature,
FeaturePrerequisite,
FeatureSpecific,
LevelPrerequisite,
SpellPrerequisite
} from '@/models/2014/feature'
import SpellModel from '@/models/2014/spell'
import SubclassModel, { Subclass } from '@/models/2014/subclass'
import { escapeRegExp } from '@/util'
import {
FEATURE_SORT_FIELD_MAP,
FeatureArgs,
FeatureArgsSchema,
FeatureIndexArgsSchema,
FeatureOrderField
} from './args'
@Resolver(Feature)
export class FeatureResolver {
@Query(() => [Feature], {
description: 'Gets all features, optionally filtered and sorted.'
})
async features(@Args(() => FeatureArgs) args: FeatureArgs): Promise<Feature[]> {
const validatedArgs = FeatureArgsSchema.parse(args)
const query = FeatureModel.find()
const filters: any[] = []
if (validatedArgs.name != null && validatedArgs.name !== '') {
filters.push({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
if (validatedArgs.level) {
const levelQuery = buildMongoQueryFromNumberFilter(validatedArgs.level)
if (levelQuery) {
filters.push({ level: levelQuery })
}
}
if (validatedArgs.class && validatedArgs.class.length > 0) {
filters.push({ 'class.index': { $in: validatedArgs.class } })
}
if (validatedArgs.subclass && validatedArgs.subclass.length > 0) {
filters.push({ 'subclass.index': { $in: validatedArgs.subclass } })
}
if (filters.length > 0) {
query.where({ $and: filters })
}
const sortQuery = buildSortPipeline<FeatureOrderField>({
order: validatedArgs.order,
sortFieldMap: FEATURE_SORT_FIELD_MAP,
defaultSortField: FeatureOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => Feature, { nullable: true, description: 'Gets a single feature by its index.' })
async feature(@Arg('index', () => String) indexInput: string): Promise<Feature | null> {
const { index } = FeatureIndexArgsSchema.parse({ index: indexInput })
return FeatureModel.findOne({ index }).lean()
}
@FieldResolver(() => Class, { nullable: true })
async class(@Root() feature: Feature): Promise<Class | null> {
return resolveSingleReference(feature.class, ClassModel)
}
@FieldResolver(() => Feature, { nullable: true })
async parent(@Root() feature: Feature): Promise<Feature | null> {
return resolveSingleReference(feature.parent, FeatureModel)
}
@FieldResolver(() => Subclass, { nullable: true })
async subclass(@Root() feature: Feature): Promise<Subclass | null> {
return resolveSingleReference(feature.subclass, SubclassModel)
}
@FieldResolver(() => [FeaturePrerequisiteUnion], {
nullable: true,
description: 'Resolves the prerequisites array, fetching referenced Features or Spells.'
})
async prerequisites(
@Root() feature: Feature
): Promise<Array<LevelPrerequisite | FeaturePrerequisite | SpellPrerequisite> | null> {
const prereqsData = feature.prerequisites
if (!prereqsData || prereqsData.length === 0) {
return null
}
const resolvedPrereqsPromises = prereqsData.map(
async (
prereq
): Promise<LevelPrerequisite | FeaturePrerequisite | SpellPrerequisite | null> => {
switch (prereq.type) {
case 'level': {
return prereq as LevelPrerequisite
}
case 'feature': {
const featureUrl = (prereq as FeaturePrerequisite).feature
const referencedFeature = await FeatureModel.findOne({ url: featureUrl }).lean()
if (referencedFeature) {
return {
type: 'feature',
feature: referencedFeature
} as unknown as FeaturePrerequisite
} else {
console.warn(`Could not find prerequisite feature with url: ${featureUrl}`)
return null
}
}
case 'spell': {
const spellUrl = (prereq as SpellPrerequisite).spell
const referencedSpell = await SpellModel.findOne({ url: spellUrl }).lean()
if (referencedSpell) {
return {
type: 'spell',
spell: referencedSpell
} as unknown as SpellPrerequisite
} else {
console.warn(`Could not find prerequisite spell with index: ${spellUrl}`)
return null
}
}
default: {
console.warn(`Unknown prerequisite type found: ${prereq.type}`)
return null
}
}
}
)
const resolvedPrereqs = (await Promise.all(resolvedPrereqsPromises)).filter(
(p) => p !== null
) as Array<LevelPrerequisite | FeaturePrerequisite | SpellPrerequisite>
return resolvedPrereqs.length > 0 ? resolvedPrereqs : null
}
}
@Resolver(FeatureSpecific)
export class FeatureSpecificResolver {
@FieldResolver(() => [Feature], { nullable: true })
async invocations(@Root() featureSpecific: FeatureSpecific): Promise<Feature[]> {
return resolveMultipleReferences(featureSpecific.invocations, FeatureModel)
}
}
================================================
FILE: src/graphql/2014/resolvers/index.ts
================================================
// This file will export an array of all resolver classes
import { AbilityScoreResolver } from './abilityScore/resolver'
import { AlignmentResolver } from './alignment/resolver'
import { BackgroundResolver, EquipmentRefResolver } from './background/resolver'
import {
ClassEquipmentResolver,
ClassResolver,
MultiClassingPrereqResolver,
MultiClassingResolver
} from './class/resolver'
import { ConditionResolver } from './condition/resolver'
import { DamageTypeResolver } from './damageType/resolver'
import { ContentFieldResolver, EquipmentResolver } from './equipment/resolver'
import { EquipmentCategoryResolver } from './equipmentCategory/resolver'
import { FeatResolver, PrerequisiteResolver } from './feat/resolver'
import { FeatureResolver, FeatureSpecificResolver } from './feature/resolver'
import { LanguageResolver } from './language/resolver'
import { LevelResolver } from './level/resolver'
import { MagicItemResolver } from './magicItem/resolver'
import { MagicSchoolResolver } from './magicSchool/resolver'
import {
ArmorClassArmorResolver,
ArmorClassConditionResolver,
ArmorClassSpellResolver,
DifficultyClassResolver,
MonsterActionResolver,
MonsterProficiencyResolver,
MonsterResolver,
SpecialAbilitySpellcastingResolver,
SpecialAbilitySpellResolver
} from './monster/resolver'
import { ProficiencyResolver } from './proficiency/resolver'
import { RaceAbilityBonusResolver, RaceResolver } from './race/resolver'
import { RuleResolver } from './rule/resolver'
import { RuleSectionResolver } from './ruleSection/resolver'
import { SkillResolver } from './skill/resolver'
import { SpellDamageResolver, SpellResolver, SpellDCResolver } from './spell/resolver'
import { SubclassResolver, SubclassSpellResolver } from './subclass/resolver'
import { SubraceAbilityBonusResolver, SubraceResolver } from './subrace/resolver'
import { ActionDamageResolver, TraitResolver, TraitSpecificResolver } from './trait/resolver'
import { WeaponPropertyResolver } from './weaponProperty/resolver'
const collectionResolvers = [
AbilityScoreResolver,
AlignmentResolver,
BackgroundResolver,
ClassResolver,
ConditionResolver,
DamageTypeResolver,
EquipmentCategoryResolver,
EquipmentResolver,
FeatResolver,
FeatureResolver,
LanguageResolver,
LevelResolver,
MagicItemResolver,
MagicSchoolResolver,
MonsterResolver,
ProficiencyResolver,
RaceResolver,
RuleResolver,
RuleSectionResolver,
SkillResolver,
SpellResolver,
SubclassResolver,
SubraceResolver,
TraitResolver,
WeaponPropertyResolver
] as const
const fieldResolvers = [
// Background
EquipmentRefResolver,
// Feat
PrerequisiteResolver,
// Trait
TraitSpecificResolver,
ActionDamageResolver,
// Feature
FeatureSpecificResolver,
// Race
RaceAbilityBonusResolver,
// Subrace
SubraceAbilityBonusResolver,
// Class
MultiClassingResolver,
MultiClassingPrereqResolver,
ClassEquipmentResolver,
// Subclass
SubclassSpellResolver,
// Spell
SpellDamageResolver,
SpellDCResolver,
// Equipment
ContentFieldResolver,
// Monster Field Resolvers
ArmorClassArmorResolver,
ArmorClassSpellResolver,
ArmorClassConditionResolver,
MonsterProficiencyResolver,
SpecialAbilitySpellcastingResolver,
SpecialAbilitySpellResolver,
MonsterActionResolver,
DifficultyClassResolver
] as const
// Export a new mutable array combining the readonly ones
export const resolvers = [...collectionResolvers, ...fieldResolvers] as const
================================================
FILE: src/graphql/2014/resolvers/language/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum LanguageOrderField {
NAME = 'name',
TYPE = 'type',
SCRIPT = 'script'
}
export const LANGUAGE_SORT_FIELD_MAP: Record<LanguageOrderField, string> = {
[LanguageOrderField.NAME]: 'name',
[LanguageOrderField.TYPE]: 'type',
[LanguageOrderField.SCRIPT]: 'script'
}
registerEnumType(LanguageOrderField, {
name: 'LanguageOrderField',
description: 'Fields to sort Languages by'
})
@InputType()
export class LanguageOrder implements BaseOrderInterface<LanguageOrderField> {
@Field(() => LanguageOrderField)
by!: LanguageOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => LanguageOrder, { nullable: true })
then_by?: LanguageOrder
}
export const LanguageOrderSchema: z.ZodType<LanguageOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(LanguageOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: LanguageOrderSchema.optional()
})
)
export const LanguageArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
type: z.string().optional(),
script: z.array(z.string()).optional(),
order: LanguageOrderSchema.optional()
})
export const LanguageIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class LanguageArgs extends BaseFilterArgs {
@Field(() => String, {
nullable: true,
description:
'Filter by language type (e.g., Standard, Exotic) - case-insensitive exact match after normalization'
})
type?: string
@Field(() => [String], {
nullable: true,
description: 'Filter by one or more language scripts (e.g., ["Common", "Elvish"])'
})
script?: string[]
@Field(() => LanguageOrder, {
nullable: true,
description: 'Specify sorting order for languages.'
})
order?: LanguageOrder
}
================================================
FILE: src/graphql/2014/resolvers/language/resolver.ts
================================================
import { Arg, Args, Query, Resolver } from 'type-graphql'
import { buildSortPipeline } from '@/graphql/common/args'
import LanguageModel, { Language } from '@/models/2014/language'
import { escapeRegExp } from '@/util'
import {
LANGUAGE_SORT_FIELD_MAP,
LanguageArgs,
LanguageArgsSchema,
LanguageIndexArgsSchema,
LanguageOrderField
} from './args'
@Resolver(Language)
export class LanguageResolver {
@Query(() => Language, { nullable: true, description: 'Gets a single language by its index.' })
async language(@Arg('index', () => String) indexInput: string): Promise<Language | null> {
const { index } = LanguageIndexArgsSchema.parse({ index: indexInput })
return LanguageModel.findOne({ index }).lean()
}
@Query(() => [Language], { description: 'Gets all languages, optionally filtered and sorted.' })
async languages(@Args(() => LanguageArgs) args: LanguageArgs): Promise<Language[]> {
const validatedArgs = LanguageArgsSchema.parse(args)
const query = LanguageModel.find()
const filters: any[] = []
if (validatedArgs.name != null && validatedArgs.name !== '') {
filters.push({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
if (validatedArgs.type != null && validatedArgs.type !== '') {
filters.push({ type: { $regex: new RegExp(escapeRegExp(validatedArgs.type), 'i') } })
}
if (validatedArgs.script && validatedArgs.script.length > 0) {
filters.push({ script: { $in: validatedArgs.script } })
}
if (filters.length > 0) {
query.where({ $and: filters })
}
const sortQuery = buildSortPipeline<LanguageOrderField>({
order: validatedArgs.order,
sortFieldMap: LANGUAGE_SORT_FIELD_MAP,
defaultSortField: LanguageOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip !== undefined) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit !== undefined) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
}
================================================
FILE: src/graphql/2014/resolvers/level/args.ts
================================================
import { ArgsType, Field, InputType, Int, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseIndexArgsSchema,
BaseOrderInterface,
BasePaginationArgs,
BasePaginationArgsSchema
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
import { NumberFilterInput, NumberFilterInputSchema } from '@/graphql/common/inputs'
export enum LevelOrderField {
LEVEL = 'level',
CLASS = 'class',
SUBCLASS = 'subclass'
}
export const LEVEL_SORT_FIELD_MAP: Record<LevelOrderField, string> = {
[LevelOrderField.LEVEL]: 'level',
[LevelOrderField.CLASS]: 'class.name',
[LevelOrderField.SUBCLASS]: 'subclass.name'
}
registerEnumType(LevelOrderField, {
name: 'LevelOrderField',
description: 'Fields to sort Levels by'
})
@InputType()
export class LevelOrder implements BaseOrderInterface<LevelOrderField> {
@Field(() => LevelOrderField)
by!: LevelOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => LevelOrder, { nullable: true })
then_by?: LevelOrder
}
export const LevelOrderSchema: z.ZodType<LevelOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(LevelOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: LevelOrderSchema.optional()
})
)
export const LevelArgsSchema = z.object({
...BasePaginationArgsSchema.shape,
class: z.array(z.string()).optional(),
subclass: z.array(z.string()).optional(),
level: NumberFilterInputSchema.optional(),
ability_score_bonuses: z.number().int().optional(),
prof_bonus: z.number().int().optional(),
order: LevelOrderSchema.optional()
})
export const LevelIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class LevelArgs extends BasePaginationArgs {
@Field(() => [String], { nullable: true, description: 'Filter by one or more class indices' })
class?: string[]
@Field(() => [String], { nullable: true, description: 'Filter by one or more subclass indices' })
subclass?: string[]
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by level. Allows exact match, list, or range.'
})
level?: NumberFilterInput
@Field(() => Int, {
nullable: true,
description: 'Filter by the exact number of ability score bonuses granted at this level.'
})
ability_score_bonuses?: number
@Field(() => Int, {
nullable: true,
description: 'Filter by the exact proficiency bonus at this level.'
})
prof_bonus?: number
@Field(() => LevelOrder, {
nullable: true,
description:
'Specify sorting order for levels. Allows nested sorting. Defaults to LEVEL ascending.'
})
order?: LevelOrder
}
================================================
FILE: src/graphql/2014/resolvers/level/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { buildSortPipeline } from '@/graphql/common/args'
import { buildMongoQueryFromNumberFilter } from '@/graphql/common/inputs'
import { resolveMultipleReferences, resolveSingleReference } from '@/graphql/utils/resolvers'
import ClassModel, { Class } from '@/models/2014/class'
import FeatureModel, { Feature } from '@/models/2014/feature'
import LevelModel, { Level } from '@/models/2014/level'
import SubclassModel, { Subclass } from '@/models/2014/subclass'
import {
LEVEL_SORT_FIELD_MAP,
LevelArgs,
LevelArgsSchema,
LevelIndexArgsSchema,
LevelOrderField
} from './args'
@Resolver(Level)
export class LevelResolver {
@Query(() => Level, {
nullable: true,
description:
'Gets a single level by its combined index (e.g., wizard-3-evocation or fighter-5).'
})
async level(@Arg('index', () => String) indexInput: string): Promise<Level | null> {
const { index } = LevelIndexArgsSchema.parse({ index: indexInput })
return LevelModel.findOne({ index }).lean()
}
@Query(() => [Level], { description: 'Gets all levels, optionally filtered and sorted.' })
async levels(@Args(() => LevelArgs) args: LevelArgs): Promise<Level[]> {
const validatedArgs = LevelArgsSchema.parse(args)
let query = LevelModel.find()
const filters: any[] = []
if (validatedArgs.class && validatedArgs.class.length > 0) {
filters.push({ 'class.index': { $in: validatedArgs.class } })
}
if (validatedArgs.subclass && validatedArgs.subclass.length > 0) {
filters.push({ 'subclass.index': { $in: validatedArgs.subclass } })
}
if (validatedArgs.level) {
const levelQuery = buildMongoQueryFromNumberFilter(validatedArgs.level)
if (levelQuery) {
filters.push({ level: levelQuery })
}
}
if (validatedArgs.ability_score_bonuses != null) {
filters.push({ ability_score_bonuses: validatedArgs.ability_score_bonuses })
}
if (validatedArgs.prof_bonus != null) {
filters.push({ prof_bonus: validatedArgs.prof_bonus })
}
if (filters.length > 0) {
query = query.where({ $and: filters })
}
const sortQuery = buildSortPipeline<LevelOrderField>({
order: validatedArgs.order,
sortFieldMap: LEVEL_SORT_FIELD_MAP,
defaultSortField: LevelOrderField.LEVEL
})
if (Object.keys(sortQuery).length > 0) {
query = query.sort(sortQuery)
}
if (validatedArgs.skip !== undefined) {
query = query.skip(validatedArgs.skip)
}
if (validatedArgs.limit !== undefined) {
query = query.limit(validatedArgs.limit)
}
return query.lean()
}
@FieldResolver(() => Class, { nullable: true })
async class(@Root() level: Level): Promise<Class | null> {
return resolveSingleReference(level.class, ClassModel)
}
@FieldResolver(() => Subclass, { nullable: true })
async subclass(@Root() level: Level): Promise<Subclass | null> {
return resolveSingleReference(level.subclass, SubclassModel)
}
@FieldResolver(() => [Feature])
async features(@Root() level: Level): Promise<Feature[]> {
return resolveMultipleReferences(level.features, FeatureModel)
}
}
================================================
FILE: src/graphql/2014/resolvers/magicItem/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum MagicItemOrderField {
NAME = 'name',
EQUIPMENT_CATEGORY = 'equipment_category',
RARITY = 'rarity'
}
export const MAGIC_ITEM_SORT_FIELD_MAP: Record<MagicItemOrderField, string> = {
[MagicItemOrderField.NAME]: 'name',
[MagicItemOrderField.EQUIPMENT_CATEGORY]: 'equipment_category.name',
[MagicItemOrderField.RARITY]: 'rarity.name'
}
registerEnumType(MagicItemOrderField, {
name: 'MagicItemOrderField',
description: 'Fields to sort Magic Items by'
})
@InputType()
export class MagicItemOrder implements BaseOrderInterface<MagicItemOrderField> {
@Field(() => MagicItemOrderField)
by!: MagicItemOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => MagicItemOrder, { nullable: true })
then_by?: MagicItemOrder
}
export const MagicItemOrderSchema: z.ZodType<MagicItemOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(MagicItemOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: MagicItemOrderSchema.optional()
})
)
export const MagicItemArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
equipment_category: z.array(z.string()).optional(),
rarity: z.array(z.string()).optional(),
order: MagicItemOrderSchema.optional()
})
export const MagicItemIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class MagicItemArgs extends BaseFilterArgs {
@Field(() => [String], {
nullable: true,
description: 'Filter by one or more equipment category indices (e.g., ["armor", "weapon"])'
})
equipment_category?: string[]
@Field(() => [String], {
nullable: true,
description: 'Filter by one or more rarity names (e.g., ["Rare", "Legendary"])'
})
rarity?: string[]
@Field(() => MagicItemOrder, {
nullable: true,
description: 'Specify sorting order for magic items.'
})
order?: MagicItemOrder
}
================================================
FILE: src/graphql/2014/resolvers/magicItem/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { buildSortPipeline } from '@/graphql/common/args'
import { resolveMultipleReferences, resolveSingleReference } from '@/graphql/utils/resolvers'
import EquipmentCategoryModel, { EquipmentCategory } from '@/models/2014/equipmentCategory'
import MagicItemModel, { MagicItem } from '@/models/2014/magicItem'
import { escapeRegExp } from '@/util'
import {
MAGIC_ITEM_SORT_FIELD_MAP,
MagicItemArgs,
MagicItemArgsSchema,
MagicItemIndexArgsSchema,
MagicItemOrderField
} from './args'
@Resolver(MagicItem)
export class MagicItemResolver {
@Query(() => [MagicItem], {
description: 'Gets all magic items, optionally filtered and sorted.'
})
async magicItems(@Args(() => MagicItemArgs) args: MagicItemArgs): Promise<MagicItem[]> {
const validatedArgs = MagicItemArgsSchema.parse(args)
let query = MagicItemModel.find()
const filters: any[] = []
if (validatedArgs.name != null && validatedArgs.name !== '') {
filters.push({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
if (validatedArgs.equipment_category && validatedArgs.equipment_category.length > 0) {
filters.push({ 'equipment_category.index': { $in: validatedArgs.equipment_category } })
}
if (validatedArgs.rarity && validatedArgs.rarity.length > 0) {
filters.push({ 'rarity.name': { $in: validatedArgs.rarity } })
}
if (filters.length > 0) {
query = query.where({ $and: filters })
}
const sortQuery = buildSortPipeline<MagicItemOrderField>({
order: validatedArgs.order,
sortFieldMap: MAGIC_ITEM_SORT_FIELD_MAP,
defaultSortField: MagicItemOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query = query.sort(sortQuery)
}
if (validatedArgs.skip !== undefined) {
query = query.skip(validatedArgs.skip)
}
if (validatedArgs.limit !== undefined) {
query = query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => MagicItem, { nullable: true, description: 'Gets a single magic item by index.' })
async magicItem(@Arg('index', () => String) indexInput: string): Promise<MagicItem | null> {
const { index } = MagicItemIndexArgsSchema.parse({ index: indexInput })
return MagicItemModel.findOne({ index }).lean()
}
@FieldResolver(() => EquipmentCategory, { nullable: true })
async equipment_category(@Root() magicItem: MagicItem): Promise<EquipmentCategory | null> {
return resolveSingleReference(magicItem.equipment_category, EquipmentCategoryModel)
}
@FieldResolver(() => [MagicItem], { nullable: true })
async variants(@Root() magicItem: MagicItem): Promise<MagicItem[]> {
return resolveMultipleReferences(magicItem.variants, MagicItemModel)
}
}
================================================
FILE: src/graphql/2014/resolvers/magicSchool/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
export enum MagicSchoolOrderField {
NAME = 'name'
}
export const MAGIC_SCHOOL_SORT_FIELD_MAP: Record<MagicSchoolOrderField, string> = {
[MagicSchoolOrderField.NAME]: 'name'
}
registerEnumType(MagicSchoolOrderField, {
name: 'MagicSchoolOrderField',
description: 'Fields to sort Magic Schools by'
})
@InputType()
export class MagicSchoolOrder implements BaseOrderInterface<MagicSchoolOrderField> {
@Field(() => MagicSchoolOrderField)
by!: MagicSchoolOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => MagicSchoolOrder, { nullable: true })
then_by?: MagicSchoolOrder
}
export const MagicSchoolOrderSchema: z.ZodType<MagicSchoolOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(MagicSchoolOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: MagicSchoolOrderSchema.optional()
})
)
export const MagicSchoolArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
order: MagicSchoolOrderSchema.optional()
})
export const MagicSchoolIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class MagicSchoolArgs extends BaseFilterArgs {
@Field(() => MagicSchoolOrder, {
nullable: true,
description: 'Specify sorting order for magic schools.'
})
order?: MagicSchoolOrder
}
================================================
FILE: src/graphql/2014/resolvers/magicSchool/resolver.ts
================================================
import { Arg, Args, Query, Resolver } from 'type-graphql'
import { buildSortPipeline } from '@/graphql/common/args'
import MagicSchoolModel, { MagicSchool } from '@/models/2014/magicSchool'
import { escapeRegExp } from '@/util'
import {
MAGIC_SCHOOL_SORT_FIELD_MAP,
MagicSchoolArgs,
MagicSchoolArgsSchema,
MagicSchoolIndexArgsSchema,
MagicSchoolOrderField
} from './args'
@Resolver(MagicSchool)
export class MagicSchoolResolver {
@Query(() => [MagicSchool], {
description: 'Gets all magic schools, optionally filtered by name and sorted by name.'
})
async magicSchools(@Args(() => MagicSchoolArgs) args: MagicSchoolArgs): Promise<MagicSchool[]> {
const validatedArgs = MagicSchoolArgsSchema.parse(args)
const query = MagicSchoolModel.find()
if (validatedArgs.name != null && validatedArgs.name !== '') {
query.where({ name: { $regex: new RegExp(escapeRegExp(validatedArgs.name), 'i') } })
}
const sortQuery = buildSortPipeline<MagicSchoolOrderField>({
order: validatedArgs.order,
sortFieldMap: MAGIC_SCHOOL_SORT_FIELD_MAP,
defaultSortField: MagicSchoolOrderField.NAME
})
if (Object.keys(sortQuery).length > 0) {
query.sort(sortQuery)
}
if (validatedArgs.skip) {
query.skip(validatedArgs.skip)
}
if (validatedArgs.limit) {
query.limit(validatedArgs.limit)
}
return query.lean()
}
@Query(() => MagicSchool, {
nullable: true,
description: 'Gets a single magic school by index.'
})
async magicSchool(@Arg('index', () => String) indexInput: string): Promise<MagicSchool | null> {
const { index } = MagicSchoolIndexArgsSchema.parse({ index: indexInput })
return MagicSchoolModel.findOne({ index }).lean()
}
}
================================================
FILE: src/graphql/2014/resolvers/monster/args.ts
================================================
import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'
import { z } from 'zod'
import {
BaseFilterArgs,
BaseFilterArgsSchema,
BaseIndexArgsSchema,
BaseOrderInterface
} from '@/graphql/common/args'
import { OrderByDirection } from '@/graphql/common/enums'
import { NumberFilterInput, NumberFilterInputSchema } from '@/graphql/common/inputs'
export enum MonsterOrderField {
NAME = 'name',
TYPE = 'type',
SIZE = 'size',
CHALLENGE_RATING = 'challenge_rating',
STRENGTH = 'strength',
DEXTERITY = 'dexterity',
CONSTITUTION = 'constitution',
INTELLIGENCE = 'intelligence',
WISDOM = 'wisdom',
CHARISMA = 'charisma'
}
export const MONSTER_SORT_FIELD_MAP: Record<MonsterOrderField, string> = {
[MonsterOrderField.NAME]: 'name',
[MonsterOrderField.TYPE]: 'type',
[MonsterOrderField.SIZE]: 'size',
[MonsterOrderField.CHALLENGE_RATING]: 'challenge_rating',
[MonsterOrderField.STRENGTH]: 'strength',
[MonsterOrderField.DEXTERITY]: 'dexterity',
[MonsterOrderField.CONSTITUTION]: 'constitution',
[MonsterOrderField.INTELLIGENCE]: 'intelligence',
[MonsterOrderField.WISDOM]: 'wisdom',
[MonsterOrderField.CHARISMA]: 'charisma'
}
registerEnumType(MonsterOrderField, {
name: 'MonsterOrderField',
description: 'Fields to sort Monsters by'
})
@InputType()
export class MonsterOrder implements BaseOrderInterface<MonsterOrderField> {
@Field(() => MonsterOrderField)
by!: MonsterOrderField
@Field(() => OrderByDirection)
direction!: OrderByDirection
@Field(() => MonsterOrder, { nullable: true })
then_by?: MonsterOrder
}
export const MonsterOrderSchema: z.ZodType<MonsterOrder> = z.lazy(() =>
z.object({
by: z.nativeEnum(MonsterOrderField),
direction: z.nativeEnum(OrderByDirection),
then_by: MonsterOrderSchema.optional()
})
)
export const MonsterArgsSchema = z.object({
...BaseFilterArgsSchema.shape,
type: z.string().optional(),
subtype: z.string().optional(),
challenge_rating: NumberFilterInputSchema.optional(),
size: z.string().optional(),
xp: NumberFilterInputSchema.optional(),
strength: NumberFilterInputSchema.optional(),
dexterity: NumberFilterInputSchema.optional(),
constitution: NumberFilterInputSchema.optional(),
intelligence: NumberFilterInputSchema.optional(),
wisdom: NumberFilterInputSchema.optional(),
charisma: NumberFilterInputSchema.optional(),
damage_vulnerabilities: z.array(z.string()).optional(),
damage_resistances: z.array(z.string()).optional(),
damage_immunities: z.array(z.string()).optional(),
condition_immunities: z.array(z.string()).optional(),
order: MonsterOrderSchema.optional()
})
export const MonsterIndexArgsSchema = BaseIndexArgsSchema
@ArgsType()
export class MonsterArgs extends BaseFilterArgs {
@Field(() => String, {
nullable: true,
description: 'Filter by monster type (case-insensitive, exact match, e.g., "beast")'
})
type?: string
@Field(() => String, {
nullable: true,
description: 'Filter by monster subtype (case-insensitive, exact match, e.g., "goblinoid")'
})
subtype?: string
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by challenge rating'
})
challenge_rating?: NumberFilterInput
@Field(() => String, {
nullable: true,
description: 'Filter by monster size (exact match, e.g., "Medium")'
})
size?: string
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by monster XP'
})
xp?: NumberFilterInput
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by strength score'
})
strength?: NumberFilterInput
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by dexterity score'
})
dexterity?: NumberFilterInput
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by constitution score'
})
constitution?: NumberFilterInput
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by intelligence score'
})
intelligence?: NumberFilterInput
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by wisdom score'
})
wisdom?: NumberFilterInput
@Field(() => NumberFilterInput, {
nullable: true,
description: 'Filter by charisma score'
})
charisma?: NumberFilterInput
@Field(() => [String], {
nullable: true,
description: 'Filter by damage vulnerability indices'
})
damage_vulnerabilities?: string[]
@Field(() => [String], {
nullable: true,
description: 'Filter by damage resistance indices'
})
damage_resistances?: string[]
@Field(() => [String], {
nullable: true,
description: 'Filter by damage immunity indices'
})
damage_immunities?: string[]
@Field(() => [String], {
nullable: true,
description: 'Filter by condition immunity indices'
})
condition_immunities?: string[]
@Field(() => MonsterOrder, {
nullable: true,
description: 'Specify sorting order for monsters.'
})
order?: MonsterOrder
}
================================================
FILE: src/graphql/2014/resolvers/monster/resolver.ts
================================================
import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { Armor } from '@/graphql/2014/common/equipmentTypes'
import {
DamageOrDamageChoiceUnion,
ActionChoice,
ActionChoiceOption,
BreathChoice,
BreathChoiceOption,
DamageChoice,
DamageChoiceOption,
MultipleActionChoiceOption,
MonsterArmorClassUnion
} from '@/graphql/2014/types/monsterTypes'
import { normalizeCount } from '@/graphql/2014/utils/helpers'
import { buildSortPipeline } from '@/graphql/common/args'
import { buildMongoQueryFromNumberFilter } from '@/graphql/common/inputs'
import { SpellSlotCount } from '@/graphql/common/types'
import { resolveMultipleReferences, resolveSingleReference } from '@/graphql/utils/resolvers'
import AbilityScoreModel, { AbilityScore } from '@/models/2014/abilityScore'
import ConditionModel, { Condition } from '@/models/2014/condition'
import DamageTypeModel, { DamageType } from '@/models/2014/damageType'
import EquipmentModel from '@/models/2014/equipment'
import MonsterModel, {
ArmorClassArmor,
ArmorClassCondition,
ArmorClassSpell,
Monster,
MonsterAction,
MonsterProficiency,
SpecialAbilitySpell,
SpecialAbilitySpellcasting
} from '@/models/2014/monster'
i
gitextract_5nuvmdzy/ ├── .dockerignore ├── .github/ │ ├── CODEOWNERS │ ├── FUNDING.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── actions/ │ │ └── validate-pr-title/ │ │ └── action.yml │ ├── dependabot.yml │ └── workflows/ │ ├── ci.yml │ ├── codeql-analysis.yml │ ├── lint-pr.yml │ ├── release-please.yml │ └── release.yml ├── .gitignore ├── .nvmrc ├── .prettierrc ├── .redocly.yaml ├── CHANGELOG.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── app.json ├── docker-compose.yml ├── eslint.config.js ├── heroku.yml ├── nodemon.json ├── openapi-to-postman.json ├── package.json ├── release-please-config.json ├── src/ │ ├── controllers/ │ │ ├── api/ │ │ │ ├── 2014/ │ │ │ │ ├── abilityScoreController.ts │ │ │ │ ├── alignmentController.ts │ │ │ │ ├── backgroundController.ts │ │ │ │ ├── classController.ts │ │ │ │ ├── conditionController.ts │ │ │ │ ├── damageTypeController.ts │ │ │ │ ├── equipmentCategoryController.ts │ │ │ │ ├── equipmentController.ts │ │ │ │ ├── featController.ts │ │ │ │ ├── featureController.ts │ │ │ │ ├── languageController.ts │ │ │ │ ├── magicItemController.ts │ │ │ │ ├── magicSchoolController.ts │ │ │ │ ├── monsterController.ts │ │ │ │ ├── proficiencyController.ts │ │ │ │ ├── raceController.ts │ │ │ │ ├── ruleController.ts │ │ │ │ ├── ruleSectionController.ts │ │ │ │ ├── skillController.ts │ │ │ │ ├── spellController.ts │ │ │ │ ├── subclassController.ts │ │ │ │ ├── subraceController.ts │ │ │ │ ├── traitController.ts │ │ │ │ └── weaponPropertyController.ts │ │ │ ├── 2024/ │ │ │ │ ├── abilityScoreController.ts │ │ │ │ ├── alignmentController.ts │ │ │ │ ├── backgroundController.ts │ │ │ │ ├── conditionController.ts │ │ │ │ ├── damageTypeController.ts │ │ │ │ ├── equipmentCategoryController.ts │ │ │ │ ├── equipmentController.ts │ │ │ │ ├── featController.ts │ │ │ │ ├── languageController.ts │ │ │ │ ├── magicItemController.ts │ │ │ │ ├── magicSchoolController.ts │ │ │ │ ├── proficiencyController.ts │ │ │ │ ├── skillController.ts │ │ │ │ ├── speciesController.ts │ │ │ │ ├── subclassController.ts │ │ │ │ ├── subspeciesController.ts │ │ │ │ ├── traitController.ts │ │ │ │ ├── weaponMasteryPropertyController.ts │ │ │ │ └── weaponPropertyController.ts │ │ │ ├── imageController.ts │ │ │ ├── v2014Controller.ts │ │ │ └── v2024Controller.ts │ │ ├── apiController.ts │ │ ├── docsController.ts │ │ └── simpleController.ts │ ├── css/ │ │ └── custom.css │ ├── graphql/ │ │ ├── 2014/ │ │ │ ├── common/ │ │ │ │ ├── choiceTypes.ts │ │ │ │ ├── equipmentTypes.ts │ │ │ │ ├── interfaces.ts │ │ │ │ └── unions.ts │ │ │ ├── resolvers/ │ │ │ │ ├── abilityScore/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── alignment/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── background/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── class/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── condition/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── damageType/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── equipment/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── equipmentCategory/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── feat/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── feature/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── index.ts │ │ │ │ ├── language/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── level/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── magicItem/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── magicSchool/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── monster/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── proficiency/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── race/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── rule/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── ruleSection/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── skill/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── spell/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── subclass/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── subrace/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── trait/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ └── weaponProperty/ │ │ │ │ ├── args.ts │ │ │ │ └── resolver.ts │ │ │ ├── types/ │ │ │ │ ├── backgroundTypes.ts │ │ │ │ ├── featureTypes.ts │ │ │ │ ├── monsterTypes.ts │ │ │ │ ├── proficiencyTypes.ts │ │ │ │ ├── startingEquipment/ │ │ │ │ │ ├── choice.ts │ │ │ │ │ ├── common.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── optionSet.ts │ │ │ │ ├── subclassTypes.ts │ │ │ │ └── traitTypes.ts │ │ │ └── utils/ │ │ │ ├── helpers.ts │ │ │ ├── resolvers.ts │ │ │ └── startingEquipmentResolver.ts │ │ ├── 2024/ │ │ │ ├── common/ │ │ │ │ ├── choiceTypes.ts │ │ │ │ ├── equipmentTypes.ts │ │ │ │ ├── interfaces.ts │ │ │ │ ├── resolver.ts │ │ │ │ └── unions.ts │ │ │ ├── resolvers/ │ │ │ │ ├── abilityScore/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── alignment/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── background/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── condition/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── damageType/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── equipment/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── equipmentCategory/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── feat/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── index.ts │ │ │ │ ├── language/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── magicItem/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── magicSchool/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── proficiency/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── skill/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── species/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── subclass/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── subspecies/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── trait/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── weaponMasteryProperty/ │ │ │ │ │ ├── args.ts │ │ │ │ │ └── resolver.ts │ │ │ │ └── weaponProperty/ │ │ │ │ ├── args.ts │ │ │ │ └── resolver.ts │ │ │ ├── types/ │ │ │ │ └── backgroundEquipment/ │ │ │ │ ├── choice.ts │ │ │ │ ├── common.ts │ │ │ │ ├── index.ts │ │ │ │ └── optionSet.ts │ │ │ └── utils/ │ │ │ ├── backgroundEquipmentResolver.ts │ │ │ ├── choiceResolvers.ts │ │ │ └── resolvers.ts │ │ ├── common/ │ │ │ ├── args.ts │ │ │ ├── choiceTypes.ts │ │ │ ├── enums.ts │ │ │ ├── inputs.ts │ │ │ └── types.ts │ │ └── utils/ │ │ └── resolvers.ts │ ├── middleware/ │ │ ├── apolloServer.ts │ │ ├── bugsnag.ts │ │ └── errorHandler.ts │ ├── models/ │ │ ├── 2014/ │ │ │ ├── abilityScore.ts │ │ │ ├── alignment.ts │ │ │ ├── background.ts │ │ │ ├── class.ts │ │ │ ├── collection.ts │ │ │ ├── condition.ts │ │ │ ├── damageType.ts │ │ │ ├── equipment.ts │ │ │ ├── equipmentCategory.ts │ │ │ ├── feat.ts │ │ │ ├── feature.ts │ │ │ ├── language.ts │ │ │ ├── level.ts │ │ │ ├── magicItem.ts │ │ │ ├── magicSchool.ts │ │ │ ├── monster.ts │ │ │ ├── proficiency.ts │ │ │ ├── race.ts │ │ │ ├── rule.ts │ │ │ ├── ruleSection.ts │ │ │ ├── skill.ts │ │ │ ├── spell.ts │ │ │ ├── subclass.ts │ │ │ ├── subrace.ts │ │ │ ├── trait.ts │ │ │ └── weaponProperty.ts │ │ ├── 2024/ │ │ │ ├── abilityScore.ts │ │ │ ├── alignment.ts │ │ │ ├── background.ts │ │ │ ├── collection.ts │ │ │ ├── condition.ts │ │ │ ├── damageType.ts │ │ │ ├── equipment.ts │ │ │ ├── equipmentCategory.ts │ │ │ ├── feat.ts │ │ │ ├── language.ts │ │ │ ├── magicItem.ts │ │ │ ├── magicSchool.ts │ │ │ ├── proficiency.ts │ │ │ ├── skill.ts │ │ │ ├── species.ts │ │ │ ├── subclass.ts │ │ │ ├── subspecies.ts │ │ │ ├── trait.ts │ │ │ ├── weaponMasteryProperty.ts │ │ │ └── weaponProperty.ts │ │ └── common/ │ │ ├── apiReference.ts │ │ ├── areaOfEffect.ts │ │ ├── choice.ts │ │ ├── damage.ts │ │ └── difficultyClass.ts │ ├── public/ │ │ └── index.html │ ├── routes/ │ │ ├── api/ │ │ │ ├── 2014/ │ │ │ │ ├── abilityScores.ts │ │ │ │ ├── alignments.ts │ │ │ │ ├── backgrounds.ts │ │ │ │ ├── classes.ts │ │ │ │ ├── conditions.ts │ │ │ │ ├── damageTypes.ts │ │ │ │ ├── equipment.ts │ │ │ │ ├── equipmentCategories.ts │ │ │ │ ├── feats.ts │ │ │ │ ├── features.ts │ │ │ │ ├── images.ts │ │ │ │ ├── languages.ts │ │ │ │ ├── magicItems.ts │ │ │ │ ├── magicSchools.ts │ │ │ │ ├── monsters.ts │ │ │ │ ├── proficiencies.ts │ │ │ │ ├── races.ts │ │ │ │ ├── ruleSections.ts │ │ │ │ ├── rules.ts │ │ │ │ ├── skills.ts │ │ │ │ ├── spells.ts │ │ │ │ ├── subclasses.ts │ │ │ │ ├── subraces.ts │ │ │ │ ├── traits.ts │ │ │ │ └── weaponProperties.ts │ │ │ ├── 2014.ts │ │ │ ├── 2024/ │ │ │ │ ├── abilityScores.ts │ │ │ │ ├── alignments.ts │ │ │ │ ├── backgrounds.ts │ │ │ │ ├── conditions.ts │ │ │ │ ├── damageTypes.ts │ │ │ │ ├── equipment.ts │ │ │ │ ├── equipmentCategories.ts │ │ │ │ ├── feats.ts │ │ │ │ ├── languages.ts │ │ │ │ ├── magicItems.ts │ │ │ │ ├── magicSchools.ts │ │ │ │ ├── proficiencies.ts │ │ │ │ ├── skills.ts │ │ │ │ ├── species.ts │ │ │ │ ├── subclasses.ts │ │ │ │ ├── subspecies.ts │ │ │ │ ├── traits.ts │ │ │ │ ├── weaponMasteryProperties.ts │ │ │ │ └── weaponProperty.ts │ │ │ ├── 2024.ts │ │ │ └── images.ts │ │ └── api.ts │ ├── schemas/ │ │ └── schemas.ts │ ├── server.ts │ ├── start.ts │ ├── swagger/ │ │ ├── README.md │ │ ├── parameters/ │ │ │ ├── 2014/ │ │ │ │ ├── combined.yml │ │ │ │ ├── path/ │ │ │ │ │ ├── ability-scores.yml │ │ │ │ │ ├── alignments.yml │ │ │ │ │ ├── backgrounds.yml │ │ │ │ │ ├── classes.yml │ │ │ │ │ ├── common.yml │ │ │ │ │ ├── conditions.yml │ │ │ │ │ ├── damage-types.yml │ │ │ │ │ ├── equipment.yml │ │ │ │ │ ├── features.yml │ │ │ │ │ ├── languages.yml │ │ │ │ │ ├── magic-schools.yml │ │ │ │ │ ├── monsters.yml │ │ │ │ │ ├── proficiencies.yml │ │ │ │ │ ├── races.yml │ │ │ │ │ ├── rule-sections.yml │ │ │ │ │ ├── rules.yml │ │ │ │ │ ├── skills.yml │ │ │ │ │ ├── spells.yml │ │ │ │ │ ├── subclasses.yml │ │ │ │ │ ├── subraces.yml │ │ │ │ │ ├── traits.yml │ │ │ │ │ └── weapon-properties.yml │ │ │ │ └── query/ │ │ │ │ ├── classes.yml │ │ │ │ ├── monsters.yml │ │ │ │ └── spells.yml │ │ │ └── 2024/ │ │ │ └── .keepme │ │ ├── paths/ │ │ │ ├── 2014/ │ │ │ │ ├── ability-scores.yml │ │ │ │ ├── alignments.yml │ │ │ │ ├── backgrounds.yml │ │ │ │ ├── classes.yml │ │ │ │ ├── combined.yml │ │ │ │ ├── common.yml │ │ │ │ ├── conditions.yml │ │ │ │ ├── damage-types.yml │ │ │ │ ├── equipment-categories.yml │ │ │ │ ├── equipment.yml │ │ │ │ ├── feats.yml │ │ │ │ ├── features.yml │ │ │ │ ├── languages.yml │ │ │ │ ├── magic-items.yml │ │ │ │ ├── magic-schools.yml │ │ │ │ ├── monsters.yml │ │ │ │ ├── proficiencies.yml │ │ │ │ ├── races.yml │ │ │ │ ├── rule-sections.yml │ │ │ │ ├── rules.yml │ │ │ │ ├── skills.yml │ │ │ │ ├── spells.yml │ │ │ │ ├── subclasses.yml │ │ │ │ ├── subraces.yml │ │ │ │ ├── traits.yml │ │ │ │ └── weapon-properties.yml │ │ │ └── 2024/ │ │ │ └── .keepme │ │ ├── schemas/ │ │ │ ├── 2014/ │ │ │ │ ├── ability-scores.yml │ │ │ │ ├── alignments.yml │ │ │ │ ├── armor.yml │ │ │ │ ├── backgrounds.yml │ │ │ │ ├── classes.yml │ │ │ │ ├── combined.yml │ │ │ │ ├── common.yml │ │ │ │ ├── equipment.yml │ │ │ │ ├── feats.yml │ │ │ │ ├── features.yml │ │ │ │ ├── game-mechanics.yml │ │ │ │ ├── language.yml │ │ │ │ ├── monsters-common.yml │ │ │ │ ├── monsters.yml │ │ │ │ ├── multiclassing.yml │ │ │ │ ├── proficiencies.yml │ │ │ │ ├── races.yml │ │ │ │ ├── rules.yml │ │ │ │ ├── skills.yml │ │ │ │ ├── spell.yml │ │ │ │ ├── spellcasting.yml │ │ │ │ ├── subclass.yml │ │ │ │ ├── subrace.yml │ │ │ │ ├── traits.yml │ │ │ │ └── weapon.yml │ │ │ └── 2024/ │ │ │ └── .keepme │ │ └── swagger.yml │ ├── tests/ │ │ ├── controllers/ │ │ │ ├── api/ │ │ │ │ ├── 2014/ │ │ │ │ │ ├── abilityScoreController.test.ts │ │ │ │ │ ├── alignmentController.test.ts │ │ │ │ │ ├── backgroundController.test.ts │ │ │ │ │ ├── classController.test.ts │ │ │ │ │ ├── conditionController.test.ts │ │ │ │ │ ├── damageTypeController.test.ts │ │ │ │ │ ├── equipmentCategoryController.test.ts │ │ │ │ │ ├── equipmentController.test.ts │ │ │ │ │ ├── featController.test.ts │ │ │ │ │ ├── featureController.test.ts │ │ │ │ │ ├── languageController.test.ts │ │ │ │ │ ├── magicItemController.test.ts │ │ │ │ │ ├── magicSchoolController.test.ts │ │ │ │ │ ├── monsterController.test.ts │ │ │ │ │ ├── proficiencyController.test.ts │ │ │ │ │ ├── raceController.test.ts │ │ │ │ │ ├── ruleSectionController.test.ts │ │ │ │ │ ├── rulesController.test.ts │ │ │ │ │ ├── skillController.test.ts │ │ │ │ │ ├── spellController.test.ts │ │ │ │ │ ├── subclassController.test.ts │ │ │ │ │ ├── subraceController.test.ts │ │ │ │ │ ├── traitController.test.ts │ │ │ │ │ └── weaponPropertyController.test.ts │ │ │ │ ├── 2024/ │ │ │ │ │ ├── BackgroundController.test.ts │ │ │ │ │ ├── FeatController.test.ts │ │ │ │ │ ├── MagicItemController.test.ts │ │ │ │ │ ├── ProficiencyController.test.ts │ │ │ │ │ ├── SubclassController.test.ts │ │ │ │ │ ├── abilityScoreController.test.ts │ │ │ │ │ ├── alignmentController.test.ts │ │ │ │ │ ├── conditionController.test.ts │ │ │ │ │ ├── damageTypeController.test.ts │ │ │ │ │ ├── equipmentCategoryController.test.ts │ │ │ │ │ ├── equipmentController.test.ts │ │ │ │ │ ├── languageController.test.ts │ │ │ │ │ ├── magicSchoolController.test.ts │ │ │ │ │ ├── skillController.test.ts │ │ │ │ │ ├── speciesController.test.ts │ │ │ │ │ ├── subspeciesController.test.ts │ │ │ │ │ ├── traitController.test.ts │ │ │ │ │ ├── weaponMasteryPropertyController.test.ts │ │ │ │ │ └── weaponPropertyController.test.ts │ │ │ │ ├── v2014Controller.test.ts │ │ │ │ └── v2024Controller.test.ts │ │ │ ├── apiController.test.ts │ │ │ ├── globalSetup.ts │ │ │ └── simpleController.test.ts │ │ ├── factories/ │ │ │ ├── 2014/ │ │ │ │ ├── abilityScore.factory.ts │ │ │ │ ├── alignment.factory.ts │ │ │ │ ├── background.factory.ts │ │ │ │ ├── class.factory.ts │ │ │ │ ├── collection.factory.ts │ │ │ │ ├── common.factory.ts │ │ │ │ ├── condition.factory.ts │ │ │ │ ├── damageType.factory.ts │ │ │ │ ├── equipment.factory.ts │ │ │ │ ├── equipmentCategory.factory.ts │ │ │ │ ├── feat.factory.ts │ │ │ │ ├── feature.factory.ts │ │ │ │ ├── language.factory.ts │ │ │ │ ├── level.factory.ts │ │ │ │ ├── magicItem.factory.ts │ │ │ │ ├── magicSchool.factory.ts │ │ │ │ ├── monster.factory.ts │ │ │ │ ├── proficiency.factory.ts │ │ │ │ ├── race.factory.ts │ │ │ │ ├── rule.factory.ts │ │ │ │ ├── ruleSection.factory.ts │ │ │ │ ├── skill.factory.ts │ │ │ │ ├── spell.factory.ts │ │ │ │ ├── subclass.factory.ts │ │ │ │ ├── subrace.factory.ts │ │ │ │ ├── trait.factory.ts │ │ │ │ └── weaponProperty.factory.ts │ │ │ └── 2024/ │ │ │ ├── abilityScore.factory.ts │ │ │ ├── alignment.factory.ts │ │ │ ├── background.factory.ts │ │ │ ├── collection.factory.ts │ │ │ ├── common.factory.ts │ │ │ ├── condition.factory.ts │ │ │ ├── damageType.factory.ts │ │ │ ├── equipment.factory.ts │ │ │ ├── equipmentCategory.factory.ts │ │ │ ├── feat.factory.ts │ │ │ ├── language.factory.ts │ │ │ ├── magicItem.factory.ts │ │ │ ├── magicSchool.factory.ts │ │ │ ├── proficiency.factory.ts │ │ │ ├── skill.factory.ts │ │ │ ├── species.factory.ts │ │ │ ├── subclass.factory.ts │ │ │ ├── subspecies.factory.ts │ │ │ ├── trait.factory.ts │ │ │ ├── weaponMasteryProperty.factory.ts │ │ │ └── weaponProperty.factory.ts │ │ ├── integration/ │ │ │ ├── api/ │ │ │ │ ├── 2014/ │ │ │ │ │ ├── abilityScores.itest.ts │ │ │ │ │ ├── classes.itest.ts │ │ │ │ │ ├── conditions.itest.ts │ │ │ │ │ ├── damageTypes.itest.ts │ │ │ │ │ ├── equipment.itest.ts │ │ │ │ │ ├── equipmentCategories.itest.ts │ │ │ │ │ ├── feats.itest.ts │ │ │ │ │ ├── features.itest.ts │ │ │ │ │ ├── languages.itest.ts │ │ │ │ │ ├── magicItems.itest.ts │ │ │ │ │ ├── magicSchools.itest.ts │ │ │ │ │ ├── monsters.itest.ts │ │ │ │ │ ├── proficiencies.itest.ts │ │ │ │ │ ├── races.itest.ts │ │ │ │ │ ├── ruleSections.itest.ts │ │ │ │ │ ├── rules.itest.ts │ │ │ │ │ ├── skills.itest.ts │ │ │ │ │ ├── spells.itest.ts │ │ │ │ │ ├── subclasses.itest.ts │ │ │ │ │ ├── subraces.itest.ts │ │ │ │ │ ├── traits.itest.ts │ │ │ │ │ └── weaponProperties.itest.ts │ │ │ │ ├── 2024/ │ │ │ │ │ ├── abilityScores.itest.ts │ │ │ │ │ ├── alignment.itest.ts │ │ │ │ │ ├── background.itest.ts │ │ │ │ │ ├── condition.itest.ts │ │ │ │ │ ├── damageType.itest.ts │ │ │ │ │ ├── equipment.itest.ts │ │ │ │ │ ├── equipmentCategories.itest.ts │ │ │ │ │ ├── feat.itest.ts │ │ │ │ │ ├── language.itest.ts │ │ │ │ │ ├── magicItem.itest.ts │ │ │ │ │ ├── magicSchool.itest.ts │ │ │ │ │ ├── proficiency.itest.ts │ │ │ │ │ ├── skills.itest.ts │ │ │ │ │ ├── species.itest.ts │ │ │ │ │ ├── subclass.itest.ts │ │ │ │ │ ├── subspecies.itest.ts │ │ │ │ │ ├── traits.itest.ts │ │ │ │ │ ├── weaponMasteryProperty.itest.ts │ │ │ │ │ └── weaponProperty.itest.ts │ │ │ │ ├── abilityScores.itest.ts │ │ │ │ ├── classes.itest.ts │ │ │ │ ├── conditions.itest.ts │ │ │ │ ├── damageTypes.itest.ts │ │ │ │ ├── equipment.itest.ts │ │ │ │ ├── equipmentCategories.itest.ts │ │ │ │ ├── feats.itest.ts │ │ │ │ ├── features.itest.ts │ │ │ │ ├── languages.itest.ts │ │ │ │ ├── magicItems.itest.ts │ │ │ │ ├── magicSchools.itest.ts │ │ │ │ ├── monsters.itest.ts │ │ │ │ ├── proficiencies.itest.ts │ │ │ │ ├── races.itest.ts │ │ │ │ ├── ruleSections.itest.ts │ │ │ │ ├── rules.itest.ts │ │ │ │ ├── skills.itest.ts │ │ │ │ ├── spells.itest.ts │ │ │ │ ├── subclasses.itest.ts │ │ │ │ ├── subraces.itest.ts │ │ │ │ ├── traits.itest.ts │ │ │ │ └── weaponProperties.itest.ts │ │ │ └── server.itest.ts │ │ ├── support/ │ │ │ ├── db.ts │ │ │ ├── index.ts │ │ │ ├── requestHelpers.ts │ │ │ └── types.d.ts │ │ ├── util/ │ │ │ └── data.test.ts │ │ └── vitest.setup.ts │ └── util/ │ ├── RedisClient.ts │ ├── awsS3Client.ts │ ├── data.ts │ ├── environmentVariables.ts │ ├── index.ts │ ├── modelOptions.ts │ ├── prewarmCache.ts │ └── regex.ts ├── tsconfig.json ├── vitest.config.integration.ts └── vitest.config.ts
SYMBOL INDEX (767 symbols across 181 files)
FILE: src/controllers/api/2014/classController.ts
type ShowLevelsForClassQuery (line 19) | interface ShowLevelsForClassQuery {
FILE: src/controllers/api/2014/magicItemController.ts
type IndexQuery (line 7) | interface IndexQuery {
FILE: src/controllers/api/2014/monsterController.ts
type IndexQuery (line 7) | interface IndexQuery {
FILE: src/controllers/api/2014/ruleController.ts
type IndexQuery (line 7) | interface IndexQuery {
FILE: src/controllers/api/2014/ruleSectionController.ts
type IndexQuery (line 7) | interface IndexQuery {
FILE: src/controllers/api/2014/spellController.ts
type IndexQuery (line 7) | interface IndexQuery {
FILE: src/controllers/api/imageController.ts
constant BUCKET_NAME (line 5) | const BUCKET_NAME = 'dnd-5e-api-images'
constant AWS_REGION (line 6) | const AWS_REGION = awsRegion || 'us-east-1'
FILE: src/controllers/simpleController.ts
type IndexQuery (line 8) | interface IndexQuery {
class SimpleController (line 12) | class SimpleController {
method constructor (line 15) | constructor(Schema: ReturnModelType<any>) {
method index (line 19) | async index(req: Request, res: Response, next: NextFunction) {
method show (line 50) | async show(req: Request, res: Response, next: NextFunction) {
FILE: src/graphql/2014/common/choiceTypes.ts
class LanguageChoiceOption (line 11) | class LanguageChoiceOption {
class LanguageChoiceOptionSet (line 20) | class LanguageChoiceOptionSet {
class LanguageChoice (line 33) | class LanguageChoice {
class ProficiencyChoiceOption (line 49) | class ProficiencyChoiceOption {
class ProficiencyChoiceOptionSet (line 60) | class ProficiencyChoiceOptionSet {
class ProficiencyChoice (line 75) | class ProficiencyChoice {
class PrerequisiteChoiceOption (line 93) | class PrerequisiteChoiceOption {
class PrerequisiteChoiceOptionSet (line 105) | class PrerequisiteChoiceOptionSet {
class PrerequisiteChoice (line 114) | class PrerequisiteChoice {
class AbilityScoreBonusChoiceOption (line 130) | class AbilityScoreBonusChoiceOption {
class AbilityScoreBonusChoiceOptionSet (line 142) | class AbilityScoreBonusChoiceOptionSet {
class AbilityScoreBonusChoice (line 151) | class AbilityScoreBonusChoice {
FILE: src/graphql/2014/common/equipmentTypes.ts
class Armor (line 11) | class Armor extends Equipment {
class Weapon (line 32) | class Weapon extends Equipment {
class Tool (line 62) | class Tool extends Equipment {
class Gear (line 68) | class Gear extends Equipment {}
class Pack (line 74) | class Pack extends Gear {
class Ammunition (line 80) | class Ammunition extends Gear {
class Vehicle (line 86) | class Vehicle extends Equipment {
FILE: src/graphql/2014/common/unions.ts
function resolveEquipmentType (line 9) | function resolveEquipmentType(
FILE: src/graphql/2014/resolvers/abilityScore/args.ts
type AbilityScoreOrderField (line 12) | enum AbilityScoreOrderField {
constant ABILITY_SCORE_SORT_FIELD_MAP (line 17) | const ABILITY_SCORE_SORT_FIELD_MAP: Record<AbilityScoreOrderField, strin...
class AbilityScoreOrder (line 28) | class AbilityScoreOrder implements BaseOrderInterface<AbilityScoreOrderF...
class AbilityScoreArgs (line 56) | class AbilityScoreArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/abilityScore/resolver.ts
class AbilityScoreResolver (line 18) | class AbilityScoreResolver {
method abilityScores (line 22) | async abilityScores(
method abilityScore (line 68) | async abilityScore(@Arg('index', () => String) indexInput: string): Pr...
method skills (line 74) | async skills(@Root() abilityScore: AbilityScore): Promise<Skill[]> {
FILE: src/graphql/2014/resolvers/alignment/args.ts
type AlignmentOrderField (line 12) | enum AlignmentOrderField {
constant ALIGNMENT_SORT_FIELD_MAP (line 16) | const ALIGNMENT_SORT_FIELD_MAP: Record<AlignmentOrderField, string> = {
class AlignmentOrder (line 26) | class AlignmentOrder implements BaseOrderInterface<AlignmentOrderField> {
class AlignmentArgs (line 53) | class AlignmentArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/alignment/resolver.ts
class AlignmentResolver (line 16) | class AlignmentResolver {
method alignments (line 20) | async alignments(@Args(() => AlignmentArgs) args: AlignmentArgs): Prom...
method alignment (line 50) | async alignment(@Arg('index', () => String) indexInput: string): Promi...
FILE: src/graphql/2014/resolvers/background/args.ts
type BackgroundOrderField (line 12) | enum BackgroundOrderField {
constant BACKGROUND_SORT_FIELD_MAP (line 16) | const BACKGROUND_SORT_FIELD_MAP: Record<BackgroundOrderField, string> = {
class BackgroundOrder (line 26) | class BackgroundOrder implements BaseOrderInterface<BackgroundOrderField> {
class BackgroundArgs (line 53) | class BackgroundArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/background/resolver.ts
class BackgroundResolver (line 34) | class BackgroundResolver {
method backgrounds (line 38) | async backgrounds(@Args(() => BackgroundArgs) args: BackgroundArgs): P...
method background (line 67) | async background(@Arg('index', () => String) indexInput: string): Prom...
method starting_proficiencies (line 73) | async starting_proficiencies(@Root() background: Background): Promise<...
method flaws (line 81) | async flaws(@Root() background: Background): Promise<StringChoice | nu...
method bonds (line 89) | async bonds(@Root() background: Background): Promise<StringChoice | nu...
method personality_traits (line 97) | async personality_traits(@Root() background: Background): Promise<Stri...
method ideals (line 105) | async ideals(@Root() background: Background): Promise<IdealChoice> {
method language_options (line 141) | async language_options(@Root() background: Background): Promise<Langua...
method starting_equipment_options (line 149) | async starting_equipment_options(
class EquipmentRefResolver (line 157) | class EquipmentRefResolver {
method equipment (line 159) | async equipment(@Root() equipmentRef: EquipmentRef): Promise<Equipment...
FILE: src/graphql/2014/resolvers/class/args.ts
type ClassOrderField (line 13) | enum ClassOrderField {
constant CLASS_SORT_FIELD_MAP (line 18) | const CLASS_SORT_FIELD_MAP: Record<ClassOrderField, string> = {
class ClassOrder (line 29) | class ClassOrder implements BaseOrderInterface<ClassOrderField> {
class ClassArgs (line 57) | class ClassArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/class/resolver.ts
class ClassResolver (line 41) | class ClassResolver {
method classes (line 45) | async classes(@Args(() => ClassArgs) args: ClassArgs): Promise<Class[]> {
method class (line 86) | async class(@Arg('index', () => String) indexInput: string): Promise<C...
method class_levels (line 92) | async class_levels(@Root() classData: Class): Promise<Level[]> {
method proficiencies (line 102) | async proficiencies(@Root() classData: Class): Promise<APIReference[]> {
method saving_throws (line 107) | async saving_throws(@Root() classData: Class): Promise<APIReference[]> {
method subclasses (line 112) | async subclasses(@Root() classData: Class): Promise<APIReference[]> {
method spells (line 117) | async spells(@Root() classData: Class): Promise<Spell[]> {
method proficiency_choices (line 122) | async proficiency_choices(@Root() classData: Class): Promise<Proficien...
method starting_equipment_options (line 130) | async starting_equipment_options(
class MultiClassingResolver (line 138) | class MultiClassingResolver {
method proficiencies (line 140) | async proficiencies(@Root() multiClassing: MultiClassing): Promise<API...
method proficiency_choices (line 145) | async proficiency_choices(@Root() multiClassing: MultiClassing): Promi...
method prerequisite_options (line 150) | async prerequisite_options(
class MultiClassingPrereqResolver (line 158) | class MultiClassingPrereqResolver {
method ability_score (line 160) | async ability_score(@Root() prerequisite: MultiClassingPrereq): Promis...
class ClassEquipmentResolver (line 166) | class ClassEquipmentResolver {
method equipment (line 168) | async equipment(@Root() classEquipment: ClassEquipment): Promise<typeo...
function resolvePrerequisiteChoice (line 173) | async function resolvePrerequisiteChoice(
FILE: src/graphql/2014/resolvers/condition/args.ts
type ConditionOrderField (line 12) | enum ConditionOrderField {
constant CONDITION_SORT_FIELD_MAP (line 16) | const CONDITION_SORT_FIELD_MAP: Record<ConditionOrderField, string> = {
class ConditionOrder (line 26) | class ConditionOrder implements BaseOrderInterface<ConditionOrderField> {
class ConditionArgs (line 53) | class ConditionArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/condition/resolver.ts
class ConditionResolver (line 16) | class ConditionResolver {
method conditions (line 20) | async conditions(@Args(() => ConditionArgs) args: ConditionArgs): Prom...
method condition (line 49) | async condition(@Arg('index', () => String) indexInput: string): Promi...
FILE: src/graphql/2014/resolvers/damageType/args.ts
type DamageTypeOrderField (line 12) | enum DamageTypeOrderField {
constant DAMAGE_TYPE_SORT_FIELD_MAP (line 16) | const DAMAGE_TYPE_SORT_FIELD_MAP: Record<DamageTypeOrderField, string> = {
class DamageTypeOrder (line 26) | class DamageTypeOrder implements BaseOrderInterface<DamageTypeOrderField> {
class DamageTypeArgs (line 53) | class DamageTypeArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/damageType/resolver.ts
class DamageTypeResolver (line 16) | class DamageTypeResolver {
method damageTypes (line 20) | async damageTypes(@Args(() => DamageTypeArgs) args: DamageTypeArgs): P...
method damageType (line 49) | async damageType(@Arg('index', () => String) indexInput: string): Prom...
FILE: src/graphql/2014/resolvers/equipment/args.ts
type EquipmentOrderField (line 12) | enum EquipmentOrderField {
constant EQUIPMENT_SORT_FIELD_MAP (line 18) | const EQUIPMENT_SORT_FIELD_MAP: Record<EquipmentOrderField, string> = {
class EquipmentOrder (line 30) | class EquipmentOrder implements BaseOrderInterface<EquipmentOrderField> {
class EquipmentArgs (line 58) | class EquipmentArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/equipment/resolver.ts
class EquipmentResolver (line 20) | class EquipmentResolver {
method equipments (line 24) | async equipments(
method equipment (line 66) | async equipment(
method properties (line 74) | async properties(@Root() equipment: Equipment): Promise<WeaponProperty...
class ContentFieldResolver (line 80) | class ContentFieldResolver {
method item (line 85) | async item(@Root() content: Content): Promise<typeof AnyEquipment | nu...
FILE: src/graphql/2014/resolvers/equipmentCategory/args.ts
type EquipmentCategoryOrderField (line 12) | enum EquipmentCategoryOrderField {
constant EQUIPMENT_CATEGORY_SORT_FIELD_MAP (line 16) | const EQUIPMENT_CATEGORY_SORT_FIELD_MAP: Record<EquipmentCategoryOrderFi...
class EquipmentCategoryOrder (line 26) | class EquipmentCategoryOrder implements BaseOrderInterface<EquipmentCate...
class EquipmentCategoryArgs (line 53) | class EquipmentCategoryArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/equipmentCategory/resolver.ts
class EquipmentCategoryResolver (line 19) | class EquipmentCategoryResolver {
method equipmentCategories (line 23) | async equipmentCategories(
method equipmentCategory (line 57) | async equipmentCategory(
method equipment (line 65) | async equipment(
FILE: src/graphql/2014/resolvers/feat/args.ts
type FeatOrderField (line 12) | enum FeatOrderField {
constant FEAT_SORT_FIELD_MAP (line 16) | const FEAT_SORT_FIELD_MAP: Record<FeatOrderField, string> = {
class FeatOrder (line 26) | class FeatOrder implements BaseOrderInterface<FeatOrderField> {
class FeatArgs (line 53) | class FeatArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/feat/resolver.ts
class FeatResolver (line 18) | class FeatResolver {
method feats (line 22) | async feats(@Args(() => FeatArgs) args: FeatArgs): Promise<Feat[]> {
method feat (line 51) | async feat(@Arg('index', () => String) indexInput: string): Promise<Fe...
class PrerequisiteResolver (line 58) | class PrerequisiteResolver {
method ability_score (line 60) | async ability_score(@Root() prerequisite: Prerequisite): Promise<Abili...
FILE: src/graphql/2014/resolvers/feature/args.ts
type FeatureOrderField (line 13) | enum FeatureOrderField {
constant FEATURE_SORT_FIELD_MAP (line 20) | const FEATURE_SORT_FIELD_MAP: Record<FeatureOrderField, string> = {
class FeatureOrder (line 33) | class FeatureOrder implements BaseOrderInterface<FeatureOrderField> {
class FeatureArgs (line 63) | class FeatureArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/feature/resolver.ts
class FeatureResolver (line 28) | class FeatureResolver {
method features (line 32) | async features(@Args(() => FeatureArgs) args: FeatureArgs): Promise<Fe...
method feature (line 78) | async feature(@Arg('index', () => String) indexInput: string): Promise...
method class (line 84) | async class(@Root() feature: Feature): Promise<Class | null> {
method parent (line 89) | async parent(@Root() feature: Feature): Promise<Feature | null> {
method subclass (line 94) | async subclass(@Root() feature: Feature): Promise<Subclass | null> {
method prerequisites (line 102) | async prerequisites(
class FeatureSpecificResolver (line 162) | class FeatureSpecificResolver {
method invocations (line 164) | async invocations(@Root() featureSpecific: FeatureSpecific): Promise<F...
FILE: src/graphql/2014/resolvers/language/args.ts
type LanguageOrderField (line 12) | enum LanguageOrderField {
constant LANGUAGE_SORT_FIELD_MAP (line 18) | const LANGUAGE_SORT_FIELD_MAP: Record<LanguageOrderField, string> = {
class LanguageOrder (line 30) | class LanguageOrder implements BaseOrderInterface<LanguageOrderField> {
class LanguageArgs (line 59) | class LanguageArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/language/resolver.ts
class LanguageResolver (line 16) | class LanguageResolver {
method language (line 18) | async language(@Arg('index', () => String) indexInput: string): Promis...
method languages (line 24) | async languages(@Args(() => LanguageArgs) args: LanguageArgs): Promise...
FILE: src/graphql/2014/resolvers/level/args.ts
type LevelOrderField (line 13) | enum LevelOrderField {
constant LEVEL_SORT_FIELD_MAP (line 19) | const LEVEL_SORT_FIELD_MAP: Record<LevelOrderField, string> = {
class LevelOrder (line 31) | class LevelOrder implements BaseOrderInterface<LevelOrderField> {
class LevelArgs (line 63) | class LevelArgs extends BasePaginationArgs {
FILE: src/graphql/2014/resolvers/level/resolver.ts
class LevelResolver (line 20) | class LevelResolver {
method level (line 26) | async level(@Arg('index', () => String) indexInput: string): Promise<L...
method levels (line 32) | async levels(@Args(() => LevelArgs) args: LevelArgs): Promise<Level[]> {
method class (line 86) | async class(@Root() level: Level): Promise<Class | null> {
method subclass (line 91) | async subclass(@Root() level: Level): Promise<Subclass | null> {
method features (line 96) | async features(@Root() level: Level): Promise<Feature[]> {
FILE: src/graphql/2014/resolvers/magicItem/args.ts
type MagicItemOrderField (line 12) | enum MagicItemOrderField {
constant MAGIC_ITEM_SORT_FIELD_MAP (line 18) | const MAGIC_ITEM_SORT_FIELD_MAP: Record<MagicItemOrderField, string> = {
class MagicItemOrder (line 30) | class MagicItemOrder implements BaseOrderInterface<MagicItemOrderField> {
class MagicItemArgs (line 59) | class MagicItemArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/magicItem/resolver.ts
class MagicItemResolver (line 18) | class MagicItemResolver {
method magicItems (line 22) | async magicItems(@Args(() => MagicItemArgs) args: MagicItemArgs): Prom...
method magicItem (line 65) | async magicItem(@Arg('index', () => String) indexInput: string): Promi...
method equipment_category (line 71) | async equipment_category(@Root() magicItem: MagicItem): Promise<Equipm...
method variants (line 76) | async variants(@Root() magicItem: MagicItem): Promise<MagicItem[]> {
FILE: src/graphql/2014/resolvers/magicSchool/args.ts
type MagicSchoolOrderField (line 12) | enum MagicSchoolOrderField {
constant MAGIC_SCHOOL_SORT_FIELD_MAP (line 16) | const MAGIC_SCHOOL_SORT_FIELD_MAP: Record<MagicSchoolOrderField, string>...
class MagicSchoolOrder (line 26) | class MagicSchoolOrder implements BaseOrderInterface<MagicSchoolOrderFie...
class MagicSchoolArgs (line 53) | class MagicSchoolArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/magicSchool/resolver.ts
class MagicSchoolResolver (line 16) | class MagicSchoolResolver {
method magicSchools (line 20) | async magicSchools(@Args(() => MagicSchoolArgs) args: MagicSchoolArgs)...
method magicSchool (line 52) | async magicSchool(@Arg('index', () => String) indexInput: string): Pro...
FILE: src/graphql/2014/resolvers/monster/args.ts
type MonsterOrderField (line 13) | enum MonsterOrderField {
constant MONSTER_SORT_FIELD_MAP (line 26) | const MONSTER_SORT_FIELD_MAP: Record<MonsterOrderField, string> = {
class MonsterOrder (line 45) | class MonsterOrder implements BaseOrderInterface<MonsterOrderField> {
class MonsterArgs (line 87) | class MonsterArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/monster/resolver.ts
class MonsterResolver (line 57) | class MonsterResolver {
method monsters (line 61) | async monsters(@Args(() => MonsterArgs) args: MonsterArgs): Promise<Mo...
method monster (line 145) | async monster(@Arg('index', () => String) indexInput: string): Promise...
method condition_immunities (line 151) | async condition_immunities(@Root() monster: Monster): Promise<APIRefer...
method forms (line 156) | async forms(@Root() monster: Monster): Promise<APIReference[] | null> {
method armor_class (line 162) | async armor_class(@Root() monster: Monster): Promise<Array<typeof Mons...
class ArmorClassArmorResolver (line 168) | class ArmorClassArmorResolver {
method armor (line 170) | async armor(@Root() acArmor: ArmorClassArmor): Promise<Array<typeof Ar...
class ArmorClassSpellResolver (line 179) | class ArmorClassSpellResolver {
method spell (line 181) | async spell(@Root() acSpell: ArmorClassSpell): Promise<Spell | null> {
class ArmorClassConditionResolver (line 187) | class ArmorClassConditionResolver {
method condition (line 189) | async condition(@Root() acCondition: ArmorClassCondition): Promise<Con...
class MonsterProficiencyResolver (line 195) | class MonsterProficiencyResolver {
method proficiency (line 197) | async proficiency(@Root() monsterProficiency: MonsterProficiency): Pro...
class DifficultyClassResolver (line 203) | class DifficultyClassResolver {
method dc_type (line 208) | async dc_type(@Root() difficultyClass: DifficultyClass): Promise<Abili...
class SpecialAbilitySpellcastingResolver (line 214) | class SpecialAbilitySpellcastingResolver {
method ability (line 216) | async ability(@Root() spellcasting: SpecialAbilitySpellcasting): Promi...
method slots (line 221) | async slots(@Root() spellcasting: SpecialAbilitySpellcasting): Promise...
class SpecialAbilitySpellResolver (line 243) | class SpecialAbilitySpellResolver {
method resolveSpell (line 245) | async resolveSpell(@Root() abilitySpell: SpecialAbilitySpell): Promise...
class MonsterActionResolver (line 253) | class MonsterActionResolver {
method damage (line 255) | async damage(@Root() action: MonsterAction): Promise<(Damage | DamageC...
method action_options (line 273) | async action_options(@Root() action: MonsterAction): Promise<ActionCho...
method options (line 278) | async options(@Root() action: MonsterAction): Promise<BreathChoice | n...
function resolveBreathChoice (line 283) | async function resolveBreathChoice(
function resolveDamageChoice (line 348) | async function resolveDamageChoice(
function resolveActionChoice (line 389) | async function resolveActionChoice(
FILE: src/graphql/2014/resolvers/proficiency/args.ts
type ProficiencyOrderField (line 12) | enum ProficiencyOrderField {
constant PROFICIENCY_SORT_FIELD_MAP (line 17) | const PROFICIENCY_SORT_FIELD_MAP: Record<ProficiencyOrderField, string> = {
class ProficiencyOrder (line 28) | class ProficiencyOrder implements BaseOrderInterface<ProficiencyOrderFie...
class ProficiencyArgs (line 59) | class ProficiencyArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/proficiency/resolver.ts
class ProficiencyResolver (line 24) | class ProficiencyResolver {
method proficiencies (line 28) | async proficiencies(@Args(() => ProficiencyArgs) args: ProficiencyArgs...
method proficiency (line 78) | async proficiency(@Arg('index', () => String) indexInput: string): Pro...
method classes (line 84) | async classes(@Root() proficiency: Proficiency): Promise<Class[]> {
method races (line 89) | async races(@Root() proficiency: Proficiency): Promise<Race[]> {
method reference (line 94) | async reference(@Root() proficiency: Proficiency): Promise<typeof Prof...
FILE: src/graphql/2014/resolvers/race/args.ts
type RaceOrderField (line 13) | enum RaceOrderField {
constant RACE_SORT_FIELD_MAP (line 17) | const RACE_SORT_FIELD_MAP: Record<RaceOrderField, string> = {
class RaceOrder (line 27) | class RaceOrder implements BaseOrderInterface<RaceOrderField> {
class RaceArgs (line 58) | class RaceArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/race/resolver.ts
class RaceResolver (line 29) | class RaceResolver {
method races (line 31) | async races(@Args(() => RaceArgs) args: RaceArgs): Promise<Race[]> {
method race (line 84) | async race(@Arg('index', () => String) indexInput: string): Promise<Ra...
method languages (line 90) | async languages(@Root() race: Race): Promise<Language[]> {
method subraces (line 95) | async subraces(@Root() race: Race): Promise<Subrace[]> {
method traits (line 100) | async traits(@Root() race: Race): Promise<Trait[]> {
method language_options (line 105) | async language_options(@Root() race: Race): Promise<LanguageChoice | n...
method ability_bonus_options (line 110) | async ability_bonus_options(@Root() race: Race): Promise<AbilityScoreB...
class RaceAbilityBonusResolver (line 116) | class RaceAbilityBonusResolver {
method ability_score (line 118) | async ability_score(@Root() raceAbilityBonus: RaceAbilityBonus): Promi...
function resolveAbilityScoreBonusChoice (line 123) | async function resolveAbilityScoreBonusChoice(
FILE: src/graphql/2014/resolvers/rule/args.ts
type RuleOrderField (line 12) | enum RuleOrderField {
constant RULE_SORT_FIELD_MAP (line 16) | const RULE_SORT_FIELD_MAP: Record<RuleOrderField, string> = {
class RuleOrder (line 26) | class RuleOrder implements BaseOrderInterface<RuleOrderField> {
class RuleArgs (line 53) | class RuleArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/rule/resolver.ts
class RuleResolver (line 18) | class RuleResolver {
method rules (line 22) | async rules(@Args(() => RuleArgs) args: RuleArgs): Promise<Rule[]> {
method rule (line 50) | async rule(@Arg('index', () => String) indexInput: string): Promise<Ru...
method subsections (line 56) | async subsections(@Root() rule: Rule): Promise<RuleSection[]> {
FILE: src/graphql/2014/resolvers/ruleSection/args.ts
type RuleSectionOrderField (line 12) | enum RuleSectionOrderField {
constant RULE_SECTION_SORT_FIELD_MAP (line 16) | const RULE_SECTION_SORT_FIELD_MAP: Record<RuleSectionOrderField, string>...
class RuleSectionOrder (line 26) | class RuleSectionOrder implements BaseOrderInterface<RuleSectionOrderFie...
class RuleSectionArgs (line 53) | class RuleSectionArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/ruleSection/resolver.ts
class RuleSectionResolver (line 16) | class RuleSectionResolver {
method ruleSections (line 20) | async ruleSections(@Args(() => RuleSectionArgs) args: RuleSectionArgs)...
method ruleSection (line 51) | async ruleSection(@Arg('index', () => String) indexInput: string): Pro...
FILE: src/graphql/2014/resolvers/skill/args.ts
type SkillOrderField (line 12) | enum SkillOrderField {
constant SKILL_SORT_FIELD_MAP (line 17) | const SKILL_SORT_FIELD_MAP: Record<SkillOrderField, string> = {
class SkillOrder (line 28) | class SkillOrder implements BaseOrderInterface<SkillOrderField> {
class SkillArgs (line 56) | class SkillArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/skill/resolver.ts
class SkillResolver (line 18) | class SkillResolver {
method skills (line 22) | async skills(@Args(() => SkillArgs) args: SkillArgs): Promise<Skill[]> {
method skill (line 61) | async skill(@Arg('index', () => String) indexInput: string): Promise<S...
method ability_score (line 67) | async ability_score(@Root() skill: Skill): Promise<AbilityScore | null> {
FILE: src/graphql/2014/resolvers/spell/args.ts
class AreaOfEffectFilterInput (line 21) | class AreaOfEffectFilterInput {
type SpellOrderField (line 36) | enum SpellOrderField {
constant SPELL_SORT_FIELD_MAP (line 43) | const SPELL_SORT_FIELD_MAP: Record<SpellOrderField, string> = {
class SpellOrder (line 56) | class SpellOrder implements BaseOrderInterface<SpellOrderField> {
class SpellArgs (line 95) | class SpellArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/spell/resolver.ts
class SpellResolver (line 25) | class SpellResolver {
method spells (line 27) | async spells(@Args(() => SpellArgs) args: SpellArgs): Promise<Spell[]> {
method spell (line 105) | async spell(@Arg('index', () => String) indexInput: string): Promise<S...
method classes (line 111) | async classes(@Root() spell: Spell): Promise<Class[]> {
method school (line 116) | async school(@Root() spell: Spell): Promise<MagicSchool | null> {
method subclasses (line 121) | async subclasses(@Root() spell: Spell): Promise<Subclass[]> {
method heal_at_slot_level (line 129) | async heal_at_slot_level(@Root() spell: Spell): Promise<LevelValue[] |...
class SpellDamageResolver (line 135) | class SpellDamageResolver {
method damage_type (line 137) | async damage_type(@Root() spellDamage: SpellDamage): Promise<DamageTyp...
method damage_at_slot_level (line 142) | async damage_at_slot_level(@Root() spellDamage: SpellDamage): Promise<...
method damage_at_character_level (line 150) | async damage_at_character_level(@Root() spellDamage: SpellDamage): Pro...
class SpellDCResolver (line 156) | class SpellDCResolver {
method dc_type (line 158) | async dc_type(@Root() spellDC: SpellDC): Promise<AbilityScore | null> {
FILE: src/graphql/2014/resolvers/subclass/args.ts
type SubclassOrderField (line 12) | enum SubclassOrderField {
constant SUBCLASS_SORT_FIELD_MAP (line 16) | const SUBCLASS_SORT_FIELD_MAP: Record<SubclassOrderField, string> = {
class SubclassOrder (line 26) | class SubclassOrder implements BaseOrderInterface<SubclassOrderField> {
class SubclassArgs (line 53) | class SubclassArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/subclass/resolver.ts
class SubclassResolver (line 22) | class SubclassResolver {
method subclasses (line 26) | async subclasses(@Args(() => SubclassArgs) args: SubclassArgs): Promis...
method subclass (line 56) | async subclass(@Arg('index', () => String) indexInput: string): Promis...
method class (line 62) | async class(@Root() subclass: Subclass): Promise<Class | null> {
method subclass_levels (line 67) | async subclass_levels(@Root() subclass: Subclass): Promise<Level[]> {
class SubclassSpellResolver (line 75) | class SubclassSpellResolver {
method prerequisites (line 80) | async prerequisites(
method spell (line 112) | async spell(
FILE: src/graphql/2014/resolvers/subrace/args.ts
type SubraceOrderField (line 12) | enum SubraceOrderField {
constant SUBRACE_SORT_FIELD_MAP (line 16) | const SUBRACE_SORT_FIELD_MAP: Record<SubraceOrderField, string> = {
class SubraceOrder (line 26) | class SubraceOrder implements BaseOrderInterface<SubraceOrderField> {
class SubraceArgs (line 53) | class SubraceArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/subrace/resolver.ts
class SubraceResolver (line 20) | class SubraceResolver {
method subraces (line 24) | async subraces(@Args(() => SubraceArgs) args: SubraceArgs): Promise<Su...
method subrace (line 53) | async subrace(@Arg('index', () => String) indexInput: string): Promise...
method race (line 59) | async race(@Root() subrace: Subrace): Promise<Race | null> {
method racial_traits (line 64) | async racial_traits(@Root() subrace: Subrace): Promise<Trait[]> {
class SubraceAbilityBonusResolver (line 69) | class SubraceAbilityBonusResolver {
method ability_score (line 71) | async ability_score(
FILE: src/graphql/2014/resolvers/trait/args.ts
type TraitOrderField (line 12) | enum TraitOrderField {
constant TRAIT_SORT_FIELD_MAP (line 16) | const TRAIT_SORT_FIELD_MAP: Record<TraitOrderField, string> = {
class TraitOrder (line 26) | class TraitOrder implements BaseOrderInterface<TraitOrderField> {
class TraitArgs (line 53) | class TraitArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/trait/resolver.ts
class TraitResolver (line 39) | class TraitResolver {
method traits (line 43) | async traits(@Args(() => TraitArgs) args: TraitArgs): Promise<Trait[]> {
method trait (line 72) | async trait(@Arg('index', () => String) indexInput: string): Promise<T...
method proficiencies (line 78) | async proficiencies(@Root() trait: Trait): Promise<Proficiency[]> {
method races (line 83) | async races(@Root() trait: Trait): Promise<Race[]> {
method subraces (line 88) | async subraces(@Root() trait: Trait): Promise<Subrace[]> {
method parent (line 93) | async parent(@Root() trait: Trait): Promise<Trait | null> {
method language_options (line 98) | async language_options(@Root() trait: Trait): Promise<LanguageChoice |...
method proficiency_choices (line 103) | async proficiency_choices(@Root() trait: Trait): Promise<ProficiencyCh...
class TraitSpecificResolver (line 110) | class TraitSpecificResolver {
method damage_type (line 112) | async damage_type(@Root() traitSpecific: TraitSpecific): Promise<Damag...
method subtrait_options (line 117) | async subtrait_options(@Root() traitSpecific: TraitSpecific) {
method spell_options (line 122) | async spell_options(@Root() traitSpecific: TraitSpecific) {
class ActionDamageResolver (line 128) | class ActionDamageResolver {
method damage_at_character_level (line 133) | async damage_at_character_level(
function resolveTraitChoice (line 140) | async function resolveTraitChoice(
function resolveSpellChoice (line 166) | async function resolveSpellChoice(
FILE: src/graphql/2014/resolvers/weaponProperty/args.ts
type WeaponPropertyOrderField (line 12) | enum WeaponPropertyOrderField {
constant WEAPON_PROPERTY_SORT_FIELD_MAP (line 16) | const WEAPON_PROPERTY_SORT_FIELD_MAP: Record<WeaponPropertyOrderField, s...
class WeaponPropertyOrder (line 26) | class WeaponPropertyOrder implements BaseOrderInterface<WeaponPropertyOr...
class WeaponPropertyArgs (line 53) | class WeaponPropertyArgs extends BaseFilterArgs {
FILE: src/graphql/2014/resolvers/weaponProperty/resolver.ts
class WeaponPropertyResolver (line 16) | class WeaponPropertyResolver {
method weaponProperties (line 20) | async weaponProperties(
method weaponProperty (line 54) | async weaponProperty(
FILE: src/graphql/2014/types/backgroundTypes.ts
class IdealOption (line 7) | class IdealOption {
class IdealOptionSet (line 19) | class IdealOptionSet {
class IdealChoice (line 28) | class IdealChoice {
FILE: src/graphql/2014/types/monsterTypes.ts
class BreathChoiceOption (line 15) | class BreathChoiceOption {
class BreathChoiceOptionSet (line 30) | class BreathChoiceOptionSet {
class BreathChoice (line 39) | class BreathChoice {
class DamageChoiceOption (line 55) | class DamageChoiceOption {
class DamageChoiceOptionSet (line 64) | class DamageChoiceOptionSet {
class DamageChoice (line 73) | class DamageChoice {
class ActionChoiceOption (line 89) | class ActionChoiceOption {
class MultipleActionChoiceOption (line 107) | class MultipleActionChoiceOption {
class ActionChoiceOptionSet (line 116) | class ActionChoiceOptionSet {
class ActionChoice (line 125) | class ActionChoice {
method resolveType (line 144) | resolveType(value) {
FILE: src/graphql/2014/types/startingEquipment/choice.ts
class StartingEquipmentChoice (line 7) | class StartingEquipmentChoice {
FILE: src/graphql/2014/types/startingEquipment/common.ts
class ProficiencyPrerequisite (line 10) | class ProficiencyPrerequisite {
class CountedReferenceOption (line 21) | class CountedReferenceOption {
class EquipmentCategorySet (line 42) | class EquipmentCategorySet {
class EquipmentCategoryChoice (line 57) | class EquipmentCategoryChoice {
class EquipmentCategoryChoiceOption (line 74) | class EquipmentCategoryChoiceOption {
class MultipleItemsOption (line 104) | class MultipleItemsOption {
FILE: src/graphql/2014/types/startingEquipment/optionSet.ts
class EquipmentOptionSet (line 11) | class EquipmentOptionSet {
FILE: src/graphql/2014/types/traitTypes.ts
class TraitChoiceOption (line 7) | class TraitChoiceOption {
class TraitChoiceOptionSet (line 18) | class TraitChoiceOptionSet {
class TraitChoice (line 29) | class TraitChoice {
class SpellChoiceOption (line 41) | class SpellChoiceOption {
class SpellChoiceOptionSet (line 52) | class SpellChoiceOptionSet {
class SpellChoice (line 63) | class SpellChoice {
FILE: src/graphql/2014/utils/helpers.ts
function normalizeCount (line 35) | function normalizeCount(count: string | number): number {
FILE: src/graphql/2014/utils/resolvers.ts
function resolveLanguageChoice (line 19) | async function resolveLanguageChoice(
function resolveProficiencyChoice (line 58) | async function resolveProficiencyChoice(
function resolveProficiencyChoiceArray (line 105) | async function resolveProficiencyChoiceArray(
FILE: src/graphql/2014/utils/startingEquipmentResolver.ts
type ProficiencyPrerequisite (line 24) | interface ProficiencyPrerequisite {
function resolveStartingEquipmentChoices (line 30) | async function resolveStartingEquipmentChoices(
function resolveStartingEquipmentChoice (line 47) | async function resolveStartingEquipmentChoice(
function resolveStartingEquipmentOptionSet (line 64) | async function resolveStartingEquipmentOptionSet(
function resolveEquipmentOptionSet (line 84) | async function resolveEquipmentOptionSet(
function resolveEquipmentOptionUnion (line 105) | async function resolveEquipmentOptionUnion(
function resolveProficiencyPrerequisites (line 125) | async function resolveProficiencyPrerequisites(
function resolveCountedReferenceOption (line 144) | async function resolveCountedReferenceOption(
function resolveEquipmentCategoryChoiceOption (line 164) | async function resolveEquipmentCategoryChoiceOption(
function resolveMultipleItemsOption (line 198) | async function resolveMultipleItemsOption(
FILE: src/graphql/2024/common/choiceTypes.ts
class ScorePrerequisiteOption2024 (line 9) | class ScorePrerequisiteOption2024 {
class ScorePrerequisiteOptionSet2024 (line 21) | class ScorePrerequisiteOptionSet2024 {
class ScorePrerequisiteChoice2024 (line 32) | class ScorePrerequisiteChoice2024 {
class Proficiency2024ChoiceOption (line 49) | class Proficiency2024ChoiceOption {
class Proficiency2024ChoiceOptionSet (line 58) | class Proficiency2024ChoiceOptionSet {
class Proficiency2024Choice (line 67) | class Proficiency2024Choice {
FILE: src/graphql/2024/common/equipmentTypes.ts
class Armor (line 20) | class Armor extends Equipment2024 {
class Weapon (line 44) | class Weapon extends Equipment2024 {
class AdventuringGear (line 65) | class AdventuringGear extends Equipment2024 {}
class Pack (line 71) | class Pack extends AdventuringGear {
class Ammunition (line 77) | class Ammunition extends AdventuringGear {
class Tool (line 86) | class Tool extends Equipment2024 {
FILE: src/graphql/2024/common/resolver.ts
class DifficultyClassResolver (line 8) | class DifficultyClassResolver {
method dc_type (line 10) | async dc_type(@Root() dc: DifficultyClass): Promise<AbilityScore2024 |...
FILE: src/graphql/2024/common/unions.ts
function resolveEquipmentType (line 7) | function resolveEquipmentType(
FILE: src/graphql/2024/resolvers/abilityScore/args.ts
type AbilityScoreOrderField (line 12) | enum AbilityScoreOrderField {
constant ABILITY_SCORE_SORT_FIELD_MAP (line 17) | const ABILITY_SCORE_SORT_FIELD_MAP: Record<AbilityScoreOrderField, strin...
class AbilityScoreOrder (line 28) | class AbilityScoreOrder implements BaseOrderInterface<AbilityScoreOrderF...
class AbilityScoreArgs (line 56) | class AbilityScoreArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/abilityScore/resolver.ts
class AbilityScoreResolver (line 18) | class AbilityScoreResolver {
method abilityScores (line 22) | async abilityScores(
method abilityScore (line 68) | async abilityScore(
method skills (line 76) | async skills(@Root() abilityScore: AbilityScore2024): Promise<Skill202...
FILE: src/graphql/2024/resolvers/alignment/args.ts
type AlignmentOrderField (line 12) | enum AlignmentOrderField {
constant ALIGNMENT_SORT_FIELD_MAP (line 16) | const ALIGNMENT_SORT_FIELD_MAP: Record<AlignmentOrderField, string> = {
class AlignmentOrder (line 26) | class AlignmentOrder implements BaseOrderInterface<AlignmentOrderField> {
class AlignmentArgs (line 53) | class AlignmentArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/alignment/resolver.ts
class AlignmentResolver (line 16) | class AlignmentResolver {
method alignments (line 20) | async alignments(@Args(() => AlignmentArgs) args: AlignmentArgs): Prom...
method alignment (line 53) | async alignment(@Arg('index', () => String) indexInput: string): Promi...
FILE: src/graphql/2024/resolvers/background/args.ts
type BackgroundOrderField (line 12) | enum BackgroundOrderField {
constant BACKGROUND_SORT_FIELD_MAP (line 16) | const BACKGROUND_SORT_FIELD_MAP: Record<BackgroundOrderField, string> = {
class BackgroundOrder (line 26) | class BackgroundOrder implements BaseOrderInterface<BackgroundOrderField> {
class BackgroundArgs (line 53) | class BackgroundArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/background/resolver.ts
class BackgroundResolver (line 24) | class BackgroundResolver {
method backgrounds (line 28) | async backgrounds(@Args(() => BackgroundArgs) args: BackgroundArgs): P...
method background (line 58) | async background(
method ability_scores (line 66) | async ability_scores(@Root() background: Background2024): Promise<Abil...
method feat (line 71) | async feat(@Root() background: Background2024): Promise<Feat2024 | nul...
method proficiencies (line 76) | async proficiencies(@Root() background: Background2024): Promise<Profi...
method proficiency_choices (line 81) | async proficiency_choices(
method equipment_options (line 88) | async equipment_options(
FILE: src/graphql/2024/resolvers/condition/args.ts
type ConditionOrderField (line 12) | enum ConditionOrderField {
constant CONDITION_SORT_FIELD_MAP (line 16) | const CONDITION_SORT_FIELD_MAP: Record<ConditionOrderField, string> = {
class ConditionOrder (line 26) | class ConditionOrder implements BaseOrderInterface<ConditionOrderField> {
class ConditionArgs (line 53) | class ConditionArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/condition/resolver.ts
class ConditionResolver (line 16) | class ConditionResolver {
method conditions (line 20) | async conditions(@Args(() => ConditionArgs) args: ConditionArgs): Prom...
method condition (line 52) | async condition(@Arg('index', () => String) indexInput: string): Promi...
FILE: src/graphql/2024/resolvers/damageType/args.ts
type DamageTypeOrderField (line 12) | enum DamageTypeOrderField {
constant DAMAGE_TYPE_SORT_FIELD_MAP (line 16) | const DAMAGE_TYPE_SORT_FIELD_MAP: Record<DamageTypeOrderField, string> = {
class DamageTypeOrder (line 26) | class DamageTypeOrder implements BaseOrderInterface<DamageTypeOrderField> {
class DamageTypeArgs (line 53) | class DamageTypeArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/damageType/resolver.ts
class DamageTypeResolver (line 16) | class DamageTypeResolver {
method damageTypes (line 20) | async damageTypes(@Args(() => DamageTypeArgs) args: DamageTypeArgs): P...
method damageType (line 52) | async damageType(@Arg('index', () => String) indexInput: string): Prom...
FILE: src/graphql/2024/resolvers/equipment/args.ts
type EquipmentOrderField (line 12) | enum EquipmentOrderField {
constant EQUIPMENT_SORT_FIELD_MAP (line 18) | const EQUIPMENT_SORT_FIELD_MAP: Record<EquipmentOrderField, string> = {
class EquipmentOrder (line 30) | class EquipmentOrder implements BaseOrderInterface<EquipmentOrderField> {
class EquipmentArgs (line 57) | class EquipmentArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/equipment/resolver.ts
class EquipmentResolver (line 22) | class EquipmentResolver {
method equipments (line 26) | async equipments(
method equipment (line 68) | async equipment(
method properties (line 76) | async properties(@Root() equipment: Equipment2024): Promise<WeaponProp...
class ContentFieldResolver (line 83) | class ContentFieldResolver {
method item (line 88) | async item(@Root() content: Content): Promise<typeof AnyEquipment | nu...
class ToolResolver (line 98) | class ToolResolver {
method ability (line 100) | async ability(@Root() tool: Tool): Promise<AbilityScore2024 | null> {
method craft (line 106) | async craft(@Root() tool: Tool): Promise<Array<typeof AnyEquipment> | ...
FILE: src/graphql/2024/resolvers/equipmentCategory/args.ts
type EquipmentCategoryOrderField (line 12) | enum EquipmentCategoryOrderField {
constant EQUIPMENT_CATEGORY_SORT_FIELD_MAP (line 16) | const EQUIPMENT_CATEGORY_SORT_FIELD_MAP: Record<EquipmentCategoryOrderFi...
class EquipmentCategoryOrder (line 26) | class EquipmentCategoryOrder implements BaseOrderInterface<EquipmentCate...
class EquipmentCategoryArgs (line 52) | class EquipmentCategoryArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/equipmentCategory/resolver.ts
class EquipmentCategoryResolver (line 18) | class EquipmentCategoryResolver {
method equipmentCategories (line 22) | async equipmentCategories(
method equipmentCategory (line 56) | async equipmentCategory(
method equipment (line 65) | async equipment(@Root() equipmentCategory: EquipmentCategory2024): Pro...
FILE: src/graphql/2024/resolvers/feat/args.ts
type FeatOrderField (line 12) | enum FeatOrderField {
constant FEAT_SORT_FIELD_MAP (line 17) | const FEAT_SORT_FIELD_MAP: Record<FeatOrderField, string> = {
class FeatOrder (line 28) | class FeatOrder implements BaseOrderInterface<FeatOrderField> {
class FeatArgs (line 56) | class FeatArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/feat/resolver.ts
class FeatResolver (line 12) | class FeatResolver {
method feats (line 16) | async feats(@Args(() => FeatArgs) args: FeatArgs): Promise<Feat2024[]> {
method feat (line 55) | async feat(@Arg('index', () => String) indexInput: string): Promise<Fe...
method prerequisite_options (line 61) | async prerequisite_options(@Root() feat: Feat2024): Promise<ScorePrere...
FILE: src/graphql/2024/resolvers/language/args.ts
type LanguageOrderField (line 12) | enum LanguageOrderField {
constant LANGUAGE_SORT_FIELD_MAP (line 18) | const LANGUAGE_SORT_FIELD_MAP: Record<LanguageOrderField, string> = {
class LanguageOrder (line 30) | class LanguageOrder implements BaseOrderInterface<LanguageOrderField> {
class LanguageArgs (line 59) | class LanguageArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/language/resolver.ts
class LanguageResolver (line 16) | class LanguageResolver {
method language (line 21) | async language(@Arg('index', () => String) indexInput: string): Promis...
method languages (line 29) | async languages(@Args(() => LanguageArgs) args: LanguageArgs): Promise...
FILE: src/graphql/2024/resolvers/magicItem/args.ts
type MagicItemOrderField (line 12) | enum MagicItemOrderField {
constant MAGIC_ITEM_SORT_FIELD_MAP (line 16) | const MAGIC_ITEM_SORT_FIELD_MAP: Record<MagicItemOrderField, string> = {
class MagicItemOrder (line 26) | class MagicItemOrder implements BaseOrderInterface<MagicItemOrderField> {
class MagicItemArgs (line 55) | class MagicItemArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/magicItem/resolver.ts
class MagicItemResolver (line 18) | class MagicItemResolver {
method magicItems (line 22) | async magicItems(@Args(() => MagicItemArgs) args: MagicItemArgs): Prom...
method magicItem (line 67) | async magicItem(@Arg('index', () => String) indexInput: string): Promi...
method equipment_category (line 73) | async equipment_category(@Root() magicItem: MagicItem2024): Promise<Eq...
method variants (line 78) | async variants(@Root() magicItem: MagicItem2024): Promise<MagicItem202...
FILE: src/graphql/2024/resolvers/magicSchool/args.ts
type MagicSchoolOrderField (line 12) | enum MagicSchoolOrderField {
constant MAGIC_SCHOOL_SORT_FIELD_MAP (line 16) | const MAGIC_SCHOOL_SORT_FIELD_MAP: Record<MagicSchoolOrderField, string>...
class MagicSchoolOrder (line 26) | class MagicSchoolOrder implements BaseOrderInterface<MagicSchoolOrderFie...
class MagicSchoolArgs (line 53) | class MagicSchoolArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/magicSchool/resolver.ts
class MagicSchoolResolver (line 16) | class MagicSchoolResolver {
method magicSchools (line 20) | async magicSchools(
method magicSchool (line 54) | async magicSchool(
FILE: src/graphql/2024/resolvers/proficiency/args.ts
type ProficiencyOrderField (line 12) | enum ProficiencyOrderField {
constant PROFICIENCY_SORT_FIELD_MAP (line 17) | const PROFICIENCY_SORT_FIELD_MAP: Record<ProficiencyOrderField, string> = {
class ProficiencyOrder (line 28) | class ProficiencyOrder implements BaseOrderInterface<ProficiencyOrderFie...
class ProficiencyArgs (line 56) | class ProficiencyArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/proficiency/resolver.ts
class ProficiencyResolver (line 18) | class ProficiencyResolver {
method proficiencies (line 22) | async proficiencies(
method proficiency (line 66) | async proficiency(
method backgrounds (line 74) | async backgrounds(@Root() proficiency: Proficiency2024): Promise<Backg...
FILE: src/graphql/2024/resolvers/skill/args.ts
type SkillOrderField (line 12) | enum SkillOrderField {
constant SKILL_SORT_FIELD_MAP (line 17) | const SKILL_SORT_FIELD_MAP: Record<SkillOrderField, string> = {
class SkillOrder (line 28) | class SkillOrder implements BaseOrderInterface<SkillOrderField> {
class SkillArgs (line 56) | class SkillArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/skill/resolver.ts
class SkillResolver (line 18) | class SkillResolver {
method skills (line 22) | async skills(@Args(() => SkillArgs) args: SkillArgs): Promise<Skill202...
method skill (line 61) | async skill(@Arg('index', () => String) indexInput: string): Promise<S...
method ability_score (line 67) | async ability_score(@Root() skill: Skill2024): Promise<AbilityScore202...
FILE: src/graphql/2024/resolvers/species/args.ts
type SpeciesOrderField (line 12) | enum SpeciesOrderField {
constant SPECIES_SORT_FIELD_MAP (line 16) | const SPECIES_SORT_FIELD_MAP: Record<SpeciesOrderField, string> = {
class SpeciesOrder (line 26) | class SpeciesOrder implements BaseOrderInterface<SpeciesOrderField> {
class SpeciesArgs (line 53) | class SpeciesArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/species/resolver.ts
class SpeciesResolver (line 19) | class SpeciesResolver {
method species2024 (line 23) | async species2024(@Args(() => SpeciesArgs) args: SpeciesArgs): Promise...
method species2024ByIndex (line 52) | async species2024ByIndex(
method subspecies (line 62) | async subspecies(@Root() species: Species2024): Promise<Subspecies2024...
method traits (line 69) | async traits(@Root() species: Species2024): Promise<Trait2024[]> {
FILE: src/graphql/2024/resolvers/subclass/args.ts
type SubclassOrderField (line 12) | enum SubclassOrderField {
constant SUBCLASS_SORT_FIELD_MAP (line 16) | const SUBCLASS_SORT_FIELD_MAP: Record<SubclassOrderField, string> = {
class SubclassOrder (line 26) | class SubclassOrder implements BaseOrderInterface<SubclassOrderField> {
class SubclassArgs (line 53) | class SubclassArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/subclass/resolver.ts
class SubclassResolver (line 16) | class SubclassResolver {
method subclasses (line 20) | async subclasses(@Args(() => SubclassArgs) args: SubclassArgs): Promis...
method subclass (line 52) | async subclass(@Arg('index', () => String) indexInput: string): Promis...
FILE: src/graphql/2024/resolvers/subspecies/args.ts
type SubspeciesOrderField (line 12) | enum SubspeciesOrderField {
constant SUBSPECIES_SORT_FIELD_MAP (line 16) | const SUBSPECIES_SORT_FIELD_MAP: Record<SubspeciesOrderField, string> = {
class SubspeciesOrder (line 26) | class SubspeciesOrder implements BaseOrderInterface<SubspeciesOrderField> {
class SubspeciesArgs (line 53) | class SubspeciesArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/subspecies/resolver.ts
class SubspeciesResolver (line 20) | class SubspeciesResolver {
method subspecies2024 (line 24) | async subspecies2024(
method subspecies2024ByIndex (line 58) | async subspecies2024ByIndex(
method species (line 68) | async species(@Root() subspecies: Subspecies2024): Promise<Species2024...
method traits (line 75) | async traits(@Root() subspecies: Subspecies2024): Promise<Trait2024[]> {
method damage_type (line 83) | async damage_type(@Root() subspecies: Subspecies2024): Promise<DamageT...
FILE: src/graphql/2024/resolvers/trait/args.ts
type TraitOrderField (line 12) | enum TraitOrderField {
constant TRAIT_SORT_FIELD_MAP (line 16) | const TRAIT_SORT_FIELD_MAP: Record<TraitOrderField, string> = {
class TraitOrder (line 26) | class TraitOrder implements BaseOrderInterface<TraitOrderField> {
class TraitArgs (line 53) | class TraitArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/trait/resolver.ts
class TraitResolver (line 19) | class TraitResolver {
method traits2024 (line 23) | async traits2024(@Args(() => TraitArgs) args: TraitArgs): Promise<Trai...
method trait2024 (line 52) | async trait2024(@Arg('index', () => String) indexInput: string): Promi...
method species (line 60) | async species(@Root() trait: Trait2024): Promise<Species2024[]> {
method subspecies (line 67) | async subspecies(@Root() trait: Trait2024): Promise<Subspecies2024[]> {
FILE: src/graphql/2024/resolvers/weaponMasteryProperty/args.ts
type WeaponMasteryPropertyOrderField (line 12) | enum WeaponMasteryPropertyOrderField {
constant WEAPON_MASTERY_PROPERTY_SORT_FIELD_MAP (line 16) | const WEAPON_MASTERY_PROPERTY_SORT_FIELD_MAP: Record<
class WeaponMasteryPropertyOrder (line 29) | class WeaponMasteryPropertyOrder
class WeaponMasteryPropertyArgs (line 58) | class WeaponMasteryPropertyArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/weaponMasteryProperty/resolver.ts
class WeaponMasteryPropertyResolver (line 18) | class WeaponMasteryPropertyResolver {
method weaponMasteryProperties (line 23) | async weaponMasteryProperties(
method weaponMasteryProperty (line 57) | async weaponMasteryProperty(
FILE: src/graphql/2024/resolvers/weaponProperty/args.ts
type WeaponPropertyOrderField (line 12) | enum WeaponPropertyOrderField {
constant WEAPON_PROPERTY_SORT_FIELD_MAP (line 16) | const WEAPON_PROPERTY_SORT_FIELD_MAP: Record<WeaponPropertyOrderField, s...
class WeaponPropertyOrder (line 26) | class WeaponPropertyOrder implements BaseOrderInterface<WeaponPropertyOr...
class WeaponPropertyArgs (line 53) | class WeaponPropertyArgs extends BaseFilterArgs {
FILE: src/graphql/2024/resolvers/weaponProperty/resolver.ts
class WeaponPropertyResolver (line 16) | class WeaponPropertyResolver {
method weaponProperties (line 20) | async weaponProperties(
method weaponProperty (line 54) | async weaponProperty(
FILE: src/graphql/2024/types/backgroundEquipment/choice.ts
class BackgroundEquipmentChoice2024 (line 6) | class BackgroundEquipmentChoice2024 {
FILE: src/graphql/2024/types/backgroundEquipment/common.ts
class MoneyOption2024 (line 10) | class MoneyOption2024 {
class CountedReferenceOption2024 (line 24) | class CountedReferenceOption2024 {
class EquipmentCategorySet2024 (line 38) | class EquipmentCategorySet2024 {
class EquipmentCategoryChoice2024 (line 49) | class EquipmentCategoryChoice2024 {
class ChoiceItemOption2024 (line 61) | class ChoiceItemOption2024 {
method resolveType (line 77) | resolveType(value) {
class MultipleItemsOption2024 (line 86) | class MultipleItemsOption2024 {
FILE: src/graphql/2024/types/backgroundEquipment/optionSet.ts
class EquipmentOptionSet2024 (line 6) | class EquipmentOptionSet2024 {
method resolveType (line 18) | resolveType(value) {
FILE: src/graphql/2024/utils/backgroundEquipmentResolver.ts
function resolveBackgroundEquipmentChoices (line 26) | async function resolveBackgroundEquipmentChoices(
function resolveBackgroundEquipmentChoice (line 41) | async function resolveBackgroundEquipmentChoice(
function resolveEquipmentOptionSet (line 57) | async function resolveEquipmentOptionSet(
function resolveEquipmentOptionUnion (line 79) | async function resolveEquipmentOptionUnion(
function resolveCountedReferenceOption (line 97) | async function resolveCountedReferenceOption(
function resolveMultipleItemsOption (line 114) | async function resolveMultipleItemsOption(
function resolveChoiceItemOption (line 146) | async function resolveChoiceItemOption(option: ChoiceOption): Promise<Ch...
function resolveMoneyOption (line 186) | function resolveMoneyOption(option: MoneyOption): MoneyOption2024 {
FILE: src/graphql/2024/utils/choiceResolvers.ts
function resolveScorePrerequisiteChoice (line 19) | async function resolveScorePrerequisiteChoice(
function resolveProficiency2024Choice (line 50) | async function resolveProficiency2024Choice(
function resolveProficiency2024ChoiceArray (line 80) | async function resolveProficiency2024ChoiceArray(
FILE: src/graphql/common/args.ts
class BasePaginationArgs (line 14) | class BasePaginationArgs {
class BaseFilterArgs (line 36) | class BaseFilterArgs extends BasePaginationArgs {
type BaseOrderInterface (line 52) | interface BaseOrderInterface<TOrderFieldValue extends string> {
type BuildSortPipelineArgs (line 58) | interface BuildSortPipelineArgs<TOrderFieldValue extends string> {
function buildSortPipeline (line 65) | function buildSortPipeline<TOrderFieldValue extends string>({
FILE: src/graphql/common/choiceTypes.ts
class StringChoiceOption (line 7) | class StringChoiceOption {
class StringChoiceOptionSet (line 16) | class StringChoiceOptionSet {
class StringChoice (line 25) | class StringChoice {
FILE: src/graphql/common/enums.ts
type OrderByDirection (line 3) | enum OrderByDirection {
FILE: src/graphql/common/inputs.ts
class NumberRangeFilterInput (line 16) | class NumberRangeFilterInput {
class NumberFilterInput (line 41) | class NumberFilterInput {
function buildMongoQueryFromNumberFilter (line 69) | function buildMongoQueryFromNumberFilter(
FILE: src/graphql/common/types.ts
class LevelValue (line 4) | class LevelValue {
class SpellSlotCount (line 13) | class SpellSlotCount {
FILE: src/graphql/utils/resolvers.ts
function resolveSingleReference (line 17) | async function resolveSingleReference<T>(
function resolveMultipleReferences (line 27) | async function resolveMultipleReferences<T>(
function resolveReferenceOptionArray (line 39) | async function resolveReferenceOptionArray<
function resolveStringChoice (line 58) | function resolveStringChoice(choiceData: Choice): StringChoice {
FILE: src/models/2014/abilityScore.ts
class AbilityScore (line 15) | class AbilityScore {
type AbilityScoreDocument (line 46) | type AbilityScoreDocument = DocumentType<AbilityScore>
FILE: src/models/2014/alignment.ts
class Alignment (line 9) | class Alignment {
type AlignmentDocument (line 40) | type AlignmentDocument = DocumentType<Alignment>
FILE: src/models/2014/background.ts
class EquipmentRef (line 13) | class EquipmentRef {
class BackgroundFeature (line 23) | @ObjectType({ description: 'A special feature granted by the background....
class Background (line 38) | class Background {
type BackgroundDocument (line 93) | type BackgroundDocument = DocumentType<Background>
FILE: src/models/2014/class.ts
class ClassEquipment (line 16) | class ClassEquipment {
class SpellcastingInfo (line 27) | class SpellcastingInfo {
class Spellcasting (line 38) | class Spellcasting {
class MultiClassingPrereq (line 53) | class MultiClassingPrereq {
class MultiClassing (line 64) | class MultiClassing {
class Class (line 90) | class Class {
type ClassDocument (line 168) | type ClassDocument = DocumentType<Class>
FILE: src/models/2014/collection.ts
class Collection (line 7) | class Collection {
type CollectionDocument (line 12) | type CollectionDocument = DocumentType<Collection>
FILE: src/models/2014/condition.ts
class Condition (line 9) | class Condition {
type ConditionDocument (line 30) | type ConditionDocument = DocumentType<Condition>
FILE: src/models/2014/damageType.ts
class DamageType (line 9) | class DamageType {
type DamageTypeDocument (line 30) | type DamageTypeDocument = DocumentType<DamageType>
FILE: src/models/2014/equipment.ts
class ArmorClass (line 12) | class ArmorClass {
class Content (line 27) | class Content {
class Cost (line 38) | class Cost {
class Range (line 49) | class Range {
class Speed (line 60) | class Speed {
class ThrowRange (line 71) | class ThrowRange {
class Equipment (line 85) | class Equipment implements IEquipment {
type EquipmentDocument (line 188) | type EquipmentDocument = DocumentType<Equipment>
FILE: src/models/2014/equipmentCategory.ts
class EquipmentCategory (line 12) | class EquipmentCategory {
type EquipmentCategoryDocument (line 33) | type EquipmentCategoryDocument = DocumentType<EquipmentCategory>
FILE: src/models/2014/feat.ts
class Prerequisite (line 11) | class Prerequisite {
class Feat (line 28) | class Feat {
type FeatDocument (line 53) | type FeatDocument = DocumentType<Feat>
FILE: src/models/2014/feature.ts
class LevelPrerequisite (line 15) | class LevelPrerequisite {
class FeaturePrerequisite (line 26) | class FeaturePrerequisite {
class SpellPrerequisite (line 37) | class SpellPrerequisite {
type Prerequisite (line 47) | type Prerequisite = LevelPrerequisite | FeaturePrerequisite | SpellPrere...
class FeatureSpecific (line 50) | class FeatureSpecific {
class Feature (line 70) | class Feature {
type FeatureDocument (line 128) | type FeatureDocument = DocumentType<Feature>
FILE: src/models/2014/language.ts
class Language (line 9) | class Language {
type LanguageDocument (line 45) | type LanguageDocument = DocumentType<Language>
FILE: src/models/2014/level.ts
class ClassSpecificCreatingSpellSlot (line 14) | class ClassSpecificCreatingSpellSlot {
class ClassSpecificMartialArt (line 25) | class ClassSpecificMartialArt {
class ClassSpecificSneakAttack (line 36) | class ClassSpecificSneakAttack {
class ClassSpecific (line 47) | class ClassSpecific {
class LevelSpellcasting (line 241) | class LevelSpellcasting {
class SubclassSpecific (line 291) | class SubclassSpecific {
class Level (line 311) | class Level {
type LevelDocument (line 378) | type LevelDocument = DocumentType<Level>
FILE: src/models/2014/magicItem.ts
class Rarity (line 11) | class Rarity {
class MagicItem (line 21) | class MagicItem {
type MagicItemDocument (line 76) | type MagicItemDocument = DocumentType<MagicItem>
FILE: src/models/2014/magicSchool.ts
class MagicSchool (line 11) | class MagicSchool {
type MagicSchoolDocument (line 32) | type MagicSchoolDocument = DocumentType<MagicSchool>
FILE: src/models/2014/monster.ts
class ActionOption (line 18) | class ActionOption {
class ActionUsage (line 33) | class ActionUsage {
class MonsterAction (line 48) | class MonsterAction {
class ArmorClassDex (line 94) | class ArmorClassDex {
class ArmorClassNatural (line 112) | class ArmorClassNatural {
class ArmorClassArmor (line 130) | class ArmorClassArmor {
class ArmorClassSpell (line 152) | class ArmorClassSpell {
class ArmorClassCondition (line 176) | class ArmorClassCondition {
class LegendaryAction (line 200) | class LegendaryAction {
class MonsterProficiency (line 226) | class MonsterProficiency {
class Reaction (line 239) | class Reaction {
class Sense (line 257) | class Sense {
class SpecialAbilityUsage (line 280) | class SpecialAbilityUsage {
class SpecialAbilitySpell (line 301) | class SpecialAbilitySpell {
class SpecialAbilitySpellcasting (line 323) | class SpecialAbilitySpellcasting {
class SpecialAbility (line 358) | class SpecialAbility {
class MonsterSpeed (line 398) | class MonsterSpeed {
class Monster (line 426) | class Monster {
type MonsterDocument (line 580) | type MonsterDocument = DocumentType<Monster>
FILE: src/models/2014/proficiency.ts
class Proficiency (line 15) | class Proficiency {
type ProficiencyDocument (line 49) | type ProficiencyDocument = DocumentType<Proficiency>
FILE: src/models/2014/race.ts
class RaceAbilityBonus (line 15) | class RaceAbilityBonus {
class Race (line 30) | class Race {
type RaceDocument (line 98) | type RaceDocument = DocumentType<Race>
FILE: src/models/2014/rule.ts
class Rule (line 12) | class Rule {
type RuleDocument (line 39) | type RuleDocument = DocumentType<Rule>
FILE: src/models/2014/ruleSection.ts
class RuleSection (line 9) | class RuleSection {
type RuleSectionDocument (line 32) | type RuleSectionDocument = DocumentType<RuleSection>
FILE: src/models/2014/skill.ts
class Skill (line 14) | class Skill {
type SkillDocument (line 40) | type SkillDocument = DocumentType<Skill>
FILE: src/models/2014/spell.ts
class SpellDamage (line 17) | class SpellDamage {
class SpellDC (line 32) | class SpellDC {
class Spell (line 51) | class Spell {
type SpellDocument (line 152) | type SpellDocument = DocumentType<Spell>
FILE: src/models/2014/subclass.ts
class Prerequisite (line 13) | class Prerequisite {
class SubclassSpell (line 28) | class SubclassSpell {
class Subclass (line 42) | class Subclass {
type SubclassDocument (line 85) | type SubclassDocument = DocumentType<Subclass>
FILE: src/models/2014/subrace.ts
class SubraceAbilityBonus (line 13) | class SubraceAbilityBonus {
class Subrace (line 28) | class Subrace {
type SubraceDocument (line 66) | type SubraceDocument = DocumentType<Subrace>
FILE: src/models/2014/trait.ts
class ActionDamage (line 18) | class ActionDamage {
class Usage (line 29) | class Usage {
class TraitActionDC (line 40) | class TraitActionDC {
class Action (line 51) | class Action {
class TraitSpecific (line 82) | class TraitSpecific {
class Trait (line 107) | class Trait {
type TraitDocument (line 162) | type TraitDocument = DocumentType<Trait>
FILE: src/models/2014/weaponProperty.ts
class WeaponProperty (line 11) | class WeaponProperty {
type WeaponPropertyDocument (line 34) | type WeaponPropertyDocument = DocumentType<WeaponProperty>
FILE: src/models/2024/abilityScore.ts
class AbilityScore2024 (line 14) | class AbilityScore2024 {
type AbilityScoreDocument (line 45) | type AbilityScoreDocument = DocumentType<AbilityScore2024>
FILE: src/models/2024/alignment.ts
class Alignment2024 (line 11) | class Alignment2024 {
type AlignmentDocument (line 42) | type AlignmentDocument = DocumentType<Alignment2024>
FILE: src/models/2024/background.ts
class BackgroundFeatReference (line 10) | class BackgroundFeatReference {
class Background2024 (line 30) | class Background2024 {
type BackgroundDocument (line 62) | type BackgroundDocument = DocumentType<Background2024>
FILE: src/models/2024/collection.ts
class Collection2024 (line 7) | class Collection2024 {
type CollectionDocument (line 12) | type CollectionDocument = DocumentType<Collection2024>
FILE: src/models/2024/condition.ts
class Condition2024 (line 9) | class Condition2024 {
type ConditionDocument (line 30) | type ConditionDocument = DocumentType<Condition2024>
FILE: src/models/2024/damageType.ts
class DamageType2024 (line 9) | class DamageType2024 {
type DamageTypeDocument (line 30) | type DamageTypeDocument = DocumentType<DamageType2024>
FILE: src/models/2024/equipment.ts
class ArmorClass (line 12) | class ArmorClass {
class Content (line 27) | class Content {
class Cost (line 38) | class Cost {
class Range (line 49) | class Range {
class ThrowRange (line 60) | class ThrowRange {
class Utilize (line 71) | class Utilize {
class Equipment2024 (line 85) | class Equipment2024 {
type EquipmentDocument (line 179) | type EquipmentDocument = DocumentType<Equipment2024>
FILE: src/models/2024/equipmentCategory.ts
class EquipmentCategory2024 (line 12) | class EquipmentCategory2024 {
type EquipmentCategoryDocument (line 33) | type EquipmentCategoryDocument = DocumentType<EquipmentCategory2024>
FILE: src/models/2024/feat.ts
class FeatPrerequisites2024 (line 10) | class FeatPrerequisites2024 {
class Feat2024 (line 25) | class Feat2024 {
type FeatDocument (line 63) | type FeatDocument = DocumentType<Feat2024>
FILE: src/models/2024/language.ts
class Language2024 (line 9) | class Language2024 {
type LanguageDocument (line 34) | type LanguageDocument = DocumentType<Language2024>
FILE: src/models/2024/magicItem.ts
class Rarity2024 (line 11) | class Rarity2024 {
class MagicItem2024 (line 21) | class MagicItem2024 {
type MagicItemDocument (line 84) | type MagicItemDocument = DocumentType<MagicItem2024>
FILE: src/models/2024/magicSchool.ts
class MagicSchool2024 (line 11) | class MagicSchool2024 {
type MagicSchoolDocument (line 32) | type MagicSchoolDocument = DocumentType<MagicSchool2024>
FILE: src/models/2024/proficiency.ts
class Proficiency2024 (line 10) | class Proficiency2024 {
type ProficiencyDocument (line 41) | type ProficiencyDocument = DocumentType<Proficiency2024>
FILE: src/models/2024/skill.ts
class Skill2024 (line 14) | class Skill2024 {
type SkillDocument (line 39) | type SkillDocument = DocumentType<Skill2024>
FILE: src/models/2024/species.ts
class Species2024 (line 13) | class Species2024 {
type SpeciesDocument (line 56) | type SpeciesDocument = DocumentType<Species2024>
FILE: src/models/2024/subclass.ts
class SubclassFeature2024 (line 8) | class SubclassFeature2024 {
class Subclass2024 (line 24) | class Subclass2024 {
type SubclassDocument (line 53) | type SubclassDocument = DocumentType<Subclass2024>
FILE: src/models/2024/subspecies.ts
class SubspeciesTrait (line 11) | class SubspeciesTrait {
class Subspecies2024 (line 33) | class Subspecies2024 {
type SubspeciesDocument (line 65) | type SubspeciesDocument = DocumentType<Subspecies2024>
FILE: src/models/2024/trait.ts
class Trait2024 (line 13) | class Trait2024 {
type TraitDocument (line 54) | type TraitDocument = DocumentType<Trait2024>
FILE: src/models/2024/weaponMasteryProperty.ts
class WeaponMasteryProperty2024 (line 12) | class WeaponMasteryProperty2024 {
type WeaponMasteryPropertyDocument (line 35) | type WeaponMasteryPropertyDocument = DocumentType<WeaponMasteryProperty2...
FILE: src/models/2024/weaponProperty.ts
class WeaponProperty2024 (line 11) | class WeaponProperty2024 {
type WeaponPropertyDocument (line 34) | type WeaponPropertyDocument = DocumentType<WeaponProperty2024>
FILE: src/models/common/apiReference.ts
class APIReference (line 6) | class APIReference {
FILE: src/models/common/areaOfEffect.ts
class AreaOfEffect (line 5) | class AreaOfEffect {
FILE: src/models/common/choice.ts
class OptionSet (line 8) | class OptionSet {
class EquipmentCategoryOptionSet (line 13) | class EquipmentCategoryOptionSet extends OptionSet {
class ResourceListOptionSet (line 18) | class ResourceListOptionSet extends OptionSet {
class OptionsArrayOptionSet (line 23) | class OptionsArrayOptionSet extends OptionSet {
class Option (line 29) | class Option {
class ReferenceOption (line 34) | class ReferenceOption extends Option {
class ActionOption (line 39) | class ActionOption extends Option {
class MultipleOption (line 53) | class MultipleOption extends Option {
class StringOption (line 58) | class StringOption extends Option {
class IdealOption (line 63) | class IdealOption extends Option {
class CountedReferenceOption (line 71) | class CountedReferenceOption extends Option {
class ScorePrerequisiteOption (line 93) | class ScorePrerequisiteOption extends Option {
class AbilityBonusOption (line 101) | class AbilityBonusOption extends Option {
class BreathOption (line 109) | class BreathOption extends Option {
class DamageOption (line 120) | class DamageOption extends Option {
class Choice (line 131) | class Choice {
class ChoiceOption (line 145) | class ChoiceOption extends Option {
class MoneyOption (line 150) | class MoneyOption extends Option {
FILE: src/models/common/damage.ts
class Damage (line 8) | class Damage {
FILE: src/models/common/difficultyClass.ts
class DifficultyClass (line 11) | class DifficultyClass {
FILE: src/tests/controllers/globalSetup.ts
function setup (line 12) | async function setup(): Promise<() => Promise<void>> {
FILE: src/tests/factories/2024/feat.factory.ts
constant FEAT_TYPES (line 6) | const FEAT_TYPES = ['origin', 'general', 'fighting-style', 'epic-boon'] ...
FILE: src/tests/factories/2024/magicItem.factory.ts
constant RARITY_NAMES (line 8) | const RARITY_NAMES = ['Common', 'Uncommon', 'Rare', 'Very Rare', 'Legend...
FILE: src/tests/support/db.ts
function generateUniqueDbUri (line 12) | function generateUniqueDbUri(baseName: string): string {
function setupIsolatedDatabase (line 30) | function setupIsolatedDatabase(uri: string): void {
function teardownIsolatedDatabase (line 48) | function teardownIsolatedDatabase(): void {
function setupModelCleanup (line 72) | function setupModelCleanup(model: Model<any>): void {
FILE: src/tests/support/types.d.ts
type MockResponse (line 3) | type MockResponse = {
FILE: src/util/modelOptions.ts
function removeInternalFields (line 7) | function removeInternalFields(obj: any): any {
function srdModelOptions (line 36) | function srdModelOptions(collectionName: string): ClassDecorator {
FILE: src/util/prewarmCache.ts
type PrewarmData (line 12) | type PrewarmData = {
Condensed preview — 567 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,292K chars).
[
{
"path": ".dockerignore",
"chars": 155,
"preview": "/node_modules/\ndist\n\n# Git / VCS\n.git/\n\n# Editor / IDE\n.vscode/\n\n# Environment / Secrets\n.env\n\n# Logs\n*.log\nnpm-debug.lo"
},
{
"path": ".github/CODEOWNERS",
"chars": 43,
"preview": "# Default owner for all files\n* @bagelbits\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 614,
"preview": "# These are supported funding model platforms\n\ngithub: [5e-bits]\npatreon: # Replace with a single Patreon username\nopen_"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 494,
"preview": "## What does this do?\n\n\\<It's not clear if I don't update this text with relevant info\\>\n\n## How was it tested?\n\n\\<It's "
},
{
"path": ".github/actions/validate-pr-title/action.yml",
"chars": 1308,
"preview": "name: 'Validate PR Title'\ndescription: 'Validates that PR titles follow conventional commit format with custom types'\nin"
},
{
"path": ".github/dependabot.yml",
"chars": 925,
"preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
},
{
"path": ".github/workflows/ci.yml",
"chars": 1400,
"preview": "# This workflow will do a clean install of node dependencies, build the source code and run tests across different versi"
},
{
"path": ".github/workflows/codeql-analysis.yml",
"chars": 2393,
"preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
},
{
"path": ".github/workflows/lint-pr.yml",
"chars": 396,
"preview": "name: \"Lint PR\"\n\non:\n pull_request_target:\n types:\n - opened\n - edited\n - synchronize\n\njobs:\n main:\n"
},
{
"path": ".github/workflows/release-please.yml",
"chars": 922,
"preview": "name: Release Please\n\non:\n push:\n branches: [main]\n workflow_dispatch:\n\npermissions:\n contents: write\n pull-reque"
},
{
"path": ".github/workflows/release.yml",
"chars": 4172,
"preview": "name: Release\n\non:\n release:\n types: [published]\n workflow_dispatch:\n repository_dispatch:\n\nenv:\n REGISTRY: ghcr."
},
{
"path": ".gitignore",
"chars": 780,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscov"
},
{
"path": ".nvmrc",
"chars": 8,
"preview": "lts/jod\n"
},
{
"path": ".prettierrc",
"chars": 137,
"preview": "{\n \"printWidth\": 100,\n \"singleQuote\": true,\n \"trailingComma\": \"none\",\n \"tabWidth\": 2,\n \"semi\": false,\n \"quoteProps"
},
{
"path": ".redocly.yaml",
"chars": 132,
"preview": "extends:\n - recommended\n\nrules:\n operation-operationId: off\n operation-4xx-response: off\n # no-invalid-media-type-ex"
},
{
"path": "CHANGELOG.md",
"chars": 13853,
"preview": "# [5.1.0](https://github.com/5e-bits/5e-srd-api/compare/v5.0.0...v5.1.0) (2025-09-15)\n\n\n### Features\n\n* **2024:** Add eq"
},
{
"path": "Dockerfile",
"chars": 1737,
"preview": "# ---- Builder Stage ----\nFROM node:22-alpine AS builder\n\nWORKDIR /app\n\nCOPY package.json ./\n# Copy the package-lock.jso"
},
{
"path": "LICENSE.md",
"chars": 1096,
"preview": "MIT License\n\nCopyright (c) [2018-2020] [Adrian Padua, Christopher Ward]\n\nPermission is hereby granted, free of charge, t"
},
{
"path": "README.md",
"chars": 6622,
"preview": "# 5e-srd-api\n\n[ => {\n res.redirect('https://5"
},
{
"path": "src/controllers/simpleController.ts",
"chars": 2103,
"preview": "import { ReturnModelType } from '@typegoose/typegoose'\nimport { NextFunction, Request, Response } from 'express'\n\nimport"
},
{
"path": "src/css/custom.css",
"chars": 3087,
"preview": "/* Reset and base styles */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n:root {\n --primary-color: #d819"
},
{
"path": "src/graphql/2014/common/choiceTypes.ts",
"chars": 5785,
"preview": "import { Field, Int, ObjectType } from 'type-graphql'\n\nimport { AbilityScore } from '@/models/2014/abilityScore'\nimport "
},
{
"path": "src/graphql/2014/common/equipmentTypes.ts",
"chars": 3656,
"preview": "import { Field, Int, ObjectType } from 'type-graphql'\n\nimport { ArmorClass, Content, Equipment, Range, Speed, ThrowRange"
},
{
"path": "src/graphql/2014/common/interfaces.ts",
"chars": 747,
"preview": "import { Field, Float, InterfaceType } from 'type-graphql'\n\nimport { Cost } from '@/models/2014/equipment'\n\n@InterfaceTy"
},
{
"path": "src/graphql/2014/common/unions.ts",
"chars": 2235,
"preview": "import { createUnionType } from 'type-graphql'\n\nimport { ProficiencyChoice } from '@/graphql/2014/common/choiceTypes'\nim"
},
{
"path": "src/graphql/2014/resolvers/abilityScore/args.ts",
"chars": 1854,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/abilityScore/resolver.ts",
"chars": 2426,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2014/resolvers/alignment/args.ts",
"chars": 1506,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/alignment/resolver.ts",
"chars": 1677,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2014/resolvers/background/args.ts",
"chars": 1530,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/background/resolver.ts",
"chars": 5713,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { LanguageChoice } from '@/graphq"
},
{
"path": "src/graphql/2014/resolvers/class/args.ts",
"chars": 1813,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/class/resolver.ts",
"chars": 7291,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport {\n PrerequisiteChoice,\n Prerequ"
},
{
"path": "src/graphql/2014/resolvers/condition/args.ts",
"chars": 1506,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/condition/resolver.ts",
"chars": 1684,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2014/resolvers/damageType/args.ts",
"chars": 1533,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/damageType/resolver.ts",
"chars": 1715,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2014/resolvers/equipment/args.ts",
"chars": 1907,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/equipment/resolver.ts",
"chars": 3052,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { AnyEquipment } from '@/graphql/"
},
{
"path": "src/graphql/2014/resolvers/equipmentCategory/args.ts",
"chars": 1703,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/equipmentCategory/resolver.ts",
"chars": 2790,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { EquipmentOrMagicItem } from '@/"
},
{
"path": "src/graphql/2014/resolvers/feat/args.ts",
"chars": 1386,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/feat/resolver.ts",
"chars": 2023,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2014/resolvers/feature/args.ts",
"chars": 2310,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/feature/resolver.ts",
"chars": 5768,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { FeaturePrerequisiteUnion } from"
},
{
"path": "src/graphql/2014/resolvers/index.ts",
"chars": 3484,
"preview": "// This file will export an array of all resolver classes\nimport { AbilityScoreResolver } from './abilityScore/resolver'"
},
{
"path": "src/graphql/2014/resolvers/language/args.ts",
"chars": 2023,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/language/resolver.ts",
"chars": 2074,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2014/resolvers/level/args.ts",
"chars": 2656,
"preview": "import { ArgsType, Field, InputType, Int, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n Bas"
},
{
"path": "src/graphql/2014/resolvers/level/resolver.ts",
"chars": 3229,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2014/resolvers/magicItem/args.ts",
"chars": 2127,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/magicItem/resolver.ts",
"chars": 2832,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2014/resolvers/magicSchool/args.ts",
"chars": 1557,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/magicSchool/resolver.ts",
"chars": 1752,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2014/resolvers/monster/args.ts",
"chars": 5040,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/monster/resolver.ts",
"chars": 14865,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { Armor } from '@/graphql/2014/co"
},
{
"path": "src/graphql/2014/resolvers/proficiency/args.ts",
"chars": 2249,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/proficiency/resolver.ts",
"chars": 4070,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { ProficiencyReference } from '@/"
},
{
"path": "src/graphql/2014/resolvers/race/args.ts",
"chars": 2306,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/race/resolver.ts",
"chars": 5595,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport {\n AbilityScoreBonusChoice,\n Ab"
},
{
"path": "src/graphql/2014/resolvers/rule/args.ts",
"chars": 1409,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/rule/resolver.ts",
"chars": 1851,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2014/resolvers/ruleSection/args.ts",
"chars": 1580,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/ruleSection/resolver.ts",
"chars": 1700,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2014/resolvers/skill/args.ts",
"chars": 1703,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/skill/resolver.ts",
"chars": 2239,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2014/resolvers/spell/args.ts",
"chars": 4586,
"preview": "import { ArgsType, Field, InputType, Int, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n Bas"
},
{
"path": "src/graphql/2014/resolvers/spell/resolver.ts",
"chars": 6367,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { mapLevelObjectToArray } from '@"
},
{
"path": "src/graphql/2014/resolvers/subclass/args.ts",
"chars": 1484,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/subclass/resolver.ts",
"chars": 3813,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { SubclassSpellPrerequisiteUnion "
},
{
"path": "src/graphql/2014/resolvers/subrace/args.ts",
"chars": 1458,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/subrace/resolver.ts",
"chars": 2690,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2014/resolvers/trait/args.ts",
"chars": 1410,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/trait/resolver.ts",
"chars": 6215,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { LanguageChoice, ProficiencyChoi"
},
{
"path": "src/graphql/2014/resolvers/weaponProperty/args.ts",
"chars": 1631,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2014/resolvers/weaponProperty/resolver.ts",
"chars": 1851,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2014/types/backgroundTypes.ts",
"chars": 1362,
"preview": "import { Field, Int, ObjectType } from 'type-graphql'\n\nimport { Alignment } from '@/models/2014/alignment'\n\n// --- Backg"
},
{
"path": "src/graphql/2014/types/featureTypes.ts",
"chars": 733,
"preview": "import { createUnionType } from 'type-graphql'\n\nimport { FeaturePrerequisite, LevelPrerequisite, SpellPrerequisite } fro"
},
{
"path": "src/graphql/2014/types/monsterTypes.ts",
"chars": 6321,
"preview": "import { createUnionType, Field, Int, ObjectType } from 'type-graphql'\n\nimport {\n ArmorClassArmor,\n ArmorClassConditio"
},
{
"path": "src/graphql/2014/types/proficiencyTypes.ts",
"chars": 704,
"preview": "import { createUnionType } from 'type-graphql'\n\nimport { AbilityScore } from '@/models/2014/abilityScore'\nimport { Equip"
},
{
"path": "src/graphql/2014/types/startingEquipment/choice.ts",
"chars": 945,
"preview": "import { Field, Int, ObjectType } from 'type-graphql'\n\nimport { EquipmentCategorySet } from './common'\nimport { Equipmen"
},
{
"path": "src/graphql/2014/types/startingEquipment/common.ts",
"chars": 4060,
"preview": "import { createUnionType, Field, Int, ObjectType } from 'type-graphql'\n\nimport { Equipment } from '@/models/2014/equipme"
},
{
"path": "src/graphql/2014/types/startingEquipment/index.ts",
"chars": 78,
"preview": "export * from './choice'\nexport * from './common'\nexport * from './optionSet'\n"
},
{
"path": "src/graphql/2014/types/startingEquipment/optionSet.ts",
"chars": 1728,
"preview": "import { createUnionType, Field, ObjectType } from 'type-graphql'\n\nimport {\n CountedReferenceOption,\n EquipmentCategor"
},
{
"path": "src/graphql/2014/types/subclassTypes.ts",
"chars": 721,
"preview": "import { createUnionType } from 'type-graphql'\n\nimport { Feature } from '@/models/2014/feature'\nimport { Level } from '@"
},
{
"path": "src/graphql/2014/types/traitTypes.ts",
"chars": 2440,
"preview": "import { Field, Int, ObjectType } from 'type-graphql'\n\nimport { Spell } from '@/models/2014/spell'\nimport { Trait } from"
},
{
"path": "src/graphql/2014/utils/helpers.ts",
"chars": 1326,
"preview": "import { LevelValue } from '@/graphql/common/types'\n\n/**\n * Converts a Record<number, string> or Record<string, string> "
},
{
"path": "src/graphql/2014/utils/resolvers.ts",
"chars": 3494,
"preview": "import {\n LanguageChoice,\n LanguageChoiceOption,\n LanguageChoiceOptionSet,\n ProficiencyChoice,\n ProficiencyChoiceOp"
},
{
"path": "src/graphql/2014/utils/startingEquipmentResolver.ts",
"chars": 7765,
"preview": "import EquipmentModel, { Equipment } from '@/models/2014/equipment'\nimport EquipmentCategoryModel, { EquipmentCategory }"
},
{
"path": "src/graphql/2024/common/choiceTypes.ts",
"chars": 2972,
"preview": "import { Field, Int, ObjectType } from 'type-graphql'\n\nimport { AbilityScore2024 } from '@/models/2024/abilityScore'\nimp"
},
{
"path": "src/graphql/2024/common/equipmentTypes.ts",
"chars": 3459,
"preview": "import { Field, Int, ObjectType } from 'type-graphql'\n\nimport { AbilityScore2024 } from '@/models/2024/abilityScore'\nimp"
},
{
"path": "src/graphql/2024/common/interfaces.ts",
"chars": 754,
"preview": "import { Field, Float, InterfaceType } from 'type-graphql'\n\nimport { Cost } from '@/models/2024/equipment'\n\n@InterfaceTy"
},
{
"path": "src/graphql/2024/common/resolver.ts",
"chars": 562,
"preview": "import { FieldResolver, Resolver, Root } from 'type-graphql'\n\nimport { resolveSingleReference } from '@/graphql/utils/re"
},
{
"path": "src/graphql/2024/common/unions.ts",
"chars": 1709,
"preview": "import { createUnionType } from 'type-graphql'\n\nimport { APIReference } from '@/models/common/apiReference'\n\nimport { Am"
},
{
"path": "src/graphql/2024/resolvers/abilityScore/args.ts",
"chars": 1854,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/abilityScore/resolver.ts",
"chars": 2474,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2024/resolvers/alignment/args.ts",
"chars": 1506,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/alignment/resolver.ts",
"chars": 1711,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2024/resolvers/background/args.ts",
"chars": 1530,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/background/resolver.ts",
"chars": 3631,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { Proficiency2024Choice } from '@"
},
{
"path": "src/graphql/2024/resolvers/condition/args.ts",
"chars": 1506,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/condition/resolver.ts",
"chars": 1718,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2024/resolvers/damageType/args.ts",
"chars": 1533,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/damageType/resolver.ts",
"chars": 1749,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2024/resolvers/equipment/args.ts",
"chars": 1893,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/equipment/resolver.ts",
"chars": 3758,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { Tool } from '@/graphql/2024/com"
},
{
"path": "src/graphql/2024/resolvers/equipmentCategory/args.ts",
"chars": 1689,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/equipmentCategory/resolver.ts",
"chars": 3273,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { AnyEquipment } from '@/graphql/"
},
{
"path": "src/graphql/2024/resolvers/feat/args.ts",
"chars": 1606,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/feat/resolver.ts",
"chars": 2248,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { ScorePrerequisiteChoice2024 } f"
},
{
"path": "src/graphql/2024/resolvers/index.ts",
"chars": 1871,
"preview": "import { DifficultyClassResolver } from '../common/resolver'\nimport { AbilityScoreResolver } from './abilityScore/resolv"
},
{
"path": "src/graphql/2024/resolvers/language/args.ts",
"chars": 2023,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/language/resolver.ts",
"chars": 2114,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2024/resolvers/magicItem/args.ts",
"chars": 1921,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/magicItem/resolver.ts",
"chars": 2823,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2024/resolvers/magicSchool/args.ts",
"chars": 1557,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/magicSchool/resolver.ts",
"chars": 1792,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2024/resolvers/proficiency/args.ts",
"chars": 1798,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/proficiency/resolver.ts",
"chars": 2420,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2024/resolvers/skill/args.ts",
"chars": 1703,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/skill/resolver.ts",
"chars": 2279,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2024/resolvers/species/args.ts",
"chars": 1460,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/species/resolver.ts",
"chars": 2433,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2024/resolvers/subclass/args.ts",
"chars": 1484,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/subclass/resolver.ts",
"chars": 1674,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2024/resolvers/subspecies/args.ts",
"chars": 1532,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/subspecies/resolver.ts",
"chars": 2959,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2024/resolvers/trait/args.ts",
"chars": 1414,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/trait/resolver.ts",
"chars": 2362,
"preview": "import { Arg, Args, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/gra"
},
{
"path": "src/graphql/2024/resolvers/weaponMasteryProperty/args.ts",
"chars": 1810,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/weaponMasteryProperty/resolver.ts",
"chars": 2076,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2024/resolvers/weaponProperty/args.ts",
"chars": 1631,
"preview": "import { ArgsType, Field, InputType, registerEnumType } from 'type-graphql'\nimport { z } from 'zod'\n\nimport {\n BaseFilt"
},
{
"path": "src/graphql/2024/resolvers/weaponProperty/resolver.ts",
"chars": 1875,
"preview": "import { Arg, Args, Query, Resolver } from 'type-graphql'\n\nimport { buildSortPipeline } from '@/graphql/common/args'\nimp"
},
{
"path": "src/graphql/2024/types/backgroundEquipment/choice.ts",
"chars": 640,
"preview": "import { Field, Int, ObjectType } from 'type-graphql'\n\nimport { EquipmentOptionSet2024 } from './optionSet'\n\n@ObjectType"
},
{
"path": "src/graphql/2024/types/backgroundEquipment/common.ts",
"chars": 3350,
"preview": "import { createUnionType, Field, Int, ObjectType } from 'type-graphql'\n\nimport { EquipmentCategory2024 } from '@/models/"
},
{
"path": "src/graphql/2024/types/backgroundEquipment/index.ts",
"chars": 78,
"preview": "export * from './choice'\nexport * from './common'\nexport * from './optionSet'\n"
},
{
"path": "src/graphql/2024/types/backgroundEquipment/optionSet.ts",
"chars": 1090,
"preview": "import { createUnionType, Field, ObjectType } from 'type-graphql'\n\nimport { CountedReferenceOption2024, MoneyOption2024,"
},
{
"path": "src/graphql/2024/utils/backgroundEquipmentResolver.ts",
"chars": 5580,
"preview": "import EquipmentModel from '@/models/2024/equipment'\nimport EquipmentCategoryModel, { EquipmentCategory2024 } from '@/mo"
},
{
"path": "src/graphql/2024/utils/choiceResolvers.ts",
"chars": 2861,
"preview": "import {\n Proficiency2024Choice,\n Proficiency2024ChoiceOption,\n Proficiency2024ChoiceOptionSet,\n ScorePrerequisiteCh"
},
{
"path": "src/graphql/2024/utils/resolvers.ts",
"chars": 0,
"preview": ""
},
{
"path": "src/graphql/common/args.ts",
"chars": 3153,
"preview": "import { ArgsType, Field, Int } from 'type-graphql'\nimport { z } from 'zod'\n\nimport { OrderByDirection } from '@/graphql"
},
{
"path": "src/graphql/common/choiceTypes.ts",
"chars": 1221,
"preview": "import { Field, Int, ObjectType } from 'type-graphql'\n\n// --- Generic String Choice Types ---\n@ObjectType({\n descriptio"
},
{
"path": "src/graphql/common/enums.ts",
"chars": 245,
"preview": "import { registerEnumType } from 'type-graphql'\n\nexport enum OrderByDirection {\n ASC = 'ASC',\n DESC = 'DESC'\n}\n\nregist"
},
{
"path": "src/graphql/common/inputs.ts",
"chars": 3064,
"preview": "import { Field, InputType, Int } from 'type-graphql'\nimport { z } from 'zod'\n\n// Zod schema for NumberRangeFilterInput\ne"
}
]
// ... and 367 more files (download for full content)
About this extraction
This page contains the full source code of the 5e-bits/5e-srd-api GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 567 files (1.2 MB), approximately 315.4k tokens, and a symbol index with 767 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.