@profplum700/etsy-v3-api-client 3.0.1 → 4.1.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/CHANGELOG.md CHANGED
@@ -1,4 +1,24 @@
1
1
  # Changelog
2
+
3
+ ## 4.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f285f3f: Add an `enableScaleValidation` option to `updateListingInventory`, sent as Etsy's temporary `enable_scale_validation` query parameter. Etsy makes scale validation mandatory on 1 January 2027; opting in now surfaces payloads whose scaled properties lack a valid, consistent `scale_id`.
8
+
9
+ ## 4.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - a147325: Keep partial rate-limit settings from erasing retry defaults, track the QPD fallback over a rolling 24-hour window, serialize concurrent request reservations, honor Etsy's shared Retry-After cooldown, avoid automatically replaying mutations, and align inventory update inputs with Etsy's accepted request fields.
14
+
15
+ ### Minor Changes
16
+
17
+ - a147325: Add an async token refresh persistence callback that the client awaits before exposing refreshed tokens. Keep the existing synchronous callback type unchanged.
18
+
19
+ ### Patch Changes
20
+
21
+ - a147325: Retry persistence of Etsy-rotated tokens without repeating OAuth refresh, fence refresh persistence against concurrent token updates or clears, preserve legacy synchronous `refreshSave` clearing behavior while requiring a paired clear callback for new async persistence, preserve quota-probe response identity for safe exhausted-quota recovery (custom transports must migrate from uncorrelated `updateFromHeaders` calls to the reservation-aware API), keep active-listing pagination metadata within its accepted offset limit, accept listing IDs returned by the list tool, page large inventory results, validate local OAuth callback addresses, verify same-listing price targets from one final inventory snapshot, and include required `property_values` in non-variation inventory examples.
2
22
 
3
23
  All notable changes to this project will be documented in this file.
4
24
 
package/README.md CHANGED
@@ -33,7 +33,23 @@ yarn add @profplum700/etsy-v3-api-client
33
33
  pnpm add @profplum700/etsy-v3-api-client
34
34
  ```
35
35
 
36
- ## 🔧 Quick Start
36
+ ## 🤖 Connect an agent to your Etsy shop
37
+
38
+ The companion [`@profplum700/etsy-mcp-server`](packages/etsy-mcp-server/README.md) package runs locally over stdio and exposes read-only shop, active-listing, and listing-inventory tools. It requires Node.js 24+, your own approved Etsy developer app, and an OS credential store. It does not use a hosted endpoint.
39
+
40
+ Register `http://localhost:3030/oauth/redirect` as the Etsy app callback if required, then run this in a local interactive terminal:
41
+
42
+ ```bash
43
+ npx --yes @profplum700/etsy-mcp-server@latest setup
44
+ ```
45
+
46
+ Enter your keystring and shared secret in the hidden terminal prompts, authorize only `shops_r` and `listings_r` in Etsy, then choose whether to add the server to your user-level Codex MCP configuration. For another stdio client, configure `npx --yes @profplum700/etsy-mcp-server@latest serve` as its launch command.
47
+
48
+ You can connect multiple shops without replacing earlier profiles. Run `setup --reuse-app --manual-browser` to reuse the saved developer app credentials, then open the printed authorization URL in the browser profile signed in to the shop you want to add. Use `status` to see saved shops and `use <shop name or ID>` to choose which one MCP tools query. `disconnect` removes only the active shop; `disconnect --all` removes every saved profile. Use `remove-codex` to remove the Codex entry without disconnecting any shop.
49
+
50
+ **Prompt for an agent:** “Set up the local Etsy MCP server. Do not ask me to paste Etsy credentials into chat or save them in a repository or environment file. Run the setup command in an interactive local terminal so I can enter them there; request only `shops_r` and `listings_r`, verify my shop, and offer the user-level Codex connection.”
51
+
52
+ ## 🔧 Quick Start
37
53
 
38
54
  ### Basic Setup
39
55
 
@@ -173,7 +189,7 @@ const client = new EtsyClient({
173
189
  rateLimiting: {
174
190
  enabled: true,
175
191
  maxRequestsPerSecond: 5,
176
- maxRequestsPerDay: 5000
192
+ maxRequestsPerDay: 5000 // Conservative local fallback; Etsy response headers report this app's actual quota
177
193
  },
178
194
  caching: {
179
195
  enabled: true,
@@ -292,18 +308,34 @@ const client = new EtsyClient({
292
308
  // ... other config
293
309
  rateLimiting: {
294
310
  enabled: true,
295
- maxRequestsPerSecond: 5, // Requests per second (Etsy's limit)
296
- maxRequestsPerDay: 5000, // Requests per day (Etsy's limit)
297
- minRequestInterval: 200 // Minimum ms between requests (1000 / 5 QPS)
311
+ maxRequestsPerSecond: 5, // Conservative QPS fallback
312
+ maxRequestsPerDay: 5000, // Conservative rolling 24-hour fallback
313
+ minRequestInterval: 200 // Minimum ms between requests (5 QPS fallback)
298
314
  }
299
315
  });
300
316
 
301
- // Check remaining requests
302
- const remaining = client.getRemainingRequests();
303
- console.log(`${remaining} requests remaining today`);
304
- ```
305
-
306
- ## 🔄 Caching
317
+ // Check remaining requests in the rolling local window
318
+ const remaining = client.getRemainingRequests();
319
+ console.log(`${remaining} requests remaining in the rolling 24-hour window`);
320
+ ```
321
+
322
+ When building a custom transport around the exported `EtsyRateLimiter`, keep the reservation ID attached to its response so concurrent, out-of-order headers cannot incorrectly restore exhausted quota:
323
+
324
+ ```typescript
325
+ const reservationId = await rateLimiter.waitForRateLimitWithReservation();
326
+ let response: Response;
327
+ try {
328
+ response = await fetch(url);
329
+ } catch (error) {
330
+ rateLimiter.releaseRequestSlot(reservationId);
331
+ throw error;
332
+ }
333
+ rateLimiter.updateFromHeaders(response.headers, reservationId);
334
+ ```
335
+
336
+ `waitForRateLimit()` remains available for source compatibility when you only need dispatch pacing. For custom transports that also feed response headers back to the limiter, use the reservation-aware method above; uncorrelated positive headers cannot safely reopen exhausted quota.
337
+
338
+ ## 🔄 Caching
307
339
 
308
340
  Optional response caching to improve performance:
309
341