@profplum700/etsy-v3-api-client 2.5.3 → 3.0.0

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.
@@ -639,6 +639,11 @@ class FileTokenStorage {
639
639
  }
640
640
  }
641
641
 
642
+ const ETSY_RATE_LIMITS = {
643
+ MAX_REQUESTS_PER_DAY: 5000,
644
+ MAX_REQUESTS_PER_SECOND: 5,
645
+ MIN_REQUEST_INTERVAL: 200,
646
+ };
642
647
  class EtsyRateLimiter {
643
648
  constructor(config) {
644
649
  this.requestCount = 0;
@@ -647,9 +652,9 @@ class EtsyRateLimiter {
647
652
  this.isHeaderBasedLimiting = false;
648
653
  this.currentRetryCount = 0;
649
654
  this.config = {
650
- maxRequestsPerDay: 10000,
651
- maxRequestsPerSecond: 10,
652
- minRequestInterval: 100,
655
+ maxRequestsPerDay: ETSY_RATE_LIMITS.MAX_REQUESTS_PER_DAY,
656
+ maxRequestsPerSecond: ETSY_RATE_LIMITS.MAX_REQUESTS_PER_SECOND,
657
+ minRequestInterval: ETSY_RATE_LIMITS.MIN_REQUEST_INTERVAL,
653
658
  maxRetries: 3,
654
659
  baseDelayMs: 1000,
655
660
  maxDelayMs: 30000,
@@ -704,7 +709,7 @@ class EtsyRateLimiter {
704
709
  if (value === null)
705
710
  return undefined;
706
711
  const num = parseInt(value, 10);
707
- return isNaN(num) ? undefined : num;
712
+ return Number.isNaN(num) ? undefined : num;
708
713
  };
709
714
  return {
710
715
  limitPerSecond: parseNumber(getHeader('x-limit-per-second')),
@@ -1045,7 +1050,7 @@ class FieldValidator {
1045
1050
  const value = data[this.field];
1046
1051
  if (value === undefined || value === null)
1047
1052
  return null;
1048
- if (typeof value !== 'number' || isNaN(value)) {
1053
+ if (typeof value !== 'number' || Number.isNaN(value)) {
1049
1054
  return {
1050
1055
  field: this.field,
1051
1056
  message: options.message || `${this.field} must be a number`,
@@ -1254,7 +1259,11 @@ function combineValidators(...validators) {
1254
1259
 
1255
1260
  class DefaultLogger {
1256
1261
  debug(message, ...args) {
1257
- const isDevelopment = window.location?.hostname === 'localhost' || window.location?.hostname === '127.0.0.1';
1262
+ let isDevelopment;
1263
+ {
1264
+ const host = window.location?.hostname;
1265
+ isDevelopment = host === 'localhost' || host === '127.0.0.1';
1266
+ }
1258
1267
  if (isDevelopment) {
1259
1268
  console.log(`[DEBUG] ${message}`, ...args);
1260
1269
  }
@@ -1296,6 +1305,9 @@ class MemoryCache {
1296
1305
  }
1297
1306
  class EtsyClient {
1298
1307
  constructor(config) {
1308
+ if (!config.sharedSecret) {
1309
+ throw new EtsyAuthError('sharedSecret is REQUIRED for Etsy API v3 application usage. See: https://github.com/profplum700/etsy-v3-api-client/issues/21');
1310
+ }
1299
1311
  this.tokenManager = new TokenManager(config);
1300
1312
  this.baseUrl = config.baseUrl || 'https://api.etsy.com/v3/application';
1301
1313
  this.logger = new DefaultLogger();
@@ -1303,9 +1315,9 @@ class EtsyClient {
1303
1315
  this.sharedSecret = config.sharedSecret;
1304
1316
  if (config.rateLimiting?.enabled !== false) {
1305
1317
  this.rateLimiter = new EtsyRateLimiter({
1306
- maxRequestsPerDay: config.rateLimiting?.maxRequestsPerDay || 10000,
1307
- maxRequestsPerSecond: config.rateLimiting?.maxRequestsPerSecond || 10,
1308
- minRequestInterval: config.rateLimiting?.minRequestInterval ?? 100,
1318
+ maxRequestsPerDay: config.rateLimiting?.maxRequestsPerDay || ETSY_RATE_LIMITS.MAX_REQUESTS_PER_DAY,
1319
+ maxRequestsPerSecond: config.rateLimiting?.maxRequestsPerSecond || ETSY_RATE_LIMITS.MAX_REQUESTS_PER_SECOND,
1320
+ minRequestInterval: config.rateLimiting?.minRequestInterval ?? ETSY_RATE_LIMITS.MIN_REQUEST_INTERVAL,
1309
1321
  maxRetries: config.rateLimiting?.maxRetries,
1310
1322
  baseDelayMs: config.rateLimiting?.baseDelayMs,
1311
1323
  maxDelayMs: config.rateLimiting?.maxDelayMs,
@@ -1336,7 +1348,12 @@ class EtsyClient {
1336
1348
  if (useCache && this.cache && requestOptions.method === 'GET') {
1337
1349
  const cached = await this.cache.get(cacheKey);
1338
1350
  if (cached) {
1339
- return JSON.parse(cached);
1351
+ try {
1352
+ return JSON.parse(cached);
1353
+ }
1354
+ catch {
1355
+ await this.cache.delete(cacheKey);
1356
+ }
1340
1357
  }
1341
1358
  }
1342
1359
  await this.rateLimiter.waitForRateLimit();
@@ -1410,10 +1427,7 @@ class EtsyClient {
1410
1427
  return body;
1411
1428
  }
1412
1429
  getApiKey() {
1413
- if (this.sharedSecret) {
1414
- return `${this.keystring}:${this.sharedSecret}`;
1415
- }
1416
- return this.keystring;
1430
+ return `${this.keystring}:${this.sharedSecret}`;
1417
1431
  }
1418
1432
  async getUser() {
1419
1433
  return this.makeRequest('/users/me');
@@ -2084,6 +2098,22 @@ class EtsyClient {
2084
2098
  async deleteListingVideo(shopId, listingId, videoId) {
2085
2099
  await this.makeRequest(`/shops/${shopId}/listings/${listingId}/videos/${videoId}`, { method: 'DELETE' }, false);
2086
2100
  }
2101
+ async getListingPersonalizations(listingId) {
2102
+ return this.makeRequest(`/listings/${listingId}/personalization`);
2103
+ }
2104
+ async updateListingPersonalization(shopId, listingId, params) {
2105
+ const { supports_multiple_personalization_questions, ...body } = params;
2106
+ const query = supports_multiple_personalization_questions
2107
+ ? '?supports_multiple_personalization_questions=true'
2108
+ : '';
2109
+ return this.makeRequest(`/shops/${shopId}/listings/${listingId}/personalization${query}`, {
2110
+ method: 'POST',
2111
+ body: JSON.stringify(body)
2112
+ }, false);
2113
+ }
2114
+ async deleteListingPersonalization(shopId, listingId) {
2115
+ await this.makeRequest(`/shops/${shopId}/listings/${listingId}/personalization`, { method: 'DELETE' }, false);
2116
+ }
2087
2117
  async createListingTranslation(shopId, listingId, language, params) {
2088
2118
  const body = this.buildFormBody(params);
2089
2119
  return this.makeRequest(`/shops/${shopId}/listings/${listingId}/translations/${language}`, {
@@ -2541,9 +2571,9 @@ class GlobalRequestQueue {
2541
2571
  this.requestCount = 0;
2542
2572
  this.dailyReset = new Date();
2543
2573
  this.lastRequestTime = 0;
2544
- this.maxRequestsPerDay = 10000;
2545
- this.maxRequestsPerSecond = 10;
2546
- this.minRequestInterval = 100;
2574
+ this.maxRequestsPerDay = ETSY_RATE_LIMITS.MAX_REQUESTS_PER_DAY;
2575
+ this.maxRequestsPerSecond = ETSY_RATE_LIMITS.MAX_REQUESTS_PER_SECOND;
2576
+ this.minRequestInterval = ETSY_RATE_LIMITS.MIN_REQUEST_INTERVAL;
2547
2577
  this.setNextDailyReset();
2548
2578
  }
2549
2579
  static getInstance() {
@@ -2628,11 +2658,12 @@ class GlobalRequestQueue {
2628
2658
  try {
2629
2659
  let result;
2630
2660
  if (item.timeout) {
2631
- const remainingTimeout = item.timeout - elapsed;
2661
+ const timeout = item.timeout;
2662
+ const remainingTimeout = timeout - elapsed;
2632
2663
  let timeoutId;
2633
2664
  const timeoutPromise = new Promise((_, reject) => {
2634
2665
  timeoutId = setTimeout(() => {
2635
- reject(new Error(`Request timeout after ${item.timeout}ms (exceeded during execution)`));
2666
+ reject(new Error(`Request timeout after ${timeout}ms (exceeded during execution)`));
2636
2667
  }, remainingTimeout);
2637
2668
  if (timeoutId && typeof timeoutId.unref === 'function') {
2638
2669
  timeoutId.unref();
@@ -2941,7 +2972,18 @@ class EtsyWebhookHandler {
2941
2972
  }
2942
2973
  }
2943
2974
  parseEvent(payload) {
2944
- const data = typeof payload === 'string' ? JSON.parse(payload) : payload;
2975
+ let data;
2976
+ if (typeof payload === 'string') {
2977
+ try {
2978
+ data = JSON.parse(payload);
2979
+ }
2980
+ catch {
2981
+ throw new Error('Invalid webhook payload: malformed JSON');
2982
+ }
2983
+ }
2984
+ else {
2985
+ data = payload;
2986
+ }
2945
2987
  if (!data.type || !data.data) {
2946
2988
  throw new Error('Invalid webhook event format');
2947
2989
  }
@@ -3331,7 +3373,6 @@ function createCacheStorage(config = {}) {
3331
3373
  case 'lfu':
3332
3374
  return new LFUCache(config);
3333
3375
  case 'ttl':
3334
- return new LRUCache(config);
3335
3376
  default:
3336
3377
  return new LRUCache(config);
3337
3378
  }
@@ -4028,7 +4069,7 @@ function createCachingPlugin(config = {}) {
4028
4069
  };
4029
4070
  }
4030
4071
  function createRateLimitPlugin(config = {}) {
4031
- const maxRequestsPerSecond = config.maxRequestsPerSecond || 10;
4072
+ const maxRequestsPerSecond = config.maxRequestsPerSecond || ETSY_RATE_LIMITS.MAX_REQUESTS_PER_SECOND;
4032
4073
  const requests = [];
4033
4074
  return {
4034
4075
  name: 'rateLimit',
@@ -4082,5 +4123,5 @@ function getLibraryInfo() {
4082
4123
  };
4083
4124
  }
4084
4125
 
4085
- export { AuthHelper, BatchQueryExecutor, BulkOperationManager, COMMON_SCOPE_COMBINATIONS, CacheWithInvalidation, CreateListingSchema, DEFAULT_RETRY_CONFIG, ETSY_SCOPES, EncryptedFileTokenStorage, EtsyApiError, EtsyAuthError, EtsyClient, EtsyRateLimitError, EtsyRateLimiter, EtsyWebhookHandler, FieldValidator, FileTokenStorage, GlobalRequestQueue, LFUCache, LIBRARY_NAME, LRUCache, ListingQueryBuilder, LocalStorageTokenStorage, MemoryTokenStorage, PaginatedResults, PluginManager, ReceiptQueryBuilder, RedisCacheStorage, RetryManager, SecureTokenStorage, SessionStorageTokenStorage, TokenManager, UpdateListingSchema, UpdateShopSchema, VERSION, ValidationException, Validator, WebhookSecurity, combineValidators, createAnalyticsPlugin, createAuthHelper, createBatchQuery, createBulkOperationManager, createCacheStorage, createCacheWithInvalidation, createCachingPlugin, createCodeChallenge, createDefaultTokenStorage, createEtsyClient, createListingQuery, createLoggingPlugin, createPaginatedResults, createRateLimitPlugin, createRateLimiter, createReceiptQuery, createRedisCacheStorage, createRetryPlugin, createTokenManager, createValidator, createWebhookHandler, createWebhookSecurity, decryptAES256GCM, EtsyClient as default, defaultRateLimiter, deriveKeyFromPassword, encryptAES256GCM, executeBulkOperation, field, generateCodeVerifier, generateEncryptionKey, generateRandomBase64Url, generateState, getAvailableStorage, getEnvironmentInfo, getGlobalQueue, getLibraryInfo, hasLocalStorage, hasSessionStorage, isBrowser$1 as isBrowser, isNode, isSecureStorageSupported, sha256, sha256Base64Url, validate, validateEncryptionKey, validateOrThrow, withQueryBuilder, withQueue, withRetry };
4126
+ export { AuthHelper, BatchQueryExecutor, BulkOperationManager, COMMON_SCOPE_COMBINATIONS, CacheWithInvalidation, CreateListingSchema, DEFAULT_RETRY_CONFIG, ETSY_RATE_LIMITS, ETSY_SCOPES, EncryptedFileTokenStorage, EtsyApiError, EtsyAuthError, EtsyClient, EtsyRateLimitError, EtsyRateLimiter, EtsyWebhookHandler, FieldValidator, FileTokenStorage, GlobalRequestQueue, LFUCache, LIBRARY_NAME, LRUCache, ListingQueryBuilder, LocalStorageTokenStorage, MemoryTokenStorage, PaginatedResults, PluginManager, ReceiptQueryBuilder, RedisCacheStorage, RetryManager, SecureTokenStorage, SessionStorageTokenStorage, TokenManager, UpdateListingSchema, UpdateShopSchema, VERSION, ValidationException, Validator, WebhookSecurity, combineValidators, createAnalyticsPlugin, createAuthHelper, createBatchQuery, createBulkOperationManager, createCacheStorage, createCacheWithInvalidation, createCachingPlugin, createCodeChallenge, createDefaultTokenStorage, createEtsyClient, createListingQuery, createLoggingPlugin, createPaginatedResults, createRateLimitPlugin, createRateLimiter, createReceiptQuery, createRedisCacheStorage, createRetryPlugin, createTokenManager, createValidator, createWebhookHandler, createWebhookSecurity, decryptAES256GCM, EtsyClient as default, defaultRateLimiter, deriveKeyFromPassword, encryptAES256GCM, executeBulkOperation, field, generateCodeVerifier, generateEncryptionKey, generateRandomBase64Url, generateState, getAvailableStorage, getEnvironmentInfo, getGlobalQueue, getLibraryInfo, hasLocalStorage, hasSessionStorage, isBrowser$1 as isBrowser, isNode, isSecureStorageSupported, sha256, sha256Base64Url, validate, validateEncryptionKey, validateOrThrow, withQueryBuilder, withQueue, withRetry };
4086
4127
  //# sourceMappingURL=browser.esm.js.map