@porulle/adapter-shopify 0.73.2 → 0.74.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/graphql.d.ts CHANGED
@@ -28,4 +28,4 @@ export interface ShopifyGraphqlTarget {
28
28
  */
29
29
  export declare function shopifyGraphql<T>(target: ShopifyGraphqlTarget, query: string, variables: Record<string, unknown>, schema: z.ZodType<T>): Promise<Result<T, ChannelConnectorError>>;
30
30
  /** `gid://shopify/<Type>/<id>` for a numeric id; the adapter keys everything by the numeric id. */
31
- export declare function shopifyGid(type: "Product" | "ProductVariant", id: string): string;
31
+ export declare function shopifyGid(type: "Product" | "ProductVariant" | "InventoryItem" | "Order", id: string): string;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ChannelConnector } from "@porulle/core";
2
2
  export { SHOPIFY_API_VERSION } from "./graphql.js";
3
3
  export { CATALOG_ITEMS_QUERY, CATALOG_PAGE_QUERY, VARIANTS_PAGE_QUERY } from "./catalog.js";
4
+ export { INVENTORY_ITEM_VARIANT_QUERY } from "./webhooks.js";
4
5
  export { REQUIRED_SCOPES, normalizeShopDomain, parseShopifyCredentials } from "./oauth.js";
5
6
  export type { ShopifyCredentials } from "./oauth.js";
6
7
  export interface ShopifyConnectorOptions {
package/dist/index.js CHANGED
@@ -2,10 +2,12 @@ import { createHmac, timingSafeEqual } from "node:crypto";
2
2
  import { CHANNEL_CANCEL_REFUSED, CHANNEL_OUT_OF_STOCK, defineChannelConnector, Err, Ok } from "@porulle/core";
3
3
  import { z } from "zod";
4
4
  import { readCatalogItems, readCatalogPage } from "./catalog.js";
5
+ import { decodeShopifyWebhook } from "./webhooks.js";
5
6
  import { shopifyGid, shopifyGraphql } from "./graphql.js";
6
7
  import { REQUIRED_SCOPES, buildAuthorizeUrl, exchangeCallback, normalizeShopDomain, parseShopifyCredentials, refreshIfExpiring, validShopDomain, } from "./oauth.js";
7
8
  export { SHOPIFY_API_VERSION } from "./graphql.js";
8
9
  export { CATALOG_ITEMS_QUERY, CATALOG_PAGE_QUERY, VARIANTS_PAGE_QUERY } from "./catalog.js";
10
+ export { INVENTORY_ITEM_VARIANT_QUERY } from "./webhooks.js";
9
11
  export { REQUIRED_SCOPES, normalizeShopDomain, parseShopifyCredentials } from "./oauth.js";
10
12
  export const INVENTORY_QUERY = `query PorulleInventoryPage($after: String) {
11
13
  productVariants(first: 250, after: $after, sortKey: ID) { pageInfo { hasNextPage endCursor } nodes { legacyResourceId inventoryQuantity } }
@@ -288,7 +290,9 @@ export function shopifyConnector(options) {
288
290
  if (!created.ok)
289
291
  return created;
290
292
  const { order, userErrors } = created.value.orderCreate;
291
- if (userErrors.some((error) => error.code === "INVENTORY_CLAIM_FAILED")) {
293
+ // Live Shopify (2026-10, measured) refuses stock as `INVALID` on `["order","lineItems"]` with "Line items
294
+ // Unable to reserve inventory", not the documented `INVENTORY_CLAIM_FAILED`; both are read as out of stock.
295
+ if (userErrors.some((error) => error.code === "INVENTORY_CLAIM_FAILED" || /unable to reserve inventory/i.test(error.message))) {
292
296
  return Err({ code: CHANNEL_OUT_OF_STOCK, message: `The store does not have the stock: ${userErrors.map((error) => error.message).join("; ")}.`, retriable: false });
293
297
  }
294
298
  if (userErrors.length > 0 || !order) {
@@ -393,6 +397,9 @@ export function shopifyConnector(options) {
393
397
  }
394
398
  return Ok({ id, topic, shopDomain, data });
395
399
  },
400
+ async decodeWebhook(store, event) {
401
+ return decodeShopifyWebhook(target(store), event);
402
+ },
396
403
  async refundExecute() {
397
404
  return Err({ code: "NOT_IMPLEMENTED", message: "Refunds are issued by the platform, not executed in the Shopify store." });
398
405
  },