@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.
package/dist/node.esm.js CHANGED
@@ -673,6 +673,11 @@ class FileTokenStorage {
673
673
  }
674
674
  }
675
675
 
676
+ const ETSY_RATE_LIMITS = {
677
+ MAX_REQUESTS_PER_DAY: 5000,
678
+ MAX_REQUESTS_PER_SECOND: 5,
679
+ MIN_REQUEST_INTERVAL: 200,
680
+ };
676
681
  class EtsyRateLimiter {
677
682
  constructor(config) {
678
683
  this.requestCount = 0;
@@ -681,9 +686,9 @@ class EtsyRateLimiter {
681
686
  this.isHeaderBasedLimiting = false;
682
687
  this.currentRetryCount = 0;
683
688
  this.config = {
684
- maxRequestsPerDay: 10000,
685
- maxRequestsPerSecond: 10,
686
- minRequestInterval: 100,
689
+ maxRequestsPerDay: ETSY_RATE_LIMITS.MAX_REQUESTS_PER_DAY,
690
+ maxRequestsPerSecond: ETSY_RATE_LIMITS.MAX_REQUESTS_PER_SECOND,
691
+ minRequestInterval: ETSY_RATE_LIMITS.MIN_REQUEST_INTERVAL,
687
692
  maxRetries: 3,
688
693
  baseDelayMs: 1000,
689
694
  maxDelayMs: 30000,
@@ -738,7 +743,7 @@ class EtsyRateLimiter {
738
743
  if (value === null)
739
744
  return undefined;
740
745
  const num = parseInt(value, 10);
741
- return isNaN(num) ? undefined : num;
746
+ return Number.isNaN(num) ? undefined : num;
742
747
  };
743
748
  return {
744
749
  limitPerSecond: parseNumber(getHeader('x-limit-per-second')),
@@ -1079,7 +1084,7 @@ class FieldValidator {
1079
1084
  const value = data[this.field];
1080
1085
  if (value === undefined || value === null)
1081
1086
  return null;
1082
- if (typeof value !== 'number' || isNaN(value)) {
1087
+ if (typeof value !== 'number' || Number.isNaN(value)) {
1083
1088
  return {
1084
1089
  field: this.field,
1085
1090
  message: options.message || `${this.field} must be a number`,
@@ -1288,9 +1293,14 @@ function combineValidators(...validators) {
1288
1293
 
1289
1294
  class DefaultLogger {
1290
1295
  debug(message, ...args) {
1291
- const isDevelopment = isNode$1
1292
- ? process.env.NODE_ENV === 'development'
1293
- : window.location?.hostname === 'localhost' || window.location?.hostname === '127.0.0.1';
1296
+ let isDevelopment;
1297
+ if (isNode$1) {
1298
+ isDevelopment = process.env.NODE_ENV === 'development';
1299
+ }
1300
+ else {
1301
+ const host = window.location?.hostname;
1302
+ isDevelopment = host === 'localhost' || host === '127.0.0.1';
1303
+ }
1294
1304
  if (isDevelopment) {
1295
1305
  console.log(`[DEBUG] ${message}`, ...args);
1296
1306
  }
@@ -1332,6 +1342,9 @@ class MemoryCache {
1332
1342
  }
1333
1343
  class EtsyClient {
1334
1344
  constructor(config) {
1345
+ if (!config.sharedSecret) {
1346
+ throw new EtsyAuthError('sharedSecret is REQUIRED for Etsy API v3 application usage. See: https://github.com/profplum700/etsy-v3-api-client/issues/21');
1347
+ }
1335
1348
  this.tokenManager = new TokenManager(config);
1336
1349
  this.baseUrl = config.baseUrl || 'https://api.etsy.com/v3/application';
1337
1350
  this.logger = new DefaultLogger();
@@ -1339,9 +1352,9 @@ class EtsyClient {
1339
1352
  this.sharedSecret = config.sharedSecret;
1340
1353
  if (config.rateLimiting?.enabled !== false) {
1341
1354
  this.rateLimiter = new EtsyRateLimiter({
1342
- maxRequestsPerDay: config.rateLimiting?.maxRequestsPerDay || 10000,
1343
- maxRequestsPerSecond: config.rateLimiting?.maxRequestsPerSecond || 10,
1344
- minRequestInterval: config.rateLimiting?.minRequestInterval ?? 100,
1355
+ maxRequestsPerDay: config.rateLimiting?.maxRequestsPerDay || ETSY_RATE_LIMITS.MAX_REQUESTS_PER_DAY,
1356
+ maxRequestsPerSecond: config.rateLimiting?.maxRequestsPerSecond || ETSY_RATE_LIMITS.MAX_REQUESTS_PER_SECOND,
1357
+ minRequestInterval: config.rateLimiting?.minRequestInterval ?? ETSY_RATE_LIMITS.MIN_REQUEST_INTERVAL,
1345
1358
  maxRetries: config.rateLimiting?.maxRetries,
1346
1359
  baseDelayMs: config.rateLimiting?.baseDelayMs,
1347
1360
  maxDelayMs: config.rateLimiting?.maxDelayMs,
@@ -1372,7 +1385,12 @@ class EtsyClient {
1372
1385
  if (useCache && this.cache && requestOptions.method === 'GET') {
1373
1386
  const cached = await this.cache.get(cacheKey);
1374
1387
  if (cached) {
1375
- return JSON.parse(cached);
1388
+ try {
1389
+ return JSON.parse(cached);
1390
+ }
1391
+ catch {
1392
+ await this.cache.delete(cacheKey);
1393
+ }
1376
1394
  }
1377
1395
  }
1378
1396
  await this.rateLimiter.waitForRateLimit();
@@ -1446,10 +1464,7 @@ class EtsyClient {
1446
1464
  return body;
1447
1465
  }
1448
1466
  getApiKey() {
1449
- if (this.sharedSecret) {
1450
- return `${this.keystring}:${this.sharedSecret}`;
1451
- }
1452
- return this.keystring;
1467
+ return `${this.keystring}:${this.sharedSecret}`;
1453
1468
  }
1454
1469
  async getUser() {
1455
1470
  return this.makeRequest('/users/me');
@@ -2120,6 +2135,22 @@ class EtsyClient {
2120
2135
  async deleteListingVideo(shopId, listingId, videoId) {
2121
2136
  await this.makeRequest(`/shops/${shopId}/listings/${listingId}/videos/${videoId}`, { method: 'DELETE' }, false);
2122
2137
  }
2138
+ async getListingPersonalizations(listingId) {
2139
+ return this.makeRequest(`/listings/${listingId}/personalization`);
2140
+ }
2141
+ async updateListingPersonalization(shopId, listingId, params) {
2142
+ const { supports_multiple_personalization_questions, ...body } = params;
2143
+ const query = supports_multiple_personalization_questions
2144
+ ? '?supports_multiple_personalization_questions=true'
2145
+ : '';
2146
+ return this.makeRequest(`/shops/${shopId}/listings/${listingId}/personalization${query}`, {
2147
+ method: 'POST',
2148
+ body: JSON.stringify(body)
2149
+ }, false);
2150
+ }
2151
+ async deleteListingPersonalization(shopId, listingId) {
2152
+ await this.makeRequest(`/shops/${shopId}/listings/${listingId}/personalization`, { method: 'DELETE' }, false);
2153
+ }
2123
2154
  async createListingTranslation(shopId, listingId, language, params) {
2124
2155
  const body = this.buildFormBody(params);
2125
2156
  return this.makeRequest(`/shops/${shopId}/listings/${listingId}/translations/${language}`, {
@@ -2602,9 +2633,9 @@ class GlobalRequestQueue {
2602
2633
  this.requestCount = 0;
2603
2634
  this.dailyReset = new Date();
2604
2635
  this.lastRequestTime = 0;
2605
- this.maxRequestsPerDay = 10000;
2606
- this.maxRequestsPerSecond = 10;
2607
- this.minRequestInterval = 100;
2636
+ this.maxRequestsPerDay = ETSY_RATE_LIMITS.MAX_REQUESTS_PER_DAY;
2637
+ this.maxRequestsPerSecond = ETSY_RATE_LIMITS.MAX_REQUESTS_PER_SECOND;
2638
+ this.minRequestInterval = ETSY_RATE_LIMITS.MIN_REQUEST_INTERVAL;
2608
2639
  this.setNextDailyReset();
2609
2640
  }
2610
2641
  static getInstance() {
@@ -2689,11 +2720,12 @@ class GlobalRequestQueue {
2689
2720
  try {
2690
2721
  let result;
2691
2722
  if (item.timeout) {
2692
- const remainingTimeout = item.timeout - elapsed;
2723
+ const timeout = item.timeout;
2724
+ const remainingTimeout = timeout - elapsed;
2693
2725
  let timeoutId;
2694
2726
  const timeoutPromise = new Promise((_, reject) => {
2695
2727
  timeoutId = setTimeout(() => {
2696
- reject(new Error(`Request timeout after ${item.timeout}ms (exceeded during execution)`));
2728
+ reject(new Error(`Request timeout after ${timeout}ms (exceeded during execution)`));
2697
2729
  }, remainingTimeout);
2698
2730
  if (timeoutId && typeof timeoutId.unref === 'function') {
2699
2731
  timeoutId.unref();
@@ -3010,7 +3042,18 @@ class EtsyWebhookHandler {
3010
3042
  }
3011
3043
  }
3012
3044
  parseEvent(payload) {
3013
- const data = typeof payload === 'string' ? JSON.parse(payload) : payload;
3045
+ let data;
3046
+ if (typeof payload === 'string') {
3047
+ try {
3048
+ data = JSON.parse(payload);
3049
+ }
3050
+ catch {
3051
+ throw new Error('Invalid webhook payload: malformed JSON');
3052
+ }
3053
+ }
3054
+ else {
3055
+ data = payload;
3056
+ }
3014
3057
  if (!data.type || !data.data) {
3015
3058
  throw new Error('Invalid webhook event format');
3016
3059
  }
@@ -3400,7 +3443,6 @@ function createCacheStorage(config = {}) {
3400
3443
  case 'lfu':
3401
3444
  return new LFUCache(config);
3402
3445
  case 'ttl':
3403
- return new LRUCache(config);
3404
3446
  default:
3405
3447
  return new LRUCache(config);
3406
3448
  }
@@ -4215,7 +4257,7 @@ function createCachingPlugin(config = {}) {
4215
4257
  };
4216
4258
  }
4217
4259
  function createRateLimitPlugin(config = {}) {
4218
- const maxRequestsPerSecond = config.maxRequestsPerSecond || 10;
4260
+ const maxRequestsPerSecond = config.maxRequestsPerSecond || ETSY_RATE_LIMITS.MAX_REQUESTS_PER_SECOND;
4219
4261
  const requests = [];
4220
4262
  return {
4221
4263
  name: 'rateLimit',
@@ -4269,5 +4311,5 @@ function getLibraryInfo() {
4269
4311
  };
4270
4312
  }
4271
4313
 
4272
- 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$1 as isNode, isSecureStorageSupported, sha256, sha256Base64Url, validate, validateEncryptionKey, validateOrThrow, withQueryBuilder, withQueue, withRetry };
4314
+ 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$1 as isNode, isSecureStorageSupported, sha256, sha256Base64Url, validate, validateEncryptionKey, validateOrThrow, withQueryBuilder, withQueue, withRetry };
4273
4315
  //# sourceMappingURL=node.esm.js.map