poe-js-sdk
Version:
TypeScript SDK for the Path of Exile API
907 lines (906 loc) • 30.3 kB
JavaScript
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
};