diff --git a/.changeset/funny-feet-sit.md b/.changeset/funny-feet-sit.md new file mode 100644 index 0000000..bd0c760 --- /dev/null +++ b/.changeset/funny-feet-sit.md @@ -0,0 +1,5 @@ +--- +"@gw2api/types": patch +--- + +Improve endpoint type resolution for endpoints that are both authenticated and localized diff --git a/.changeset/hot-balloons-add.md b/.changeset/hot-balloons-add.md new file mode 100644 index 0000000..0fd0692 --- /dev/null +++ b/.changeset/hot-balloons-add.md @@ -0,0 +1,17 @@ +--- +"@gw2api/types": patch +--- + +Add types for character endpoints +- `/v2/characters` +- `/v2/characters/:id/backstory` +- `/v2/characters/:id/buildtabs` +- `/v2/characters/:id/core` +- `/v2/characters/:id/crafting` +- `/v2/characters/:id/equipment` +- `/v2/characters/:id/equipmenttabs` +- `/v2/characters/:id/inventory` +- `/v2/characters/:id/recipes` +- `/v2/characters/:id/skills` +- `/v2/characters/:id/specializations` +- `/v2/characters/:id/training` diff --git a/packages/types/data/character.ts b/packages/types/data/character.ts new file mode 100644 index 0000000..541a274 --- /dev/null +++ b/packages/types/data/character.ts @@ -0,0 +1,331 @@ +import type { SchemaAfter, SchemaVersion } from "../schema"; +import type { ItemStack } from "./item"; +import type { CraftingDiscipline } from "./recipe"; + +/** + * Character info + * Required scopes: characters + * + * @see https://wiki.guildwars2.com/wiki/API:2/characters + */ +export type Character = + // always included. Some of these are `Partial<>` because they are only included with some scopes. This should be typed in the future if the scopes are known... + CharacterBackstory & CharacterCore & CharacterCrafting & Partial & Partial & Partial & Partial & ( + // Schema version 2019-12-19T00:00:00.000Z or later, this endpoint will include v2/characters/:id/buildtabs and v2/characters/:id/equipmenttabs and will no longer include v2/characters/:id/skills and v2/characters/:id/specializations + Schema extends undefined ? (Partial & Partial & Partial): + Schema extends SchemaAfter<'2019-12-19T00:00:00.000Z'> | 'latest' ? ({ build_tabs?: CharacterBuildTab[], equipment_tabs?: CharacterEquipmentTab[] } & Partial & CharacterExtras_2019_12_19) : + (Partial & Partial & CharacterExtras) + ); + +interface CharacterExtras { + /** Trained WvW abilities */ + wvw_abilities: { + /** Ability id (/v2/wvw/abilities) */ + id: number; + + /** Current rank */ + rank: number; + }[]; + + /** PvP Equipment */ + equipment_pvp: CharacterPvpEquipment; + + flags: [] | ['Beta']; +} + +interface CharacterExtras_2019_12_19 { + /** Number of build tabs unlocked for this character */ + build_tabs_unlocked: number; + + /** Active build tab index */ + active_build_tab: number; + + /** Number of equipment tabs unlocked for this character */ + equipment_tabs_unlocked: number; + + /** Active equipment tab index */ + active_equipment_tab: number; +} + +/** + * Character Backstory + * Required scopes: characters + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/backstory + */ +export interface CharacterBackstory { + /** Backstory ids, see /v2/backstory/answers */ + backstory: string[] +} + +/** + * Character Build Tab + * Required scopes: builds, characters + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/buildtabs + */ +export interface CharacterBuildTab { + /** The index of this tab */ + tab: number; + + /** Whether or not this is the tab selected on the character currently */ + is_active: boolean; + + /** Build of the tab */ + build: { + + /** Name of the build */ + name: string; + + /** Profession of the build */ + profession: Profession; + + /** specializations */ + specializations: [CharacterSpecializationSelection, CharacterSpecializationSelection, CharacterSpecializationSelection]; + + /** Selected skills */ + skills: CharacterSkillSelection; + + /** Selected aquatic skills */ + aquatic_skills: CharacterSkillSelection; + + /** Selected legend ids (Revenant only) */ + legends?: [string | null, string | null]; + + /** Selected aquatic legend ids (Revenant only) */ + aquatic_legends?: [string | null, string | null]; + + /** Selected pet ids (ranger only) */ + pets?: { + /** Selected terrestrial pet ids */ + terrestrial: [number | null, number | null] + + /** Selected aquatic pet ids */ + aquatic: [number | null, number | null] + } + } +} + +/** + * Core character info + * Requires scopes: characters + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/core + */ +export type CharacterCore = + Schema extends undefined ? CharacterCoreBase : + Schema extends SchemaAfter<'2019-02-21T00:00:00.000Z'> | 'latest' ? CharacterCore_2019_02_21 : + CharacterCoreBase; + +interface CharacterCoreBase { + /** The characters name */ + name: string; + + /** The characters race */ + race: Race; + + /** The characters gender */ + gender: Gender; + + /** The characters profession */ + profession: Profession; + + /** The characters level */ + level: number; + + /** The guild id of the guild this characters is representing */ + guild?: string; + + /** Playtime in seconds */ + age: number; + + /** Creation timestamp (ISO-8601) */ + created: string; + + /** Death counter */ + deaths: number; + + /** Title id of the currently selected title (/v2/titles) */ + title?: number; +} + +interface CharacterCore_2019_02_21 extends CharacterCoreBase { + /** Last modification timestamp */ + last_modified: string; +} + +/** + * Crafting disciplines available to the character + * Requires scopes: characters + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/crafting + */ +export interface CharacterCrafting { + /** All unlocked crafting disciplines for this character */ + crafting: { + /** Crafting discipline */ + discipline: CraftingDiscipline; + + /** The crafting level */ + rating: number; + + /** Crafting discipline is currently active */ + active: boolean; + }[] +} + +/** + * Character equipment + * Required scopes: builds, characters, inventories + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/equipment + */ +export interface CharacterEquipment { + equipment: CharacterEquipmentEntry[] +} + +export type CharacterEquipmentEntry = + Schema extends undefined ? CharacterEquipmentEntryBase : + Schema extends SchemaAfter<'2019-12-19T00:00:00.000Z'> | 'latest' ? CharacterEquipmentEntry_2019_12_19 : + CharacterEquipmentEntryBase; + +interface CharacterEquipmentEntryBase extends Omit { + /** The equipment slot in which the item is slotted. */ + slot: EquipmentSlot; +} + +interface CharacterEquipmentEntry_2019_12_19 extends Omit { + /** The equipment slot in which the item is slotted. `undefined` if equipment is in an inactive tab. */ + slot: EquipmentSlot | undefined; + + /** Location the item is stored in */ + location: 'Equipped' | 'Armory' | 'EquippedFromLegendaryArmory' | 'LegendaryArmory'; + + /** Which tabs reuse this item */ + tabs: number[]; +} + +type EquipmentSlot = 'HelmAquatic' | 'Backpack' | 'Coat' | 'Boots' | 'Gloves' | 'Helm' | 'Leggings' | 'Shoulders' | 'Accessory1' | 'Accessory2' | 'Ring1' | 'Ring2' | 'Amulet' | 'WeaponAquaticA' | 'WeaponAquaticB' | 'WeaponA1' | 'WeaponA2' | 'WeaponB1' | 'WeaponB2' | 'Sickle' | 'Axe' | 'Pick'; + +/** + * Characters equipment template tabs. + * Requires scopes: characters, builds + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/equipmenttabs + */ +export interface CharacterEquipmentTab { + /** Tab index */ + tab: number; + + /** Name of the equipment tab */ + name: string; + + /** Whether or not this is the tab selected on the character currently */ + is_active: boolean; + + /** Equipped items */ + equipment: Omit, 'tabs'>[]; + + /** PvP Equipment */ + equipment_pvp: CharacterPvpEquipment; +} + +/** + * Characters inventory + * Requires scopes: characters, inventories + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/inventory + */ +export interface CharacterInventory { + /** The characters bags */ + bags: { + /** THe item id of the bag */ + id: number; + + /** The size of the bag */ + size: number; + + /** The inventory of the bag */ + inventory: Array + }[] +} + +/** + * Recipes unlocked by a character + * Requires scopes: characters, inventories + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/recipes + */ +export interface CharacterRecipes { + /** Recipe ids */ + recipes: number[] +} + +/** + * Skills equipped by a character + * Requires scopes: characters, builds + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/skills + */ +export interface CharacterSkills { + /** Equipped skills per gamemode */ + skills: Record<'pve' | 'pvp' | 'wvw', CharacterSkillSelection> +} + +export interface CharacterSkillSelection { + /** Heal skill id */ + heal: number | null; + + /** Utility skill ids */ + utilities: [number | null, number | null, number | null]; + + /** Elite skill id */ + elite: number | null; + + /** Legend ids (Revenant only) */ + legends?: [string | null, string | null]; +} + +/** + * Character specializations + * Requires scopes: characters, builds + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/specializations + */ +export interface CharacterSpecializations { + specializations: Record<'pve' | 'pvp' | 'wvw', [CharacterSpecializationSelection, CharacterSpecializationSelection, CharacterSpecializationSelection]> +} + +export interface CharacterSpecializationSelection { + /** Specializations id */ + id: number | null; + + /** Selected major traits */ + traits: [number | null, number | null, number | null]; +} + +/** + * Character skill trees + * Requires scopes: characters, builds + * @see https://wiki.guildwars2.com/wiki/API:2/characters/:id/training + */ +export interface CharacterTraining { + /** Trained skill trees */ + training: { + /** Skill tree id (/v2/professions training) */ + id: number; + + /** Amount of spent hero points in this skill tree */ + spent: number; + + /** Fully trained */ + done: boolean; + }[]; +} + +export interface CharacterPvpEquipment { + /** Equipped PvP Amulet id (/v2/pvp/amulets) */ + amulet: number | null; + + /** Equipped rune item id */ + rune: number | null; + + /** Equipped sigil item ids */ + sigils: [number | null, number | null, number | null, number | null]; +} + +export type Race = 'Asura' | 'Charr' | 'Human' | 'Norn' | 'Sylvary'; + +export type Gender = 'Male' | 'Female'; + +export type Profession = 'Elementalist' | 'Engineer' | 'Guardian' | 'Mesmer' | 'Necromancer' | 'Ranger' | 'Revenant' | 'Thief' | 'Warrior'; diff --git a/packages/types/data/item.ts b/packages/types/data/item.ts index 81b74cb..355fdd7 100644 --- a/packages/types/data/item.ts +++ b/packages/types/data/item.ts @@ -98,7 +98,7 @@ export interface ItemStack { skin?: number; /** Dyes applied */ - dyes?: number[]; + dyes?: Array; /** Item ids of Runes/Sigils */ upgrades?: number[]; diff --git a/packages/types/data/recipe.ts b/packages/types/data/recipe.ts new file mode 100644 index 0000000..241fc45 --- /dev/null +++ b/packages/types/data/recipe.ts @@ -0,0 +1,2 @@ + +export type CraftingDiscipline = 'Armorsmith'| 'Artificer'| 'Chef'| 'Huntsman'| 'Jeweler'| 'Leatherworker'| 'Scribe'| 'Tailor'| 'Weaponsmith'; diff --git a/packages/types/endpoints.ts b/packages/types/endpoints.ts index e101969..3720eaf 100644 --- a/packages/types/endpoints.ts +++ b/packages/types/endpoints.ts @@ -5,6 +5,7 @@ import type { AccountInventory } from './data/account-inventory'; import type { AccountMaterials } from './data/account-material'; import type { AccountWallet } from './data/account-wallet'; import type { AccountWizardsVaultListing, AccountWizardsVaultMetaObjectives, AccountWizardsVaultSpecialObjectives } from './data/account-wizardsvault'; +import type { Character, CharacterBackstory, CharacterBuildTab, CharacterCore, CharacterCrafting, CharacterEquipment, CharacterEquipmentTab, CharacterInventory, CharacterRecipes, CharacterSkills, CharacterSpecializations, CharacterTraining } from './data/character'; import type { Listing, Price, TransactionCurrent, TransactionHistoric } from './data/commerce'; import type { Createsubtoken } from './data/createsubtoken'; import type { Item } from './data/item'; @@ -319,9 +320,15 @@ type BulkExpandedEndpointUrl | BulkExpandedManyEndpointUrl type BulkExpandedResponseType = + // base endpoint returns a list of ids Url extends Endpoint ? Id[] : + // make sure the id does not include a slash (if there are sub-endpoints, they have to be listed first in `EndpointType`) + Url extends `${Endpoint}/${Id}/${string}` ? unknown : + // handle single id requests (`endpoint/:id` and `endpoint?id=:id`) Url extends BulkExpandedSingleEndpointUrl ? T : + // handle multiple id requests (either `endpoint?ids=:ids` or paginated) Url extends BulkExpandedManyEndpointUrl ? T[] : + // otherwise this is not a known bulk request unknown // createsubtoken request @@ -347,16 +354,17 @@ export type AuthenticatedOptions = { } export type OptionsByEndpoint = - Endpoint extends KnownBulkExpandedEndpoint ? Options : - Endpoint extends BulkExpandedManyEndpointUrl ? Options & LocalizedOptions : - Endpoint extends BulkExpandedSingleEndpointUrl ? Options & LocalizedOptions : - Endpoint extends KnownLocalizedEndpoint ? Options & LocalizedOptions : + Endpoint extends BulkExpandedEndpointUrl ? Options & AuthenticatedOptions & LocalizedOptions : + Endpoint extends BulkExpandedEndpointUrl ? Options & LocalizedOptions : + Endpoint extends BulkExpandedEndpointUrl ? Options & AuthenticatedOptions : + Endpoint extends KnownAuthenticatedEndpoint & KnownLocalizedEndpoint ? Options & AuthenticatedOptions & LocalizedOptions : Endpoint extends KnownAuthenticatedEndpoint ? Options & AuthenticatedOptions : + Endpoint extends KnownLocalizedEndpoint ? Options & LocalizedOptions : Endpoint extends CreateSubtokenUrl<'/v2/createsubtoken'> ? Options & AuthenticatedOptions : Options // result type for endpoint -export type EndpointType = +export type EndpointType = Url extends '/v2/account' ? Account : Url extends '/v2/account/achievements' ? AccountAchievement[] : Url extends '/v2/account/bank' ? AccountBank : @@ -386,6 +394,18 @@ export type EndpointType : + Url extends `/v2/characters/${string}/crafting` ? CharacterCrafting : + Url extends `/v2/characters/${string}/equipment` ? CharacterEquipment : + Url extends `/v2/characters/${string}/equipmenttabs` ? CharacterEquipmentTab[] : + Url extends `/v2/characters/${string}/inventory` ? CharacterInventory : + Url extends `/v2/characters/${string}/recipes` ? CharacterRecipes : + Url extends `/v2/characters/${string}/skills` ? CharacterSkills : + Url extends `/v2/characters/${string}/specializations` ? CharacterSpecializations : + Url extends `/v2/characters/${string}/training` ? CharacterTraining : + Url extends BulkExpandedEndpointUrl<'/v2/characters', string> ? BulkExpandedResponseType<'/v2/characters', Url, string, Character> : Url extends CreateSubtokenUrl<'/v2/createsubtoken'> ? Createsubtoken : Url extends BulkExpandedEndpointUrl<'/v2/items', number> ? BulkExpandedResponseType<'/v2/items', Url, number, Item> : Url extends BulkExpandedEndpointUrl<'/v2/materials', number> ? BulkExpandedResponseType<'/v2/materials', Url, number, MaterialCategory> :