@porulle/adapter-shopify 0.57.0 → 0.58.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/index.js CHANGED
@@ -212,6 +212,19 @@ export function shopifyReauthorizeUrl(options, params) {
212
212
  }
213
213
  export function shopifyConnector(options = {}) {
214
214
  const fetchImpl = options.fetchImpl ?? fetch;
215
+ /** One `products.json` page of variant stock (`inventory_quantity`, all locations summed), keyed by variant id. */
216
+ const inventoryPage = async (store, cursor) => {
217
+ const token = credentials(store);
218
+ if (!token)
219
+ return Err({ code: "SHOPIFY_CREDENTIALS_REQUIRED", message: "Shopify accessToken is required." });
220
+ const url = cursor ?? `${apiBase(store, version, options.baseUrl)}/products.json?limit=250&fields=id,variants`;
221
+ const result = await request(fetchImpl, url, token);
222
+ if (!result.ok)
223
+ return result;
224
+ const levels = result.value.data.products.flatMap((product) => (product.variants ?? [])
225
+ .map((variant) => ({ externalId: String(variant.id), available: Math.max(0, variant.inventory_quantity ?? 0) })));
226
+ return Ok({ levels, nextCursor: nextPageUrl(result.value.response) });
227
+ };
215
228
  const version = options.apiVersion ?? "2024-10";
216
229
  const currencyCache = new Map();
217
230
  return defineChannelConnector({
@@ -391,25 +404,23 @@ export function shopifyConnector(options = {}) {
391
404
  }
392
405
  const wanted = ids === undefined ? undefined : new Set(ids);
393
406
  const levels = [];
394
- let url = `${base}/products.json?limit=250&fields=id,variants`;
407
+ let cursor = null;
395
408
  const seen = new Set();
396
- while (url !== null) {
397
- if (seen.has(url))
409
+ do {
410
+ const page = await inventoryPage(store, cursor);
411
+ if (!page.ok)
412
+ return page;
413
+ levels.push(...page.value.levels.filter((entry) => wanted === undefined || wanted.has(entry.externalId)));
414
+ cursor = page.value.nextCursor;
415
+ if (cursor !== null && seen.has(cursor))
398
416
  return Err({ code: "SHOPIFY_API_FAILED", message: "Shopify pagination repeated a page." });
399
- seen.add(url);
400
- const result = await request(fetchImpl, url, token);
401
- if (!result.ok)
402
- return result;
403
- for (const product of result.value.data.products) {
404
- for (const variant of product.variants ?? []) {
405
- if (wanted === undefined || wanted.has(String(variant.id)))
406
- levels.push(level(variant));
407
- }
408
- }
409
- url = nextPageUrl(result.value.response);
410
- }
417
+ if (cursor !== null)
418
+ seen.add(cursor);
419
+ } while (cursor !== null);
411
420
  return Ok(levels);
412
421
  },
422
+ /** One `products.json` page (250 products) of variant stock; the cursor is Shopify's next-page URL. */
423
+ fetchInventoryPage: (store, cursor) => inventoryPage(store, cursor),
413
424
  async pushCatalog(store, items, opts) {
414
425
  const oauthStartUrl = options.appUrl ? shopifyOAuthStartUrl(options.appUrl, store.storeDomain) : undefined;
415
426
  return executePushCatalog({