bgg-sdk
Version:
Node SDK for querying the Board Game Geek (BGG) XML2 API
1 lines • 75.5 kB
Source Map (JSON)
{"version":3,"sources":["../src/lib/axios.ts","../src/lib/helpers.ts","../src/routes/collection.ts","../src/routes/family.ts","../src/routes/forum.ts","../src/routes/forumList.ts","../src/routes/guild.ts","../src/routes/hot.ts","../src/routes/search.ts","../src/routes/thing.ts","../src/routes/thread.ts","../src/routes/user.ts","../src/routes/plays/index.ts","../src/routes/plays/username.ts","../src/routes/plays/id.ts","../src/routes/types/contentTypes.ts","../src/routes/types/params.ts","../src/routes/types/payloads.ts","../src/index.ts"],"sourcesContent":["import axiosBase from \"axios\";\nimport axiosRetry from \"axios-retry\";\nimport convert from \"xml-js\";\n\nexport const axios = axiosBase.create({\n baseURL: \"https://boardgamegeek.com/xmlapi2/\",\n});\n\naxiosRetry(axios, {\n retries: 3,\n retryDelay: axiosRetry.exponentialDelay,\n});\n\naxios.interceptors.response.use(\n (response) => {\n try {\n const jsonData = convert.xml2js(response.data, { compact: true });\n response.data = jsonData;\n return response;\n } catch (error) {\n throw new Error(\"Failed to parse XML data from BGG\");\n }\n },\n (error) => {\n return Promise.reject(`Unexpected error calling BGG API: ${error.stack}`);\n },\n);\n","/* \n The xml-js conversion will return an object if there is only one element, but an array if there are multiple elements.\n This is a problem when we try to iterate over the elements, as the same tag type may sometimes be an object and sometimes an array.\n This function will ensure that the value is always an array.\n*/\nexport const enforceArray = <T>(value: T | Array<T> | undefined): Array<T> => {\n if (!value) return [];\n if (Array.isArray(value)) return value;\n return [value];\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsCollection } from \"~/routes/types/params\";\nimport { PayloadCollection } from \"~/routes/types/payloads\";\n\nexport type ParamsTransformed = Omit<ParamsCollection, \"id\"> & {\n id?: string;\n};\n\nexport const transformParams = (\n params: ParamsCollection,\n): ParamsTransformed => {\n return {\n ...params,\n id: params.id?.join(\",\"),\n };\n};\n\ntype ApiResponseItem = {\n _attributes: {\n objecttype: string;\n objectid: string;\n subtype: string;\n collid: string;\n };\n name: {\n _attributes: {\n sortindex: string;\n };\n _text: string;\n };\n yearpublished?: {\n _text: string;\n };\n image?: {\n _text: string;\n };\n thumbnail?: {\n _text: string;\n };\n status: {\n _attributes: {\n own: string;\n prevowned: string;\n fortrade: string;\n want: string;\n wanttoplay: string;\n wanttobuy: string;\n wishlist: string;\n preordered: string;\n lastmodified: string;\n };\n };\n numplays: {\n _text: string;\n };\n};\n\ntype ApiResponse = {\n items?: {\n _attributes: {\n termsofuse: string;\n totalitems: string;\n pubdate: string;\n };\n item: ApiResponseItem | Array<ApiResponseItem>;\n };\n};\n\nconst transformData = (data: ApiResponse): PayloadCollection => {\n if (!data.items) {\n return null;\n }\n\n return {\n attributes: {\n termsOfUse: data.items._attributes.termsofuse,\n totalItems: data.items._attributes.totalitems,\n pubDate: data.items._attributes.pubdate,\n },\n items: enforceArray(data.items.item).map((data) => {\n return {\n id: data._attributes.objectid,\n collId: data._attributes.collid,\n type: data._attributes.objecttype,\n name: data.name._text,\n yearPublished: data.yearpublished?._text,\n image: data.image?._text,\n thumbnail: data.thumbnail?._text,\n status: {\n own: data.status._attributes.own,\n prevOwned: data.status._attributes.prevowned,\n forTrade: data.status._attributes.fortrade,\n want: data.status._attributes.want,\n wantToPlay: data.status._attributes.wanttoplay,\n wantToBuy: data.status._attributes.wanttobuy,\n wishList: data.status._attributes.wishlist,\n preOrdered: data.status._attributes.preordered,\n lastModified: data.status._attributes.lastmodified,\n },\n numPlays: data.numplays._text,\n };\n }),\n };\n};\n\nexport const collection = async (\n params: ParamsCollection,\n): Promise<PayloadCollection> => {\n const { data } = await axios.get<ApiResponse>(\"/collection\", {\n params: transformParams(params),\n });\n\n return transformData(data);\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsFamily } from \"~/routes/types/params\";\nimport { PayloadFamily } from \"~/routes/types/payloads\";\n\nexport type ParamsTransformed = Omit<ParamsFamily, \"id\" | \"type\"> & {\n id: string;\n type?: string;\n};\n\nexport const transformParams = (params: ParamsFamily): ParamsTransformed => {\n return {\n ...params,\n id: params.id.join(\",\"),\n type: params.type?.join(\",\"),\n };\n};\n\ntype ApiResponseName = {\n _attributes: {\n type: string;\n sortindex: string;\n value: string;\n };\n};\n\ntype ApiResponseLink = {\n _attributes: {\n type: string;\n id: string;\n value: string;\n inbound: string;\n };\n};\n\ntype ApiResponseBody = {\n _attributes: {\n type: string;\n id: string;\n };\n thumbnail: {\n _text: string;\n };\n image: {\n _text: string;\n };\n name: ApiResponseName | Array<ApiResponseName>;\n description: {\n _text: string;\n };\n link: ApiResponseLink | Array<ApiResponseLink>;\n};\n\ntype ApiResponse = {\n items: {\n _attributes: { termsofuse: string };\n item?: ApiResponseBody | Array<ApiResponseBody>;\n };\n};\n\nconst transformData = (data: ApiResponse): PayloadFamily => {\n return {\n attributes: {\n termsOfUse: data.items._attributes.termsofuse,\n },\n items: enforceArray(data.items.item).map((data) => {\n return {\n id: data._attributes.id,\n type: data._attributes.type,\n thumbnail: data.thumbnail._text,\n image: data.image._text,\n description: data.description._text,\n names: enforceArray(data.name).map((name) => ({\n type: name._attributes.type,\n sortIndex: name._attributes.sortindex,\n value: name._attributes.value,\n })),\n links: enforceArray(data.link).map((link) => ({\n type: link._attributes.type,\n id: link._attributes.id,\n value: link._attributes.value,\n inbound: link._attributes.inbound === \"true\",\n })),\n };\n }),\n };\n};\n\nexport const family = async (params: ParamsFamily): Promise<PayloadFamily> => {\n const { data } = await axios.get<ApiResponse>(\"/family\", {\n params: transformParams(params),\n });\n\n return transformData(data);\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsForum } from \"~/routes/types/params\";\nimport { PayloadForum } from \"~/routes/types/payloads\";\n\ntype ApiResponse = {\n forum: {\n _attributes: {\n id: string;\n title: string;\n numthreads: string;\n numposts: string;\n lastpostdate: string;\n noposting: string;\n termsofuse: string;\n };\n threads: {\n thread?: Array<{\n _attributes: {\n id: string;\n subject: string;\n author: string;\n numarticles: string;\n postdate: string;\n lastpostdate: string;\n };\n }>;\n };\n };\n};\n\nconst transformData = (data: ApiResponse): PayloadForum => {\n return {\n attributes: {\n id: data.forum._attributes.id,\n title: data.forum._attributes.title,\n numThreads: data.forum._attributes.numthreads,\n numPosts: data.forum._attributes.numposts,\n lastPostDate: data.forum._attributes.lastpostdate,\n noPosting: data.forum._attributes.noposting,\n termsOfUse: data.forum._attributes.termsofuse,\n },\n threads: enforceArray(data.forum.threads.thread).map((thread) => ({\n id: thread._attributes.id,\n subject: thread._attributes.subject,\n author: thread._attributes.author,\n numArticles: thread._attributes.numarticles,\n postDate: thread._attributes.postdate,\n lastPostDate: thread._attributes.lastpostdate,\n })),\n };\n};\n\nexport const forum = async (params: ParamsForum): Promise<PayloadForum> => {\n // If the id provided is not a valid forum, BGG returns 200 with an html error page.\n // Catch xml parse error and return null.\n\n try {\n const { data } = await axios.get<ApiResponse>(\"/forum\", {\n params,\n });\n\n return transformData(data);\n } catch (error) {\n return null;\n }\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsForumList } from \"~/routes/types/params\";\nimport { PayloadForumList } from \"~/routes/types/payloads\";\nimport { thing, family } from \"~/routes/types/contentTypes\";\n\ntype ApiResponse = {\n forums: {\n _attributes: {\n type: thing | family;\n id: string;\n termsofuse: string;\n };\n forum?: Array<{\n _attributes: {\n id: string;\n groupid: string;\n title: string;\n noposting: string;\n description: string;\n numthreads: string;\n numposts: string;\n lastpostdate: string;\n };\n }>;\n };\n};\n\nconst transformData = (data: ApiResponse): PayloadForumList => {\n return {\n attributes: {\n type: data.forums._attributes.type,\n termsOfUse: data.forums._attributes.termsofuse,\n id: data.forums._attributes.id,\n },\n forums: enforceArray(data.forums.forum).map((forum) => ({\n id: forum._attributes.id,\n groupId: forum._attributes.groupid,\n title: forum._attributes.title,\n noPosting: forum._attributes.noposting,\n description: forum._attributes.description,\n numThreads: forum._attributes.numthreads,\n numPosts: forum._attributes.numposts,\n lastPostDate: forum._attributes.lastpostdate,\n })),\n };\n};\n\nexport const forumList = async (\n params: ParamsForumList,\n): Promise<PayloadForumList> => {\n const { data } = await axios.get<ApiResponse>(\"/forumlist\", {\n params,\n });\n\n return transformData(data);\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsGuild } from \"~/routes/types/params\";\nimport { PayloadGuild } from \"~/routes/types/payloads\";\n\ntype ApiResponseError = {\n _attributes: {\n id: string;\n termsofuse: string;\n };\n error: {\n _text: string;\n };\n};\n\ntype ApiResponseSuccess = {\n _attributes: {\n id: string;\n name: string;\n created: string;\n termsofuse: string;\n };\n category: { _text: string };\n website: { _text: string };\n manager: { _text: string };\n description: { _text: string };\n location: {\n addr1: { _text?: string };\n addr2: { _text?: string };\n city: { _text?: string };\n stateorprovince: { _text?: string };\n postalcode: { _text?: string };\n country: { _text?: string };\n };\n members?: {\n _attributes: { count: string; page: string };\n member: Array<{\n _attributes: { name: string; date: string };\n }>;\n };\n};\n\ntype ApiResponse = {\n guild: ApiResponseSuccess | ApiResponseError;\n};\n\nconst transformData = (data: ApiResponse): PayloadGuild => {\n if (\"error\" in data.guild) {\n return {\n attributes: {\n termsOfUse: data.guild._attributes.termsofuse,\n },\n guild: null,\n };\n }\n\n return {\n attributes: {\n termsOfUse: data.guild._attributes.termsofuse,\n id: data.guild._attributes.id,\n name: data.guild._attributes.name,\n created: data.guild._attributes.created,\n },\n guild: {\n id: data.guild._attributes.id,\n name: data.guild._attributes.name,\n created: data.guild._attributes.created,\n category: data.guild.category._text,\n website: data.guild.website._text,\n manager: data.guild.manager._text,\n description: data.guild.description._text,\n location: {\n addr1: data.guild.location.addr1._text,\n addr2: data.guild.location.addr2._text,\n city: data.guild.location.city._text,\n stateorprovince: data.guild.location.stateorprovince._text,\n postalcode: data.guild.location.postalcode._text,\n country: data.guild.location.country._text,\n },\n members: data.guild.members && {\n count: data.guild.members._attributes.count,\n page: data.guild.members._attributes.page,\n member: enforceArray(data.guild.members.member).map((member) => ({\n name: member._attributes.name,\n date: member._attributes.date,\n })),\n },\n },\n };\n};\n\nexport const guild = async (params: ParamsGuild): Promise<PayloadGuild> => {\n const { data } = await axios.get<ApiResponse>(\"/guild\", {\n params,\n });\n\n return transformData(data);\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsHot } from \"~/routes/types/params\";\nimport { PayloadHot } from \"~/routes/types/payloads\";\n\nexport type ParamsTransformed =\n | (Omit<ParamsHot, \"type\"> & {\n type: string;\n })\n | undefined;\n\nexport const transformParams = (args?: ParamsHot): ParamsTransformed => {\n if (!args) return undefined;\n\n return {\n type: args.type.join(\",\"),\n };\n};\n\ntype ApiResponseBody = {\n _attributes: { id: string; rank: string };\n name: { _attributes: { value: string } };\n yearpublished?: { _attributes: { value: string } };\n thumbnail: { _attributes: { value: string } };\n};\n\ntype ApiResponse = {\n items: {\n _attributes: { termsofuse: string };\n item?: ApiResponseBody | Array<ApiResponseBody>;\n };\n};\n\nconst transformData = (data: ApiResponse): PayloadHot => {\n return {\n attributes: {\n termsofuse: data.items._attributes.termsofuse,\n },\n items: enforceArray(data.items.item).map((data) => {\n return {\n id: data._attributes.id,\n rank: data._attributes.rank,\n name: data.name._attributes.value,\n yearPublished: data.yearpublished?._attributes.value,\n thumbnail: data.thumbnail._attributes.value,\n };\n }),\n };\n};\n\nexport const hot = async (params?: ParamsHot): Promise<PayloadHot> => {\n const { data } = await axios.get<ApiResponse>(\"/hot\", {\n params: transformParams(params),\n });\n\n return transformData(data);\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsSearch } from \"~/routes/types/params\";\nimport { PayloadSearch } from \"~/routes/types/payloads\";\n\nexport const endpoint = \"/search\";\n\nexport type ParamsTransformed = Omit<ParamsSearch, \"type\"> & {\n type?: string;\n};\n\nexport const transformParams = (args: ParamsSearch): ParamsTransformed => {\n return {\n ...args,\n type: args.type ? args.type.join(\",\") : undefined,\n };\n};\n\ntype ApiResponseBody = {\n _attributes: { type: string; id: string };\n name: { _attributes: { type: string; value: string } };\n yearpublished?: { _attributes: { value: string } };\n};\n\ntype ApiResponse = {\n items: {\n _attributes: { total: string; termsofuse: string };\n item?: ApiResponseBody | Array<ApiResponseBody>;\n };\n};\n\nconst transformData = (data: ApiResponse): PayloadSearch => {\n return {\n attributes: {\n termsofuse: data.items._attributes.termsofuse,\n },\n items: enforceArray(data.items.item).map((data) => {\n return {\n id: data._attributes.id,\n type: data._attributes.type,\n name: data.name._attributes.value,\n yearPublished: data.yearpublished?._attributes.value,\n };\n }),\n };\n};\n\nexport const search = async (args: ParamsSearch): Promise<PayloadSearch> => {\n const params = transformParams(args);\n const { data } = await axios.get<ApiResponse>(endpoint, { params });\n\n return transformData(data);\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsThing } from \"~/routes/types/params\";\nimport {\n PayloadThing,\n PayloadThingPolls,\n PayloadThingPollLanguageDependence,\n PayloadThingPollSuggestedPlayerAge,\n PayloadThingPollNumPlayers,\n} from \"~/routes/types/payloads\";\n\nexport const endpoint = \"/thing\";\n\nexport type ParamsTransformed = Omit<ParamsThing, \"id\" | \"type\"> & {\n id: string;\n type?: string;\n};\n\nexport const transformParams = (args: ParamsThing): ParamsTransformed => {\n return {\n ...args,\n id: args.id.join(\",\"),\n type: args.type?.join(\",\"),\n };\n};\n\ntype ApiResponsePollLanguageDependence = {\n _attributes: {\n name: \"language_dependence\";\n title: string;\n totalvotes: string;\n };\n results: {\n result: [\n {\n _attributes: {\n level: string;\n value: string;\n numvotes: string;\n };\n },\n ];\n };\n};\n\ntype ApiResponsePollSuggestedPlayerAge = {\n _attributes: {\n name: \"suggested_playerage\";\n title: string;\n totalvotes: string;\n };\n results: {\n result: Array<{\n _attributes: {\n value: string;\n numvotes: string;\n };\n }>;\n };\n};\n\ntype ApiResponsePollNumPlayers = {\n _attributes: {\n name: \"suggested_numplayers\";\n title: string;\n totalvotes: string;\n };\n results: Array<{\n _attributes: {\n numplayers: string;\n };\n result: Array<{\n _attributes: {\n value: string;\n numvotes: string;\n };\n }>;\n }>;\n};\n\ntype ApiResponsePolls = Array<\n | ApiResponsePollLanguageDependence\n | ApiResponsePollNumPlayers\n | ApiResponsePollSuggestedPlayerAge\n>;\n\ntype ApiResponseName = {\n _attributes: {\n type: string;\n sortindex: string;\n value: string;\n };\n};\n\ntype ApiResponseLink = {\n _attributes: {\n type: string;\n id: string;\n value: string;\n };\n};\n\ntype ApiResponseVideos = {\n _attributes: {\n id: string;\n title: string;\n category: string;\n language: string;\n link: string;\n username: string;\n userid: string;\n postdate: string;\n };\n};\n\ntype ApiResponseMarketplaceListing = {\n listdate: {\n _attributes: {\n value: string;\n };\n };\n price: {\n _attributes: {\n currency: string;\n value: string;\n };\n };\n condition: {\n _attributes: {\n value: string;\n };\n };\n notes: {\n _attributes: {\n value: string;\n };\n };\n link: {\n _attributes: {\n href: string;\n title: string;\n };\n };\n};\n\ntype ApiResponseStatisticsRatingRank = {\n _attributes: {\n type: string;\n id: string;\n name: string;\n friendlyname: string;\n value: string;\n bayesaverage: string;\n };\n};\n\ntype ApiResponseStatisticsRating = {\n usersrated: {\n _attributes: {\n value: string;\n };\n };\n average: {\n _attributes: {\n value: string;\n };\n };\n bayesaverage: {\n _attributes: {\n value: string;\n };\n };\n ranks: {\n rank:\n | ApiResponseStatisticsRatingRank\n | Array<ApiResponseStatisticsRatingRank>;\n };\n stddev: {\n _attributes: {\n value: string;\n };\n };\n median: {\n _attributes: {\n value: string;\n };\n };\n owned: {\n _attributes: {\n value: string;\n };\n };\n trading: {\n _attributes: {\n value: string;\n };\n };\n wanting: {\n _attributes: {\n value: string;\n };\n };\n wishing: {\n _attributes: {\n value: string;\n };\n };\n numcomments: {\n _attributes: {\n value: string;\n };\n };\n numweights: {\n _attributes: {\n value: string;\n };\n };\n averageweight: {\n _attributes: {\n value: string;\n };\n };\n};\n\ntype ApiResponseBody = {\n _attributes: {\n type: string;\n id: string;\n };\n thumbnail?: {\n _text: string;\n };\n image?: {\n _text: string;\n };\n name: ApiResponseName | Array<ApiResponseName>;\n description: {\n _text: string;\n };\n yearpublished: {\n _attributes: {\n value: string;\n };\n };\n minplayers: {\n _attributes: {\n value: string;\n };\n };\n maxplayers: {\n _attributes: {\n value: string;\n };\n };\n playingtime: {\n _attributes: {\n value: string;\n };\n };\n minplaytime: {\n _attributes: {\n value: string;\n };\n };\n maxplaytime: {\n _attributes: {\n value: string;\n };\n };\n minage: {\n _attributes: {\n value: string;\n };\n };\n link: ApiResponseLink | Array<ApiResponseLink>;\n poll?: ApiResponsePolls;\n videos?: {\n _attributes: {\n total: string;\n };\n video: ApiResponseVideos | Array<ApiResponseVideos>;\n };\n versions?: {\n item: Array<{\n _attributes: {\n type: string;\n id: string;\n };\n thumbnail?: {\n _text: string;\n };\n image?: {\n _text: string;\n };\n link: ApiResponseLink | Array<ApiResponseLink>;\n name: ApiResponseName | Array<ApiResponseName>;\n yearpublished: {\n _attributes: {\n value: string;\n };\n };\n productcode: {\n _attributes: {\n value: string;\n };\n };\n width: {\n _attributes: {\n value: string;\n };\n };\n length: {\n _attributes: {\n value: string;\n };\n };\n depth: {\n _attributes: {\n value: string;\n };\n };\n weight: {\n _attributes: {\n value: string;\n };\n };\n }>;\n };\n // Same for both rating comments and regular comments\n // Per the API documentation, if both are defined in the query regular comments will be returned\n comments?: {\n _attributes: {\n page: string;\n totalitems: string;\n };\n comment: Array<{\n _attributes: {\n username: string;\n rating: string;\n value: string;\n };\n }>;\n };\n statistics?: {\n _attributes: {\n page: string;\n };\n ratings: ApiResponseStatisticsRating;\n };\n marketplacelistings?: {\n listing:\n | ApiResponseMarketplaceListing\n | Array<ApiResponseMarketplaceListing>;\n };\n};\n\ntype ApiResponse = {\n items: {\n _attributes: {\n termsofuse: string;\n };\n item?: ApiResponseBody | Array<ApiResponseBody>;\n };\n};\n\nconst transformPollLanguageDependence = (\n poll: ApiResponsePollLanguageDependence,\n): PayloadThingPollLanguageDependence => {\n return {\n name: poll._attributes.name,\n title: poll._attributes.title,\n totalvotes: poll._attributes.totalvotes,\n results: enforceArray(poll.results.result).map((result) => {\n return {\n level: result._attributes.level,\n value: result._attributes.value,\n numvotes: result._attributes.numvotes,\n };\n }),\n };\n};\n\nconst transformPollSuggestedPlayerAge = (\n poll: ApiResponsePollSuggestedPlayerAge,\n): PayloadThingPollSuggestedPlayerAge => {\n return {\n name: poll._attributes.name,\n title: poll._attributes.title,\n totalvotes: poll._attributes.totalvotes,\n results: enforceArray(poll.results.result).map((result) => {\n return {\n value: result._attributes.value,\n numvotes: result._attributes.numvotes,\n };\n }),\n };\n};\n\nconst transformPollSuggestedNumPlayers = (\n poll: ApiResponsePollNumPlayers,\n): PayloadThingPollNumPlayers => {\n return {\n name: poll._attributes.name,\n title: poll._attributes.title,\n totalvotes: poll._attributes.totalvotes,\n results: enforceArray(poll.results).map((result) => {\n return {\n numplayers: result._attributes.numplayers,\n result: enforceArray(result.result).map((result) => {\n return {\n value: result._attributes.value,\n numvotes: result._attributes.numvotes,\n };\n }),\n };\n }),\n };\n};\n\n// Typescript doesn't recognize discriminated unions for nested properties, so this is a workaround\n// Avoids needing overly complex type guards\nconst transformPoll = (apiPolls: ApiResponsePolls): PayloadThingPolls => {\n const polls: PayloadThingPolls = [];\n\n apiPolls.forEach((apiPoll) => {\n switch (apiPoll._attributes.name) {\n case \"language_dependence\": {\n polls.push(\n transformPollLanguageDependence(\n apiPoll as ApiResponsePollLanguageDependence,\n ),\n );\n break;\n }\n case \"suggested_playerage\": {\n polls.push(\n transformPollSuggestedPlayerAge(\n apiPoll as ApiResponsePollSuggestedPlayerAge,\n ),\n );\n break;\n }\n case \"suggested_numplayers\": {\n polls.push(\n transformPollSuggestedNumPlayers(\n apiPoll as ApiResponsePollNumPlayers,\n ),\n );\n break;\n }\n }\n });\n\n return polls;\n};\n\nconst transformData = (data: ApiResponse): PayloadThing => {\n return {\n attributes: {\n termsofuse: data.items._attributes.termsofuse,\n },\n items: enforceArray(data.items.item).map((data) => {\n return {\n id: data._attributes.id,\n type: data._attributes.type,\n thumbnail: data.thumbnail?._text,\n image: data.image?._text,\n names: enforceArray(data.name).map((name) => {\n return {\n type: name._attributes.type,\n sortindex: name._attributes.sortindex,\n value: name._attributes.value,\n };\n }),\n description: data.description._text,\n yearPublished: data.yearpublished._attributes.value,\n minPlayers: data.minplayers._attributes.value,\n maxPlayers: data.maxplayers._attributes.value,\n playingTime: data.playingtime._attributes.value,\n minPlayTime: data.minplaytime._attributes.value,\n maxPlayTime: data.maxplaytime._attributes.value,\n minAge: data.minage._attributes.value,\n links: enforceArray(data.link).map((link) => {\n return {\n type: link._attributes.type,\n id: link._attributes.id,\n value: link._attributes.value,\n };\n }),\n polls: transformPoll(enforceArray(data.poll)),\n comments: data.comments && {\n page: data.comments._attributes.page,\n total: data.comments._attributes.totalitems,\n comment: enforceArray(data.comments.comment).map((comment) => {\n return {\n username: comment._attributes.username,\n rating: comment._attributes.rating,\n value: comment._attributes.value,\n };\n }),\n },\n marketplace: data.marketplacelistings && {\n listings: enforceArray(data.marketplacelistings.listing).map(\n (listing) => {\n return {\n listDate: listing.listdate._attributes.value,\n price: {\n currency: listing.price._attributes.currency,\n value: listing.price._attributes.value,\n },\n condition: listing.condition._attributes.value,\n notes: listing.notes._attributes.value,\n link: {\n href: listing.link._attributes.href,\n title: listing.link._attributes.title,\n },\n };\n },\n ),\n },\n statistics: data.statistics && {\n page: data.statistics._attributes.page,\n ratings: {\n usersRated: data.statistics.ratings.usersrated._attributes.value,\n average: data.statistics.ratings.average._attributes.value,\n bayesAverage:\n data.statistics.ratings.bayesaverage._attributes.value,\n ranks: enforceArray(data.statistics.ratings.ranks.rank).map(\n (rank) => {\n return {\n type: rank._attributes.type,\n id: rank._attributes.id,\n name: rank._attributes.name,\n friendlyName: rank._attributes.friendlyname,\n value: rank._attributes.value,\n bayesAverage: rank._attributes.bayesaverage,\n };\n },\n ),\n },\n stdDev: data.statistics.ratings.stddev._attributes.value,\n median: data.statistics.ratings.median._attributes.value,\n owned: data.statistics.ratings.owned._attributes.value,\n trading: data.statistics.ratings.trading._attributes.value,\n wanting: data.statistics.ratings.wanting._attributes.value,\n wishing: data.statistics.ratings.wishing._attributes.value,\n numComments: data.statistics.ratings.numcomments._attributes.value,\n numWeights: data.statistics.ratings.numweights._attributes.value,\n averageWeight:\n data.statistics.ratings.averageweight._attributes.value,\n },\n versions:\n data.versions &&\n enforceArray(data.versions.item).map((item) => {\n return {\n id: item._attributes.id,\n type: item._attributes.type,\n thumbnail: item.thumbnail?._text,\n image: item.image?._text,\n links: enforceArray(item.link).map((link) => {\n return {\n type: link._attributes.type,\n id: link._attributes.id,\n value: link._attributes.value,\n };\n }),\n names: enforceArray(item.name).map((name) => {\n return {\n type: name._attributes.type,\n sortindex: name._attributes.sortindex,\n value: name._attributes.value,\n };\n }),\n yearPublished: item.yearpublished._attributes.value,\n productCode: item.productcode._attributes.value,\n width: item.width._attributes.value,\n length: item.length._attributes.value,\n depth: item.depth._attributes.value,\n weight: item.weight._attributes.value,\n };\n }),\n videos: data.videos && {\n total: data.videos._attributes.total,\n videos: enforceArray(data.videos.video).map((video) => {\n return {\n id: video._attributes.id,\n title: video._attributes.title,\n category: video._attributes.category,\n language: video._attributes.language,\n link: video._attributes.link,\n username: video._attributes.username,\n userid: video._attributes.userid,\n postdate: video._attributes.postdate,\n };\n }),\n },\n };\n }),\n };\n};\n\nexport const thing = async (params: ParamsThing): Promise<PayloadThing> => {\n const { data } = await axios.get<ApiResponse>(endpoint, {\n params: transformParams(params),\n });\n\n return transformData(data);\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsThread } from \"~/routes/types/params\";\nimport { PayloadThread } from \"~/routes/types/payloads\";\n\nexport const endpoint = \"/thread\";\n\ntype ApiResponseError = {\n error: {\n _attributes: {\n message: string;\n };\n };\n};\n\ntype ApiResponseSuccess = {\n thread: {\n _attributes: {\n id: string;\n numarticles: string;\n link: string;\n termsofuse: string;\n };\n subject: { _text: string };\n articles: {\n article: Array<{\n _attributes: {\n id: string;\n username: string;\n link: string;\n postdate: string;\n editdate: string;\n numedits: string;\n };\n subject: { _text: string };\n body: { _text: string };\n }>;\n };\n };\n};\n\ntype ApiResponse = ApiResponseSuccess | ApiResponseError;\n\nconst transformData = (data: ApiResponse): PayloadThread => {\n if (\"error\" in data) {\n return null;\n }\n\n return {\n attributes: {\n id: data.thread._attributes.id,\n numArticles: data.thread._attributes.numarticles,\n link: data.thread._attributes.link,\n termsOfUse: data.thread._attributes.termsofuse,\n },\n subject: data.thread.subject._text,\n articles: enforceArray(data.thread.articles.article).map((article) => ({\n id: article._attributes.id,\n username: article._attributes.username,\n link: article._attributes.link,\n postDate: article._attributes.postdate,\n editDate: article._attributes.editdate,\n numEdits: article._attributes.numedits,\n body: article.body._text,\n })),\n };\n};\n\nexport const thread = async (params: ParamsThread): Promise<PayloadThread> => {\n const { data } = await axios.get<ApiResponse>(endpoint, {\n params,\n });\n\n return transformData(data);\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsUser } from \"~/routes/types/params\";\nimport { PayloadUser } from \"~/routes/types/payloads\";\n\nexport const endpoint = \"/user\";\n\ntype ApiResponseBuddy = {\n _attributes: {\n id: string;\n name: string;\n };\n};\n\ntype ApiResponseGuild = {\n _attributes: {\n id: string;\n name: string;\n };\n};\n\ntype ApiResponseHot = {\n _attributes: {\n rank: string;\n type: string;\n id: string;\n name: string;\n };\n};\n\ntype ApiResponseTop = {\n _attributes: {\n rank: string;\n type: string;\n id: string;\n name: string;\n };\n};\n\ntype ApiResponse = {\n user: {\n _attributes: {\n id: string;\n name: string;\n termsofuse: string;\n };\n firstname: {\n _attributes: {\n value: string;\n };\n };\n lastname: {\n _attributes: {\n value: string;\n };\n };\n avatarlink: {\n _attributes: {\n value: string;\n };\n };\n yearregistered: {\n _attributes: {\n value: string;\n };\n };\n lastlogin: {\n _attributes: {\n value: string;\n };\n };\n stateorprovince: {\n _attributes: {\n value: string;\n };\n };\n country: {\n _attributes: {\n value: string;\n };\n };\n webaddress: {\n _attributes: {\n value: string;\n };\n };\n xboxaccount: {\n _attributes: {\n value: string;\n };\n };\n wiiaccount: {\n _attributes: {\n value: string;\n };\n };\n psnaccount: {\n _attributes: {\n value: string;\n };\n };\n battlenetaccount: {\n _attributes: {\n value: string;\n };\n };\n steamaccount: {\n _attributes: {\n value: string;\n };\n };\n traderating: {\n _attributes: {\n value: string;\n };\n };\n buddies?: {\n _attributes: {\n total: string;\n page: string;\n };\n buddy: ApiResponseBuddy | Array<ApiResponseBuddy>;\n };\n guilds?: {\n _attributes: {\n total: string;\n page: string;\n };\n guild: ApiResponseGuild | Array<ApiResponseGuild>;\n };\n hot?: {\n _attributes: {\n domain: string;\n };\n item: ApiResponseHot | Array<ApiResponseHot>;\n };\n top?: {\n _attributes: {\n domain: string;\n };\n item: ApiResponseTop | Array<ApiResponseTop>;\n };\n };\n};\n\nconst transformData = (data: ApiResponse): PayloadUser => {\n return {\n termsOfUse: data.user._attributes.termsofuse,\n user: {\n id: data.user._attributes.id,\n name: data.user._attributes.name,\n termsOfUse: data.user._attributes.termsofuse,\n firstName: data.user.firstname._attributes.value,\n lastName: data.user.lastname._attributes.value,\n avatarLink: data.user.avatarlink._attributes.value,\n yearRegistered: data.user.yearregistered._attributes.value,\n lastLogin: data.user.lastlogin._attributes.value,\n stateOrProvince: data.user.stateorprovince._attributes.value,\n country: data.user.country._attributes.value,\n webAddress: data.user.webaddress._attributes.value,\n xboxAccount: data.user.xboxaccount._attributes.value,\n wiiAccount: data.user.wiiaccount._attributes.value,\n psnAccount: data.user.psnaccount._attributes.value,\n battlenetAccount: data.user.battlenetaccount._attributes.value,\n steamAccount: data.user.steamaccount._attributes.value,\n tradeRating: data.user.traderating._attributes.value,\n buddies: data.user.buddies && {\n total: data.user.buddies._attributes.total,\n page: data.user.buddies._attributes.page,\n buddy: enforceArray(data.user.buddies.buddy).map((data) => ({\n id: data._attributes.id,\n name: data._attributes.name,\n })),\n },\n guilds: data.user.guilds && {\n total: data.user.guilds._attributes.total,\n page: data.user.guilds._attributes.page,\n guild: enforceArray(data.user.guilds.guild).map((data) => ({\n id: data._attributes.id,\n name: data._attributes.name,\n })),\n },\n hot: data.user.hot && {\n domain: data.user.hot._attributes.domain,\n item: enforceArray(data.user.hot.item).map((data) => ({\n rank: data._attributes.rank,\n type: data._attributes.type,\n id: data._attributes.id,\n name: data._attributes.name,\n })),\n },\n top: data.user.top && {\n domain: data.user.top._attributes.domain,\n item: enforceArray(data.user.top.item).map((data) => ({\n rank: data._attributes.rank,\n type: data._attributes.type,\n id: data._attributes.id,\n name: data._attributes.name,\n })),\n },\n },\n };\n};\n\nexport const user = async (params: ParamsUser): Promise<PayloadUser | null> => {\n // If the id provided is not a valid forum, BGG returns 200 with an html error page.\n // Catch xml parse error and return null.\n\n try {\n const { data } = await axios.get<ApiResponse>(endpoint, {\n params,\n });\n\n return transformData(data);\n } catch (error) {\n return null;\n }\n};\n","export { username } from \"./username\";\nexport { id } from \"./id\";\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsPlaysUsername } from \"~/routes/types/params\";\nimport { PayloadPlaysUsername } from \"~/routes/types/payloads\";\nimport {\n ApiResponseAttributesBase,\n ApiResponseBase,\n} from \"~/routes/plays/types\";\n\ntype ApiResponseAttributes = ApiResponseAttributesBase & {\n username: string;\n userid: string;\n};\n\ntype ApiResponseItemSubtype = {\n _attributes: {\n value: string;\n };\n};\n\ntype ApiResponse = {\n _attributes: {\n id: string;\n date: string;\n quantity: string;\n length: string;\n incomplete: string;\n nowinstats: string;\n location: string;\n };\n item: {\n _attributes: {\n name: string;\n objecttype: string;\n objectid: string;\n };\n subtypes: {\n subtype: ApiResponseItemSubtype | Array<ApiResponseItemSubtype>;\n };\n };\n};\n\ntype ApiResponseError = {\n div: {\n _attributes: {\n class: string;\n };\n _text: string;\n };\n};\n\ntype ApiResponsePlaysUsername =\n | ApiResponseBase<ApiResponseAttributes, ApiResponse>\n | ApiResponseError;\n\nconst transformData = (\n data: ApiResponsePlaysUsername,\n): PayloadPlaysUsername => {\n if (\"div\" in data) {\n return null;\n }\n\n return {\n attributes: {\n termsofuse: data.plays._attributes.termsofuse,\n username: data.plays._attributes.username,\n userid: data.plays._attributes.userid,\n total: data.plays._attributes.total,\n page: data.plays._attributes.page,\n },\n plays: enforceArray(data.plays.play).map((play) => ({\n id: play._attributes.id,\n date: play._attributes.date,\n quantity: play._attributes.quantity,\n length: play._attributes.length,\n incomplete: play._attributes.incomplete,\n nowinstats: play._attributes.nowinstats,\n location: play._attributes.location,\n item: {\n name: play.item._attributes.name,\n objecttype: play.item._attributes.objecttype,\n objectid: play.item._attributes.objectid,\n subtypes: enforceArray(play.item.subtypes.subtype).map(\n (subtype) => subtype._attributes.value,\n ),\n },\n })),\n };\n};\n\nexport const username = async (\n params: ParamsPlaysUsername,\n): Promise<PayloadPlaysUsername> => {\n const { data } = await axios.get<ApiResponsePlaysUsername | ApiResponseError>(\n \"/plays\",\n {\n params,\n },\n );\n return transformData(data);\n};\n","import { axios } from \"~/lib/axios\";\nimport { enforceArray } from \"~/lib/helpers\";\n\nimport { ParamsPlaysId } from \"~/routes/types/params\";\nimport { PayloadPlaysId } from \"~/routes/types/payloads\";\nimport {\n ApiResponseAttributesBase,\n ApiResponseBase,\n} from \"~/routes/plays/types\";\n\ntype ApiResponsePlayers = {\n _attributes: {\n username: string;\n userid: string;\n name: string;\n startposition: string;\n color: string;\n score: string;\n new: string;\n rating: string;\n win: string;\n };\n};\n\ntype ApiResponse = {\n _attributes: {\n id: string;\n date: string;\n quantity: string;\n length: string;\n incomplete: string;\n nowinstats: string;\n location: string;\n };\n item: {\n _attributes: {\n name: string;\n objecttype: string;\n objectid: string;\n };\n subtypes: {\n subtype: {\n _attributes: {\n value: string;\n };\n };\n };\n };\n players?: {\n player: ApiResponsePlayers | Array<ApiResponsePlayers>;\n };\n};\n\ntype ApiResponsePlaysId = ApiResponseBase<\n ApiResponseAttributesBase,\n ApiResponse\n>;\n\nconst transformData = (data: ApiResponsePlaysId): PayloadPlaysId => {\n const { play, _attributes } = data.plays;\n\n return {\n attributes: {\n termsofuse: _attributes.termsofuse,\n total: _attributes.total,\n page: _attributes.page,\n },\n plays: enforceArray(play).map((play) => ({\n id: play._attributes.id,\n date: play._attributes.date,\n quantity: play._attributes.quantity,\n length: play._attributes.length,\n incomplete: play._attributes.incomplete,\n nowInStats: play._attributes.nowinstats,\n location: play._attributes.location,\n item: {\n name: play.item._attributes.name,\n objectType: play.item._attributes.objecttype,\n objectId: play.item._attributes.objectid,\n subtypes: enforceArray(play.item.subtypes.subtype).map(\n (subtype) => subtype._attributes.value,\n ),\n },\n players:\n play.players?.player &&\n enforceArray(play.players.player).map((player) => ({\n username: player._attributes.username,\n userid: player._attributes.userid,\n name: player._attributes.name,\n startPosition: player._attributes.startposition,\n color: player._attributes.color,\n score: player._attributes.score,\n new: player._attributes.new,\n rating: player._attributes.rating,\n win: player._attributes.win,\n })),\n })),\n };\n};\n\nexport const id = async (params: ParamsPlaysId): Promise<PayloadPlaysId> => {\n const { data } = await axios.get<ApiResponsePlaysId>(\"/plays\", { params });\n return transformData(data);\n};\n","export type boardgame = \"boardgame\";\nexport type boardgameaccessory = \"boardgameaccessory\";\nexport type boardgamecompany = \"boardgamecompany\";\nexport type boardgamecompilation = \"boardgamecompilation\";\nexport type boardgameexpansion = \"boardgameexpansion\";\nexport type boardgamefamily = \"boardgamefamily\";\nexport type boardgameimplementation = \"boardgameimplementation\";\nexport type boardgameintegration = \"boardgameintegration\";\nexport type boardgameperson = \"boardgameperson\";\nexport type family = \"family\";\nexport type rpg = \"rpg\";\nexport type rpgcompany = \"rpgcompany\";\nexport type rpgissue = \"rpgissue\";\nexport type rpgitem = \"rpgitem\";\nexport type rpgperiodical = \"rpgperiodical\";\nexport type rpgperson = \"rpgperson\";\nexport type thing = \"thing\";\nexport type videogame = \"videogame\";\nexport type videogamecompany = \"videogamecompany\";\n\n/** YYYY-MM-DD */\nexport type Date = string;\n\n/** YYYY-MM-DD HH:MM:SS */\nexport type DateTime = string;\n","import {\n boardgame,\n boardgameaccessory,\n boardgamecompany,\n boardgamecompilation,\n boardgameexpansion,\n boardgamefamily,\n boardgameimplementation,\n boardgameintegration,\n boardgameperson,\n Date,\n DateTime,\n family,\n rpg,\n rpgcompany,\n rpgissue,\n rpgitem,\n rpgperiodical,\n rpgperson,\n thing,\n videogame,\n videogamecompany,\n} from \"~/routes/types/contentTypes\";\n\nexport type ParamsPlayBase = {\n minDate?: Date;\n maxDate?: Date;\n subtype?:\n | boardgame\n | boardgameexpansion\n | boardgameaccessory\n | boardgameintegration\n | boardgamecompilation\n | boardgameimplementation\n | rpg\n | rpgitem\n | videogame;\n page?: string;\n};\n\nexport type ParamsPlaysId = ParamsPlayBase & {\n id: string;\n type: thing | family;\n};\n\nexport type ParamsPlaysUsername = ParamsPlayBase & {\n username: string;\n};\n\nexport type ParamsCollection = {\n username: string;\n version?: true;\n subtype?:\n | boardgame\n | boardgameaccessory\n | boardgameexpansion\n | rpgissue\n | rpgitem\n | videogame;\n excludesubtype?: string;\n id?: Array<string>;\n brief?: true;\n stats?: true;\n own?: 0 | 1;\n rated?: 0 | 1;\n played?: 0 | 1;\n comment?: 0 | 1;\n trade?: 0 | 1;\n want?: 0 | 1;\n wishlist?: 0 | 1;\n wishlistpriority?: 1 | 2 | 3 | 4 | 5;\n preordered?: 0 | 1;\n wanttoplay?: 0 | 1;\n wanttobuy?: 0 | 1;\n prevowned?: 0 | 1;\n hasparts?: 0 | 1;\n wantparts?: 0 | 1;\n minrating?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;\n rating?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;\n minbggrating?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;\n bggrating?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;\n minplays?: string;\n maxplays?: string;\n showprivate?: true;\n collid?: string;\n modifiedsince?: string;\n};\n\nexport type ParamsFamily = {\n id: Array<string>;\n type?: Array<rpg | rpgperiodical | boardgamefamily>;\n};\n\nexport type ParamsForum = {\n id: string;\n page?: string;\n};\n\nexport type ParamsForumList = {\n id: string;\n type: thing | family;\n};\n\nexport type ParamsGuild = {\n id: string;\n members?: true;\n sort?: \"username\" | \"date\";\n page?: string;\n};\n\nexport type ParamsHot = {\n type: Array<\n | boardgame\n | boardgamecompany\n | boardgameperson\n | rpg\n | rpgcompany\n | rpgperson\n | videogame\n | videogamecompany\n >;\n};\n\nexport type ParamsSearch = {\n query: string;\n type?: Array<\n boardgame | boardgameaccessory | boardgameexpansion | rpgitem | videogame\n >;\n exact?: boolean;\n};\n\nexport type ParamsThing = {\n id: Array<string>;\n type?: Array<\n | boardgame\n | boardgameaccessory\n | boardgameexpansion\n | rpgissue\n | rpgitem\n | videogame\n >;\n versions?: true;\n videos?: true;\n stats?: true;\n marketplace?: true;\n comments?: true;\n ratingcomments?: true;\n page?: string;\n pagesize?: string;\n};\n\nexport type ParamsThread = {\n id: string;\n minArticleId?: string;\n minArticleDate?: Date | DateTime;\n count?: string;\n};\n\nexport type ParamsUser = {\n name: string;\n buddies?: true;\n guilds?: true;\n hot?: true;\n top?: true;\n domain?: boardgame | rpg | videogame;\n page?: string;\n};\n","import { thing, family } from \"~/routes/types/contentTypes\";\n\nexport type PayloadPlaysId = {\n attributes: {\n termsofuse: string;\n total: string;\n page: string;\n };\n plays: Array<{\n id: string;\n date: string;\n quantity: string;\n length: string;\n incomplete: string;\n nowInStats: string;\n location: string;\n item: {\n name: string;\n objectType: string;\n objectId: string;\n subtypes: Array<string>;\n };\n players?: Array<{\n username: string;\n userid: string;\n name: string;\n startPosition: string;\n color: string;\n score: string;\n new: string;\n rating: string;\n win: string;\n }>;\n }>;\n};\n\nexport type PayloadPlaysUsername = {\n attributes: {\n termsofuse: string;\n username: string;\n userid: string;\n total: string;\n page: string;\n };\n plays: Array<{\n id: string;\n date: string;\n quantity: string;\n length: string;\n incomplete: string;\n nowinstats: string;\n location: string;\n item: {\n name: string;\n objecttype: string;\n objectid: string;\n