UNPKG

poe-js-sdk

Version:

TypeScript SDK for the Path of Exile API

907 lines (906 loc) 30.3 kB
import d from "axios"; class f extends Error { /** * @param message Error message * @param opts.code Optional PoE error code * @param opts.status HTTP status code * @param opts.url Request URL if available * @param opts.details Raw error payload * @param opts.headers Response headers */ constructor(t, e) { super(t), this.name = "PoEApiError", this.code = e.code ?? void 0, this.status = e.status, this.url = e.url ?? void 0, this.details = e.details, this.headers = e.headers ?? void 0; } } class y { // epoch ms when requests may resume /** * Create a new PoE API client. * @param config Client configuration (User-Agent required) */ constructor(t) { if (this.rateLimitInfo = {}, this.nextAvailableAt = 0, !t.userAgent?.startsWith("OAuth ") || !/\(contact: .+\)/.test(t.userAgent)) throw new Error( 'User-Agent must start with "OAuth " and include a contact: e.g. "OAuth myapp/1.0.0 (contact: you@example.com)"' ); this.client = d.create({ baseURL: t.baseURL || "https://api.pathofexile.com", headers: { "User-Agent": t.userAgent, ...t.accessToken && { Authorization: `Bearer ${t.accessToken}` } } }), this.client.interceptors?.request?.use && this.client.interceptors.request.use(async (e) => { const s = Date.now(); if (this.nextAvailableAt > s) { const i = this.nextAvailableAt - s; await new Promise((r) => setTimeout(r, i)); } return e; }), this.client.interceptors.response.use( (e) => (this.updateRateLimitInfo(e), this.updateWaitFromHeaders(e), e), (e) => { if (e.response) { if (this.updateRateLimitInfo(e.response), this.updateWaitFromHeaders(e.response), e.response.status === 429) { const i = e.response.headers["retry-after"], r = i ? Number.parseInt(i) : 0; !Number.isNaN(r) && r > 0 && (this.nextAvailableAt = Date.now() + r * 1e3); } const s = e.response.data; if (s && typeof s == "object" && "error" in s && s.error) { const i = s.error, r = e.response.status, a = e.config?.url, o = e.response.headers, c = { status: r, details: s, headers: o }; throw a && (c.url = a), typeof i.code == "number" && (c.code = i.code), new f(i.message || "API error", c); } } throw e; } ); } updateRateLimitInfo(t) { const e = t.headers, s = e["retry-after"], i = {}; e["x-rate-limit-policy"] && (i.policy = e["x-rate-limit-policy"]), e["x-rate-limit-rules"] && (i.rules = e["x-rate-limit-rules"]), e["x-rate-limit-account"] && (i.account = e["x-rate-limit-account"]), e["x-rate-limit-ip"] && (i.ip = e["x-rate-limit-ip"]), e["x-rate-limit-client"] && (i.client = e["x-rate-limit-client"]), e["x-rate-limit-account-state"] && (i.accountState = e["x-rate-limit-account-state"]), e["x-rate-limit-ip-state"] && (i.ipState = e["x-rate-limit-ip-state"]), e["x-rate-limit-client-state"] && (i.clientState = e["x-rate-limit-client-state"]), s && (i.retryAfter = Number.parseInt(s)), this.rateLimitInfo = i; } /** * Get the latest parsed rate limit header values. * Includes optional `*-State` and `Retry-After` values when present. */ getRateLimitInfo() { return { ...this.rateLimitInfo }; } /** * Update the access token used for Authorization header (Bearer scheme). */ setAccessToken(t) { this.client.defaults.headers.Authorization = `Bearer ${t}`; } updateWaitFromHeaders(t) { const e = t.headers, s = [ e["x-rate-limit-account-state"], e["x-rate-limit-ip-state"], e["x-rate-limit-client-state"] ].filter(Boolean); for (const i of s) { const r = String(i).split(","); for (const a of r) { const o = a.split(":"); if (o.length === 3) { const c = Number.parseInt(o[2] ?? "0"); if (!Number.isNaN(c) && c > 0) { const u = Date.now() + c * 1e3; u > this.nextAvailableAt && (this.nextAvailableAt = u); } } } } } // Profile endpoints /** * Get account profile. * @see https://www.pathofexile.com/developer/docs/reference#profile */ async getProfile() { return (await this.client.get("/profile")).data; } // League endpoints /** * List leagues. * @see https://www.pathofexile.com/developer/docs/reference#leagues-list */ async getLeagues(t) { const e = typeof t == "string" || t === void 0 ? { realm: t } : t, s = {}; return e?.realm && (s.realm = e.realm), e && "type" in e && e.type && (s.type = e.type), e && "season" in e && e.season && (s.season = e.season), e && "limit" in e && e.limit !== void 0 && (s.limit = e.limit), e && "offset" in e && e.offset !== void 0 && (s.offset = e.offset), (await this.client.get("/league", { params: s })).data; } /** * Get a specific league by id. * @see https://www.pathofexile.com/developer/docs/reference#leagues-get */ async getLeague(t, e) { const s = e ? { realm: e } : {}; return (await this.client.get( `/league/${t}`, { params: s } )).data; } /** * Get league ladder (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#leagues-ladder */ async getLeagueLadder(t, e) { const s = {}; return e?.realm && (s.realm = e.realm), e?.offset !== void 0 && (s.offset = e.offset), e?.limit !== void 0 && (s.limit = e.limit), e?.sort && (s.sort = e.sort), e?.class && e.sort === "class" && (s.class = e.class), (await this.client.get(`/league/${t}/ladder`, { params: s })).data; } // League Event Ladder (PoE1 only) /** * Get league event ladder (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#leagues-event-ladder */ async getLeagueEventLadder(t, e) { const s = {}; return e?.realm && (s.realm = e.realm), e?.offset !== void 0 && (s.offset = e.offset), e?.limit !== void 0 && (s.limit = e.limit), (await this.client.get(`/league/${t}/event-ladder`, { params: s })).data; } // Character endpoints /** * List account characters. * @see https://www.pathofexile.com/developer/docs/reference#characters-list */ async getCharacters(t) { const e = t ? `/character/${t}` : "/character"; return (await this.client.get(e)).data; } /** * Get a character by name with equipment, inventory, and passives. * @see https://www.pathofexile.com/developer/docs/reference#characters-get */ async getCharacter(t, e) { const s = e ? `/character/${e}/${t}` : `/character/${t}`; return (await this.client.get( s )).data; } // Stash endpoints (PoE1 only) /** * List account stash tabs for a league (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#stashes-list */ async getStashes(t, e) { const s = e ? `/stash/${e}/${t}` : `/stash/${t}`; return (await this.client.get(s)).data; } /** * Get a specific stash or substash (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#stashes-get */ async getStash(t, e, s, i) { const r = i ? `/stash/${i}/${t}` : `/stash/${t}`, a = s ? `${r}/${e}/${s}` : `${r}/${e}`; return (await this.client.get(a)).data; } // League account endpoints (PoE1 only) /** * Get league account info such as atlas passives (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#leagueaccounts */ async getLeagueAccount(t, e) { const s = e ? `/league-account/${e}/${t}` : `/league-account/${t}`; return (await this.client.get( s )).data; } // PvP Match endpoints (PoE1 only) /** * List PvP matches (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#matches-list */ async getPvpMatches(t) { const e = typeof t == "string" || t === void 0 ? { realm: t } : t, s = {}; return e?.realm && (s.realm = e.realm), e && "type" in e && e.type && (s.type = e.type), e && "season" in e && e.season && (s.season = e.season), e && "league" in e && e.league && (s.league = e.league), (await this.client.get( "/pvp-match", { params: s } )).data; } /** * Get a PvP match by id (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#matches-get */ async getPvpMatch(t, e) { const s = e ? { realm: e } : {}; return (await this.client.get( `/pvp-match/${t}`, { params: s } )).data; } // PvP Match Ladder (PoE1 only) /** * Get PvP match ladder (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#matches-ladder */ async getPvpMatchLadder(t, e) { const s = {}; return e?.realm && (s.realm = e.realm), e?.limit !== void 0 && (s.limit = e.limit), e?.offset !== void 0 && (s.offset = e.offset), (await this.client.get(`/pvp-match/${t}/ladder`, { params: s })).data; } // Public Stash API (PoE1 only) /** * Get public stashes stream page (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#publicstashes-list */ async getPublicStashes(t) { const e = t?.realm ? `/public-stash-tabs/${t.realm}` : "/public-stash-tabs", s = t?.id ? { id: t.id } : {}; return (await this.client.get(e, { params: s })).data; } // Currency Exchange API /** * Get currency exchange markets history. * @see https://www.pathofexile.com/developer/docs/reference#currencyexchange-list */ async getCurrencyExchange(t, e) { let s = "/currency-exchange"; return t && (s += `/${t}`), e && (s += `/${e}`), (await this.client.get(s)).data; } // Item Filter endpoints /** * List item filters on the account. * @see https://www.pathofexile.com/developer/docs/reference#itemfilters-list */ async getItemFilters() { return (await this.client.get("/item-filter")).data; } /** * Get an item filter by id. * @see https://www.pathofexile.com/developer/docs/reference#itemfilters-get */ async getItemFilter(t) { return (await this.client.get(`/item-filter/${t}`)).data; } /** * Create an item filter. Optionally validate against current game version. * @see https://www.pathofexile.com/developer/docs/reference#itemfilters-post */ async createItemFilter(t, e) { return (await this.client.post("/item-filter", t, { params: e?.validate ? { validate: !0 } : {} })).data; } /** * Update an item filter (partial). Optionally validate. * @see https://www.pathofexile.com/developer/docs/reference#itemfilters-patch */ async updateItemFilter(t, e, s) { return (await this.client.post(`/item-filter/${t}`, e, { params: s?.validate ? { validate: !0 } : {} })).data; } // Account Leagues (PoE1 only) /** * Get account leagues including private (PoE1 only). * @see https://www.pathofexile.com/developer/docs/reference#accountleagues-list */ async getAccountLeagues(t) { const e = t ? { realm: t } : {}; return (await this.client.get( "/account/leagues", { params: e } )).data; } // Guild Stashes (PoE1 only) /** * List guild stash tabs for a league (PoE1 only; special scope required). * @see https://www.pathofexile.com/developer/docs/reference#guildstashes-list */ async getGuildStashes(t, e) { const s = e ? `/guild/${e}/stash/${t}` : `/guild/stash/${t}`; return (await this.client.get(s)).data; } /** * Get a guild stash or substash (PoE1 only; special scope required). * @see https://www.pathofexile.com/developer/docs/reference#guildstashes-get */ async getGuildStash(t, e, s, i) { const r = i ? `/guild/${i}/stash/${t}` : `/guild/stash/${t}`, a = s ? `${r}/${e}/${s}` : `${r}/${e}`; return (await this.client.get(a)).data; } } class _ { constructor(t) { if (t.userAgent && (!t.userAgent.startsWith("OAuth ") || !/\(contact: .+\)/.test(t.userAgent))) throw new Error( 'User-Agent must start with "OAuth " and include a contact: e.g. "OAuth myapp/1.0.0 (contact: you@example.com)"' ); this.client = d.create({ baseURL: t.baseURL || "https://www.pathofexile.com/api/trade2", headers: { "Content-Type": "application/json", "User-Agent": t.userAgent || "OAuth poe-js-sdk/1.0.0 (contact: change-me@example.com)", Cookie: `POESESSID=${t.poesessid}`, Accept: "*/*", Connection: "keep-alive" } }); } /** * Search for items using trade search query * @param league League name (e.g., 'Standard', 'Hardcore') * @param query Trade search query * @param realm Realm ('pc', 'xbox', 'sony', 'poe2') * @see https://www.pathofexile.com/trade/search */ async search(t, e, s = "pc") { const i = `/search/${s}/${t}`; return (await this.client.post(i, e)).data; } /** * Fetch item details by result IDs * @param resultIds Array of result IDs from search response * @param queryId Query ID from search response * @param realm Realm ('pc', 'xbox', 'sony', 'poe2') * @see https://www.pathofexile.com/trade/fetch */ async fetch(t, e) { const i = `/fetch/${t.slice(0, 10).join(",")}?query=${e}`; return (await this.client.get(i)).data; } /** * Search and fetch items in one call * @param league League name * @param query Trade search query * @param limit Maximum number of items to fetch (default: 10) * @param realm Realm ('pc', 'xbox', 'sony', 'poe2') */ async searchAndFetch(t, e, s = 10, i = "pc") { const r = await this.search(t, e, i); if (r.result.length === 0) return { search: r, items: { result: [] } }; const a = await this.fetch( r.result.slice(0, s), r.id ); return { search: r, items: a }; } /** * Get whisper message for an item * @param itemId Item ID * @param queryId Query ID * @param realm Realm ('pc', 'xbox', 'sony', 'poe2') * @see https://www.pathofexile.com/trade/whisper */ async getWhisper(t, e) { const s = `/whisper/${t}?query=${e}`; return (await this.client.get(s)).data; } } const h = {}, l = class l { /** * Generate PKCE code verifier and code challenge (S256). */ static generatePKCE() { const t = h.randomBytes(32).toString("base64url"), e = h.createHash("sha256").update(t).digest("base64url"); return { codeVerifier: t, codeChallenge: e }; } /** * Build authorization URL for the Authorization Code flow (supports PKCE). * @param config Client configuration * @param state Opaque anti-CSRF token * @param pkce Optional PKCE values (recommended for public clients) * @see https://www.pathofexile.com/developer/docs/authorization#authorization_code */ static buildAuthUrl(t, e, s) { const i = new URLSearchParams({ client_id: t.clientId, response_type: "code", redirect_uri: t.redirectUri, scope: t.scopes.join(" "), state: e }); return s && (i.append("code_challenge", s.codeChallenge), i.append("code_challenge_method", "S256")), `${this.AUTH_URL}?${i.toString()}`; } /** * Exchange authorization code for an access token. * @see https://www.pathofexile.com/developer/docs/authorization#tokens */ static async exchangeCodeForToken(t, e, s) { const i = new URLSearchParams({ client_id: t.clientId, grant_type: "authorization_code", code: e, redirect_uri: t.redirectUri }); t.clientSecret && i.append("client_secret", t.clientSecret), s && i.append("code_verifier", s); const r = await fetch(this.TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: i }); if (!r.ok) throw new Error(`Token exchange failed: ${r.statusText}`); return r.json(); } /** * Refresh an access token using a refresh token. * @see https://www.pathofexile.com/developer/docs/authorization#tokens */ static async refreshToken(t, e) { const s = new URLSearchParams({ client_id: t.clientId, grant_type: "refresh_token", refresh_token: e }); t.clientSecret && s.append("client_secret", t.clientSecret); const i = await fetch(this.TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: s }); if (!i.ok) throw new Error(`Token refresh failed: ${i.statusText}`); return i.json(); } /** * Client Credentials grant for service scopes (confidential clients only). * Note: Public clients cannot request `service:*` scopes. * @see https://www.pathofexile.com/developer/docs/authorization#client_credentials */ static async getClientCredentialsToken(t) { if (!t.clientSecret) throw new Error("Client secret is required for client_credentials grant"); const e = new URLSearchParams({ client_id: t.clientId, client_secret: t.clientSecret, grant_type: "client_credentials", scope: t.scopes.join(" ") }), s = await fetch(this.TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: e }); if (!s.ok) throw new Error(`Client credentials failed: ${s.statusText}`); return s.json(); } }; l.AUTH_URL = "https://www.pathofexile.com/oauth/authorize", l.TOKEN_URL = "https://www.pathofexile.com/oauth/token"; let p = l; class g { constructor() { this.query = { query: { stats: [], status: { option: "online" }, filters: {} } }; } /** * Set online status filter */ onlineOnly(t = !0) { return this.query.query.status = { option: t ? "online" : "any" }, this; } /** * Add item category filter */ category(t) { return this.query.query.filters?.type_filters || (this.query.query.filters.type_filters = { filters: {} }), this.query.query.filters.type_filters.filters.category = { option: t }, this; } /** * Add item rarity filter */ rarity(t) { return this.query.query.filters?.type_filters || (this.query.query.filters.type_filters = { filters: {} }), this.query.query.filters.type_filters.filters.rarity = { option: t }, this; } /** * Add price filter */ price(t, e, s) { return this.query.query.filters?.trade_filters || (this.query.query.filters.trade_filters = { filters: {} }), this.query.query.filters.trade_filters.filters.price = { option: t, ...e !== void 0 && { min: e }, ...s !== void 0 && { max: s } }, this; } /** * Add account name filter */ account(t) { return this.query.query.filters?.trade_filters || (this.query.query.filters.trade_filters = { filters: {} }), this.query.query.filters.trade_filters.filters.account = { input: t }, this; } /** * Add stat filters with AND logic */ andStats(t) { const e = { type: "and", filters: t.map((s) => ({ id: s.id, value: { ...s.min !== void 0 && { min: s.min }, ...s.max !== void 0 && { max: s.max } }, disabled: !1 })) }; return this.query.query.stats.push(e), this; } /** * Add stat filters with OR logic */ orStats(t) { const e = { type: "or", filters: t.map((s) => ({ id: s.id, value: { ...s.min !== void 0 && { min: s.min }, ...s.max !== void 0 && { max: s.max } }, disabled: !1 })) }; return this.query.query.stats.push(e), this; } /** * Add weighted stat filters */ weightedStats(t) { const e = { type: "weight2", filters: t.map((s) => ({ id: s.id, value: { weight: s.weight }, disabled: !1 })) }; return this.query.query.stats.push(e), this; } /** * Build the final query */ build() { return { ...this.query }; } /** * Reset the builder */ reset() { return this.query = { query: { stats: [], status: { option: "online" }, filters: {} } }, this; } } const E = { // Weapons WEAPON_ONE_HAND_SWORD: "weapon.onesword", WEAPON_TWO_HAND_SWORD: "weapon.twosword", WEAPON_ONE_HAND_AXE: "weapon.oneaxe", WEAPON_TWO_HAND_AXE: "weapon.twoaxe", WEAPON_ONE_HAND_MACE: "weapon.onemace", WEAPON_TWO_HAND_MACE: "weapon.twomace", WEAPON_BOW: "weapon.bow", WEAPON_STAFF: "weapon.staff", WEAPON_WAND: "weapon.wand", WEAPON_DAGGER: "weapon.dagger", WEAPON_CLAW: "weapon.claw", // Armour ARMOUR_HELMET: "armour.helmet", ARMOUR_CHEST: "armour.chest", ARMOUR_BOOTS: "armour.boots", ARMOUR_GLOVES: "armour.gloves", ARMOUR_SHIELD: "armour.shield", // Accessories ACCESSORY_RING: "accessory.ring", ACCESSORY_AMULET: "accessory.amulet", ACCESSORY_BELT: "accessory.belt", // Gems GEM_SKILL: "gem.activegem", GEM_SUPPORT: "gem.supportgem", // Currency CURRENCY: "currency" }, A = { CHAOS: "chaos", EXALTED: "exalted", DIVINE: "divine", MIRROR: "mirror", ANCIENT: "ancient", CHROMATIC: "chromatic", JEWELLER: "jeweller", FUSING: "fusing", ALCHEMY: "alchemy", CHISEL: "chisel" }, w = { // PoE2 specific categories CROSSBOW: "weapon.crossbow", FOCUS: "armour.focus", SPEAR: "weapon.spear", FLAIL: "weapon.flail", BUCKLER: "armour.buckler", RUNE_DAGGER: "weapon.runedagger", WARSTAFF: "weapon.warstaff", // Enhanced PoE1 categories CLUSTER_JEWEL: "jewel.cluster", ABYSS_JEWEL: "jewel.abyss", HEIST_BLUEPRINT: "heistmission.blueprint", HEIST_CONTRACT: "heistmission.contract", HEIST_TOOL: "heistequipment.heisttool", HEIST_BROOCH: "heistequipment.heistreward", HEIST_GEAR: "heistequipment.heistweapon", HEIST_CLOAK: "heistequipment.heistutility", TRINKET: "accessory.trinket", SANCTUM_RELIC: "sanctum.relic", TINCTURE: "tincture", CHARM: "azmeri.charm" }, R = { // PoE2 currencies GREATER_TRANSMUTE: "greater-orb-of-transmutation", PERFECT_TRANSMUTE: "perfect-orb-of-transmutation", GREATER_AUG: "greater-orb-of-augmentation", PERFECT_AUG: "perfect-orb-of-augmentation", GREATER_CHAOS: "greater-chaos-orb", PERFECT_CHAOS: "perfect-chaos-orb", // Standard currencies CHAOS: "chaos", EXALTED: "exalted", DIVINE: "divine", MIRROR: "mirror", ANCIENT: "ancient", CHROMATIC: "chromatic", JEWELLER: "jeweller", FUSING: "fusing", ALCHEMY: "alchemy", CHISEL: "chisel" }, S = { // Life and ES LIFE: "explicit.stat_3299347043", ENERGY_SHIELD: "explicit.stat_2901986750", MANA: "explicit.stat_1050105434", // Resistances FIRE_RES: "explicit.stat_4220027924", COLD_RES: "explicit.stat_3441501978", LIGHTNING_RES: "explicit.stat_1671376347", CHAOS_RES: "explicit.stat_2923486259", ALL_RES: "explicit.stat_3372524247", // Damage ADDED_PHYS_DAMAGE: "explicit.stat_1940865751", INCREASED_PHYS_DAMAGE: "explicit.stat_1509134228", ADDED_FIRE_DAMAGE: "explicit.stat_1334060246", ADDED_COLD_DAMAGE: "explicit.stat_2387423236", ADDED_LIGHTNING_DAMAGE: "explicit.stat_1754445556", // Attack/Cast Speed ATTACK_SPEED: "explicit.stat_681332047", CAST_SPEED: "explicit.stat_2891184298", // Critical CRIT_CHANCE: "explicit.stat_587431675", CRIT_MULTI: "explicit.stat_3556824919", // Movement MOVEMENT_SPEED: "explicit.stat_2250533757" }; function q(n) { const t = []; for (const e of n) { if (!e) continue; if (t.length === 0) { t.push({ ...e, listedTimes: 1 }); continue; } const s = t.find( (i, r) => i.listing.account.name === e.listing.account.name && i.listing.price.currency === e.listing.price.currency && i.listing.price.amount === e.listing.price.amount || i.listing.account.name === e.listing.account.name && t.length - r <= 2 // Last or previous listing ); s ? (s.listedTimes += 1, s.priceRange || (s.priceRange = { min: s.listing.price.amount, max: s.listing.price.amount }), s.priceRange.min = Math.min( s.priceRange.min, e.listing.price.amount ), s.priceRange.max = Math.max( s.priceRange.max, e.listing.price.amount ), s.averagePrice = (s.priceRange.min + s.priceRange.max) / 2) : t.push({ ...e, listedTimes: 1 }); } return t; } class T { constructor() { this.query = { query: { stats: [], status: { option: "online" }, filters: {} } }; } /** * Add pseudo stats (calculated stats like total resistance) */ pseudoStats(t) { const e = { type: "and", filters: t.map((s) => ({ id: `pseudo.${s.id}`, value: { ...s.min !== void 0 && { min: s.min }, ...s.max !== void 0 && { max: s.max } }, disabled: !1 })) }; return this.query.query.stats.push(e), this; } /** * Add total resistance filter (common PoE search) */ totalResistance(t) { return this.pseudoStats([ { id: "pseudo.pseudo_total_elemental_resistance", min: t } ]); } /** * Add total life + ES filter */ totalLifeES(t) { return this.pseudoStats([{ id: "pseudo.pseudo_total_life", min: t }]); } /** * Add DPS filters for weapons */ weaponDPS(t, e, s) { const i = []; return t && i.push({ id: "pseudo.pseudo_physical_dps", min: t }), e && i.push({ id: "pseudo.pseudo_elemental_dps", min: e }), s && i.push({ id: "pseudo.pseudo_total_dps", min: s }), this.pseudoStats(i); } /** * Add item level filter */ itemLevel(t, e) { return this.query.query.filters?.misc_filters || (this.query.query.filters.misc_filters = { filters: {} }), this.query.query.filters.misc_filters.filters.ilvl = { ...t !== void 0 && { min: t }, ...e !== void 0 && { max: e } }, this; } /** * Add gem level filter */ gemLevel(t, e) { return this.query.query.filters?.misc_filters || (this.query.query.filters.misc_filters = { filters: {} }), this.query.query.filters.misc_filters.filters.gem_level = { ...t !== void 0 && { min: t }, ...e !== void 0 && { max: e } }, this; } /** * Add quality filter */ quality(t, e) { return this.query.query.filters?.misc_filters || (this.query.query.filters.misc_filters = { filters: {} }), this.query.query.filters.misc_filters.filters.quality = { ...t !== void 0 && { min: t }, ...e !== void 0 && { max: e } }, this; } /** * Add corrupted filter */ corrupted(t) { return this.query.query.filters?.misc_filters || (this.query.query.filters.misc_filters = { filters: {} }), this.query.query.filters.misc_filters.filters.corrupted = { option: t ? "true" : "false" }, this; } /** * Add influenced filter (PoE1) */ influenced(t) { this.query.query.filters?.misc_filters || (this.query.query.filters.misc_filters = { filters: {} }); for (const e of t) this.query.query.filters.misc_filters.filters[e] = { option: "true" }; return this; } build() { return { ...this.query }; } } class x { constructor() { this.limits = /* @__PURE__ */ new Map(), this.limits.set("search", { requests: [], maxRequests: 5, windowMs: 5e3 }), this.limits.set("fetch", { requests: [], maxRequests: 10, windowMs: 5e3 }); } canMakeRequest(t) { const e = this.limits.get(t); if (!e) return !0; const s = Date.now(); return e.requests = e.requests.filter( (i) => s - i < e.windowMs ), e.requests.length < e.maxRequests; } recordRequest(t) { const e = this.limits.get(t); e && e.requests.push(Date.now()); } getWaitTime(t) { const e = this.limits.get(t); if (!e || e.requests.length === 0) return 0; const s = Math.min(...e.requests), i = e.windowMs - (Date.now() - s); return Math.max(0, i); } } class C { /** * @param client PoE API client * @param league League id/name * @param options Ladder parameters (realm, sort/class, limit) */ constructor(t, e, s = {}) { this.offset = 0, this.ended = !1, this.entries = [], this.total = 0, this.client = t, this.league = e, this.options = { limit: 200, ...s }; } /** Load the first ladder page, resetting state. */ async loadFirst() { this.offset = 0, this.ended = !1; const t = { offset: this.offset }; this.options.realm !== void 0 && (t.realm = this.options.realm), this.options.sort !== void 0 && (t.sort = this.options.sort), this.options.class !== void 0 && (t.class = this.options.class), this.options.limit !== void 0 && (t.limit = this.options.limit); const e = await this.client.getLeagueLadder(this.league, t); return this.entries = e.ladder.entries, this.total = e.ladder.total, this.offset = this.entries.length, this.entries.length === 0 && (this.ended = !0), e.ladder; } /** Fetch the next page of ladder entries, or undefined when no more. */ async next() { if (this.ended) return; const t = { offset: this.offset }; this.options.realm !== void 0 && (t.realm = this.options.realm), this.options.sort !== void 0 && (t.sort = this.options.sort), this.options.class !== void 0 && (t.class = this.options.class), this.options.limit !== void 0 && (t.limit = this.options.limit); const e = await this.client.getLeagueLadder(this.league, t), s = e.ladder.entries; if (!s || s.length === 0) { this.ended = !0; return; } return this.entries.push(...s), this.total = e.ladder.total, this.offset += s.length, s; } } async function* L(n, t = {}) { let e = t.startId; const s = t.idleWaitMs ?? 2e3; for (; ; ) { const i = {}; t.realm !== void 0 && (i.realm = t.realm), e !== void 0 && (i.id = e); const r = await n.getPublicStashes(i); yield r; const a = r.next_change_id === e; e = r.next_change_id, (r.stashes.length === 0 || a) && await new Promise((o) => setTimeout(o, s)); } } export { T as AdvancedTradeQueryBuilder, S as COMMON_STAT_IDS, A as Currencies, w as ENHANCED_CATEGORIES, R as ENHANCED_CURRENCIES, E as ItemCategories, C as LadderPager, p as OAuthHelper, y as PoEApiClient, _ as TradeClient, g as TradeQueryBuilder, x as TradeRateLimiter, q as groupTradeResults, L as publicStashStream };