@profplum700/etsy-v3-api-client 2.4.2 → 2.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -1
- package/README.md +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.esm.js +161 -47
- package/dist/browser.esm.js.map +1 -1
- package/dist/browser.umd.js +1 -1
- package/dist/browser.umd.js.map +1 -1
- package/dist/index.cjs +161 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +78 -41
- package/dist/index.esm.js +161 -47
- package/dist/index.esm.js.map +1 -1
- package/dist/node.cjs +161 -47
- package/dist/node.cjs.map +1 -1
- package/dist/node.esm.js +161 -47
- package/dist/node.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/browser.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).EtsyApiClient={})}(this,function(e){"use strict";class t extends Error{constructor(e,t,s,i,r){if(super(e),this._statusCode=t,this._response=s,this._retryAfter=i,this.name="EtsyApiError",this.endpoint=r,this.timestamp=new Date,this.details={statusCode:t||0,retryAfter:i,endpoint:r,timestamp:this.timestamp},s&&"object"==typeof s){const e=s;this.details.errorCode=e.error_code||e.code,this.details.field=e.field,this.details.suggestion=e.suggestion||e.message,Array.isArray(e.errors)&&(this.details.validationErrors=e.errors.map(e=>{if(e&&"object"==typeof e){const t=e;return{field:String(t.field||"unknown"),message:String(t.message||"Validation error")}}return{field:"unknown",message:String(e)}}))}this.suggestions=this.generateSuggestions(),this.docsUrl=this.generateDocsUrl()}get statusCode(){return this._statusCode}get response(){return this._response}isRetryable(){if(!this._statusCode)return!1;if(429===this._statusCode)return!0;if(this._statusCode>=500&&this._statusCode<600)return!0;return!![408,409,423,425].includes(this._statusCode)}getRetryAfter(){return this._retryAfter||null}getRateLimitReset(){return 429===this._statusCode&&this._retryAfter?new Date(Date.now()+1e3*this._retryAfter):null}generateSuggestions(){const e=[];if(!this._statusCode)return["Check your network connection and try again"];switch(this._statusCode){case 400:e.push("Review the Etsy API documentation for this endpoint"),e.push("Check all required parameters are provided"),e.push("Validate parameter formats and types"),this.details.validationErrors&&this.details.validationErrors.length>0&&(e.push("\nValidation errors:"),this.details.validationErrors.forEach(t=>{e.push(` • ${t.field}: ${t.message}`)})),this.details.field&&e.push(`Field causing issue: ${this.details.field}`);break;case 401:e.push("Verify your access token is valid and not expired"),e.push("Check if you need to refresh the token"),e.push("Ensure you completed the OAuth flow correctly"),this.endpoint?.includes("/shops/")&&e.push("Verify the shop_id matches the authenticated user");break;case 403:e.push("Check if your OAuth app has the required scopes:"),this.endpoint?.includes("/listings")&&(e.push(" • listings_r (for reading listings)"),e.push(" • listings_w (for creating/updating listings)")),(this.endpoint?.includes("/receipts")||this.endpoint?.includes("/transactions"))&&e.push(" • transactions_r (for reading orders)"),this.endpoint?.includes("/shops")&&(e.push(" • shops_r (for reading shop data)"),e.push(" • shops_w (for updating shop data)")),e.push("Verify your app is approved for production access"),e.push("Check if the resource belongs to the authenticated user");break;case 404:e.push("Verify the resource ID exists and is spelled correctly"),this.endpoint?.includes("/listings/")&&(e.push("Check if the listing is active and not deleted"),e.push("Ensure the listing belongs to the authenticated shop")),this.endpoint?.includes("/shops/")&&e.push("Verify the shop ID is correct"),this.endpoint?.includes("/receipts/")&&(e.push("Check if the receipt ID is valid"),e.push("Ensure you have access to this shop's receipts"));break;case 409:e.push("Resource state conflict detected"),e.push("Check if the resource was modified by another process"),e.push("Try fetching the latest resource state before updating");break;case 429:{const t=this.getRateLimitReset(),s=t?t.toLocaleTimeString():"shortly";e.push(`Rate limit exceeded. Resets at ${s}`),e.push("Implement exponential backoff retry logic"),e.push("Consider caching responses to reduce API calls"),e.push("Check if you can batch multiple operations"),this._retryAfter&&e.push(`Wait ${this._retryAfter} seconds before retrying`);break}case 500:case 502:case 503:case 504:e.push("This is an Etsy server error, not your code"),e.push("Retry the request after a short delay (exponential backoff)"),e.push("Check Etsy API status: https://status.etsy.com"),this.isRetryable()&&e.push("This error is retryable - the request can be safely retried");break;default:e.push("Check the Etsy API documentation for this endpoint"),e.push("Review your request parameters and format"),this.details.suggestion&&e.push(`Etsy suggestion: ${this.details.suggestion}`)}return e}generateDocsUrl(){return`https://github.com/profplum700/etsy-v3-api-client/blob/main/docs/troubleshooting/ERROR_CODES.md#${this.details.errorCode?.toLowerCase()||this._statusCode?.toString()||"unknown"}`}getUserFriendlyMessage(){let e=this.message;if(this.details.suggestion&&(e+=`\nSuggestion: ${this.details.suggestion}`),this.isRetryable()){const t=this.getRetryAfter();e+=t?`\nRetry after ${t} seconds.`:"\nThis error can be retried."}return e}toString(){const e=[`EtsyApiError: ${this.message}`,`Status Code: ${this._statusCode||"Unknown"}`];return this.details.errorCode&&e.push(`Error Code: ${this.details.errorCode}`),this.endpoint&&e.push(`Endpoint: ${this.endpoint}`),e.push(`Timestamp: ${this.timestamp.toISOString()}`),this.suggestions.length>0&&(e.push("\nSuggestions:"),this.suggestions.forEach(t=>{t.startsWith(" •")?e.push(t):e.push(` • ${t}`)})),e.push(`\nDocumentation: ${this.docsUrl}`),e.join("\n")}toJSON(){return{name:this.name,message:this.message,statusCode:this._statusCode,errorCode:this.details.errorCode,endpoint:this.endpoint,suggestions:this.suggestions,docsUrl:this.docsUrl,timestamp:this.timestamp.toISOString(),details:this.details,isRetryable:this.isRetryable()}}}class s extends Error{constructor(e,t){super(e),this._code=t,this.name="EtsyAuthError"}get code(){return this._code}}class i extends Error{constructor(e,t,s="unknown"){super(e),this._retryAfter=t,this.name="EtsyRateLimitError",this.errorType=s}get retryAfter(){return this._retryAfter}isRetryable(){return"qpd_exhausted"!==this.errorType}}const r="undefined"!=typeof window&&void 0!==window.document,n=!1,a="function"==typeof globalThis.importScripts&&"undefined"!=typeof navigator,o="undefined"!=typeof fetch,h="undefined"!=typeof crypto&&void 0!==crypto.subtle,c=(()=>{try{return"undefined"!=typeof localStorage}catch{return!1}})(),u=(()=>{try{return"undefined"!=typeof sessionStorage}catch{return!1}})();function l(){if(!o)throw new Error("Fetch API is not available. Please use Node.js 18+ or a modern browser.")}class d{constructor(){this.tokens=null}async save(e){this.tokens={...e}}async load(){return this.tokens?{...this.tokens}:null}async clear(){this.tokens=null}}class g{constructor(e,t,s){this.currentTokens=null,this.keystring=e.keystring,this.refreshCallback=e.refreshSave,this.storage=t,this.rotationConfig=s,this.currentTokens={access_token:e.accessToken,refresh_token:e.refreshToken,expires_at:e.expiresAt,token_type:"Bearer",scope:""},this.rotationConfig?.enabled&&this.rotationConfig?.autoSchedule&&this.startRotationScheduler()}async getAccessToken(){if(!this.currentTokens&&(this.storage&&(this.currentTokens=await this.storage.load()),!this.currentTokens))throw new s("No tokens available","NO_TOKENS");const e=new Date,t=new Date(this.currentTokens.expires_at);return e.getTime()>=t.getTime()-6e4&&await this.refreshToken(),this.currentTokens.access_token}async refreshToken(){if(this.refreshPromise)return this.refreshPromise;if(!this.currentTokens)throw new s("No tokens available to refresh","NO_REFRESH_TOKEN");this.refreshPromise=this.performTokenRefresh();try{const e=await this.refreshPromise;return this.currentTokens=e,this.storage&&await this.storage.save(e),this.refreshCallback&&this.refreshCallback(e.access_token,e.refresh_token,e.expires_at),e}finally{this.refreshPromise=void 0}}async performTokenRefresh(){if(!this.currentTokens)throw new s("No tokens available","NO_TOKENS");const e=new URLSearchParams({grant_type:"refresh_token",client_id:this.keystring,refresh_token:this.currentTokens.refresh_token});try{const t=await this.fetch("https://api.etsy.com/v3/public/oauth/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:e.toString()});if(!t.ok)throw new s(`Token refresh failed: ${t.status} ${t.statusText}`,"TOKEN_REFRESH_FAILED");const i=await t.json();return{access_token:i.access_token,refresh_token:i.refresh_token,expires_at:new Date(Date.now()+1e3*i.expires_in),token_type:i.token_type,scope:i.scope}}catch(e){if(e instanceof s)throw e;throw new s(`Token refresh failed: ${e instanceof Error?e.message:"Unknown error"}`,"TOKEN_REFRESH_ERROR")}}getCurrentTokens(){return this.currentTokens?{...this.currentTokens}:null}updateTokens(e){this.currentTokens={...e}}isTokenExpired(){if(!this.currentTokens)return!0;const e=new Date,t=new Date(this.currentTokens.expires_at);return e.getTime()>=t.getTime()}willTokenExpireSoon(e=5){if(!this.currentTokens)return!0;const t=new Date,s=new Date(this.currentTokens.expires_at),i=60*e*1e3;return t.getTime()>=s.getTime()-i}async clearTokens(){this.currentTokens=null,this.storage&&await this.storage.clear()}getTimeUntilExpiration(){if(!this.currentTokens)return null;const e=new Date;return new Date(this.currentTokens.expires_at).getTime()-e.getTime()}needsProactiveRotation(){if(!this.rotationConfig?.enabled||!this.currentTokens)return!1;const e=this.getTimeUntilExpiration();if(null===e)return!1;return e<=(this.rotationConfig.rotateBeforeExpiry||9e5)}async rotateToken(){if(!this.currentTokens)throw new s("No tokens available to rotate","NO_TOKENS");const e={...this.currentTokens},t=await this.refreshToken();if(this.rotationConfig?.onRotation)try{await Promise.resolve(this.rotationConfig.onRotation(e,t))}catch(e){console.error("Token rotation callback failed:",e)}return t}startRotationScheduler(){if(!this.rotationConfig?.enabled)throw new Error("Token rotation is not enabled");this.stopRotationScheduler();const e=this.rotationConfig.checkInterval||6e4;this.rotationTimer=setInterval(async()=>{try{this.needsProactiveRotation()&&(console.log("Proactively rotating token before expiration"),await this.rotateToken())}catch(e){console.error("Failed to proactively rotate token:",e)}},e),this.rotationTimer.unref&&this.rotationTimer.unref()}stopRotationScheduler(){this.rotationTimer&&(clearInterval(this.rotationTimer),this.rotationTimer=void 0)}updateRotationConfig(e){const t=this.rotationConfig?.autoSchedule;this.rotationConfig=e,e.enabled&&e.autoSchedule&&!t?this.startRotationScheduler():e.enabled&&e.autoSchedule||this.stopRotationScheduler()}getRotationConfig(){return this.rotationConfig?{...this.rotationConfig}:void 0}async fetch(e,t){return l(),fetch(e,t)}}class m{constructor(e="etsy_tokens"){if(!c)throw new Error("localStorage is not available in this environment");this.storageKey=e}async save(e){const t=JSON.stringify(e);localStorage.setItem(this.storageKey,t)}async load(){try{const e=localStorage.getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t.expires_at&&(t.expires_at=new Date(t.expires_at)),t}catch{return null}}async clear(){localStorage.removeItem(this.storageKey)}}class p{constructor(e="etsy_tokens"){if(!u)throw new Error("sessionStorage is not available in this environment");this.storageKey=e}async save(e){const t=JSON.stringify(e);sessionStorage.setItem(this.storageKey,t)}async load(){try{const e=sessionStorage.getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t.expires_at&&(t.expires_at=new Date(t.expires_at)),t}catch{return null}}async clear(){sessionStorage.removeItem(this.storageKey)}}class f{constructor(e){this.requestCount=0,this.dailyReset=new Date,this.lastRequestTime=0,this.isHeaderBasedLimiting=!1,this.currentRetryCount=0,this.config={maxRequestsPerDay:1e4,maxRequestsPerSecond:10,minRequestInterval:100,maxRetries:3,baseDelayMs:1e3,maxDelayMs:3e4,jitter:.1,qpdWarningThreshold:80,onApproachingLimit:void 0,...e},this.setNextDailyReset()}setNextDailyReset(){const e=new Date;this.dailyReset=new Date(e),this.dailyReset.setUTCDate(this.dailyReset.getUTCDate()+1),this.dailyReset.setUTCHours(0,0,0,0)}updateFromHeaders(e){if(!e)return;const t=this.parseRateLimitHeaders(e);void 0!==t.limitPerSecond&&(this.headerLimitPerSecond=t.limitPerSecond,this.isHeaderBasedLimiting=!0),void 0!==t.remainingThisSecond&&(this.headerRemainingThisSecond=t.remainingThisSecond),void 0!==t.limitPerDay&&(this.headerLimitPerDay=t.limitPerDay,this.isHeaderBasedLimiting=!0),void 0!==t.remainingToday&&(this.headerRemainingToday=t.remainingToday,this.checkApproachingLimit())}parseRateLimitHeaders(e){const t=t=>{if(e instanceof Headers)return e.get(t);const s=t.toLowerCase();for(const t of Object.keys(e))if(t.toLowerCase()===s)return e[t]??null;return null},s=e=>{if(null===e)return;const t=parseInt(e,10);return isNaN(t)?void 0:t};return{limitPerSecond:s(t("x-limit-per-second")),remainingThisSecond:s(t("x-remaining-this-second")),limitPerDay:s(t("x-limit-per-day")),remainingToday:s(t("x-remaining-today")),retryAfter:s(t("retry-after"))}}checkApproachingLimit(){if(!this.config.onApproachingLimit)return;const e=this.headerLimitPerDay??this.config.maxRequestsPerDay,t=this.headerRemainingToday??this.getRemainingRequests(),s=(e-t)/e*100;s>=this.config.qpdWarningThreshold&&this.config.onApproachingLimit(t,e,s)}async handleRateLimitResponse(e){const t=e?this.parseRateLimitHeaders(e):{};if(this.updateFromHeaders(e),0===this.headerRemainingToday)throw new i("Daily rate limit exhausted. No requests remaining until limit resets.",t.retryAfter,"qpd_exhausted");if(this.currentRetryCount++,this.currentRetryCount>this.config.maxRetries)throw this.currentRetryCount=0,new i(`Max retries (${this.config.maxRetries}) exceeded for rate limit`,t.retryAfter,"qps_exhausted");return{shouldRetry:!0,delayMs:this.calculateBackoffDelay(this.currentRetryCount,t.retryAfter)}}calculateBackoffDelay(e,t){const s=t?1e3*t:0,i=this.config.baseDelayMs*Math.pow(2,e-1);let r=Math.min(i,this.config.maxDelayMs);if(r=Math.max(r,s),this.config.jitter>0){const e=r*this.config.jitter;r+=Math.random()*e*2-e}return Math.max(0,Math.floor(r))}resetRetryCount(){this.currentRetryCount=0}setApproachingLimitCallback(e){this.config.onApproachingLimit=e}setWarningThreshold(e){this.config.qpdWarningThreshold=e}getEffectiveMinInterval(){return void 0!==this.headerLimitPerSecond&&this.headerLimitPerSecond>0?Math.ceil(1e3/this.headerLimitPerSecond):this.config.minRequestInterval}async waitForRateLimit(){const e=Date.now();e>=this.dailyReset.getTime()&&(this.requestCount=0,this.setNextDailyReset());const t=this.headerLimitPerDay??this.config.maxRequestsPerDay;if((this.headerRemainingToday??t-this.requestCount)<=0){const s=this.dailyReset.getTime()-e;throw new i(`Daily rate limit exhausted (${t} requests). Reset in approximately ${Math.ceil(s/1e3/60)} minutes.`,Math.ceil(s/1e3),"qpd_exhausted")}const s=this.getEffectiveMinInterval(),r=e-this.lastRequestTime;if(r<s){const e=s-r;await new Promise(t=>setTimeout(t,e))}this.requestCount++,this.lastRequestTime=Date.now()}getRateLimitStatus(){const e=Date.now();e>=this.dailyReset.getTime()&&(this.requestCount=0,this.setNextDailyReset());const t=this.headerRemainingToday??Math.max(0,this.config.maxRequestsPerDay-this.requestCount);return{remainingRequests:t,resetTime:this.dailyReset,canMakeRequest:t>0&&e-this.lastRequestTime>=this.getEffectiveMinInterval(),isFromHeaders:this.isHeaderBasedLimiting,limitPerSecond:this.headerLimitPerSecond,remainingThisSecond:this.headerRemainingThisSecond,limitPerDay:this.headerLimitPerDay}}getRemainingRequests(){return this.headerRemainingToday??Math.max(0,this.config.maxRequestsPerDay-this.requestCount)}reset(){this.requestCount=0,this.lastRequestTime=0,this.currentRetryCount=0,this.headerLimitPerSecond=void 0,this.headerRemainingThisSecond=void 0,this.headerLimitPerDay=void 0,this.headerRemainingToday=void 0,this.isHeaderBasedLimiting=!1,this.setNextDailyReset()}canMakeRequest(){const e=Date.now();e>=this.dailyReset.getTime()&&(this.requestCount=0,this.setNextDailyReset());return!((this.headerRemainingToday??this.config.maxRequestsPerDay-this.requestCount)<=0)&&e-this.lastRequestTime>=this.getEffectiveMinInterval()}getTimeUntilNextRequest(){const e=Date.now()-this.lastRequestTime,t=this.getEffectiveMinInterval();return e>=t?0:t-e}getConfig(){return{...this.config}}}const y=new f;class w{constructor(e={}){this.concurrency=e.concurrency??5,this.stopOnError=e.stopOnError??!1,this.onProgress=e.onProgress,this.onItemComplete=e.onItemComplete,this.onItemError=e.onItemError}async executeBulk(e,t,s){const i=Date.now(),r=[],n=[];let a=0,o=0,h=0;const c=e.map((e,t)=>({item:e,index:t,id:s?s(e,t):t})),u=[];for(let s=0;s<Math.min(this.concurrency,e.length);s++)u.push(this.worker(c,t,r,n,()=>{a++;const t=r[r.length-1];t?.success?o++:h++,this.onProgress&&this.onProgress(a,e.length,t)}));await Promise.all(u);const l=Date.now()-i;return{total:e.length,successful:o,failed:h,results:r,errors:n,duration:l}}async worker(e,t,s,i,r){for(;e.length>0;){const n=e.shift();if(!n)break;const{item:a,index:o,id:h}=n;try{const e={success:!0,data:await t(a,o),id:h,index:o};s.push(e),this.onItemComplete&&this.onItemComplete(e),r()}catch(t){const n=t instanceof Error?t:new Error(String(t)),a={success:!1,error:n,id:h,index:o};s.push(a);const c={id:h,index:o,error:n};if(i.push(c),this.onItemError&&this.onItemError(c),r(),this.stopOnError){e.length=0;break}}}}setConcurrency(e){this.concurrency=Math.max(1,e)}getConcurrency(){return this.concurrency}}class S{constructor(){this.validators=[]}rule(e){return this.validators.push(e),this}validate(e){const t=[];for(const s of this.validators){const i=s(e);i&&t.push(i)}return{valid:0===t.length,errors:t}}}class R{constructor(e){this.field=e}required(e){return t=>{if("object"!=typeof t||null===t)return null;const s=t[this.field];return null==s||""===s?{field:this.field,message:e||`${this.field} is required`,value:s}:null}}string(e){return t=>{if("object"!=typeof t||null===t)return null;const s=t[this.field];return null==s?null:"string"!=typeof s?{field:this.field,message:e.message||`${this.field} must be a string`,value:s}:void 0!==e.min&&s.length<e.min?{field:this.field,message:e.message||`${this.field} must be at least ${e.min} characters`,value:s}:void 0!==e.max&&s.length>e.max?{field:this.field,message:e.message||`${this.field} must be at most ${e.max} characters`,value:s}:e.pattern&&!e.pattern.test(s)?{field:this.field,message:e.message||`${this.field} has invalid format`,value:s}:null}}number(e){return t=>{if("object"!=typeof t||null===t)return null;const s=t[this.field];return null==s?null:"number"!=typeof s||isNaN(s)?{field:this.field,message:e.message||`${this.field} must be a number`,value:s}:e.integer&&!Number.isInteger(s)?{field:this.field,message:e.message||`${this.field} must be an integer`,value:s}:e.positive&&s<=0?{field:this.field,message:e.message||`${this.field} must be positive`,value:s}:void 0!==e.min&&s<e.min?{field:this.field,message:e.message||`${this.field} must be at least ${e.min}`,value:s}:void 0!==e.max&&s>e.max?{field:this.field,message:e.message||`${this.field} must be at most ${e.max}`,value:s}:null}}enum(e,t){return s=>{if("object"!=typeof s||null===s)return null;const i=s[this.field];return null==i||e.includes(i)?null:{field:this.field,message:t||`${this.field} must be one of: ${e.join(", ")}`,value:i}}}array(e){return t=>{if("object"!=typeof t||null===t)return null;const s=t[this.field];if(null==s)return null;if(!Array.isArray(s))return{field:this.field,message:e.message||`${this.field} must be an array`,value:s};if(void 0!==e.min&&s.length<e.min)return{field:this.field,message:e.message||`${this.field} must have at least ${e.min} items`,value:s};if(void 0!==e.max&&s.length>e.max)return{field:this.field,message:e.message||`${this.field} must have at most ${e.max} items`,value:s};if(e.itemValidator)for(let t=0;t<s.length;t++)if(!e.itemValidator(s[t]))return{field:this.field,message:e.message||`${this.field}[${t}] has invalid value`,value:s[t]};return null}}}function v(e){return new R(e)}const k=(new S).rule(v("quantity").required()).rule(v("quantity").number({min:1,integer:!0,message:"quantity must be a positive integer"})).rule(v("title").required()).rule(v("title").string({min:1,max:140,message:"title must be 1-140 characters"})).rule(v("description").string({max:65535,message:"description must be less than 65535 characters"})).rule(v("price").required()).rule(v("price").number({min:.2,max:5e4,message:"price must be between 0.20 and 50000.00"})).rule(v("who_made").enum(["i_did","someone_else","collective"],"who_made must be one of: i_did, someone_else, collective")).rule(v("when_made").enum(["made_to_order","2020_2024","2010_2019","2005_2009","2000_2004","1990s","1980s","1970s","1960s","1950s","1940s","1930s","1920s","1910s","1900s","1800s","1700s","before_1700"])).rule(v("taxonomy_id").required()).rule(v("taxonomy_id").number({integer:!0,positive:!0,message:"taxonomy_id must be a positive integer"})),E=(new S).rule(e=>"object"==typeof e&&null!==e&&"title"in e&&void 0!==e.title?v("title").string({min:1,max:140,message:"title must be 1-140 characters"})(e):null).rule(e=>"object"==typeof e&&null!==e&&"description"in e&&void 0!==e.description?v("description").string({max:65535,message:"description must be less than 65535 characters"})(e):null).rule(e=>void 0!==e.tags?v("tags").array({max:13,message:"tags must have at most 13 items"})(e):null).rule(e=>void 0!==e.materials?v("materials").array({max:13,message:"materials must have at most 13 items"})(e):null),_=(new S).rule(e=>"object"==typeof e&&null!==e&&"title"in e&&void 0!==e.title?v("title").string({min:1,max:55,message:"shop title must be 1-55 characters"})(e):null).rule(e=>"object"==typeof e&&null!==e&&"announcement"in e&&void 0!==e.announcement?v("announcement").string({max:5e3,message:"announcement must be less than 5000 characters"})(e):null);class T extends Error{constructor(e,t){super(e),this.name="ValidationException",this.errors=t}}function x(e,t,s="Validation failed"){const i=t.validate(e);if(!i.valid)throw new T(s,i.errors)}class b{debug(e,...t){("localhost"===window.location?.hostname||"127.0.0.1"===window.location?.hostname)&&console.log(`[DEBUG] ${e}`,...t)}info(e,...t){console.log(`[INFO] ${e}`,...t)}warn(e,...t){console.warn(`[WARN] ${e}`,...t)}error(e,...t){console.error(`[ERROR] ${e}`,...t)}}class C{constructor(){this.cache=new Map}async get(e){const t=this.cache.get(e);return t?Date.now()>t.expires?(this.cache.delete(e),null):t.value:null}async set(e,t,s=3600){const i=Date.now()+1e3*s;this.cache.set(e,{value:t,expires:i})}async delete(e){this.cache.delete(e)}async clear(){this.cache.clear()}}class A{constructor(e){this.tokenManager=new g(e),this.baseUrl=e.baseUrl||"https://api.etsy.com/v3/application",this.logger=new b,this.keystring=e.keystring,this.sharedSecret=e.sharedSecret,!1!==e.rateLimiting?.enabled?this.rateLimiter=new f({maxRequestsPerDay:e.rateLimiting?.maxRequestsPerDay||1e4,maxRequestsPerSecond:e.rateLimiting?.maxRequestsPerSecond||10,minRequestInterval:e.rateLimiting?.minRequestInterval??100,maxRetries:e.rateLimiting?.maxRetries,baseDelayMs:e.rateLimiting?.baseDelayMs,maxDelayMs:e.rateLimiting?.maxDelayMs,jitter:e.rateLimiting?.jitter,qpdWarningThreshold:e.rateLimiting?.qpdWarningThreshold,onApproachingLimit:e.rateLimiting?.onApproachingLimit}):this.rateLimiter=new f(void 0),!1!==e.caching?.enabled&&(this.cache=e.caching?.storage||new C,this.cacheTtl=e.caching?.ttl||3600),this.bulkOperationManager=new w({concurrency:5,stopOnError:!1})}async makeRequest(e,t={},s=!0){const i=`${this.baseUrl}${e}`,r={method:"GET",...t},n=`${i}:${JSON.stringify(r)}`;if(s&&this.cache&&"GET"===r.method){const e=await this.cache.get(n);if(e)return JSON.parse(e)}await this.rateLimiter.waitForRateLimit();const a={Authorization:`Bearer ${await this.tokenManager.getAccessToken()}`,"x-api-key":this.getApiKey(),"Content-Type":"application/json",Accept:"application/json",...r.headers};return this.executeWithRetry(i,{...r,headers:a},n,s)}async executeWithRetry(e,r,n,a){for(;;)try{const s=await this.fetch(e,r);if(429===s.status){const{shouldRetry:e,delayMs:t}=await this.rateLimiter.handleRateLimitResponse(s.headers);if(e){this.logger.warn(`Rate limited. Retrying in ${t}ms...`),await this.sleep(t);continue}}if(!s.ok){const e=await s.text();throw new t(`Etsy API error: ${s.status} ${s.statusText}`,s.status,e)}if(this.rateLimiter.updateFromHeaders(s.headers),this.rateLimiter.resetRetryCount(),204===s.status)return;const i=s.headers?.get?.("content-length");if("0"===i)return;const o=await s.json();return a&&this.cache&&"GET"===r.method&&await this.cache.set(n,JSON.stringify(o),this.cacheTtl),o}catch(e){if(e instanceof i)throw e;if(e instanceof t||e instanceof s)throw e;throw new t(`Request failed: ${e instanceof Error?e.message:"Unknown error"}`,0,e)}}sleep(e){return new Promise(t=>setTimeout(t,e))}getApiKey(){return this.sharedSecret?`${this.keystring}:${this.sharedSecret}`:this.keystring}async getUser(){return this.makeRequest("/users/me")}async getShop(e){if(e)return this.makeRequest(`/shops/${e}`);const s=await this.getUser();if(!s.shop_id)throw new t("User does not have a shop",404);return this.makeRequest(`/shops/${s.shop_id}`)}async getShopByOwnerUserId(e){return this.makeRequest(`/users/${e}/shops`)}async getShopSections(e){let s=e;if(!s){const e=await this.getUser();if(!e.shop_id)throw new t("User does not have a shop",404);s=e.shop_id.toString()}const i=await this.makeRequest(`/shops/${s}/sections`);return this.logger.info(`Found ${i.results.length} shop sections`),i.results}async getShopSection(e,t){return this.makeRequest(`/shops/${e}/sections/${t}`)}async getListingsByShop(e,s={}){let i=e;if(!i){const e=await this.getUser();if(!e.shop_id)throw new t("User does not have a shop",404);i=e.shop_id.toString()}const r=new URLSearchParams;r.set("state",s.state||"active"),void 0!==s.limit&&r.set("limit",s.limit.toString()),void 0!==s.offset&&r.set("offset",s.offset.toString()),s.sort_on&&r.set("sort_on",s.sort_on),s.sort_order&&r.set("sort_order",s.sort_order),s.includes&&r.set("includes",s.includes.join(","));return(await this.makeRequest(`/shops/${i}/listings?${r.toString()}`)).results}async getListing(e,t){const s=t?`?includes=${t.join(",")}`:"";return this.makeRequest(`/listings/${e}${s}`)}async findAllListingsActive(e={}){const t=new URLSearchParams;e.keywords&&t.set("keywords",e.keywords),e.category&&t.set("category",e.category),void 0!==e.limit&&t.set("limit",e.limit.toString()),void 0!==e.offset&&t.set("offset",e.offset.toString()),e.sort_on&&t.set("sort_on",e.sort_on),e.sort_order&&t.set("sort_order",e.sort_order),void 0!==e.min_price&&t.set("min_price",e.min_price.toString()),void 0!==e.max_price&&t.set("max_price",e.max_price.toString()),e.tags&&t.set("tags",e.tags.join(",")),e.location&&t.set("location",e.location),e.shop_location&&t.set("shop_location",e.shop_location);return(await this.makeRequest(`/listings/active?${t.toString()}`)).results}async getListingImages(e){return(await this.makeRequest(`/listings/${e}/images`)).results}async getListingInventory(e){return this.makeRequest(`/listings/${e}/inventory`)}async getReviewsByListing(e,t={}){const s=new URLSearchParams;void 0!==t.limit&&s.set("limit",t.limit.toString()),void 0!==t.offset&&s.set("offset",t.offset.toString()),void 0!==t.min_created&&s.set("min_created",t.min_created.toString()),void 0!==t.max_created&&s.set("max_created",t.max_created.toString());return(await this.makeRequest(`/listings/${e}/reviews?${s.toString()}`)).results}async getReviewsByShop(e,t={}){const s=new URLSearchParams;void 0!==t.limit&&s.set("limit",t.limit.toString()),void 0!==t.offset&&s.set("offset",t.offset.toString()),void 0!==t.min_created&&s.set("min_created",t.min_created.toString()),void 0!==t.max_created&&s.set("max_created",t.max_created.toString());return(await this.makeRequest(`/shops/${e}/reviews?${s.toString()}`)).results}async getSellerTaxonomyNodes(){return(await this.makeRequest("/seller-taxonomy/nodes")).results}async getUserShops(){return(await this.makeRequest("/users/me/shops")).results||[]}async updateShop(e,t,s){if(s?.validate){const e=s.validateSchema||_;if(!1!==s.throwOnValidationError)x(t,e,"Invalid shop update parameters");else{const s=e.validate(t);s.valid||this.logger.warn("Shop update validation failed",s.errors)}}return this.makeRequest(`/shops/${e}`,{method:"PUT",body:JSON.stringify(t)},!1)}async createShopSection(e,t){return this.makeRequest(`/shops/${e}/sections`,{method:"POST",body:JSON.stringify(t)},!1)}async updateShopSection(e,t,s){return this.makeRequest(`/shops/${e}/sections/${t}`,{method:"PUT",body:JSON.stringify(s)},!1)}async deleteShopSection(e,t){await this.makeRequest(`/shops/${e}/sections/${t}`,{method:"DELETE"},!1)}async createDraftListing(e,t,s){if(s?.validate){const e=s.validateSchema||k;if(!1!==s.throwOnValidationError)x(t,e,"Invalid listing parameters");else{const s=e.validate(t);s.valid||this.logger.warn("Listing validation failed",s.errors)}}return this.makeRequest(`/shops/${e}/listings`,{method:"POST",body:JSON.stringify(t)},!1)}async updateListing(e,t,s,i){if(i?.validate){const e=i.validateSchema||E;if(!1!==i.throwOnValidationError)x(s,e,"Invalid listing update parameters");else{const t=e.validate(s);t.valid||this.logger.warn("Listing update validation failed",t.errors)}}return this.makeRequest(`/shops/${e}/listings/${t}`,{method:"PATCH",body:JSON.stringify(s)},!1)}async deleteListing(e){await this.makeRequest(`/listings/${e}`,{method:"DELETE"},!1)}async updateListingInventory(e,t){return this.makeRequest(`/listings/${e}/inventory`,{method:"PUT",body:JSON.stringify(t)},!1)}async uploadListingImage(e,s,i,r){const n=new FormData;n.append("image",i),void 0!==r?.rank&&n.append("rank",r.rank.toString()),void 0!==r?.overwrite&&n.append("overwrite",r.overwrite.toString()),void 0!==r?.is_watermarked&&n.append("is_watermarked",r.is_watermarked.toString()),r?.alt_text&&n.append("alt_text",r.alt_text);const a=`${this.baseUrl}/shops/${e}/listings/${s}/images`;await this.rateLimiter.waitForRateLimit();const o=await this.tokenManager.getAccessToken(),h=await this.fetch(a,{method:"POST",headers:{Authorization:`Bearer ${o}`,"x-api-key":this.getApiKey()},body:n});if(!h.ok){const e=await h.text();throw new t(`Failed to upload image: ${h.status} ${h.statusText}`,h.status,e)}return h.json()}async getListingImage(e,t,s){return this.makeRequest(`/shops/${e}/listings/${t}/images/${s}`)}async deleteListingImage(e,t,s){await this.makeRequest(`/shops/${e}/listings/${t}/images/${s}`,{method:"DELETE"},!1)}async bulkUpdateListings(e,t,s){return(s?new w(s):this.bulkOperationManager).executeBulk(t,async t=>this.updateListing(e,t.listingId.toString(),t.updates),e=>e.listingId)}async bulkUploadImages(e,t,s,i){return(i?new w(i):this.bulkOperationManager).executeBulk(s,async s=>this.uploadListingImage(e,t,s.file,{rank:s.rank,alt_text:s.altText}))}setBulkOperationConcurrency(e){this.bulkOperationManager.setConcurrency(Math.max(1,Math.min(10,e)))}async getShopReceipts(e,t){const s=new URLSearchParams;void 0!==t?.limit&&s.set("limit",t.limit.toString()),void 0!==t?.offset&&s.set("offset",t.offset.toString()),t?.sort_on&&s.set("sort_on",t.sort_on),t?.sort_order&&s.set("sort_order",t.sort_order),void 0!==t?.min_created&&s.set("min_created",t.min_created.toString()),void 0!==t?.max_created&&s.set("max_created",t.max_created.toString()),void 0!==t?.min_last_modified&&s.set("min_last_modified",t.min_last_modified.toString()),void 0!==t?.max_last_modified&&s.set("max_last_modified",t.max_last_modified.toString()),void 0!==t?.was_paid&&s.set("was_paid",t.was_paid.toString()),void 0!==t?.was_shipped&&s.set("was_shipped",t.was_shipped.toString()),void 0!==t?.was_delivered&&s.set("was_delivered",t.was_delivered.toString());return(await this.makeRequest(`/shops/${e}/receipts?${s.toString()}`)).results}async getShopReceipt(e,t){return this.makeRequest(`/shops/${e}/receipts/${t}`)}async updateShopReceipt(e,t,s){return this.makeRequest(`/shops/${e}/receipts/${t}`,{method:"PUT",body:JSON.stringify(s)},!1)}async getShopReceiptTransactions(e,t){return(await this.makeRequest(`/shops/${e}/receipts/${t}/transactions`)).results}async getShopTransaction(e,t){return this.makeRequest(`/shops/${e}/transactions/${t}`)}async getShopShippingProfiles(e){return(await this.makeRequest(`/shops/${e}/shipping-profiles`)).results}async createShopShippingProfile(e,t){return this.makeRequest(`/shops/${e}/shipping-profiles`,{method:"POST",body:JSON.stringify(t)},!1)}async getShopShippingProfile(e,t){return this.makeRequest(`/shops/${e}/shipping-profiles/${t}`)}async updateShopShippingProfile(e,t,s){return this.makeRequest(`/shops/${e}/shipping-profiles/${t}`,{method:"PUT",body:JSON.stringify(s)},!1)}async deleteShopShippingProfile(e,t){await this.makeRequest(`/shops/${e}/shipping-profiles/${t}`,{method:"DELETE"},!1)}async getShopShippingProfileDestinations(e,t){return(await this.makeRequest(`/shops/${e}/shipping-profiles/${t}/destinations`)).results}async createShopShippingProfileDestination(e,t,s){return this.makeRequest(`/shops/${e}/shipping-profiles/${t}/destinations`,{method:"POST",body:JSON.stringify(s)},!1)}async updateShopShippingProfileDestination(e,t,s,i){return this.makeRequest(`/shops/${e}/shipping-profiles/${t}/destinations/${s}`,{method:"PUT",body:JSON.stringify(i)},!1)}async deleteShopShippingProfileDestination(e,t,s){await this.makeRequest(`/shops/${e}/shipping-profiles/${t}/destinations/${s}`,{method:"DELETE"},!1)}async getShopShippingProfileUpgrades(e,t){return(await this.makeRequest(`/shops/${e}/shipping-profiles/${t}/upgrades`)).results}async createReceiptShipment(e,t,s){return this.makeRequest(`/shops/${e}/receipts/${t}/tracking`,{method:"POST",body:JSON.stringify(s)},!1)}async getShopReceiptShipments(e,t){return(await this.makeRequest(`/shops/${e}/receipts/${t}/shipments`)).results}async getShopPaymentAccountLedgerEntries(e,t){const s=new URLSearchParams;s.set("min_created",t.min_created.toString()),s.set("max_created",t.max_created.toString()),void 0!==t.limit&&s.set("limit",t.limit.toString()),void 0!==t.offset&&s.set("offset",t.offset.toString());return(await this.makeRequest(`/shops/${e}/payment-account/ledger-entries?${s.toString()}`)).results}async getShopPaymentAccountLedgerEntry(e,t){return this.makeRequest(`/shops/${e}/payment-account/ledger-entries/${t}`)}async getShopPayment(e,t){return this.makeRequest(`/shops/${e}/payment-account/payments/${t}`)}async getBuyerTaxonomyNodes(){return(await this.makeRequest("/buyer-taxonomy/nodes")).results}async getPropertiesByTaxonomyId(e){return(await this.makeRequest(`/seller-taxonomy/nodes/${e}/properties`)).results}async getListingProperties(e,t){return(await this.makeRequest(`/shops/${e}/listings/${t}/properties`)).results}async updateListingProperty(e){const{shopId:s,listingId:i,propertyId:r,valueIds:n,values:a,scaleId:o}=e,h=new URLSearchParams;n.forEach(e=>h.append("value_ids[]",e.toString())),a.forEach(e=>h.append("values[]",e)),void 0!==o&&h.append("scale_id",o.toString());const c=`${this.baseUrl}/shops/${s}/listings/${i}/properties/${r}`;await this.rateLimiter.waitForRateLimit();const u=await this.tokenManager.getAccessToken(),l=await this.fetch(c,{method:"PUT",headers:{Authorization:`Bearer ${u}`,"x-api-key":this.getApiKey(),"Content-Type":"application/x-www-form-urlencoded"},body:h.toString()});if(this.rateLimiter.updateFromHeaders(l.headers),this.rateLimiter.resetRetryCount(),!l.ok){const e=await l.text();throw new t(`Failed to update listing property: ${l.status} ${l.statusText}`,l.status,e)}return l.json()}async getShopProductionPartners(e){return(await this.makeRequest(`/shops/${e}/production-partners`)).results}getRemainingRequests(){return this.rateLimiter.getRemainingRequests()}getRateLimitStatus(){return this.rateLimiter.getRateLimitStatus()}onApproachingRateLimit(e,t){void 0!==t&&this.rateLimiter.setWarningThreshold(t),this.rateLimiter.setApproachingLimitCallback(e)}async clearCache(){this.cache&&await this.cache.clear()}getCurrentTokens(){return this.tokenManager.getCurrentTokens()}isTokenExpired(){return this.tokenManager.isTokenExpired()}async refreshToken(){return this.tokenManager.refreshToken()}async fetch(e,t){return l(),fetch(e,t)}}const P="undefined"!=typeof window;async function D(e){const t=await async function(e){if(P){const t=new Uint8Array(e);return crypto.getRandomValues(t),t}throw new Error("Crypto functions are not available in this environment")}(e);return L(t)}async function $(e){const t=new TextEncoder,s="string"==typeof e?t.encode(e):e;if(P){const e=await crypto.subtle.digest("SHA-256",s);return new Uint8Array(e)}throw new Error("SHA256 is not available in this environment")}async function q(e){return L(await $(e))}function L(e){if(P){const t=String.fromCharCode(...e);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}throw new Error("Base64URL encoding is not available in this environment")}async function I(){return D(32)}async function O(){return D(32)}async function N(e){return q(e)}class M{constructor(e){this.keystring=e.keystring,this.redirectUri=e.redirectUri,this.scopes=e.scopes,this.codeVerifier=e.codeVerifier||"",this.state=e.state||"",this.initialized=this.initialize(e)}async initialize(e){e.codeVerifier||(this.codeVerifier=await I()),e.state||(this.state=await O())}async getAuthUrl(){await this.initialized;const e=await N(this.codeVerifier);return`https://www.etsy.com/oauth/connect?${new URLSearchParams({response_type:"code",client_id:this.keystring,redirect_uri:this.redirectUri,scope:this.scopes.join(" "),state:this.state,code_challenge:e,code_challenge_method:"S256"}).toString()}`}async setAuthorizationCode(e,t){if(await this.initialized,t!==this.state)throw new s("State parameter mismatch","INVALID_STATE");this.authorizationCode=e,this.receivedState=t}async getAccessToken(){if(!this.authorizationCode)throw new s("Authorization code not set. Call setAuthorizationCode() first.","NO_AUTH_CODE");const e=new URLSearchParams({grant_type:"authorization_code",client_id:this.keystring,redirect_uri:this.redirectUri,code:this.authorizationCode,code_verifier:this.codeVerifier});try{l();const t=await fetch("https://api.etsy.com/v3/public/oauth/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:e.toString()});if(!t.ok){const e=await t.text();throw new s(`Token exchange failed: ${t.status} ${t.statusText} - ${e}`,"TOKEN_EXCHANGE_FAILED")}const i=await t.json();return{access_token:i.access_token,refresh_token:i.refresh_token,expires_at:new Date(Date.now()+1e3*i.expires_in),token_type:i.token_type,scope:i.scope}}catch(e){if(e instanceof s)throw e;throw new s(`Token exchange failed: ${e instanceof Error?e.message:"Unknown error"}`,"TOKEN_EXCHANGE_ERROR")}}async getState(){return await this.initialized,this.state}async getCodeVerifier(){return await this.initialized,this.codeVerifier}getScopes(){return[...this.scopes]}getRedirectUri(){return this.redirectUri}}const U={LISTINGS_READ:"listings_r",SHOPS_READ:"shops_r",PROFILE_READ:"profile_r",FAVORITES_READ:"favorites_r",FEEDBACK_READ:"feedback_r",TREASURY_READ:"treasury_r",LISTINGS_WRITE:"listings_w",SHOPS_WRITE:"shops_w",PROFILE_WRITE:"profile_w",FAVORITES_WRITE:"favorites_w",FEEDBACK_WRITE:"feedback_w",TREASURY_WRITE:"treasury_w",LISTINGS_DELETE:"listings_d",SHOPS_DELETE:"shops_d",PROFILE_DELETE:"profile_d",FAVORITES_DELETE:"favorites_d",FEEDBACK_DELETE:"feedback_d",TREASURY_DELETE:"treasury_d",TRANSACTIONS_READ:"transactions_r",TRANSACTIONS_WRITE:"transactions_w",BILLING_READ:"billing_r",CART_READ:"cart_r",CART_WRITE:"cart_w",RECOMMEND_READ:"recommend_r",RECOMMEND_WRITE:"recommend_w",ADDRESS_READ:"address_r",ADDRESS_WRITE:"address_w",EMAIL_READ:"email_r"},B={SHOP_READ_ONLY:[U.SHOPS_READ,U.LISTINGS_READ,U.PROFILE_READ],SHOP_MANAGEMENT:[U.SHOPS_READ,U.SHOPS_WRITE,U.LISTINGS_READ,U.LISTINGS_WRITE,U.LISTINGS_DELETE,U.PROFILE_READ,U.TRANSACTIONS_READ],BASIC_ACCESS:[U.SHOPS_READ,U.LISTINGS_READ]};class F{constructor(){this.queue=[],this.processing=!1,this.rateLimits=new Map,this.requestCount=0,this.dailyReset=new Date,this.lastRequestTime=0,this.maxRequestsPerDay=1e4,this.maxRequestsPerSecond=10,this.minRequestInterval=100,this.setNextDailyReset()}static getInstance(){return this.instance||(this.instance=new F),this.instance}static resetInstance(){this.instance=null}async enqueue(e,t={}){return new Promise((s,i)=>{const r={id:this.generateId(),request:e,resolve:s,reject:i,priority:t.priority??"normal",addedAt:Date.now(),timeout:t.timeout??3e4,endpoint:t.endpoint};this.queue.push(r),this.processing||this.processQueue().catch(e=>{console.error("Queue processing error:",e)})})}getStatus(){return{queueLength:this.queue.length,processing:this.processing,remainingRequests:Math.max(0,this.maxRequestsPerDay-this.requestCount),resetTime:this.dailyReset}}clear(){this.queue.forEach(e=>{e.reject(new Error("Queue cleared"))}),this.queue=[]}async processQueue(){if(!this.processing){this.processing=!0;try{for(;this.queue.length>0;){if(Date.now()>=this.dailyReset.getTime()&&(this.requestCount=0,this.setNextDailyReset()),this.requestCount>=this.maxRequestsPerDay){const e=this.dailyReset.getTime()-Date.now();console.warn(`Daily rate limit reached. Waiting ${Math.ceil(e/1e3/60)} minutes until reset.`),await this.delay(e),this.requestCount=0,this.setNextDailyReset()}await this.waitForRateLimit(),this.queue.sort((e,t)=>{const s={high:0,normal:1,low:2};return s[e.priority]-s[t.priority]});const e=this.queue.shift();if(!e)break;const t=Date.now()-e.addedAt;if(e.timeout&&t>e.timeout)e.reject(new Error(`Request timeout after ${t}ms (exceeded while waiting in queue)`));else{try{let s;if(e.timeout){const i=e.timeout-t;let r;const n=new Promise((t,s)=>{r=setTimeout(()=>{s(new Error(`Request timeout after ${e.timeout}ms (exceeded during execution)`))},i),r&&"function"==typeof r.unref&&r.unref()});try{s=await Promise.race([e.request(),n])}finally{r&&clearTimeout(r)}}else s=await e.request();e.resolve(s),this.requestCount++,this.lastRequestTime=Date.now()}catch(t){t instanceof i&&this.updateRateLimitInfo(t),e.reject(t)}await this.delay(this.minRequestInterval)}}}finally{this.processing=!1}}}async waitForRateLimit(){const e=Date.now(),t=this.rateLimits.get("global");if(t&&e<t.resetAt){const s=t.resetAt-e;console.log(`Global rate limit active. Waiting ${s}ms`),await this.delay(s)}const s=e-this.lastRequestTime;if(s<this.minRequestInterval){const e=this.minRequestInterval-s;await this.delay(e)}}updateRateLimitInfo(e){const t=e.retryAfter;t&&this.rateLimits.set("global",{remaining:0,resetAt:Date.now()+1e3*t})}setNextDailyReset(){const e=new Date;this.dailyReset=new Date(e),this.dailyReset.setUTCDate(this.dailyReset.getUTCDate()+1),this.dailyReset.setUTCHours(0,0,0,0)}generateId(){return`${Date.now()}_${Math.random().toString(36).substring(2,9)}`}delay(e){return new Promise(t=>{const s=setTimeout(t,e);"function"==typeof s.unref&&s.unref()})}}F.instance=null;class z{constructor(e,t={}){this.currentPage=[],this.totalCount=null,this.hasMore=!0,this.fetcher=e,this.options={limit:t.limit||25,offset:t.offset||0,maxPages:t.maxPages||1/0,maxItems:t.maxItems||1/0},this.currentOffset=this.options.offset}async*[Symbol.asyncIterator](){let e=0,t=0;for(;this.hasMore&&e<this.options.maxPages&&t<this.options.maxItems;){const s=await this.fetchPage();e++;for(const e of s){if(t>=this.options.maxItems)return;yield e,t++}if(!this.hasMore)break}}async getAll(){const e=[];for await(const t of this)e.push(t);return e}getCurrentPage(){return this.currentPage}hasNextPage(){return this.hasMore}async getNextPage(){return this.hasMore?this.fetchPage():[]}getTotalCount(){return this.totalCount}reset(){this.currentOffset=this.options.offset,this.currentPage=[],this.totalCount=null,this.hasMore=!0}async fetchPage(){const e=await this.fetcher(this.options.limit,this.currentOffset);return this.currentPage=e.results,this.totalCount=e.count,e.pagination?void 0!==e.pagination.next_offset?(this.hasMore=!0,this.currentOffset=e.pagination.next_offset):this.hasMore=!1:(this.hasMore=e.results.length===this.options.limit,this.currentOffset+=this.options.limit),this.currentPage}}const j={maxRetries:3,retryDelay:1e3,exponentialBackoff:!0,retryableStatusCodes:[429,500,502,503,504],maxRetryDelay:3e4,jitter:.1};function H(e,t){let s;if(s=t.exponentialBackoff?t.retryDelay*Math.pow(2,e-1):t.retryDelay*e,t.maxRetryDelay&&(s=Math.min(s,t.maxRetryDelay)),t.jitter&&t.jitter>0){const e=s*t.jitter;s+=Math.random()*e*2-e}return Math.max(0,Math.floor(s))}function K(e,s){if(e instanceof t){const t=e.statusCode;if(t&&s.retryableStatusCodes.includes(t))return!0}return!!(e instanceof TypeError&&e.message.includes("fetch"))}function W(e){if(e instanceof t&&429===e.statusCode){const t=e.getRetryAfter();if("number"==typeof t)return 1e3*t}return null}function V(e){return new Promise(t=>setTimeout(t,e))}async function G(e,t={}){const s={...j,...t};let i=new Error("Unknown error"),r=0;for(;r<=s.maxRetries;)try{if(t.signal?.aborted)throw new Error("Operation aborted");return await e()}catch(e){if(i=e,r++,r>s.maxRetries)throw i;if(!K(e,s))throw i;let t=H(r,s);const n=W(e);null!==n&&(t=n),s.onRetry&&s.onRetry(r,i),await V(t)}throw i}class J{constructor(e){this.config={algorithm:"sha256",verifySignatures:!0,...e},this.handlers=new Map}verifySignature(e,t){if(!this.config.verifySignatures)return!0;if(!this.crypto)throw new Error("Signature verification requires Node.js crypto module");try{const s=this.crypto.createHmac(this.config.algorithm,this.config.secret).update(e).digest("hex");return this.timingSafeEqual(s,t)}catch(e){return console.error("Signature verification failed:",e),!1}}parseEvent(e){const t="string"==typeof e?JSON.parse(e):e;if(!t.type||!t.data)throw new Error("Invalid webhook event format");const s={type:t.type,timestamp:t.timestamp||Date.now(),data:t.data,shop_id:t.shop_id,user_id:t.user_id};return this.triggerHandlers(s),s}on(e,t){this.handlers.has(e)||this.handlers.set(e,new Set);const s=this.handlers.get(e);s&&s.add(t)}off(e,t){const s=this.handlers.get(e);s&&s.delete(t)}removeAllListeners(e){e?this.handlers.delete(e):this.handlers.clear()}async triggerHandlers(e){const t=this.handlers.get(e.type);if(!t||0===t.size)return;const s=Array.from(t).map(t=>{try{return Promise.resolve(t(e.data))}catch(t){return console.error(`Error in webhook handler for ${e.type}:`,t),Promise.resolve()}});await Promise.all(s)}timingSafeEqual(e,t){if(this.crypto?.timingSafeEqual){const s=Buffer.from(e),i=Buffer.from(t);return s.length===i.length&&this.crypto.timingSafeEqual(s,i)}return e===t}getHandlerCount(e){return this.handlers.get(e)?.size||0}getRegisteredEventTypes(){return Array.from(this.handlers.keys())}}class Q{constructor(e={}){this.cache=new Map,this.currentSize=0,this.stats={hits:0,misses:0,evictions:0},this.maxSize=e.maxSize??10485760,this.maxEntries=e.maxEntries??1e3,this.ttl=1e3*(e.ttl??3600),this.trackStats=e.trackStats??!0}async get(e){const t=this.cache.get(e);return t?Date.now()>t.expires?(this.cache.delete(e),this.currentSize-=t.size,this.trackStats&&this.stats.misses++,null):(t.lastAccessed=Date.now(),t.accessCount++,this.cache.delete(e),this.cache.set(e,t),this.trackStats&&this.stats.hits++,t.value):(this.trackStats&&this.stats.misses++,null)}async set(e,t,s){const i=this.estimateSize(t),r=s?1e3*s:this.ttl,n=Date.now(),a=this.cache.get(e);for(a&&(this.currentSize-=a.size,this.cache.delete(e));(this.currentSize+i>this.maxSize||this.cache.size>=this.maxEntries)&&this.cache.size>0;)await this.evictLRU();const o={key:e,value:t,expires:n+r,size:i,accessCount:0,lastAccessed:n,created:n};this.cache.set(e,o),this.currentSize+=i}async delete(e){const t=this.cache.get(e);t&&(this.cache.delete(e),this.currentSize-=t.size)}async clear(){this.cache.clear(),this.currentSize=0,this.stats={hits:0,misses:0,evictions:0}}async evictLRU(){const e=this.cache.keys().next().value;if(e){const t=this.cache.get(e);t&&(this.currentSize-=t.size),this.cache.delete(e),this.trackStats&&this.stats.evictions++}}estimateSize(e){return 2*e.length}getStats(){const e=this.stats.hits+this.stats.misses;return{hits:this.stats.hits,misses:this.stats.misses,hitRate:e>0?this.stats.hits/e:0,missRate:e>0?this.stats.misses/e:0,size:this.currentSize,entryCount:this.cache.size,evictions:this.stats.evictions,maxSize:this.maxSize,maxEntries:this.maxEntries}}}class Y{constructor(e={}){this.cache=new Map,this.currentSize=0,this.stats={hits:0,misses:0,evictions:0},this.maxSize=e.maxSize??10485760,this.maxEntries=e.maxEntries??1e3,this.ttl=1e3*(e.ttl??3600),this.trackStats=e.trackStats??!0}async get(e){const t=this.cache.get(e);return t?Date.now()>t.expires?(this.cache.delete(e),this.currentSize-=t.size,this.trackStats&&this.stats.misses++,null):(t.accessCount++,t.lastAccessed=Date.now(),this.trackStats&&this.stats.hits++,t.value):(this.trackStats&&this.stats.misses++,null)}async set(e,t,s){const i=this.estimateSize(t),r=s?1e3*s:this.ttl,n=Date.now(),a=this.cache.get(e);for(a&&(this.currentSize-=a.size,this.cache.delete(e));(this.currentSize+i>this.maxSize||this.cache.size>=this.maxEntries)&&this.cache.size>0;)await this.evictLFU();const o={key:e,value:t,expires:n+r,size:i,accessCount:0,lastAccessed:n,created:n};this.cache.set(e,o),this.currentSize+=i}async delete(e){const t=this.cache.get(e);t&&(this.cache.delete(e),this.currentSize-=t.size)}async clear(){this.cache.clear(),this.currentSize=0,this.stats={hits:0,misses:0,evictions:0}}async evictLFU(){let e=1/0,t=null;for(const[s,i]of this.cache.entries())i.accessCount<e&&(e=i.accessCount,t=s);if(t){const e=this.cache.get(t);e&&(this.currentSize-=e.size),this.cache.delete(t),this.trackStats&&this.stats.evictions++}}estimateSize(e){return 2*e.length}getStats(){const e=this.stats.hits+this.stats.misses;return{hits:this.stats.hits,misses:this.stats.misses,hitRate:e>0?this.stats.hits/e:0,missRate:e>0?this.stats.misses/e:0,size:this.currentSize,entryCount:this.cache.size,evictions:this.stats.evictions,maxSize:this.maxSize,maxEntries:this.maxEntries}}}class X{constructor(e,t={}){this.baseCache=e,this.invalidationPatterns=new Set(t.invalidateOn?.patterns||[]),this.mutationPatterns=new Map,!1!==t.invalidateOn?.mutations&&(this.mutationPatterns.set("shops/*/update",["shops/*"]),this.mutationPatterns.set("listings/*/update",["listings/*"]),this.mutationPatterns.set("receipts/*/update",["receipts/*"]))}async get(e){return this.baseCache.get(e)}async set(e,t,s){return this.baseCache.set(e,t,s)}async delete(e){return this.baseCache.delete(e)}async clear(){return this.baseCache.clear()}async invalidatePattern(e){await this.baseCache.clear()}async invalidateOnMutation(e){const t=this.mutationPatterns.get(e);if(t)for(const e of t)await this.invalidatePattern(e)}addInvalidationPattern(e){this.invalidationPatterns.add(e)}removeInvalidationPattern(e){this.invalidationPatterns.delete(e)}addMutationPattern(e,t){this.mutationPatterns.set(e,t)}}class Z{constructor(e,t){if(this.config=e,this.keyPrefix=e.keyPrefix||"etsy:",!t)throw new Error("Redis client must be provided. Install a Redis client library (e.g., ioredis) and pass the client instance.");this.client=t}async get(e){try{return await this.client.get(this.keyPrefix+e)}catch(e){return console.error("Redis get error:",e),null}}async set(e,t,s=3600){try{await this.client.setex(this.keyPrefix+e,s,t)}catch(e){console.error("Redis set error:",e)}}async delete(e){try{await this.client.del(this.keyPrefix+e)}catch(e){console.error("Redis delete error:",e)}}async clear(){try{const e=await this.client.keys(this.keyPrefix+"*");e.length>0&&await this.client.del(...e)}catch(e){console.error("Redis clear error:",e)}}getClient(){return this.client}}class ee{constructor(e,t){this.params={},this.includeFields=[],this.client=e,this.shopId=t}where(e){return e.state&&(this.params.state=e.state),this}include(e){return this.includeFields=e,this.params.includes=e,this}limit(e){return this.params.limit=e,this}offset(e){return this.params.offset=e,this}sortBy(e,t="asc"){return this.params.sort_on=e,this.params.sort_order="asc"===t?"up":"down",this}async fetch(){if(!this.shopId)throw new Error("Shop ID is required for listing queries");return this.client.getListingsByShop(this.shopId,this.params)}async first(){this.params.limit=1;return(await this.fetch())[0]||null}async count(){return(await this.fetch()).length}}class te{constructor(e,t){this.params={},this.client=e,this.shopId=t}where(e){return Object.assign(this.params,e),this}limit(e){return this.params.limit=e,this}offset(e){return this.params.offset=e,this}sortBy(e,t="desc"){return this.params.sort_on=e,this.params.sort_order="asc"===t?"up":"down",this}async fetch(){return this.client.getShopReceipts(this.shopId,this.params)}async first(){this.params.limit=1;return(await this.fetch())[0]||null}}class se{constructor(e){this.operations=[],this.client=e}getShop(e){return this.operations.push(()=>this.client.getShop(e)),this}getListings(e,t){return this.operations.push(()=>this.client.getListingsByShop(e,t)),this}getReceipts(e,t){return this.operations.push(()=>this.client.getShopReceipts(e,t)),this}custom(e){return this.operations.push(e),this}async execute(){return Promise.all(this.operations.map(e=>e()))}async executeSequential(){const e=[];for(const t of this.operations)e.push(await t());return e}clear(){return this.operations=[],this}size(){return this.operations.length}}class ie{constructor(e){if(this.secret=e.secret,this.algorithm=e.algorithm||"sha256",!this.secret)throw new Error("Webhook secret is required");throw new Error("WebhookSecurity is only available in Node.js environments")}signRequest(e,t="hex"){if(!this.crypto)throw new Error("Crypto module not available");const s="string"==typeof e?e:JSON.stringify(e);return this.crypto.createHmac(this.algorithm,this.secret).update(s).digest(t)}verifySignature(e,t,s="hex"){if(!this.crypto)throw new Error("Crypto module not available");try{const i=this.signRequest(e,s);return this.timingSafeEqual(i,t)}catch(e){return console.error("Signature verification failed:",e),!1}}signRequestWithTimestamp(e,t,s="hex"){const i=t||Math.floor(Date.now()/1e3),r=`${i}.${"string"==typeof e?e:JSON.stringify(e)}`;return{timestamp:i,signature:this.signRequest(r,s)}}verifySignatureWithTimestamp(e,t,s,i=300,r="hex"){const n=Math.floor(Date.now()/1e3)-s;if(n<0)return console.warn("Webhook timestamp is in the future"),!1;if(n>i)return console.warn(`Webhook request is too old: ${n} seconds`),!1;const a=`${s}.${"string"==typeof e?e:JSON.stringify(e)}`;return this.verifySignature(a,t,r)}timingSafeEqual(e,t){if(!this.crypto?.timingSafeEqual)return console.warn("crypto.timingSafeEqual not available, using simple comparison"),e===t;const s=Buffer.from(e),i=Buffer.from(t);return s.length===i.length&&this.crypto.timingSafeEqual(s,i)}getAlgorithm(){return this.algorithm}updateSecret(e){if(!e)throw new Error("Secret cannot be empty");this.secret=e}}const re="2.2.0",ne="etsy-v3-api-client";e.AuthHelper=M,e.BatchQueryExecutor=se,e.BulkOperationManager=w,e.COMMON_SCOPE_COMBINATIONS=B,e.CacheWithInvalidation=X,e.CreateListingSchema=k,e.DEFAULT_RETRY_CONFIG=j,e.ETSY_SCOPES=U,e.EncryptedFileTokenStorage=class{constructor(e){throw new Error("EncryptedFileTokenStorage is only available in Node.js environments")}async save(e){throw new Error("EncryptedFileTokenStorage is only available in Node.js")}async load(){return null}async clear(){}getFilePath(){return this.filePath}async exists(){return!1}async _writeFile(e,t){}async _setFilePermissions(e,t){if("undefined"!=typeof process)try{const s={promises:{writeFile:()=>{},readFile:()=>{},unlink:()=>{}}};await s.promises.chmod(e,t)}catch{}}async _readFile(e){if("undefined"==typeof process)throw new Error("Not available");throw new Error("readFile not available in browser")}async _deleteFile(e){}async _stat(e){if("undefined"==typeof process)throw new Error("Not available");return await{writeFile:()=>{},readFile:()=>{},unlink:()=>{}}.stat(e)}},e.EtsyApiError=t,e.EtsyAuthError=s,e.EtsyClient=A,e.EtsyRateLimitError=i,e.EtsyRateLimiter=f,e.EtsyWebhookHandler=J,e.FieldValidator=R,e.FileTokenStorage=class{constructor(e){throw new Error("FileTokenStorage is only available in Node.js environments")}async save(e){throw new Error("FileTokenStorage is only available in Node.js")}async load(){return null}async clear(){}async _writeFile(e,t){}async _setFilePermissions(e,t){if("undefined"!=typeof process)try{const s={promises:{writeFile:()=>{},readFile:()=>{},unlink:()=>{}}};await s.promises.chmod(e,t)}catch{}}async _readFile(e){if("undefined"==typeof process)throw new Error("Not available");throw new Error("readFile not available in browser")}async _deleteFile(e){}},e.GlobalRequestQueue=F,e.LFUCache=Y,e.LIBRARY_NAME=ne,e.LRUCache=Q,e.ListingQueryBuilder=ee,e.LocalStorageTokenStorage=m,e.MemoryTokenStorage=d,e.PaginatedResults=z,e.PluginManager=class{constructor(){this.plugins=[]}async register(e){if(this.plugins.some(t=>t.name===e.name))throw new Error(`Plugin with name "${e.name}" is already registered`);this.plugins.push(e),e.onInit&&await e.onInit()}async unregister(e){const t=this.plugins.findIndex(t=>t.name===e);if(-1===t)return!1;const s=this.plugins[t];return!!s&&(s.onDestroy&&await s.onDestroy(),this.plugins.splice(t,1),!0)}getPlugins(){return this.plugins}getPlugin(e){return this.plugins.find(t=>t.name===e)}async executeBeforeRequest(e){let t=e;for(const e of this.plugins)e.onBeforeRequest&&(t=await e.onBeforeRequest(t));return t}async executeAfterResponse(e){let t=e;for(const e of this.plugins)e.onAfterResponse&&(t=await e.onAfterResponse(t));return t}async executeOnError(e){for(const t of this.plugins)t.onError&&await t.onError(e)}async clear(){for(const e of this.plugins)e.onDestroy&&await e.onDestroy();this.plugins=[]}},e.ReceiptQueryBuilder=te,e.RedisCacheStorage=Z,e.RetryManager=class{constructor(e={}){this.config={...j,...e}}async execute(e,t){return G(e,{...this.config,...t})}updateConfig(e){this.config={...this.config,...e}}getConfig(){return{...this.config}}},e.SecureTokenStorage=class{constructor(e={}){if(this.encryptionKey=null,"undefined"==typeof window)throw new Error("SecureTokenStorage is only available in browser environments. For Node.js, use EncryptedFileTokenStorage instead.");if(!window.crypto||!window.crypto.subtle)throw new Error("Web Crypto API is not supported in this browser. Please use a modern browser (Chrome 37+, Firefox 34+, Safari 11+, Edge 79+).");this.keyPrefix=e.keyPrefix||"etsy_token",this.derivationInput=e.derivationInput||this.getDefaultDerivationInput(),this.storage=e.useSessionStorage?sessionStorage:localStorage}async save(e){const t=await this.getEncryptionKey(),s=JSON.stringify(e),i=(new TextEncoder).encode(s),r=window.crypto.getRandomValues(new Uint8Array(12)),n=await window.crypto.subtle.encrypt({name:"AES-GCM",iv:r},t,i),a=await this.generateIntegrity(n,r),o={version:1,encrypted:this.arrayBufferToBase64(n),iv:this.arrayBufferToBase64(r),integrity:this.arrayBufferToBase64(a),expiresAt:e.expires_at.getTime(),timestamp:Date.now()};this.storage.setItem(this.keyPrefix,JSON.stringify(o))}async load(){const e=this.storage.getItem(this.keyPrefix);if(!e)return null;try{const t=JSON.parse(e);if(1!==t.version)return console.warn("Unsupported token storage version. Clearing storage."),await this.clear(),null;if(Date.now()>t.expiresAt)return console.info("Stored tokens have expired. Clearing storage."),await this.clear(),null;const s=this.base64ToArrayBuffer(t.encrypted),i=this.base64ToArrayBuffer(t.iv),r=this.base64ToArrayBuffer(t.integrity),n=await this.generateIntegrity(s,i);if(!this.compareArrayBuffers(r,n))return console.warn("Token integrity check failed. Data may have been tampered with. Clearing storage."),await this.clear(),null;const a=await this.getEncryptionKey(),o=await window.crypto.subtle.decrypt({name:"AES-GCM",iv:i},a,s),h=(new TextDecoder).decode(o),c=JSON.parse(h);return c.expires_at=new Date(c.expires_at),c}catch(e){return console.error("Failed to load tokens from storage:",e),await this.clear(),null}}async clear(){this.storage.removeItem(this.keyPrefix)}async getEncryptionKey(){if(this.encryptionKey)return this.encryptionKey;const e=await this.deriveKeyMaterial();return this.encryptionKey=await window.crypto.subtle.deriveKey({name:"PBKDF2",salt:this.stringToArrayBuffer("etsy-v3-api-client-salt"),iterations:1e5,hash:"SHA-256"},e,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]),this.encryptionKey}async deriveKeyMaterial(){const e=this.stringToArrayBuffer(this.derivationInput);return window.crypto.subtle.importKey("raw",e,"PBKDF2",!1,["deriveKey"])}async generateIntegrity(e,t){const s=new Uint8Array(e.byteLength+t.byteLength);s.set(new Uint8Array(e),0),s.set(new Uint8Array(t),e.byteLength);const i=await this.deriveKeyMaterial(),r=await window.crypto.subtle.deriveKey({name:"PBKDF2",salt:this.stringToArrayBuffer("etsy-integrity-salt"),iterations:1e5,hash:"SHA-256"},i,{name:"HMAC",hash:"SHA-256"},!1,["sign"]);return window.crypto.subtle.sign("HMAC",r,s)}getDefaultDerivationInput(){return`${window.location.hostname}:${navigator.userAgent}`}stringToArrayBuffer(e){return(new TextEncoder).encode(e)}arrayBufferToBase64(e){const t=new Uint8Array(e);let s="";for(let e=0;e<t.byteLength;e++){const i=t[e];void 0!==i&&(s+=String.fromCharCode(i))}return btoa(s)}base64ToArrayBuffer(e){const t=atob(e),s=new Uint8Array(t.length);for(let e=0;e<t.length;e++)s[e]=t.charCodeAt(e);return s.buffer}compareArrayBuffers(e,t){if(e.byteLength!==t.byteLength)return!1;const s=new Uint8Array(e),i=new Uint8Array(t);for(let e=0;e<s.length;e++)if(s[e]!==i[e])return!1;return!0}},e.SessionStorageTokenStorage=p,e.TokenManager=g,e.UpdateListingSchema=E,e.UpdateShopSchema=_,e.VERSION=re,e.ValidationException=T,e.Validator=S,e.WebhookSecurity=ie,e.combineValidators=function(...e){return{validate(t){const s=[];for(const i of e){const e=i.validate(t);e.valid||s.push(...e.errors)}return{valid:0===s.length,errors:s}}}},e.createAnalyticsPlugin=function(e){const t=new Map;return{name:"analytics",version:"1.0.0",onBeforeRequest(e){const s=`${e.method}:${e.endpoint}:${Date.now()}`;return t.set(s,Date.now()),e},onAfterResponse(s){if(e.trackEndpoint){const s=Array.from(t.keys()).pop();if(s){const i=t.get(s);if(i){const r=Date.now()-i,n=s.split(":"),a=n[0],o=n[1];o&&a&&e.trackEndpoint(o,a,r),t.delete(s)}}}return s},onError(t){e.trackError&&e.trackError(t)}}},e.createAuthHelper=function(e){return new M(e)},e.createBatchQuery=function(e){return new se(e)},e.createBulkOperationManager=function(e){return new w(e)},e.createCacheStorage=function(e={}){switch(e.strategy||"lru"){case"lru":case"ttl":default:return new Q(e);case"lfu":return new Y(e)}},e.createCacheWithInvalidation=function(e,t={}){return new X(e,t)},e.createCachingPlugin=function(e={}){const t=new Map,s=1e3*(e.ttl||300);return{name:"caching",version:"1.0.0",onBeforeRequest(s){if("GET"===s.method.toUpperCase()){const i=(t=>e.keyGenerator?e.keyGenerator(t):`${t.method}:${t.endpoint}:${JSON.stringify(t.params||{})}`)(s),r=t.get(i);r&&(Date.now(),r.expiresAt)}return s},onAfterResponse(e){const i=`GET:${e.status}`;return t.set(i,{data:e.data,expiresAt:Date.now()+s}),e},onDestroy(){t.clear()}}},e.createCodeChallenge=N,e.createDefaultTokenStorage=function(e){return e?.preferSession&&u?new p(e?.storageKey):c?new m(e?.storageKey):u?new p(e?.storageKey):new d},e.createEtsyClient=function(e){return new A(e)},e.createListingQuery=function(e,t){return new ee(e,t)},e.createLoggingPlugin=function(e={}){const s=e.logger||console,i=e.logLevel||"info",r=e=>{const t=["debug","info","warn","error"],s=t.indexOf(i);return t.indexOf(e)>=s};return{name:"logging",version:"1.0.0",onBeforeRequest:e=>(r("debug")&&s.debug("[Etsy API] Request:",{method:e.method,endpoint:e.endpoint,params:e.params}),e),onAfterResponse:e=>(r("debug")&&s.debug("[Etsy API] Response:",{status:e.status,data:e.data}),e),onError(e){r("error")&&(s.error("[Etsy API] Error:",e),e instanceof t&&s.error("[Etsy API] Error Details:",{statusCode:e.statusCode,endpoint:e.endpoint,suggestions:e.suggestions}))}}},e.createPaginatedResults=function(e,t){return new z(e,t)},e.createRateLimitPlugin=function(e={}){const t=e.maxRequestsPerSecond||10,s=[];return{name:"rateLimit",version:"1.0.0",async onBeforeRequest(i){const r=Date.now(),n=r-1e3;for(;s.length>0&&void 0!==s[0]&&s[0]<n;)s.shift();if(s.length>=t){e.onRateLimitExceeded&&e.onRateLimitExceeded();const t=s[0];if(void 0!==t){const e=t+1e3-r;e>0&&await new Promise(t=>setTimeout(t,e))}}return s.push(r),i}}},e.createRateLimiter=function(e){return new f(e)},e.createReceiptQuery=function(e,t){return new te(e,t)},e.createRedisCacheStorage=function(e,t){return new Z(e,t)},e.createRetryPlugin=function(e={}){const{retryableStatusCodes:s=[408,429,500,502,503,504]}=e;return{name:"retry",version:"1.0.0",async onError(i){if(i instanceof t){const t=i.statusCode;t&&s.includes(t)&&e.onRetry&&e.onRetry(1,i)}}}},e.createTokenManager=function(e,t){return new g(e,t)},e.createValidator=function(){return new S},e.createWebhookHandler=function(e){return new J(e)},e.createWebhookSecurity=function(e,t="sha256"){return new ie({secret:e,algorithm:t})},e.decryptAES256GCM=async function(e,t){throw new Error("AES-256-GCM decryption is only available in Node.js environments")},e.default=A,e.defaultRateLimiter=y,e.deriveKeyFromPassword=async function(e,t,s=1e5,i=32){throw new Error("Key derivation is only available in Node.js environments")},e.encryptAES256GCM=async function(e,t){throw new Error("AES-256-GCM encryption is only available in Node.js environments")},e.executeBulkOperation=async function(e,t,s){return new w(s).executeBulk(e,t)},e.field=v,e.generateCodeVerifier=I,e.generateEncryptionKey=async function(e=32){throw new Error("Key generation is only available in Node.js environments")},e.generateRandomBase64Url=D,e.generateState=O,e.getAvailableStorage=function(){return c?"localStorage":u?"sessionStorage":"memory"},e.getEnvironmentInfo=function(){return{isBrowser:r,isNode:Boolean(n),isWebWorker:a,hasFetch:o,hasWebCrypto:h,hasLocalStorage:c,hasSessionStorage:u,userAgent:r?navigator.userAgent:"Node.js",nodeVersion:void 0}},e.getGlobalQueue=function(){return F.getInstance()},e.getLibraryInfo=function(){return{name:ne,version:re,description:"JavaScript/TypeScript client for the Etsy Open API v3 with OAuth 2.0 authentication",author:"profplum700",license:"MIT",homepage:"https://github.com/ForestHillArtsHouse/etsy-v3-api-client#readme"}},e.hasLocalStorage=c,e.hasSessionStorage=u,e.isBrowser=r,e.isNode=n,e.isSecureStorageSupported=function(){return"undefined"!=typeof window&&!(!window.crypto||!window.crypto.subtle||"function"!=typeof window.crypto.subtle.encrypt||"function"!=typeof window.crypto.subtle.decrypt||"function"!=typeof window.crypto.subtle.deriveKey)},e.sha256=$,e.sha256Base64Url=q,e.validate=function(e,t){return t.validate(e)},e.validateEncryptionKey=function(e,t=32){const s=Buffer.isBuffer(e)?e:Buffer.from(e,"utf8");if(s.length!==t)throw new Error(`Invalid encryption key length: expected ${t} bytes, got ${s.length} bytes`);return!0},e.validateOrThrow=x,e.withQueryBuilder=function(e){const t=e;return t.listings=function(e){return new ee(this,e)},t.receipts=function(e){return new te(this,e)},t.batch=function(){return new se(this)},t},e.withQueue=function(e,t){return F.getInstance().enqueue(e,t)},e.withRetry=G,Object.defineProperty(e,"__esModule",{value:!0})});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).EtsyApiClient={})}(this,function(e){"use strict";class t extends Error{constructor(e,t,s,i,r){if(super(e),this._statusCode=t,this._response=s,this._retryAfter=i,this.name="EtsyApiError",this.endpoint=r,this.timestamp=new Date,this.details={statusCode:t||0,retryAfter:i,endpoint:r,timestamp:this.timestamp},s&&"object"==typeof s){const e=s;this.details.errorCode=e.error_code||e.code,this.details.field=e.field,this.details.suggestion=e.suggestion||e.message,Array.isArray(e.errors)&&(this.details.validationErrors=e.errors.map(e=>{if(e&&"object"==typeof e){const t=e;return{field:String(t.field||"unknown"),message:String(t.message||"Validation error")}}return{field:"unknown",message:String(e)}}))}this.suggestions=this.generateSuggestions(),this.docsUrl=this.generateDocsUrl()}get statusCode(){return this._statusCode}get response(){return this._response}isRetryable(){if(!this._statusCode)return!1;if(429===this._statusCode)return!0;if(this._statusCode>=500&&this._statusCode<600)return!0;return!![408,409,423,425].includes(this._statusCode)}getRetryAfter(){return this._retryAfter||null}getRateLimitReset(){return 429===this._statusCode&&this._retryAfter?new Date(Date.now()+1e3*this._retryAfter):null}generateSuggestions(){const e=[];if(!this._statusCode)return["Check your network connection and try again"];switch(this._statusCode){case 400:e.push("Review the Etsy API documentation for this endpoint"),e.push("Check all required parameters are provided"),e.push("Validate parameter formats and types"),this.details.validationErrors&&this.details.validationErrors.length>0&&(e.push("\nValidation errors:"),this.details.validationErrors.forEach(t=>{e.push(` • ${t.field}: ${t.message}`)})),this.details.field&&e.push(`Field causing issue: ${this.details.field}`);break;case 401:e.push("Verify your access token is valid and not expired"),e.push("Check if you need to refresh the token"),e.push("Ensure you completed the OAuth flow correctly"),this.endpoint?.includes("/shops/")&&e.push("Verify the shop_id matches the authenticated user");break;case 403:e.push("Check if your OAuth app has the required scopes:"),this.endpoint?.includes("/listings")&&(e.push(" • listings_r (for reading listings)"),e.push(" • listings_w (for creating/updating listings)")),(this.endpoint?.includes("/receipts")||this.endpoint?.includes("/transactions"))&&e.push(" • transactions_r (for reading orders)"),this.endpoint?.includes("/shops")&&(e.push(" • shops_r (for reading shop data)"),e.push(" • shops_w (for updating shop data)")),e.push("Verify your app is approved for production access"),e.push("Check if the resource belongs to the authenticated user");break;case 404:e.push("Verify the resource ID exists and is spelled correctly"),this.endpoint?.includes("/listings/")&&(e.push("Check if the listing is active and not deleted"),e.push("Ensure the listing belongs to the authenticated shop")),this.endpoint?.includes("/shops/")&&e.push("Verify the shop ID is correct"),this.endpoint?.includes("/receipts/")&&(e.push("Check if the receipt ID is valid"),e.push("Ensure you have access to this shop's receipts"));break;case 409:e.push("Resource state conflict detected"),e.push("Check if the resource was modified by another process"),e.push("Try fetching the latest resource state before updating");break;case 429:{const t=this.getRateLimitReset(),s=t?t.toLocaleTimeString():"shortly";e.push(`Rate limit exceeded. Resets at ${s}`),e.push("Implement exponential backoff retry logic"),e.push("Consider caching responses to reduce API calls"),e.push("Check if you can batch multiple operations"),this._retryAfter&&e.push(`Wait ${this._retryAfter} seconds before retrying`);break}case 500:case 502:case 503:case 504:e.push("This is an Etsy server error, not your code"),e.push("Retry the request after a short delay (exponential backoff)"),e.push("Check Etsy API status: https://status.etsy.com"),this.isRetryable()&&e.push("This error is retryable - the request can be safely retried");break;default:e.push("Check the Etsy API documentation for this endpoint"),e.push("Review your request parameters and format"),this.details.suggestion&&e.push(`Etsy suggestion: ${this.details.suggestion}`)}return e}generateDocsUrl(){return`https://github.com/profplum700/etsy-v3-api-client/blob/main/docs/troubleshooting/ERROR_CODES.md#${this.details.errorCode?.toLowerCase()||this._statusCode?.toString()||"unknown"}`}getUserFriendlyMessage(){let e=this.message;if(this.details.suggestion&&(e+=`\nSuggestion: ${this.details.suggestion}`),this.isRetryable()){const t=this.getRetryAfter();e+=t?`\nRetry after ${t} seconds.`:"\nThis error can be retried."}return e}toString(){const e=[`EtsyApiError: ${this.message}`,`Status Code: ${this._statusCode||"Unknown"}`];return this.details.errorCode&&e.push(`Error Code: ${this.details.errorCode}`),this.endpoint&&e.push(`Endpoint: ${this.endpoint}`),e.push(`Timestamp: ${this.timestamp.toISOString()}`),this.suggestions.length>0&&(e.push("\nSuggestions:"),this.suggestions.forEach(t=>{t.startsWith(" •")?e.push(t):e.push(` • ${t}`)})),e.push(`\nDocumentation: ${this.docsUrl}`),e.join("\n")}toJSON(){return{name:this.name,message:this.message,statusCode:this._statusCode,errorCode:this.details.errorCode,endpoint:this.endpoint,suggestions:this.suggestions,docsUrl:this.docsUrl,timestamp:this.timestamp.toISOString(),details:this.details,isRetryable:this.isRetryable()}}}class s extends Error{constructor(e,t){super(e),this._code=t,this.name="EtsyAuthError"}get code(){return this._code}}class i extends Error{constructor(e,t,s="unknown"){super(e),this._retryAfter=t,this.name="EtsyRateLimitError",this.errorType=s}get retryAfter(){return this._retryAfter}isRetryable(){return"qpd_exhausted"!==this.errorType}}const r="undefined"!=typeof window&&void 0!==window.document,n=!1,a="function"==typeof globalThis.importScripts&&"undefined"!=typeof navigator,o="undefined"!=typeof fetch,c="undefined"!=typeof crypto&&void 0!==crypto.subtle,h=(()=>{try{return"undefined"!=typeof localStorage}catch{return!1}})(),u=(()=>{try{return"undefined"!=typeof sessionStorage}catch{return!1}})();function l(){if(!o)throw new Error("Fetch API is not available. Please use Node.js 18+ or a modern browser.")}class d{constructor(){this.tokens=null}async save(e){this.tokens={...e}}async load(){return this.tokens?{...this.tokens}:null}async clear(){this.tokens=null}}class g{constructor(e,t,s){this.currentTokens=null,this.keystring=e.keystring,this.refreshCallback=e.refreshSave,this.storage=t,this.rotationConfig=s,this.currentTokens={access_token:e.accessToken,refresh_token:e.refreshToken,expires_at:e.expiresAt,token_type:"Bearer",scope:""},this.rotationConfig?.enabled&&this.rotationConfig?.autoSchedule&&this.startRotationScheduler()}async getAccessToken(){if(!this.currentTokens&&(this.storage&&(this.currentTokens=await this.storage.load()),!this.currentTokens))throw new s("No tokens available","NO_TOKENS");const e=new Date,t=new Date(this.currentTokens.expires_at);return e.getTime()>=t.getTime()-6e4&&await this.refreshToken(),this.currentTokens.access_token}async refreshToken(){if(this.refreshPromise)return this.refreshPromise;if(!this.currentTokens)throw new s("No tokens available to refresh","NO_REFRESH_TOKEN");this.refreshPromise=this.performTokenRefresh();try{const e=await this.refreshPromise;return this.currentTokens=e,this.storage&&await this.storage.save(e),this.refreshCallback&&this.refreshCallback(e.access_token,e.refresh_token,e.expires_at),e}finally{this.refreshPromise=void 0}}async performTokenRefresh(){if(!this.currentTokens)throw new s("No tokens available","NO_TOKENS");const e=new URLSearchParams({grant_type:"refresh_token",client_id:this.keystring,refresh_token:this.currentTokens.refresh_token});try{const t=await this.fetch("https://api.etsy.com/v3/public/oauth/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:e.toString()});if(!t.ok)throw new s(`Token refresh failed: ${t.status} ${t.statusText}`,"TOKEN_REFRESH_FAILED");const i=await t.json();return{access_token:i.access_token,refresh_token:i.refresh_token,expires_at:new Date(Date.now()+1e3*i.expires_in),token_type:i.token_type,scope:i.scope}}catch(e){if(e instanceof s)throw e;throw new s(`Token refresh failed: ${e instanceof Error?e.message:"Unknown error"}`,"TOKEN_REFRESH_ERROR")}}getCurrentTokens(){return this.currentTokens?{...this.currentTokens}:null}updateTokens(e){this.currentTokens={...e}}isTokenExpired(){if(!this.currentTokens)return!0;const e=new Date,t=new Date(this.currentTokens.expires_at);return e.getTime()>=t.getTime()}willTokenExpireSoon(e=5){if(!this.currentTokens)return!0;const t=new Date,s=new Date(this.currentTokens.expires_at),i=60*e*1e3;return t.getTime()>=s.getTime()-i}async clearTokens(){this.currentTokens=null,this.storage&&await this.storage.clear()}getTimeUntilExpiration(){if(!this.currentTokens)return null;const e=new Date;return new Date(this.currentTokens.expires_at).getTime()-e.getTime()}needsProactiveRotation(){if(!this.rotationConfig?.enabled||!this.currentTokens)return!1;const e=this.getTimeUntilExpiration();if(null===e)return!1;return e<=(this.rotationConfig.rotateBeforeExpiry||9e5)}async rotateToken(){if(!this.currentTokens)throw new s("No tokens available to rotate","NO_TOKENS");const e={...this.currentTokens},t=await this.refreshToken();if(this.rotationConfig?.onRotation)try{await Promise.resolve(this.rotationConfig.onRotation(e,t))}catch(e){console.error("Token rotation callback failed:",e)}return t}startRotationScheduler(){if(!this.rotationConfig?.enabled)throw new Error("Token rotation is not enabled");this.stopRotationScheduler();const e=this.rotationConfig.checkInterval||6e4;this.rotationTimer=setInterval(async()=>{try{this.needsProactiveRotation()&&(console.log("Proactively rotating token before expiration"),await this.rotateToken())}catch(e){console.error("Failed to proactively rotate token:",e)}},e),this.rotationTimer.unref&&this.rotationTimer.unref()}stopRotationScheduler(){this.rotationTimer&&(clearInterval(this.rotationTimer),this.rotationTimer=void 0)}updateRotationConfig(e){const t=this.rotationConfig?.autoSchedule;this.rotationConfig=e,e.enabled&&e.autoSchedule&&!t?this.startRotationScheduler():e.enabled&&e.autoSchedule||this.stopRotationScheduler()}getRotationConfig(){return this.rotationConfig?{...this.rotationConfig}:void 0}async fetch(e,t){return l(),fetch(e,t)}}class m{constructor(e="etsy_tokens"){if(!h)throw new Error("localStorage is not available in this environment");this.storageKey=e}async save(e){const t=JSON.stringify(e);localStorage.setItem(this.storageKey,t)}async load(){try{const e=localStorage.getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t.expires_at&&(t.expires_at=new Date(t.expires_at)),t}catch{return null}}async clear(){localStorage.removeItem(this.storageKey)}}class p{constructor(e="etsy_tokens"){if(!u)throw new Error("sessionStorage is not available in this environment");this.storageKey=e}async save(e){const t=JSON.stringify(e);sessionStorage.setItem(this.storageKey,t)}async load(){try{const e=sessionStorage.getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t.expires_at&&(t.expires_at=new Date(t.expires_at)),t}catch{return null}}async clear(){sessionStorage.removeItem(this.storageKey)}}class f{constructor(e){this.requestCount=0,this.dailyReset=new Date,this.lastRequestTime=0,this.isHeaderBasedLimiting=!1,this.currentRetryCount=0,this.config={maxRequestsPerDay:1e4,maxRequestsPerSecond:10,minRequestInterval:100,maxRetries:3,baseDelayMs:1e3,maxDelayMs:3e4,jitter:.1,qpdWarningThreshold:80,onApproachingLimit:void 0,...e},this.setNextDailyReset()}setNextDailyReset(){const e=new Date;this.dailyReset=new Date(e),this.dailyReset.setUTCDate(this.dailyReset.getUTCDate()+1),this.dailyReset.setUTCHours(0,0,0,0)}updateFromHeaders(e){if(!e)return;const t=this.parseRateLimitHeaders(e);void 0!==t.limitPerSecond&&(this.headerLimitPerSecond=t.limitPerSecond,this.isHeaderBasedLimiting=!0),void 0!==t.remainingThisSecond&&(this.headerRemainingThisSecond=t.remainingThisSecond),void 0!==t.limitPerDay&&(this.headerLimitPerDay=t.limitPerDay,this.isHeaderBasedLimiting=!0),void 0!==t.remainingToday&&(this.headerRemainingToday=t.remainingToday,this.checkApproachingLimit())}parseRateLimitHeaders(e){const t=t=>{if(e instanceof Headers)return e.get(t);const s=t.toLowerCase();for(const t of Object.keys(e))if(t.toLowerCase()===s)return e[t]??null;return null},s=e=>{if(null===e)return;const t=parseInt(e,10);return isNaN(t)?void 0:t};return{limitPerSecond:s(t("x-limit-per-second")),remainingThisSecond:s(t("x-remaining-this-second")),limitPerDay:s(t("x-limit-per-day")),remainingToday:s(t("x-remaining-today")),retryAfter:s(t("retry-after"))}}checkApproachingLimit(){if(!this.config.onApproachingLimit)return;const e=this.headerLimitPerDay??this.config.maxRequestsPerDay,t=this.headerRemainingToday??this.getRemainingRequests(),s=(e-t)/e*100;s>=this.config.qpdWarningThreshold&&this.config.onApproachingLimit(t,e,s)}async handleRateLimitResponse(e){const t=e?this.parseRateLimitHeaders(e):{};if(this.updateFromHeaders(e),0===this.headerRemainingToday)throw new i("Daily rate limit exhausted. No requests remaining until limit resets.",t.retryAfter,"qpd_exhausted");if(this.currentRetryCount++,this.currentRetryCount>this.config.maxRetries)throw this.currentRetryCount=0,new i(`Max retries (${this.config.maxRetries}) exceeded for rate limit`,t.retryAfter,"qps_exhausted");return{shouldRetry:!0,delayMs:this.calculateBackoffDelay(this.currentRetryCount,t.retryAfter)}}calculateBackoffDelay(e,t){const s=t?1e3*t:0,i=this.config.baseDelayMs*Math.pow(2,e-1);let r=Math.min(i,this.config.maxDelayMs);if(r=Math.max(r,s),this.config.jitter>0){const e=r*this.config.jitter;r+=Math.random()*e*2-e}return Math.max(0,Math.floor(r))}resetRetryCount(){this.currentRetryCount=0}setApproachingLimitCallback(e){this.config.onApproachingLimit=e}setWarningThreshold(e){this.config.qpdWarningThreshold=e}getEffectiveMinInterval(){return void 0!==this.headerLimitPerSecond&&this.headerLimitPerSecond>0?Math.ceil(1e3/this.headerLimitPerSecond):this.config.minRequestInterval}async waitForRateLimit(){const e=Date.now();e>=this.dailyReset.getTime()&&(this.requestCount=0,this.setNextDailyReset());const t=this.headerLimitPerDay??this.config.maxRequestsPerDay;if((this.headerRemainingToday??t-this.requestCount)<=0){const s=this.dailyReset.getTime()-e;throw new i(`Daily rate limit exhausted (${t} requests). Reset in approximately ${Math.ceil(s/1e3/60)} minutes.`,Math.ceil(s/1e3),"qpd_exhausted")}const s=this.getEffectiveMinInterval(),r=e-this.lastRequestTime;if(r<s){const e=s-r;await new Promise(t=>setTimeout(t,e))}this.requestCount++,this.lastRequestTime=Date.now()}getRateLimitStatus(){const e=Date.now();e>=this.dailyReset.getTime()&&(this.requestCount=0,this.setNextDailyReset());const t=this.headerRemainingToday??Math.max(0,this.config.maxRequestsPerDay-this.requestCount);return{remainingRequests:t,resetTime:this.dailyReset,canMakeRequest:t>0&&e-this.lastRequestTime>=this.getEffectiveMinInterval(),isFromHeaders:this.isHeaderBasedLimiting,limitPerSecond:this.headerLimitPerSecond,remainingThisSecond:this.headerRemainingThisSecond,limitPerDay:this.headerLimitPerDay}}getRemainingRequests(){return this.headerRemainingToday??Math.max(0,this.config.maxRequestsPerDay-this.requestCount)}reset(){this.requestCount=0,this.lastRequestTime=0,this.currentRetryCount=0,this.headerLimitPerSecond=void 0,this.headerRemainingThisSecond=void 0,this.headerLimitPerDay=void 0,this.headerRemainingToday=void 0,this.isHeaderBasedLimiting=!1,this.setNextDailyReset()}canMakeRequest(){const e=Date.now();e>=this.dailyReset.getTime()&&(this.requestCount=0,this.setNextDailyReset());return!((this.headerRemainingToday??this.config.maxRequestsPerDay-this.requestCount)<=0)&&e-this.lastRequestTime>=this.getEffectiveMinInterval()}getTimeUntilNextRequest(){const e=Date.now()-this.lastRequestTime,t=this.getEffectiveMinInterval();return e>=t?0:t-e}getConfig(){return{...this.config}}}const y=new f;class w{constructor(e={}){this.concurrency=e.concurrency??5,this.stopOnError=e.stopOnError??!1,this.onProgress=e.onProgress,this.onItemComplete=e.onItemComplete,this.onItemError=e.onItemError}async executeBulk(e,t,s){const i=Date.now(),r=[],n=[];let a=0,o=0,c=0;const h=e.map((e,t)=>({item:e,index:t,id:s?s(e,t):t})),u=[];for(let s=0;s<Math.min(this.concurrency,e.length);s++)u.push(this.worker(h,t,r,n,()=>{a++;const t=r[r.length-1];t?.success?o++:c++,this.onProgress&&this.onProgress(a,e.length,t)}));await Promise.all(u);const l=Date.now()-i;return{total:e.length,successful:o,failed:c,results:r,errors:n,duration:l}}async worker(e,t,s,i,r){for(;e.length>0;){const n=e.shift();if(!n)break;const{item:a,index:o,id:c}=n;try{const e={success:!0,data:await t(a,o),id:c,index:o};s.push(e),this.onItemComplete&&this.onItemComplete(e),r()}catch(t){const n=t instanceof Error?t:new Error(String(t)),a={success:!1,error:n,id:c,index:o};s.push(a);const h={id:c,index:o,error:n};if(i.push(h),this.onItemError&&this.onItemError(h),r(),this.stopOnError){e.length=0;break}}}}setConcurrency(e){this.concurrency=Math.max(1,e)}getConcurrency(){return this.concurrency}}class S{constructor(){this.validators=[]}rule(e){return this.validators.push(e),this}validate(e){const t=[];for(const s of this.validators){const i=s(e);i&&t.push(i)}return{valid:0===t.length,errors:t}}}class R{constructor(e){this.field=e}required(e){return t=>{if("object"!=typeof t||null===t)return null;const s=t[this.field];return null==s||""===s?{field:this.field,message:e||`${this.field} is required`,value:s}:null}}string(e){return t=>{if("object"!=typeof t||null===t)return null;const s=t[this.field];return null==s?null:"string"!=typeof s?{field:this.field,message:e.message||`${this.field} must be a string`,value:s}:void 0!==e.min&&s.length<e.min?{field:this.field,message:e.message||`${this.field} must be at least ${e.min} characters`,value:s}:void 0!==e.max&&s.length>e.max?{field:this.field,message:e.message||`${this.field} must be at most ${e.max} characters`,value:s}:e.pattern&&!e.pattern.test(s)?{field:this.field,message:e.message||`${this.field} has invalid format`,value:s}:null}}number(e){return t=>{if("object"!=typeof t||null===t)return null;const s=t[this.field];return null==s?null:"number"!=typeof s||isNaN(s)?{field:this.field,message:e.message||`${this.field} must be a number`,value:s}:e.integer&&!Number.isInteger(s)?{field:this.field,message:e.message||`${this.field} must be an integer`,value:s}:e.positive&&s<=0?{field:this.field,message:e.message||`${this.field} must be positive`,value:s}:void 0!==e.min&&s<e.min?{field:this.field,message:e.message||`${this.field} must be at least ${e.min}`,value:s}:void 0!==e.max&&s>e.max?{field:this.field,message:e.message||`${this.field} must be at most ${e.max}`,value:s}:null}}enum(e,t){return s=>{if("object"!=typeof s||null===s)return null;const i=s[this.field];return null==i||e.includes(i)?null:{field:this.field,message:t||`${this.field} must be one of: ${e.join(", ")}`,value:i}}}array(e){return t=>{if("object"!=typeof t||null===t)return null;const s=t[this.field];if(null==s)return null;if(!Array.isArray(s))return{field:this.field,message:e.message||`${this.field} must be an array`,value:s};if(void 0!==e.min&&s.length<e.min)return{field:this.field,message:e.message||`${this.field} must have at least ${e.min} items`,value:s};if(void 0!==e.max&&s.length>e.max)return{field:this.field,message:e.message||`${this.field} must have at most ${e.max} items`,value:s};if(e.itemValidator)for(let t=0;t<s.length;t++)if(!e.itemValidator(s[t]))return{field:this.field,message:e.message||`${this.field}[${t}] has invalid value`,value:s[t]};return null}}}function v(e){return new R(e)}const _=(new S).rule(v("quantity").required()).rule(v("quantity").number({min:1,integer:!0,message:"quantity must be a positive integer"})).rule(v("title").required()).rule(v("title").string({min:1,max:140,message:"title must be 1-140 characters"})).rule(v("description").string({max:65535,message:"description must be less than 65535 characters"})).rule(v("price").required()).rule(v("price").number({min:.2,max:5e4,message:"price must be between 0.20 and 50000.00"})).rule(v("who_made").enum(["i_did","someone_else","collective"],"who_made must be one of: i_did, someone_else, collective")).rule(v("when_made").enum(["made_to_order","2020_2024","2010_2019","2005_2009","2000_2004","1990s","1980s","1970s","1960s","1950s","1940s","1930s","1920s","1910s","1900s","1800s","1700s","before_1700"])).rule(v("taxonomy_id").required()).rule(v("taxonomy_id").number({integer:!0,positive:!0,message:"taxonomy_id must be a positive integer"})),k=(new S).rule(e=>"object"==typeof e&&null!==e&&"title"in e&&void 0!==e.title?v("title").string({min:1,max:140,message:"title must be 1-140 characters"})(e):null).rule(e=>"object"==typeof e&&null!==e&&"description"in e&&void 0!==e.description?v("description").string({max:65535,message:"description must be less than 65535 characters"})(e):null).rule(e=>void 0!==e.tags?v("tags").array({max:13,message:"tags must have at most 13 items"})(e):null).rule(e=>void 0!==e.materials?v("materials").array({max:13,message:"materials must have at most 13 items"})(e):null),E=(new S).rule(e=>"object"==typeof e&&null!==e&&"title"in e&&void 0!==e.title?v("title").string({min:1,max:55,message:"shop title must be 1-55 characters"})(e):null).rule(e=>"object"==typeof e&&null!==e&&"announcement"in e&&void 0!==e.announcement?v("announcement").string({max:5e3,message:"announcement must be less than 5000 characters"})(e):null);class T extends Error{constructor(e,t){super(e),this.name="ValidationException",this.errors=t}}function x(e,t,s="Validation failed"){const i=t.validate(e);if(!i.valid)throw new T(s,i.errors)}class b{debug(e,...t){("localhost"===window.location?.hostname||"127.0.0.1"===window.location?.hostname)&&console.log(`[DEBUG] ${e}`,...t)}info(e,...t){console.log(`[INFO] ${e}`,...t)}warn(e,...t){console.warn(`[WARN] ${e}`,...t)}error(e,...t){console.error(`[ERROR] ${e}`,...t)}}class C{constructor(){this.cache=new Map}async get(e){const t=this.cache.get(e);return t?Date.now()>t.expires?(this.cache.delete(e),null):t.value:null}async set(e,t,s=3600){const i=Date.now()+1e3*s;this.cache.set(e,{value:t,expires:i})}async delete(e){this.cache.delete(e)}async clear(){this.cache.clear()}}class A{constructor(e){this.tokenManager=new g(e),this.baseUrl=e.baseUrl||"https://api.etsy.com/v3/application",this.logger=new b,this.keystring=e.keystring,this.sharedSecret=e.sharedSecret,!1!==e.rateLimiting?.enabled?this.rateLimiter=new f({maxRequestsPerDay:e.rateLimiting?.maxRequestsPerDay||1e4,maxRequestsPerSecond:e.rateLimiting?.maxRequestsPerSecond||10,minRequestInterval:e.rateLimiting?.minRequestInterval??100,maxRetries:e.rateLimiting?.maxRetries,baseDelayMs:e.rateLimiting?.baseDelayMs,maxDelayMs:e.rateLimiting?.maxDelayMs,jitter:e.rateLimiting?.jitter,qpdWarningThreshold:e.rateLimiting?.qpdWarningThreshold,onApproachingLimit:e.rateLimiting?.onApproachingLimit}):this.rateLimiter=new f(void 0),!1!==e.caching?.enabled&&(this.cache=e.caching?.storage||new C,this.cacheTtl=e.caching?.ttl||3600),this.bulkOperationManager=new w({concurrency:5,stopOnError:!1})}async makeRequest(e,t={},s=!0){const i=`${this.baseUrl}${e}`,r={method:"GET",...t},n=`${i}:${JSON.stringify(r)}`;if(s&&this.cache&&"GET"===r.method){const e=await this.cache.get(n);if(e)return JSON.parse(e)}await this.rateLimiter.waitForRateLimit();const a={Authorization:`Bearer ${await this.tokenManager.getAccessToken()}`,"x-api-key":this.getApiKey(),"Content-Type":"application/json",Accept:"application/json",...r.headers};return this.executeWithRetry(i,{...r,headers:a},n,s)}async executeWithRetry(e,r,n,a){for(;;)try{const s=await this.fetch(e,r);if(429===s.status){const{shouldRetry:e,delayMs:t}=await this.rateLimiter.handleRateLimitResponse(s.headers);if(e){this.logger.warn(`Rate limited. Retrying in ${t}ms...`),await this.sleep(t);continue}}if(!s.ok){const e=await s.text();throw new t(`Etsy API error: ${s.status} ${s.statusText}`,s.status,e)}if(this.rateLimiter.updateFromHeaders(s.headers),this.rateLimiter.resetRetryCount(),204===s.status)return;const i=s.headers?.get?.("content-length");if("0"===i)return;const o=await s.json();return a&&this.cache&&"GET"===r.method&&await this.cache.set(n,JSON.stringify(o),this.cacheTtl),o}catch(e){if(e instanceof i)throw e;if(e instanceof t||e instanceof s)throw e;throw new t(`Request failed: ${e instanceof Error?e.message:"Unknown error"}`,0,e)}}sleep(e){return new Promise(t=>setTimeout(t,e))}buildFormBody(e){const t=new URLSearchParams;for(const[s,i]of Object.entries(e))if(null!=i)if(Array.isArray(i))for(const e of i)null!=e&&t.append(s,String(e));else t.append(s,String(i));return t}getApiKey(){return this.sharedSecret?`${this.keystring}:${this.sharedSecret}`:this.keystring}async getUser(){return this.makeRequest("/users/me")}async getShop(e){if(e)return this.makeRequest(`/shops/${e}`);const s=await this.getUser();if(!s.shop_id)throw new t("User does not have a shop",404);return this.makeRequest(`/shops/${s.shop_id}`)}async getShopByOwnerUserId(e){return this.makeRequest(`/users/${e}/shops`)}async getShopSections(e){let s=e;if(!s){const e=await this.getUser();if(!e.shop_id)throw new t("User does not have a shop",404);s=e.shop_id.toString()}const i=await this.makeRequest(`/shops/${s}/sections`);return this.logger.info(`Found ${i.results.length} shop sections`),i.results}async getShopSection(e,t){return this.makeRequest(`/shops/${e}/sections/${t}`)}async getListingsByShop(e,s={}){let i=e;if(!i){const e=await this.getUser();if(!e.shop_id)throw new t("User does not have a shop",404);i=e.shop_id.toString()}const r=new URLSearchParams;r.set("state",s.state||"active"),void 0!==s.limit&&r.set("limit",s.limit.toString()),void 0!==s.offset&&r.set("offset",s.offset.toString()),s.sort_on&&r.set("sort_on",s.sort_on),s.sort_order&&r.set("sort_order",s.sort_order),s.includes&&r.set("includes",s.includes.join(",")),void 0!==s.legacy&&r.set("legacy",s.legacy.toString());return(await this.makeRequest(`/shops/${i}/listings?${r.toString()}`)).results}async getListing(e,t){const s=Array.isArray(t)?{includes:t}:t,i=new URLSearchParams;s?.includes&&i.set("includes",s.includes.join(",")),s?.language&&i.set("language",s.language),void 0!==s?.legacy&&i.set("legacy",s.legacy.toString()),void 0!==s?.allow_suggested_title&&i.set("allow_suggested_title",s.allow_suggested_title.toString());const r=i.toString(),n=r?`?${r}`:"";return this.makeRequest(`/listings/${e}${n}`)}async findAllListingsActive(e={}){const t=new URLSearchParams;e.keywords&&t.set("keywords",e.keywords),void 0!==e.limit&&t.set("limit",e.limit.toString()),void 0!==e.offset&&t.set("offset",e.offset.toString()),e.sort_on&&t.set("sort_on",e.sort_on),e.sort_order&&t.set("sort_order",e.sort_order),void 0!==e.min_price&&t.set("min_price",e.min_price.toString()),void 0!==e.max_price&&t.set("max_price",e.max_price.toString()),void 0!==e.taxonomy_id&&t.set("taxonomy_id",e.taxonomy_id.toString()),e.shop_location&&t.set("shop_location",e.shop_location),void 0!==e.legacy&&t.set("legacy",e.legacy.toString());return(await this.makeRequest(`/listings/active?${t.toString()}`)).results}async getListingImages(e){return(await this.makeRequest(`/listings/${e}/images`)).results}async getListingInventory(e,t={}){const s=new URLSearchParams;void 0!==t.show_deleted&&s.set("show_deleted",t.show_deleted.toString()),t.includes&&s.set("includes",t.includes),void 0!==t.legacy&&s.set("legacy",t.legacy.toString());const i=s.toString(),r=i?`?${i}`:"";return this.makeRequest(`/listings/${e}/inventory${r}`)}async getReviewsByListing(e,t={}){const s=new URLSearchParams;void 0!==t.limit&&s.set("limit",t.limit.toString()),void 0!==t.offset&&s.set("offset",t.offset.toString()),void 0!==t.min_created&&s.set("min_created",t.min_created.toString()),void 0!==t.max_created&&s.set("max_created",t.max_created.toString());return(await this.makeRequest(`/listings/${e}/reviews?${s.toString()}`)).results}async getReviewsByShop(e,t={}){const s=new URLSearchParams;void 0!==t.limit&&s.set("limit",t.limit.toString()),void 0!==t.offset&&s.set("offset",t.offset.toString()),void 0!==t.min_created&&s.set("min_created",t.min_created.toString()),void 0!==t.max_created&&s.set("max_created",t.max_created.toString());return(await this.makeRequest(`/shops/${e}/reviews?${s.toString()}`)).results}async getSellerTaxonomyNodes(){return(await this.makeRequest("/seller-taxonomy/nodes")).results}async getUserShops(){const e=await this.getUser();return(await this.makeRequest(`/users/${e.user_id}/shops`)).results||[]}async updateShop(e,t,s){if(s?.validate){const e=s.validateSchema||E;if(!1!==s.throwOnValidationError)x(t,e,"Invalid shop update parameters");else{const s=e.validate(t);s.valid||this.logger.warn("Shop update validation failed",s.errors)}}const i=this.buildFormBody(t);return this.makeRequest(`/shops/${e}`,{method:"PUT",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},!1)}async createShopSection(e,t){const s=this.buildFormBody(t);return this.makeRequest(`/shops/${e}/sections`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s.toString()},!1)}async updateShopSection(e,t,s){const i=this.buildFormBody(s);return this.makeRequest(`/shops/${e}/sections/${t}`,{method:"PUT",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},!1)}async deleteShopSection(e,t){await this.makeRequest(`/shops/${e}/sections/${t}`,{method:"DELETE"},!1)}async createDraftListing(e,t,s){if(s?.validate){const e=s.validateSchema||_;if(!1!==s.throwOnValidationError)x(t,e,"Invalid listing parameters");else{const s=e.validate(t);s.valid||this.logger.warn("Listing validation failed",s.errors)}}const i=s?.legacy,r=void 0!==i?`?legacy=${i}`:"",n=this.buildFormBody(t);return this.makeRequest(`/shops/${e}/listings${r}`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:n.toString()},!1)}async updateListing(e,t,s,i){if(i?.validate){const e=i.validateSchema||k;if(!1!==i.throwOnValidationError)x(s,e,"Invalid listing update parameters");else{const t=e.validate(s);t.valid||this.logger.warn("Listing update validation failed",t.errors)}}const r=i?.legacy,n=void 0!==r?`?legacy=${r}`:"",a=this.buildFormBody(s);return this.makeRequest(`/shops/${e}/listings/${t}${n}`,{method:"PATCH",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:a.toString()},!1)}async deleteListing(e){await this.makeRequest(`/listings/${e}`,{method:"DELETE"},!1)}async updateListingInventory(e,t,s){const i=s?.legacy,r=void 0!==i?`?legacy=${i}`:"";return this.makeRequest(`/listings/${e}/inventory${r}`,{method:"PUT",body:JSON.stringify(t)},!1)}async uploadListingImage(e,s,i,r){const n=new FormData;n.append("image",i),void 0!==r?.rank&&n.append("rank",r.rank.toString()),void 0!==r?.overwrite&&n.append("overwrite",r.overwrite.toString()),void 0!==r?.is_watermarked&&n.append("is_watermarked",r.is_watermarked.toString()),r?.alt_text&&n.append("alt_text",r.alt_text);const a=`${this.baseUrl}/shops/${e}/listings/${s}/images`;await this.rateLimiter.waitForRateLimit();const o=await this.tokenManager.getAccessToken(),c=await this.fetch(a,{method:"POST",headers:{Authorization:`Bearer ${o}`,"x-api-key":this.getApiKey()},body:n});if(!c.ok){const e=await c.text();throw new t(`Failed to upload image: ${c.status} ${c.statusText}`,c.status,e)}return c.json()}async getListingImage(e,t){return this.makeRequest(`/listings/${e}/images/${t}`)}async deleteListingImage(e,t,s){await this.makeRequest(`/shops/${e}/listings/${t}/images/${s}`,{method:"DELETE"},!1)}async bulkUpdateListings(e,t,s){return(s?new w(s):this.bulkOperationManager).executeBulk(t,async t=>this.updateListing(e,t.listingId.toString(),t.updates),e=>e.listingId)}async bulkUploadImages(e,t,s,i){return(i?new w(i):this.bulkOperationManager).executeBulk(s,async s=>this.uploadListingImage(e,t,s.file,{rank:s.rank,alt_text:s.altText}))}setBulkOperationConcurrency(e){this.bulkOperationManager.setConcurrency(Math.max(1,Math.min(10,e)))}async getShopReceipts(e,t){const s=new URLSearchParams;void 0!==t?.limit&&s.set("limit",t.limit.toString()),void 0!==t?.offset&&s.set("offset",t.offset.toString()),t?.sort_on&&s.set("sort_on",t.sort_on),t?.sort_order&&s.set("sort_order",t.sort_order),void 0!==t?.min_created&&s.set("min_created",t.min_created.toString()),void 0!==t?.max_created&&s.set("max_created",t.max_created.toString()),void 0!==t?.min_last_modified&&s.set("min_last_modified",t.min_last_modified.toString()),void 0!==t?.max_last_modified&&s.set("max_last_modified",t.max_last_modified.toString()),void 0!==t?.was_paid&&s.set("was_paid",t.was_paid.toString()),void 0!==t?.was_shipped&&s.set("was_shipped",t.was_shipped.toString()),void 0!==t?.was_delivered&&s.set("was_delivered",t.was_delivered.toString()),void 0!==t?.was_canceled&&s.set("was_canceled",t.was_canceled.toString()),void 0!==t?.legacy&&s.set("legacy",t.legacy.toString());return(await this.makeRequest(`/shops/${e}/receipts?${s.toString()}`)).results}async getShopReceipt(e,t,s={}){const i=new URLSearchParams;void 0!==s.legacy&&i.set("legacy",s.legacy.toString());const r=i.toString(),n=r?`?${r}`:"";return this.makeRequest(`/shops/${e}/receipts/${t}${n}`)}async updateShopReceipt(e,t,s,i){const r=i?.legacy,n=void 0!==r?`?legacy=${r}`:"",a=this.buildFormBody(s);return this.makeRequest(`/shops/${e}/receipts/${t}${n}`,{method:"PUT",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:a.toString()},!1)}async getShopReceiptTransactions(e,t,s={}){const i=new URLSearchParams;void 0!==s.legacy&&i.set("legacy",s.legacy.toString());const r=i.toString(),n=r?`?${r}`:"";return(await this.makeRequest(`/shops/${e}/receipts/${t}/transactions${n}`)).results}async getShopTransaction(e,t){return this.makeRequest(`/shops/${e}/transactions/${t}`)}async getShopShippingProfiles(e){return(await this.makeRequest(`/shops/${e}/shipping-profiles`)).results}async createShopShippingProfile(e,t){const s=this.buildFormBody(t);return this.makeRequest(`/shops/${e}/shipping-profiles`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s.toString()},!1)}async getShopShippingProfile(e,t){return this.makeRequest(`/shops/${e}/shipping-profiles/${t}`)}async updateShopShippingProfile(e,t,s){const i=this.buildFormBody(s);return this.makeRequest(`/shops/${e}/shipping-profiles/${t}`,{method:"PUT",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},!1)}async deleteShopShippingProfile(e,t){await this.makeRequest(`/shops/${e}/shipping-profiles/${t}`,{method:"DELETE"},!1)}async getShopShippingProfileDestinations(e,t,s={}){const i=new URLSearchParams;void 0!==s.limit&&i.set("limit",s.limit.toString()),void 0!==s.offset&&i.set("offset",s.offset.toString());const r=i.toString(),n=r?`?${r}`:"";return(await this.makeRequest(`/shops/${e}/shipping-profiles/${t}/destinations${n}`)).results}async createShopShippingProfileDestination(e,t,s){const i=this.buildFormBody(s);return this.makeRequest(`/shops/${e}/shipping-profiles/${t}/destinations`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},!1)}async updateShopShippingProfileDestination(e,t,s,i){const r=this.buildFormBody(i);return this.makeRequest(`/shops/${e}/shipping-profiles/${t}/destinations/${s}`,{method:"PUT",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:r.toString()},!1)}async deleteShopShippingProfileDestination(e,t,s){await this.makeRequest(`/shops/${e}/shipping-profiles/${t}/destinations/${s}`,{method:"DELETE"},!1)}async getShopShippingProfileUpgrades(e,t){return(await this.makeRequest(`/shops/${e}/shipping-profiles/${t}/upgrades`)).results}async createReceiptShipment(e,t,s,i){const r=i?.legacy,n=void 0!==r?`?legacy=${r}`:"",a=this.buildFormBody(s);return this.makeRequest(`/shops/${e}/receipts/${t}/tracking${n}`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:a.toString()},!1)}async getShopPaymentAccountLedgerEntries(e,t){const s=new URLSearchParams;s.set("min_created",t.min_created.toString()),s.set("max_created",t.max_created.toString()),void 0!==t.limit&&s.set("limit",t.limit.toString()),void 0!==t.offset&&s.set("offset",t.offset.toString());return(await this.makeRequest(`/shops/${e}/payment-account/ledger-entries?${s.toString()}`)).results}async getShopPaymentAccountLedgerEntry(e,t){return this.makeRequest(`/shops/${e}/payment-account/ledger-entries/${t}`)}async getPayments(e,t){const s=new URLSearchParams;s.set("payment_ids",t.join(","));return(await this.makeRequest(`/shops/${e}/payments?${s.toString()}`)).results}async getShopPayment(e,s){const i=await this.getPayments(e,[Number(s)]);if(0===i.length)throw new t("Payment not found",404);return i[0]}async getBuyerTaxonomyNodes(){return(await this.makeRequest("/buyer-taxonomy/nodes")).results}async getPropertiesByTaxonomyId(e){return(await this.makeRequest(`/seller-taxonomy/nodes/${e}/properties`)).results}async getListingProperties(e,t){return(await this.makeRequest(`/shops/${e}/listings/${t}/properties`)).results}async updateListingProperty(e){const{shopId:s,listingId:i,propertyId:r,valueIds:n,values:a,scaleId:o}=e,c=new URLSearchParams;n.forEach(e=>c.append("value_ids",e.toString())),a.forEach(e=>c.append("values",e)),void 0!==o&&c.append("scale_id",o.toString());const h=`${this.baseUrl}/shops/${s}/listings/${i}/properties/${r}`;await this.rateLimiter.waitForRateLimit();const u=await this.tokenManager.getAccessToken(),l=await this.fetch(h,{method:"PUT",headers:{Authorization:`Bearer ${u}`,"x-api-key":this.getApiKey(),"Content-Type":"application/x-www-form-urlencoded"},body:c.toString()});if(this.rateLimiter.updateFromHeaders(l.headers),this.rateLimiter.resetRetryCount(),!l.ok){const e=await l.text();throw new t(`Failed to update listing property: ${l.status} ${l.statusText}`,l.status,e)}return l.json()}async getShopProductionPartners(e){return(await this.makeRequest(`/shops/${e}/production-partners`)).results}getRemainingRequests(){return this.rateLimiter.getRemainingRequests()}getRateLimitStatus(){return this.rateLimiter.getRateLimitStatus()}onApproachingRateLimit(e,t){void 0!==t&&this.rateLimiter.setWarningThreshold(t),this.rateLimiter.setApproachingLimitCallback(e)}async clearCache(){this.cache&&await this.cache.clear()}getCurrentTokens(){return this.tokenManager.getCurrentTokens()}isTokenExpired(){return this.tokenManager.isTokenExpired()}async refreshToken(){return this.tokenManager.refreshToken()}async fetch(e,t){return l(),fetch(e,t)}}const P="undefined"!=typeof window;async function $(e){const t=await async function(e){if(P){const t=new Uint8Array(e);return crypto.getRandomValues(t),t}throw new Error("Crypto functions are not available in this environment")}(e);return q(t)}async function D(e){const t=new TextEncoder,s="string"==typeof e?t.encode(e):e;if(P){const e=await crypto.subtle.digest("SHA-256",s);return new Uint8Array(e)}throw new Error("SHA256 is not available in this environment")}async function L(e){return q(await D(e))}function q(e){if(P){const t=String.fromCharCode(...e);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}throw new Error("Base64URL encoding is not available in this environment")}async function I(){return $(32)}async function O(){return $(32)}async function M(e){return L(e)}class N{constructor(e){this.keystring=e.keystring,this.redirectUri=e.redirectUri,this.scopes=e.scopes,this.codeVerifier=e.codeVerifier||"",this.state=e.state||"",this.initialized=this.initialize(e)}async initialize(e){e.codeVerifier||(this.codeVerifier=await I()),e.state||(this.state=await O())}async getAuthUrl(){await this.initialized;const e=await M(this.codeVerifier);return`https://www.etsy.com/oauth/connect?${new URLSearchParams({response_type:"code",client_id:this.keystring,redirect_uri:this.redirectUri,scope:this.scopes.join(" "),state:this.state,code_challenge:e,code_challenge_method:"S256"}).toString()}`}async setAuthorizationCode(e,t){if(await this.initialized,t!==this.state)throw new s("State parameter mismatch","INVALID_STATE");this.authorizationCode=e,this.receivedState=t}async getAccessToken(){if(!this.authorizationCode)throw new s("Authorization code not set. Call setAuthorizationCode() first.","NO_AUTH_CODE");const e=new URLSearchParams({grant_type:"authorization_code",client_id:this.keystring,redirect_uri:this.redirectUri,code:this.authorizationCode,code_verifier:this.codeVerifier});try{l();const t=await fetch("https://api.etsy.com/v3/public/oauth/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:e.toString()});if(!t.ok){const e=await t.text();throw new s(`Token exchange failed: ${t.status} ${t.statusText} - ${e}`,"TOKEN_EXCHANGE_FAILED")}const i=await t.json();return{access_token:i.access_token,refresh_token:i.refresh_token,expires_at:new Date(Date.now()+1e3*i.expires_in),token_type:i.token_type,scope:i.scope}}catch(e){if(e instanceof s)throw e;throw new s(`Token exchange failed: ${e instanceof Error?e.message:"Unknown error"}`,"TOKEN_EXCHANGE_ERROR")}}async getState(){return await this.initialized,this.state}async getCodeVerifier(){return await this.initialized,this.codeVerifier}getScopes(){return[...this.scopes]}getRedirectUri(){return this.redirectUri}}const U={LISTINGS_READ:"listings_r",SHOPS_READ:"shops_r",PROFILE_READ:"profile_r",FAVORITES_READ:"favorites_r",FEEDBACK_READ:"feedback_r",TREASURY_READ:"treasury_r",LISTINGS_WRITE:"listings_w",SHOPS_WRITE:"shops_w",PROFILE_WRITE:"profile_w",FAVORITES_WRITE:"favorites_w",FEEDBACK_WRITE:"feedback_w",TREASURY_WRITE:"treasury_w",LISTINGS_DELETE:"listings_d",SHOPS_DELETE:"shops_d",PROFILE_DELETE:"profile_d",FAVORITES_DELETE:"favorites_d",FEEDBACK_DELETE:"feedback_d",TREASURY_DELETE:"treasury_d",TRANSACTIONS_READ:"transactions_r",TRANSACTIONS_WRITE:"transactions_w",BILLING_READ:"billing_r",CART_READ:"cart_r",CART_WRITE:"cart_w",RECOMMEND_READ:"recommend_r",RECOMMEND_WRITE:"recommend_w",ADDRESS_READ:"address_r",ADDRESS_WRITE:"address_w",EMAIL_READ:"email_r"},B={SHOP_READ_ONLY:[U.SHOPS_READ,U.LISTINGS_READ,U.PROFILE_READ],SHOP_MANAGEMENT:[U.SHOPS_READ,U.SHOPS_WRITE,U.LISTINGS_READ,U.LISTINGS_WRITE,U.LISTINGS_DELETE,U.PROFILE_READ,U.TRANSACTIONS_READ],BASIC_ACCESS:[U.SHOPS_READ,U.LISTINGS_READ]};class F{constructor(){this.queue=[],this.processing=!1,this.rateLimits=new Map,this.requestCount=0,this.dailyReset=new Date,this.lastRequestTime=0,this.maxRequestsPerDay=1e4,this.maxRequestsPerSecond=10,this.minRequestInterval=100,this.setNextDailyReset()}static getInstance(){return this.instance||(this.instance=new F),this.instance}static resetInstance(){this.instance=null}async enqueue(e,t={}){return new Promise((s,i)=>{const r={id:this.generateId(),request:e,resolve:s,reject:i,priority:t.priority??"normal",addedAt:Date.now(),timeout:t.timeout??3e4,endpoint:t.endpoint};this.queue.push(r),this.processing||this.processQueue().catch(e=>{console.error("Queue processing error:",e)})})}getStatus(){return{queueLength:this.queue.length,processing:this.processing,remainingRequests:Math.max(0,this.maxRequestsPerDay-this.requestCount),resetTime:this.dailyReset}}clear(){this.queue.forEach(e=>{e.reject(new Error("Queue cleared"))}),this.queue=[]}async processQueue(){if(!this.processing){this.processing=!0;try{for(;this.queue.length>0;){if(Date.now()>=this.dailyReset.getTime()&&(this.requestCount=0,this.setNextDailyReset()),this.requestCount>=this.maxRequestsPerDay){const e=this.dailyReset.getTime()-Date.now();console.warn(`Daily rate limit reached. Waiting ${Math.ceil(e/1e3/60)} minutes until reset.`),await this.delay(e),this.requestCount=0,this.setNextDailyReset()}await this.waitForRateLimit(),this.queue.sort((e,t)=>{const s={high:0,normal:1,low:2};return s[e.priority]-s[t.priority]});const e=this.queue.shift();if(!e)break;const t=Date.now()-e.addedAt;if(e.timeout&&t>e.timeout)e.reject(new Error(`Request timeout after ${t}ms (exceeded while waiting in queue)`));else{try{let s;if(e.timeout){const i=e.timeout-t;let r;const n=new Promise((t,s)=>{r=setTimeout(()=>{s(new Error(`Request timeout after ${e.timeout}ms (exceeded during execution)`))},i),r&&"function"==typeof r.unref&&r.unref()});try{s=await Promise.race([e.request(),n])}finally{r&&clearTimeout(r)}}else s=await e.request();e.resolve(s),this.requestCount++,this.lastRequestTime=Date.now()}catch(t){t instanceof i&&this.updateRateLimitInfo(t),e.reject(t)}await this.delay(this.minRequestInterval)}}}finally{this.processing=!1}}}async waitForRateLimit(){const e=Date.now(),t=this.rateLimits.get("global");if(t&&e<t.resetAt){const s=t.resetAt-e;console.log(`Global rate limit active. Waiting ${s}ms`),await this.delay(s)}const s=e-this.lastRequestTime;if(s<this.minRequestInterval){const e=this.minRequestInterval-s;await this.delay(e)}}updateRateLimitInfo(e){const t=e.retryAfter;t&&this.rateLimits.set("global",{remaining:0,resetAt:Date.now()+1e3*t})}setNextDailyReset(){const e=new Date;this.dailyReset=new Date(e),this.dailyReset.setUTCDate(this.dailyReset.getUTCDate()+1),this.dailyReset.setUTCHours(0,0,0,0)}generateId(){return`${Date.now()}_${Math.random().toString(36).substring(2,9)}`}delay(e){return new Promise(t=>{const s=setTimeout(t,e);"function"==typeof s.unref&&s.unref()})}}F.instance=null;class z{constructor(e,t={}){this.currentPage=[],this.totalCount=null,this.hasMore=!0,this.fetcher=e,this.options={limit:t.limit||25,offset:t.offset||0,maxPages:t.maxPages||1/0,maxItems:t.maxItems||1/0},this.currentOffset=this.options.offset}async*[Symbol.asyncIterator](){let e=0,t=0;for(;this.hasMore&&e<this.options.maxPages&&t<this.options.maxItems;){const s=await this.fetchPage();e++;for(const e of s){if(t>=this.options.maxItems)return;yield e,t++}if(!this.hasMore)break}}async getAll(){const e=[];for await(const t of this)e.push(t);return e}getCurrentPage(){return this.currentPage}hasNextPage(){return this.hasMore}async getNextPage(){return this.hasMore?this.fetchPage():[]}getTotalCount(){return this.totalCount}reset(){this.currentOffset=this.options.offset,this.currentPage=[],this.totalCount=null,this.hasMore=!0}async fetchPage(){const e=await this.fetcher(this.options.limit,this.currentOffset);return this.currentPage=e.results,this.totalCount=e.count,e.pagination?void 0!==e.pagination.next_offset?(this.hasMore=!0,this.currentOffset=e.pagination.next_offset):this.hasMore=!1:(this.hasMore=e.results.length===this.options.limit,this.currentOffset+=this.options.limit),this.currentPage}}const j={maxRetries:3,retryDelay:1e3,exponentialBackoff:!0,retryableStatusCodes:[429,500,502,503,504],maxRetryDelay:3e4,jitter:.1};function H(e,t){let s;if(s=t.exponentialBackoff?t.retryDelay*Math.pow(2,e-1):t.retryDelay*e,t.maxRetryDelay&&(s=Math.min(s,t.maxRetryDelay)),t.jitter&&t.jitter>0){const e=s*t.jitter;s+=Math.random()*e*2-e}return Math.max(0,Math.floor(s))}function K(e,s){if(e instanceof t){const t=e.statusCode;if(t&&s.retryableStatusCodes.includes(t))return!0}return!!(e instanceof TypeError&&e.message.includes("fetch"))}function W(e){if(e instanceof t&&429===e.statusCode){const t=e.getRetryAfter();if("number"==typeof t)return 1e3*t}return null}function V(e){return new Promise(t=>setTimeout(t,e))}async function G(e,t={}){const s={...j,...t};let i=new Error("Unknown error"),r=0;for(;r<=s.maxRetries;)try{if(t.signal?.aborted)throw new Error("Operation aborted");return await e()}catch(e){if(i=e,r++,r>s.maxRetries)throw i;if(!K(e,s))throw i;let t=H(r,s);const n=W(e);null!==n&&(t=n),s.onRetry&&s.onRetry(r,i),await V(t)}throw i}class J{constructor(e){this.config={algorithm:"sha256",verifySignatures:!0,...e},this.handlers=new Map}verifySignature(e,t){if(!this.config.verifySignatures)return!0;if(!this.crypto)throw new Error("Signature verification requires Node.js crypto module");try{const s=this.crypto.createHmac(this.config.algorithm,this.config.secret).update(e).digest("hex");return this.timingSafeEqual(s,t)}catch(e){return console.error("Signature verification failed:",e),!1}}parseEvent(e){const t="string"==typeof e?JSON.parse(e):e;if(!t.type||!t.data)throw new Error("Invalid webhook event format");const s={type:t.type,timestamp:t.timestamp||Date.now(),data:t.data,shop_id:t.shop_id,user_id:t.user_id};return this.triggerHandlers(s),s}on(e,t){this.handlers.has(e)||this.handlers.set(e,new Set);const s=this.handlers.get(e);s&&s.add(t)}off(e,t){const s=this.handlers.get(e);s&&s.delete(t)}removeAllListeners(e){e?this.handlers.delete(e):this.handlers.clear()}async triggerHandlers(e){const t=this.handlers.get(e.type);if(!t||0===t.size)return;const s=Array.from(t).map(t=>{try{return Promise.resolve(t(e.data))}catch(t){return console.error(`Error in webhook handler for ${e.type}:`,t),Promise.resolve()}});await Promise.all(s)}timingSafeEqual(e,t){if(this.crypto?.timingSafeEqual){const s=Buffer.from(e),i=Buffer.from(t);return s.length===i.length&&this.crypto.timingSafeEqual(s,i)}return e===t}getHandlerCount(e){return this.handlers.get(e)?.size||0}getRegisteredEventTypes(){return Array.from(this.handlers.keys())}}class Q{constructor(e={}){this.cache=new Map,this.currentSize=0,this.stats={hits:0,misses:0,evictions:0},this.maxSize=e.maxSize??10485760,this.maxEntries=e.maxEntries??1e3,this.ttl=1e3*(e.ttl??3600),this.trackStats=e.trackStats??!0}async get(e){const t=this.cache.get(e);return t?Date.now()>t.expires?(this.cache.delete(e),this.currentSize-=t.size,this.trackStats&&this.stats.misses++,null):(t.lastAccessed=Date.now(),t.accessCount++,this.cache.delete(e),this.cache.set(e,t),this.trackStats&&this.stats.hits++,t.value):(this.trackStats&&this.stats.misses++,null)}async set(e,t,s){const i=this.estimateSize(t),r=s?1e3*s:this.ttl,n=Date.now(),a=this.cache.get(e);for(a&&(this.currentSize-=a.size,this.cache.delete(e));(this.currentSize+i>this.maxSize||this.cache.size>=this.maxEntries)&&this.cache.size>0;)await this.evictLRU();const o={key:e,value:t,expires:n+r,size:i,accessCount:0,lastAccessed:n,created:n};this.cache.set(e,o),this.currentSize+=i}async delete(e){const t=this.cache.get(e);t&&(this.cache.delete(e),this.currentSize-=t.size)}async clear(){this.cache.clear(),this.currentSize=0,this.stats={hits:0,misses:0,evictions:0}}async evictLRU(){const e=this.cache.keys().next().value;if(e){const t=this.cache.get(e);t&&(this.currentSize-=t.size),this.cache.delete(e),this.trackStats&&this.stats.evictions++}}estimateSize(e){return 2*e.length}getStats(){const e=this.stats.hits+this.stats.misses;return{hits:this.stats.hits,misses:this.stats.misses,hitRate:e>0?this.stats.hits/e:0,missRate:e>0?this.stats.misses/e:0,size:this.currentSize,entryCount:this.cache.size,evictions:this.stats.evictions,maxSize:this.maxSize,maxEntries:this.maxEntries}}}class Y{constructor(e={}){this.cache=new Map,this.currentSize=0,this.stats={hits:0,misses:0,evictions:0},this.maxSize=e.maxSize??10485760,this.maxEntries=e.maxEntries??1e3,this.ttl=1e3*(e.ttl??3600),this.trackStats=e.trackStats??!0}async get(e){const t=this.cache.get(e);return t?Date.now()>t.expires?(this.cache.delete(e),this.currentSize-=t.size,this.trackStats&&this.stats.misses++,null):(t.accessCount++,t.lastAccessed=Date.now(),this.trackStats&&this.stats.hits++,t.value):(this.trackStats&&this.stats.misses++,null)}async set(e,t,s){const i=this.estimateSize(t),r=s?1e3*s:this.ttl,n=Date.now(),a=this.cache.get(e);for(a&&(this.currentSize-=a.size,this.cache.delete(e));(this.currentSize+i>this.maxSize||this.cache.size>=this.maxEntries)&&this.cache.size>0;)await this.evictLFU();const o={key:e,value:t,expires:n+r,size:i,accessCount:0,lastAccessed:n,created:n};this.cache.set(e,o),this.currentSize+=i}async delete(e){const t=this.cache.get(e);t&&(this.cache.delete(e),this.currentSize-=t.size)}async clear(){this.cache.clear(),this.currentSize=0,this.stats={hits:0,misses:0,evictions:0}}async evictLFU(){let e=1/0,t=null;for(const[s,i]of this.cache.entries())i.accessCount<e&&(e=i.accessCount,t=s);if(t){const e=this.cache.get(t);e&&(this.currentSize-=e.size),this.cache.delete(t),this.trackStats&&this.stats.evictions++}}estimateSize(e){return 2*e.length}getStats(){const e=this.stats.hits+this.stats.misses;return{hits:this.stats.hits,misses:this.stats.misses,hitRate:e>0?this.stats.hits/e:0,missRate:e>0?this.stats.misses/e:0,size:this.currentSize,entryCount:this.cache.size,evictions:this.stats.evictions,maxSize:this.maxSize,maxEntries:this.maxEntries}}}class X{constructor(e,t={}){this.baseCache=e,this.invalidationPatterns=new Set(t.invalidateOn?.patterns||[]),this.mutationPatterns=new Map,!1!==t.invalidateOn?.mutations&&(this.mutationPatterns.set("shops/*/update",["shops/*"]),this.mutationPatterns.set("listings/*/update",["listings/*"]),this.mutationPatterns.set("receipts/*/update",["receipts/*"]))}async get(e){return this.baseCache.get(e)}async set(e,t,s){return this.baseCache.set(e,t,s)}async delete(e){return this.baseCache.delete(e)}async clear(){return this.baseCache.clear()}async invalidatePattern(e){await this.baseCache.clear()}async invalidateOnMutation(e){const t=this.mutationPatterns.get(e);if(t)for(const e of t)await this.invalidatePattern(e)}addInvalidationPattern(e){this.invalidationPatterns.add(e)}removeInvalidationPattern(e){this.invalidationPatterns.delete(e)}addMutationPattern(e,t){this.mutationPatterns.set(e,t)}}class Z{constructor(e,t){if(this.config=e,this.keyPrefix=e.keyPrefix||"etsy:",!t)throw new Error("Redis client must be provided. Install a Redis client library (e.g., ioredis) and pass the client instance.");this.client=t}async get(e){try{return await this.client.get(this.keyPrefix+e)}catch(e){return console.error("Redis get error:",e),null}}async set(e,t,s=3600){try{await this.client.setex(this.keyPrefix+e,s,t)}catch(e){console.error("Redis set error:",e)}}async delete(e){try{await this.client.del(this.keyPrefix+e)}catch(e){console.error("Redis delete error:",e)}}async clear(){try{const e=await this.client.keys(this.keyPrefix+"*");e.length>0&&await this.client.del(...e)}catch(e){console.error("Redis clear error:",e)}}getClient(){return this.client}}class ee{constructor(e,t){this.params={},this.includeFields=[],this.client=e,this.shopId=t}where(e){return e.state&&(this.params.state=e.state),this}include(e){return this.includeFields=e,this.params.includes=e,this}limit(e){return this.params.limit=e,this}offset(e){return this.params.offset=e,this}sortBy(e,t="asc"){return this.params.sort_on=e,this.params.sort_order="asc"===t?"up":"down",this}async fetch(){if(!this.shopId)throw new Error("Shop ID is required for listing queries");return this.client.getListingsByShop(this.shopId,this.params)}async first(){this.params.limit=1;return(await this.fetch())[0]||null}async count(){return(await this.fetch()).length}}class te{constructor(e,t){this.params={},this.client=e,this.shopId=t}where(e){return Object.assign(this.params,e),this}limit(e){return this.params.limit=e,this}offset(e){return this.params.offset=e,this}sortBy(e,t="desc"){return this.params.sort_on=e,this.params.sort_order="asc"===t?"up":"down",this}async fetch(){return this.client.getShopReceipts(this.shopId,this.params)}async first(){this.params.limit=1;return(await this.fetch())[0]||null}}class se{constructor(e){this.operations=[],this.client=e}getShop(e){return this.operations.push(()=>this.client.getShop(e)),this}getListings(e,t){return this.operations.push(()=>this.client.getListingsByShop(e,t)),this}getReceipts(e,t){return this.operations.push(()=>this.client.getShopReceipts(e,t)),this}custom(e){return this.operations.push(e),this}async execute(){return Promise.all(this.operations.map(e=>e()))}async executeSequential(){const e=[];for(const t of this.operations)e.push(await t());return e}clear(){return this.operations=[],this}size(){return this.operations.length}}class ie{constructor(e){if(this.secret=e.secret,this.algorithm=e.algorithm||"sha256",!this.secret)throw new Error("Webhook secret is required");throw new Error("WebhookSecurity is only available in Node.js environments")}signRequest(e,t="hex"){if(!this.crypto)throw new Error("Crypto module not available");const s="string"==typeof e?e:JSON.stringify(e);return this.crypto.createHmac(this.algorithm,this.secret).update(s).digest(t)}verifySignature(e,t,s="hex"){if(!this.crypto)throw new Error("Crypto module not available");try{const i=this.signRequest(e,s);return this.timingSafeEqual(i,t)}catch(e){return console.error("Signature verification failed:",e),!1}}signRequestWithTimestamp(e,t,s="hex"){const i=t||Math.floor(Date.now()/1e3),r=`${i}.${"string"==typeof e?e:JSON.stringify(e)}`;return{timestamp:i,signature:this.signRequest(r,s)}}verifySignatureWithTimestamp(e,t,s,i=300,r="hex"){const n=Math.floor(Date.now()/1e3)-s;if(n<0)return console.warn("Webhook timestamp is in the future"),!1;if(n>i)return console.warn(`Webhook request is too old: ${n} seconds`),!1;const a=`${s}.${"string"==typeof e?e:JSON.stringify(e)}`;return this.verifySignature(a,t,r)}timingSafeEqual(e,t){if(!this.crypto?.timingSafeEqual)return console.warn("crypto.timingSafeEqual not available, using simple comparison"),e===t;const s=Buffer.from(e),i=Buffer.from(t);return s.length===i.length&&this.crypto.timingSafeEqual(s,i)}getAlgorithm(){return this.algorithm}updateSecret(e){if(!e)throw new Error("Secret cannot be empty");this.secret=e}}const re="2.2.0",ne="etsy-v3-api-client";e.AuthHelper=N,e.BatchQueryExecutor=se,e.BulkOperationManager=w,e.COMMON_SCOPE_COMBINATIONS=B,e.CacheWithInvalidation=X,e.CreateListingSchema=_,e.DEFAULT_RETRY_CONFIG=j,e.ETSY_SCOPES=U,e.EncryptedFileTokenStorage=class{constructor(e){throw new Error("EncryptedFileTokenStorage is only available in Node.js environments")}async save(e){throw new Error("EncryptedFileTokenStorage is only available in Node.js")}async load(){return null}async clear(){}getFilePath(){return this.filePath}async exists(){return!1}async _writeFile(e,t){}async _setFilePermissions(e,t){if("undefined"!=typeof process)try{const s={promises:{writeFile:()=>{},readFile:()=>{},unlink:()=>{}}};await s.promises.chmod(e,t)}catch{}}async _readFile(e){if("undefined"==typeof process)throw new Error("Not available");throw new Error("readFile not available in browser")}async _deleteFile(e){}async _stat(e){if("undefined"==typeof process)throw new Error("Not available");return await{writeFile:()=>{},readFile:()=>{},unlink:()=>{}}.stat(e)}},e.EtsyApiError=t,e.EtsyAuthError=s,e.EtsyClient=A,e.EtsyRateLimitError=i,e.EtsyRateLimiter=f,e.EtsyWebhookHandler=J,e.FieldValidator=R,e.FileTokenStorage=class{constructor(e){throw new Error("FileTokenStorage is only available in Node.js environments")}async save(e){throw new Error("FileTokenStorage is only available in Node.js")}async load(){return null}async clear(){}async _writeFile(e,t){}async _setFilePermissions(e,t){if("undefined"!=typeof process)try{const s={promises:{writeFile:()=>{},readFile:()=>{},unlink:()=>{}}};await s.promises.chmod(e,t)}catch{}}async _readFile(e){if("undefined"==typeof process)throw new Error("Not available");throw new Error("readFile not available in browser")}async _deleteFile(e){}},e.GlobalRequestQueue=F,e.LFUCache=Y,e.LIBRARY_NAME=ne,e.LRUCache=Q,e.ListingQueryBuilder=ee,e.LocalStorageTokenStorage=m,e.MemoryTokenStorage=d,e.PaginatedResults=z,e.PluginManager=class{constructor(){this.plugins=[]}async register(e){if(this.plugins.some(t=>t.name===e.name))throw new Error(`Plugin with name "${e.name}" is already registered`);this.plugins.push(e),e.onInit&&await e.onInit()}async unregister(e){const t=this.plugins.findIndex(t=>t.name===e);if(-1===t)return!1;const s=this.plugins[t];return!!s&&(s.onDestroy&&await s.onDestroy(),this.plugins.splice(t,1),!0)}getPlugins(){return this.plugins}getPlugin(e){return this.plugins.find(t=>t.name===e)}async executeBeforeRequest(e){let t=e;for(const e of this.plugins)e.onBeforeRequest&&(t=await e.onBeforeRequest(t));return t}async executeAfterResponse(e){let t=e;for(const e of this.plugins)e.onAfterResponse&&(t=await e.onAfterResponse(t));return t}async executeOnError(e){for(const t of this.plugins)t.onError&&await t.onError(e)}async clear(){for(const e of this.plugins)e.onDestroy&&await e.onDestroy();this.plugins=[]}},e.ReceiptQueryBuilder=te,e.RedisCacheStorage=Z,e.RetryManager=class{constructor(e={}){this.config={...j,...e}}async execute(e,t){return G(e,{...this.config,...t})}updateConfig(e){this.config={...this.config,...e}}getConfig(){return{...this.config}}},e.SecureTokenStorage=class{constructor(e={}){if(this.encryptionKey=null,"undefined"==typeof window)throw new Error("SecureTokenStorage is only available in browser environments. For Node.js, use EncryptedFileTokenStorage instead.");if(!window.crypto||!window.crypto.subtle)throw new Error("Web Crypto API is not supported in this browser. Please use a modern browser (Chrome 37+, Firefox 34+, Safari 11+, Edge 79+).");this.keyPrefix=e.keyPrefix||"etsy_token",this.derivationInput=e.derivationInput||this.getDefaultDerivationInput(),this.storage=e.useSessionStorage?sessionStorage:localStorage}async save(e){const t=await this.getEncryptionKey(),s=JSON.stringify(e),i=(new TextEncoder).encode(s),r=window.crypto.getRandomValues(new Uint8Array(12)),n=await window.crypto.subtle.encrypt({name:"AES-GCM",iv:r},t,i),a=await this.generateIntegrity(n,r),o={version:1,encrypted:this.arrayBufferToBase64(n),iv:this.arrayBufferToBase64(r),integrity:this.arrayBufferToBase64(a),expiresAt:e.expires_at.getTime(),timestamp:Date.now()};this.storage.setItem(this.keyPrefix,JSON.stringify(o))}async load(){const e=this.storage.getItem(this.keyPrefix);if(!e)return null;try{const t=JSON.parse(e);if(1!==t.version)return console.warn("Unsupported token storage version. Clearing storage."),await this.clear(),null;if(Date.now()>t.expiresAt)return console.info("Stored tokens have expired. Clearing storage."),await this.clear(),null;const s=this.base64ToArrayBuffer(t.encrypted),i=this.base64ToArrayBuffer(t.iv),r=this.base64ToArrayBuffer(t.integrity),n=await this.generateIntegrity(s,i);if(!this.compareArrayBuffers(r,n))return console.warn("Token integrity check failed. Data may have been tampered with. Clearing storage."),await this.clear(),null;const a=await this.getEncryptionKey(),o=await window.crypto.subtle.decrypt({name:"AES-GCM",iv:i},a,s),c=(new TextDecoder).decode(o),h=JSON.parse(c);return h.expires_at=new Date(h.expires_at),h}catch(e){return console.error("Failed to load tokens from storage:",e),await this.clear(),null}}async clear(){this.storage.removeItem(this.keyPrefix)}async getEncryptionKey(){if(this.encryptionKey)return this.encryptionKey;const e=await this.deriveKeyMaterial();return this.encryptionKey=await window.crypto.subtle.deriveKey({name:"PBKDF2",salt:this.stringToArrayBuffer("etsy-v3-api-client-salt"),iterations:1e5,hash:"SHA-256"},e,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]),this.encryptionKey}async deriveKeyMaterial(){const e=this.stringToArrayBuffer(this.derivationInput);return window.crypto.subtle.importKey("raw",e,"PBKDF2",!1,["deriveKey"])}async generateIntegrity(e,t){const s=new Uint8Array(e.byteLength+t.byteLength);s.set(new Uint8Array(e),0),s.set(new Uint8Array(t),e.byteLength);const i=await this.deriveKeyMaterial(),r=await window.crypto.subtle.deriveKey({name:"PBKDF2",salt:this.stringToArrayBuffer("etsy-integrity-salt"),iterations:1e5,hash:"SHA-256"},i,{name:"HMAC",hash:"SHA-256"},!1,["sign"]);return window.crypto.subtle.sign("HMAC",r,s)}getDefaultDerivationInput(){return`${window.location.hostname}:${navigator.userAgent}`}stringToArrayBuffer(e){return(new TextEncoder).encode(e)}arrayBufferToBase64(e){const t=new Uint8Array(e);let s="";for(let e=0;e<t.byteLength;e++){const i=t[e];void 0!==i&&(s+=String.fromCharCode(i))}return btoa(s)}base64ToArrayBuffer(e){const t=atob(e),s=new Uint8Array(t.length);for(let e=0;e<t.length;e++)s[e]=t.charCodeAt(e);return s.buffer}compareArrayBuffers(e,t){if(e.byteLength!==t.byteLength)return!1;const s=new Uint8Array(e),i=new Uint8Array(t);for(let e=0;e<s.length;e++)if(s[e]!==i[e])return!1;return!0}},e.SessionStorageTokenStorage=p,e.TokenManager=g,e.UpdateListingSchema=k,e.UpdateShopSchema=E,e.VERSION=re,e.ValidationException=T,e.Validator=S,e.WebhookSecurity=ie,e.combineValidators=function(...e){return{validate(t){const s=[];for(const i of e){const e=i.validate(t);e.valid||s.push(...e.errors)}return{valid:0===s.length,errors:s}}}},e.createAnalyticsPlugin=function(e){const t=new Map;return{name:"analytics",version:"1.0.0",onBeforeRequest(e){const s=`${e.method}:${e.endpoint}:${Date.now()}`;return t.set(s,Date.now()),e},onAfterResponse(s){if(e.trackEndpoint){const s=Array.from(t.keys()).pop();if(s){const i=t.get(s);if(i){const r=Date.now()-i,n=s.split(":"),a=n[0],o=n[1];o&&a&&e.trackEndpoint(o,a,r),t.delete(s)}}}return s},onError(t){e.trackError&&e.trackError(t)}}},e.createAuthHelper=function(e){return new N(e)},e.createBatchQuery=function(e){return new se(e)},e.createBulkOperationManager=function(e){return new w(e)},e.createCacheStorage=function(e={}){switch(e.strategy||"lru"){case"lru":case"ttl":default:return new Q(e);case"lfu":return new Y(e)}},e.createCacheWithInvalidation=function(e,t={}){return new X(e,t)},e.createCachingPlugin=function(e={}){const t=new Map,s=1e3*(e.ttl||300);return{name:"caching",version:"1.0.0",onBeforeRequest(s){if("GET"===s.method.toUpperCase()){const i=(t=>e.keyGenerator?e.keyGenerator(t):`${t.method}:${t.endpoint}:${JSON.stringify(t.params||{})}`)(s),r=t.get(i);r&&(Date.now(),r.expiresAt)}return s},onAfterResponse(e){const i=`GET:${e.status}`;return t.set(i,{data:e.data,expiresAt:Date.now()+s}),e},onDestroy(){t.clear()}}},e.createCodeChallenge=M,e.createDefaultTokenStorage=function(e){return e?.preferSession&&u?new p(e?.storageKey):h?new m(e?.storageKey):u?new p(e?.storageKey):new d},e.createEtsyClient=function(e){return new A(e)},e.createListingQuery=function(e,t){return new ee(e,t)},e.createLoggingPlugin=function(e={}){const s=e.logger||console,i=e.logLevel||"info",r=e=>{const t=["debug","info","warn","error"],s=t.indexOf(i);return t.indexOf(e)>=s};return{name:"logging",version:"1.0.0",onBeforeRequest:e=>(r("debug")&&s.debug("[Etsy API] Request:",{method:e.method,endpoint:e.endpoint,params:e.params}),e),onAfterResponse:e=>(r("debug")&&s.debug("[Etsy API] Response:",{status:e.status,data:e.data}),e),onError(e){r("error")&&(s.error("[Etsy API] Error:",e),e instanceof t&&s.error("[Etsy API] Error Details:",{statusCode:e.statusCode,endpoint:e.endpoint,suggestions:e.suggestions}))}}},e.createPaginatedResults=function(e,t){return new z(e,t)},e.createRateLimitPlugin=function(e={}){const t=e.maxRequestsPerSecond||10,s=[];return{name:"rateLimit",version:"1.0.0",async onBeforeRequest(i){const r=Date.now(),n=r-1e3;for(;s.length>0&&void 0!==s[0]&&s[0]<n;)s.shift();if(s.length>=t){e.onRateLimitExceeded&&e.onRateLimitExceeded();const t=s[0];if(void 0!==t){const e=t+1e3-r;e>0&&await new Promise(t=>setTimeout(t,e))}}return s.push(r),i}}},e.createRateLimiter=function(e){return new f(e)},e.createReceiptQuery=function(e,t){return new te(e,t)},e.createRedisCacheStorage=function(e,t){return new Z(e,t)},e.createRetryPlugin=function(e={}){const{retryableStatusCodes:s=[408,429,500,502,503,504]}=e;return{name:"retry",version:"1.0.0",async onError(i){if(i instanceof t){const t=i.statusCode;t&&s.includes(t)&&e.onRetry&&e.onRetry(1,i)}}}},e.createTokenManager=function(e,t){return new g(e,t)},e.createValidator=function(){return new S},e.createWebhookHandler=function(e){return new J(e)},e.createWebhookSecurity=function(e,t="sha256"){return new ie({secret:e,algorithm:t})},e.decryptAES256GCM=async function(e,t){throw new Error("AES-256-GCM decryption is only available in Node.js environments")},e.default=A,e.defaultRateLimiter=y,e.deriveKeyFromPassword=async function(e,t,s=1e5,i=32){throw new Error("Key derivation is only available in Node.js environments")},e.encryptAES256GCM=async function(e,t){throw new Error("AES-256-GCM encryption is only available in Node.js environments")},e.executeBulkOperation=async function(e,t,s){return new w(s).executeBulk(e,t)},e.field=v,e.generateCodeVerifier=I,e.generateEncryptionKey=async function(e=32){throw new Error("Key generation is only available in Node.js environments")},e.generateRandomBase64Url=$,e.generateState=O,e.getAvailableStorage=function(){return h?"localStorage":u?"sessionStorage":"memory"},e.getEnvironmentInfo=function(){return{isBrowser:r,isNode:Boolean(n),isWebWorker:a,hasFetch:o,hasWebCrypto:c,hasLocalStorage:h,hasSessionStorage:u,userAgent:r?navigator.userAgent:"Node.js",nodeVersion:void 0}},e.getGlobalQueue=function(){return F.getInstance()},e.getLibraryInfo=function(){return{name:ne,version:re,description:"JavaScript/TypeScript client for the Etsy Open API v3 with OAuth 2.0 authentication",author:"profplum700",license:"MIT",homepage:"https://github.com/ForestHillArtsHouse/etsy-v3-api-client#readme"}},e.hasLocalStorage=h,e.hasSessionStorage=u,e.isBrowser=r,e.isNode=n,e.isSecureStorageSupported=function(){return"undefined"!=typeof window&&!(!window.crypto||!window.crypto.subtle||"function"!=typeof window.crypto.subtle.encrypt||"function"!=typeof window.crypto.subtle.decrypt||"function"!=typeof window.crypto.subtle.deriveKey)},e.sha256=D,e.sha256Base64Url=L,e.validate=function(e,t){return t.validate(e)},e.validateEncryptionKey=function(e,t=32){const s=Buffer.isBuffer(e)?e:Buffer.from(e,"utf8");if(s.length!==t)throw new Error(`Invalid encryption key length: expected ${t} bytes, got ${s.length} bytes`);return!0},e.validateOrThrow=x,e.withQueryBuilder=function(e){const t=e;return t.listings=function(e){return new ee(this,e)},t.receipts=function(e){return new te(this,e)},t.batch=function(){return new se(this)},t},e.withQueue=function(e,t){return F.getInstance().enqueue(e,t)},e.withRetry=G,Object.defineProperty(e,"__esModule",{value:!0})});
|
|
2
2
|
//# sourceMappingURL=browser.umd.js.map
|