hookwright 1.9.0 → 1.11.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.
Files changed (2) hide show
  1. package/dist/cli.js +191 -41
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -24,7 +24,7 @@ var init_package = __esm({
24
24
  "package.json"() {
25
25
  package_default = {
26
26
  name: "hookwright",
27
- version: "1.9.0",
27
+ version: "1.11.0",
28
28
  description: "Build real, signed e-commerce webhooks from a live Shopify catalogue \u2014 interactive terminal UI, no backend",
29
29
  keywords: [
30
30
  "webhook",
@@ -1142,7 +1142,6 @@ var EVENTS2 = [
1142
1142
  { eventName: "collection_page_viewed", type: "shopflo_collection_page_viewed", label: "Collection Page View", selection: "collection", subject: "one collection", envelope: "data" },
1143
1143
  { eventName: "product_page_viewed", type: "shopflo_product_page_viewed", label: "Product Page View", selection: "product", subject: "one product", envelope: "data" },
1144
1144
  { eventName: "added_to_cart_ui", type: "shopflo_added_to_cart", label: "Added To Cart", selection: "cart", subject: "cart contents", envelope: "root" },
1145
- { eventName: "checkout_clicked", type: "shopflo_checkout_clicked", label: "Checkout Clicked", selection: "cart", subject: "cart contents", envelope: "root" },
1146
1145
  { eventName: "checkout_abandoned", type: "shopflo_checkout_abandoned", label: "Abandoned Checkout", selection: "cart", subject: "cart contents", envelope: "root" },
1147
1146
  { eventName: "order_completed", type: "shopflo_order_completed", label: "Order Completed", selection: "cart", subject: "ordered items", envelope: "root" }
1148
1147
  ];
@@ -1156,33 +1155,37 @@ function selectionFor2(eventName) {
1156
1155
  }
1157
1156
  var COMMON_FIELDS2 = [
1158
1157
  { source: "event_name / eventName", target: "\xABgate\xBB", required: true, note: "the schema matches EITHER spelling" },
1159
- { source: "customer.phone / user_data.phone", target: "customer.phone", required: true, note: "missing phone is rejected on every event" },
1160
- { source: "customer.first_name / user_data.firstName", target: "customer.firstName" },
1161
- { source: "customer.last_name / user_data.lastName", target: "customer.lastName" },
1158
+ { source: "customer.phone / phone / data.user_data.phone / eventPayload.userData.phone", target: "customer.phone", required: true, note: "missing phone is rejected on every event" },
1159
+ { source: "customer.first_name / data.user_data.firstName / eventPayload.userData.firstName", target: "customer.firstName" },
1160
+ { source: "customer.last_name / data.user_data.lastName / eventPayload.userData.lastName", target: "customer.lastName" },
1162
1161
  { source: "\u2014 (first + last)", target: "customer.fullName", note: "built by the consumer, not sent" },
1163
- { source: "customer.email / user_data.email", target: "customer.email" },
1162
+ { source: "customer.email / email / data.user_data.email / eventPayload.userData.email", target: "customer.email" },
1164
1163
  { source: "channel", target: "channel" },
1165
1164
  { source: "timestamp / created_at", target: "eventTime" }
1166
1165
  ];
1167
1166
  var BROWSING_FIELDS = [
1168
- { source: "uiData.pageUrl / data.page_url", target: "page.url" },
1169
- { source: "uiData.pageTitle / data.page_title", target: "page.title" },
1170
- { source: "uiData.landingPageUrl", target: "page.landingPage" },
1171
- { source: "uiData.referrerPageUrl", target: "page.referrer" },
1172
- { source: "clientData.brandUrl", target: "storeUrl" },
1173
- { source: "session_id", target: "sessionId" }
1167
+ { source: "eventPayload.uiData.pageUrl / data.page_url", target: "page.url" },
1168
+ { source: "eventPayload.uiData.pageTitle / data.page_title", target: "page.title" },
1169
+ { source: "eventPayload.uiData.landingPageUrl / data.landing_page_url", target: "page.landingPage" },
1170
+ { source: "eventPayload.uiData.referrerPageUrl / data.referrer_page_url", target: "page.referrer" },
1171
+ { source: "eventPayload.clientData.brandUrl", target: "storeUrl" },
1172
+ { source: "session_id / sessionId", target: "sessionId" }
1174
1173
  ];
1175
1174
  var CART_FIELDS = [
1176
- { source: "line_items[].title", target: "cart.productName", required: true, note: "the first PAID line wins; freebies rank last" },
1177
- { source: "line_items[].product_image", target: "cart.image" },
1175
+ { source: "line_items[].title", target: "cart.productDetails", required: true, note: "the first PAID line wins; freebies rank last" },
1176
+ { source: "line_items[].title", target: "cart.productName", note: 'the featured line alone, without the "and N more"' },
1177
+ { source: "line_items[].image", target: "cart.image", note: "looked up from product_id when the line has none" },
1178
+ { source: "\u2014 (store domain + handle)", target: "cart.productUrl", note: "resolved, not sent" },
1179
+ { source: "\u2014 (store domain + line ids)", target: "cartLink", note: "resolved, not sent" },
1180
+ { source: "\u2014 (company shopify integration)", target: "storeUrl", note: "resolved, not sent" },
1178
1181
  { source: "line_items[].price", target: "\u2014 (ranking only)", note: "a zero price marks the line a freebie" },
1179
1182
  { source: "line_items[].quantity", target: "cart.quantity", note: "summed across the cart" },
1180
1183
  { source: "line_items[]", target: "cart.itemCount", note: "the line count, not the quantity" },
1181
- { source: "total_price", target: "pricing.totalPrice" },
1182
- { source: "subtotal_price", target: "pricing.subtotal" },
1183
- { source: "total_discount", target: "pricing.totalDiscount" },
1184
- { source: "currency", target: "pricing.currency" },
1185
- { source: "note_attributes[shopflo_checkout_url]", target: "checkoutUrl", note: "checkout_abandoned puts the link here, not in token_id" }
1184
+ { source: "total_price", target: "total_price", note: "the body is stored as sent" },
1185
+ { source: "subtotal_price", target: "subtotal_price" },
1186
+ { source: "total_discount", target: "total_discount" },
1187
+ { source: "currency", target: "currency" },
1188
+ { source: "abandoned_checkout_url", target: "checkoutUrl", required: true, note: "token_id is a bare uuid, only usable once built into a link" }
1186
1189
  ];
1187
1190
  var FIELD_MAPS2 = {
1188
1191
  store_page_view: [...COMMON_FIELDS2, ...BROWSING_FIELDS],
@@ -1198,7 +1201,6 @@ var FIELD_MAPS2 = {
1198
1201
  { source: "data.product_type", target: "product.type" }
1199
1202
  ],
1200
1203
  added_to_cart_ui: [...COMMON_FIELDS2, ...CART_FIELDS],
1201
- checkout_clicked: [...COMMON_FIELDS2, ...CART_FIELDS],
1202
1204
  checkout_abandoned: [...COMMON_FIELDS2, ...CART_FIELDS],
1203
1205
  order_completed: [
1204
1206
  ...COMMON_FIELDS2,
@@ -1311,7 +1313,7 @@ function buildPayload4(ctx) {
1311
1313
  title: product.title,
1312
1314
  price: money2(variant?.price ?? 0).toFixed(2),
1313
1315
  quantity,
1314
- product_image: imageForVariant(product, variant)
1316
+ image: imageForVariant(product, variant)
1315
1317
  }));
1316
1318
  const subtotal = money2(lineItems.reduce((sum, li) => sum + Number(li.price) * li.quantity, 0));
1317
1319
  const discount = money2(Math.min(ctx.discount ?? 0, subtotal));
@@ -1320,17 +1322,38 @@ function buildPayload4(ctx) {
1320
1322
  return {
1321
1323
  event_name: eventName,
1322
1324
  session_id: ctx.sessionId ?? `sf-${now.getTime()}`,
1323
- token_id: checkoutUrl,
1325
+ token_id: `sf-${now.getTime()}`,
1326
+ abandoned_checkout_url: checkoutUrl,
1324
1327
  source: "shopflo",
1325
1328
  channel: "web",
1326
1329
  email: cust.email,
1327
1330
  phone,
1328
1331
  created_at: now.toISOString(),
1329
- // checkout_abandoned carries the recovery link here; the consumer prefers it
1332
+ // the live note_attributes carry utm and the landing page, never a checkout url
1330
1333
  note_attributes: [
1331
1334
  { name: "landing_page", value: `/products/${items?.[0]?.product?.handle ?? ""}` },
1332
- { name: "shopflo_checkout_url", value: checkoutUrl }
1335
+ { name: "utm_source", value: "ig" },
1336
+ { name: "customer_type", value: "NEW" }
1333
1337
  ],
1338
+ utm_params: {
1339
+ utm_source: "ig",
1340
+ utm_medium: "Instagram_Feed",
1341
+ utm_campaign: "hookwright",
1342
+ utm_content: "hookwright",
1343
+ utm_term: "0",
1344
+ landing_page: `/products/${items?.[0]?.product?.handle ?? ""}`,
1345
+ landing_page_url: `https://${domain}/products/${items?.[0]?.product?.handle ?? ""}`
1346
+ },
1347
+ shipping_address: {
1348
+ city: "Bangalore",
1349
+ province: "Karnataka",
1350
+ province_code: "KA",
1351
+ zip: "560024",
1352
+ country: "India",
1353
+ country_code: "IN",
1354
+ phone,
1355
+ email: cust.email
1356
+ },
1334
1357
  line_items: lineItems,
1335
1358
  customer: { uid: "sf-user", first_name: cust.firstName, last_name: cust.lastName, email: cust.email, phone },
1336
1359
  currency,
@@ -1360,7 +1383,7 @@ function webhookSecret4(config) {
1360
1383
  }
1361
1384
  function storeOf(payload) {
1362
1385
  const brandUrl = payload?.eventPayload?.clientData?.brandUrl;
1363
- const candidate = brandUrl ?? payload?.token_id;
1386
+ const candidate = brandUrl ?? payload?.utm_params?.landing_page_url ?? payload?.data?.page_url;
1364
1387
  if (!candidate) return void 0;
1365
1388
  try {
1366
1389
  return new URL(candidate).host;
@@ -1494,7 +1517,19 @@ function nationalNumber(raw, regionCode) {
1494
1517
  // src/providers/flexype.mjs
1495
1518
  var GATE_PATH3 = "event_type";
1496
1519
  var GATE_VALUE2 = "ABANDONED_CHECKOUT";
1520
+ var EVENTS3 = [
1521
+ { eventName: "ABANDONED_CHECKOUT", type: "flexype_abandoned_checkout", label: "Abandoned Checkout", selection: "cart", subject: "cart contents" },
1522
+ { eventName: "COLLECTION_VIEWED", type: "flexype_collection_viewed", label: "Collection Viewed", selection: "collection", subject: "one collection" },
1523
+ { eventName: "PRODUCT_VIEWED", type: "flexype_product_viewed", label: "Product Viewed", selection: "product", subject: "one product" }
1524
+ ];
1497
1525
  var DEFAULT_EVENT3 = GATE_VALUE2;
1526
+ var eventDef2 = (eventName) => EVENTS3.find((e) => e.eventName === eventName) ?? EVENTS3[0];
1527
+ function eventFromPayload3(payload) {
1528
+ return payload?.event_type;
1529
+ }
1530
+ function selectionFor3(eventName) {
1531
+ return eventDef2(eventName).selection;
1532
+ }
1498
1533
  var fieldMap5 = [
1499
1534
  { source: "event_type", target: "\xABgate\xBB", required: true, note: "must be ABANDONED_CHECKOUT or the event is dropped" },
1500
1535
  { source: "payload.user.phone_with_dial_code", target: "phone", required: true, note: "falls back to dial_code + phone" },
@@ -1524,6 +1559,43 @@ var fieldMap5 = [
1524
1559
  { source: "event_id", target: "eventId" },
1525
1560
  { source: "event_time", target: "eventTime" }
1526
1561
  ];
1562
+ var ENVELOPE_FIELDS = [
1563
+ { source: "event_type", target: "\xABgate\xBB", required: true, note: "the schema matches on this" },
1564
+ { source: "payload.user.phone_with_dial_code", target: "customer.phone", required: true, note: "falls back to dial_code + phone" },
1565
+ { source: "payload.user.first_name", target: "customer.firstName" },
1566
+ { source: "payload.user.last_name", target: "customer.lastName" },
1567
+ { source: "payload.user.full_name", target: "customer.fullName", note: "falls back to first + last name" },
1568
+ { source: "payload.user.email", target: "customer.email" },
1569
+ { source: "store.shopify_domain", target: "store.shopify_domain" },
1570
+ { source: "store.name", target: "store.name" },
1571
+ { source: "store.main_domain", target: "store.main_domain" },
1572
+ { source: "event_id", target: "eventId" },
1573
+ { source: "event_time", target: "eventTime" }
1574
+ ];
1575
+ var COLLECTION_VIEWED_FIELDS = [
1576
+ ...ENVELOPE_FIELDS,
1577
+ { source: "payload.collection_viewed.name", target: "payload.collection_viewed.name", required: true },
1578
+ { source: "payload.collection_viewed.handle", target: "payload.collection_viewed.handle", required: true, note: "the url is built from this" },
1579
+ { source: "payload.collection_viewed.image", target: "payload.collection_viewed.image" },
1580
+ { source: "payload.collection_viewed.collection_id", target: "payload.collection_viewed.collection_id" },
1581
+ { source: "\u2014 (store domain + handle)", target: "collection_url", note: "derived, not sent" }
1582
+ ];
1583
+ var PRODUCT_VIEWED_FIELDS = [
1584
+ ...ENVELOPE_FIELDS,
1585
+ { source: "payload.product_viewed.title", target: "payload.product_viewed.title", required: true },
1586
+ { source: "payload.product_viewed.handle", target: "payload.product_viewed.handle", required: true, note: "the url is built from this" },
1587
+ { source: "payload.product_viewed.image", target: "payload.product_viewed.image" },
1588
+ { source: "payload.product_viewed.product_id", target: "payload.product_viewed.product_id" },
1589
+ { source: "\u2014 (store domain + handle)", target: "product_url", note: "derived, not sent" }
1590
+ ];
1591
+ var FIELD_MAPS3 = {
1592
+ ABANDONED_CHECKOUT: fieldMap5,
1593
+ COLLECTION_VIEWED: COLLECTION_VIEWED_FIELDS,
1594
+ PRODUCT_VIEWED: PRODUCT_VIEWED_FIELDS
1595
+ };
1596
+ function fieldMapFor(eventName) {
1597
+ return FIELD_MAPS3[eventName] ?? fieldMap5;
1598
+ }
1527
1599
  var editableFields5 = [
1528
1600
  { group: "Event", path: "event_id", label: "Event id" },
1529
1601
  { group: "Event", path: "event_time", label: "Event time" },
@@ -1541,12 +1613,64 @@ var editableFields5 = [
1541
1613
  { group: "Attribution", path: "payload.metadata.landing_page", label: "Landing page", optional: true }
1542
1614
  ];
1543
1615
  function buildPayload5(ctx) {
1544
- const { config, items, phone, live } = ctx;
1616
+ const { config, items, phone, live, collection } = ctx;
1617
+ const eventName = ctx.eventName ?? DEFAULT_EVENT3;
1545
1618
  const now = ctx.now ?? /* @__PURE__ */ new Date();
1546
1619
  const currency = ctx.currency || config.defaults.currency;
1547
1620
  const domain = config.shopify.domain;
1548
1621
  const cust = config.customer;
1549
1622
  const { dialCode, national } = splitPhone(phone, cust.countryCode);
1623
+ const envelope = {
1624
+ event_id: `fx-${now.getTime()}`,
1625
+ event_type: eventName,
1626
+ event_time: now.toISOString(),
1627
+ store: {
1628
+ id: 1,
1629
+ name: config.shopify.shop?.name ?? config.shopify.domain,
1630
+ shopify_domain: config.shopify.domain,
1631
+ main_domain: config.shopify.domain
1632
+ }
1633
+ };
1634
+ const user = {
1635
+ email: config.customer.email,
1636
+ full_name: `${config.customer.firstName} ${config.customer.lastName}`.trim(),
1637
+ first_name: config.customer.firstName,
1638
+ last_name: config.customer.lastName,
1639
+ dial_code: splitPhone(phone, config.customer.countryCode).dialCode,
1640
+ phone: splitPhone(phone, config.customer.countryCode).national,
1641
+ phone_with_dial_code: phone
1642
+ };
1643
+ if (eventName === "COLLECTION_VIEWED") {
1644
+ return {
1645
+ ...envelope,
1646
+ payload: {
1647
+ user,
1648
+ collection_viewed: {
1649
+ collection_id: Number(collection?.id) || 0,
1650
+ name: collection?.title ?? "",
1651
+ handle: collection?.handle ?? "",
1652
+ image: collection?.image?.src ?? "",
1653
+ timestamp: now.toISOString()
1654
+ }
1655
+ }
1656
+ };
1657
+ }
1658
+ if (eventName === "PRODUCT_VIEWED") {
1659
+ const first = items?.[0];
1660
+ return {
1661
+ ...envelope,
1662
+ payload: {
1663
+ user,
1664
+ product_viewed: {
1665
+ product_id: Number(first?.product?.id) || 0,
1666
+ title: first?.product?.title ?? "",
1667
+ handle: first?.product?.handle ?? "",
1668
+ image: imageForVariant(first?.product, first?.variant) ?? "",
1669
+ timestamp: now.toISOString()
1670
+ }
1671
+ }
1672
+ };
1673
+ }
1550
1674
  const cartItems = (items ?? []).map(({ product, variant, quantity }) => {
1551
1675
  const unit = money2(variant?.price ?? 0);
1552
1676
  return {
@@ -1638,11 +1762,16 @@ var flexype_default = {
1638
1762
  urlKey: "flexype",
1639
1763
  label: "FlexyPe",
1640
1764
  eventType: "flexype_abandoned_checkout",
1765
+ events: EVENTS3,
1641
1766
  defaultEvent: DEFAULT_EVENT3,
1767
+ gateFor: (eventName) => ({ path: GATE_PATH3, value: eventName }),
1768
+ selectionFor: selectionFor3,
1769
+ eventFromPayload: eventFromPayload3,
1642
1770
  gate: { path: GATE_PATH3, value: GATE_VALUE2 },
1643
1771
  signatureScheme: "static shared secret in the header (no body signature)",
1644
1772
  signatureHeader: "x-flexype-authorization",
1645
1773
  fieldMap: fieldMap5,
1774
+ fieldMapFor,
1646
1775
  editableFields: editableFields5,
1647
1776
  buildPayload: buildPayload5,
1648
1777
  sign: sign5,
@@ -1654,7 +1783,7 @@ var flexype_default = {
1654
1783
  // src/providers/return-prime.mjs
1655
1784
  var STATUSES = ["requested", "approved", "received", "inspected", "rejected"];
1656
1785
  var titleCase = (v) => v.charAt(0).toUpperCase() + v.slice(1);
1657
- var EVENTS3 = ["return", "exchange"].flatMap(
1786
+ var EVENTS4 = ["return", "exchange"].flatMap(
1658
1787
  (requestType) => STATUSES.map((status) => ({
1659
1788
  eventName: `${requestType}_${status}`,
1660
1789
  requestType,
@@ -1666,14 +1795,14 @@ var EVENTS3 = ["return", "exchange"].flatMap(
1666
1795
  }))
1667
1796
  );
1668
1797
  var DEFAULT_EVENT4 = "return_requested";
1669
- var eventDef2 = (eventName) => EVENTS3.find((e) => e.eventName === eventName) ?? EVENTS3.find((e) => e.eventName === DEFAULT_EVENT4);
1670
- function eventFromPayload3(payload) {
1798
+ var eventDef3 = (eventName) => EVENTS4.find((e) => e.eventName === eventName) ?? EVENTS4.find((e) => e.eventName === DEFAULT_EVENT4);
1799
+ function eventFromPayload4(payload) {
1671
1800
  const request = payload?.request ?? payload ?? {};
1672
1801
  if (!request.request_type || !request.status) return void 0;
1673
1802
  return `${request.request_type}_${request.status}`;
1674
1803
  }
1675
- function selectionFor3(eventName) {
1676
- return eventDef2(eventName).selection;
1804
+ function selectionFor4(eventName) {
1805
+ return eventDef3(eventName).selection;
1677
1806
  }
1678
1807
  var COMMON_FIELDS3 = [
1679
1808
  { source: "request_type", target: "\xABgate\xBB + requestType", required: true, note: "return or exchange" },
@@ -1698,11 +1827,11 @@ var EXCHANGE_FIELDS = [
1698
1827
  { source: "line_items[].exchange_product.price", target: "exchangeProduct.price + pricing.priceDifference", note: "difference is signed: positive means the shopper owes more" }
1699
1828
  ];
1700
1829
  var fieldMap6 = COMMON_FIELDS3;
1701
- function fieldMapFor(eventName) {
1702
- return eventDef2(eventName).requestType === "exchange" ? [...COMMON_FIELDS3, ...EXCHANGE_FIELDS] : COMMON_FIELDS3;
1830
+ function fieldMapFor2(eventName) {
1831
+ return eventDef3(eventName).requestType === "exchange" ? [...COMMON_FIELDS3, ...EXCHANGE_FIELDS] : COMMON_FIELDS3;
1703
1832
  }
1704
1833
  function editableFieldsFor3(eventName) {
1705
- const def = eventDef2(eventName);
1834
+ const def = eventDef3(eventName);
1706
1835
  const p = (path6) => def.envelope === "root" ? path6 : `request.${path6}`;
1707
1836
  return [
1708
1837
  { group: "Request", path: p("request_number"), label: "Request number" },
@@ -1728,7 +1857,7 @@ function statusBlock(reached, at, comment = null) {
1728
1857
  function buildPayload6(ctx) {
1729
1858
  const { config, items, phone } = ctx;
1730
1859
  const eventName = ctx.eventName ?? DEFAULT_EVENT4;
1731
- const def = eventDef2(eventName);
1860
+ const def = eventDef3(eventName);
1732
1861
  const now = ctx.now ?? /* @__PURE__ */ new Date();
1733
1862
  const currency = ctx.currency || config.defaults.currency;
1734
1863
  const cust = config.customer;
@@ -1930,19 +2059,19 @@ var return_prime_default = {
1930
2059
  id: "return-prime",
1931
2060
  urlKey: "return_prime",
1932
2061
  label: "Return Prime",
1933
- events: EVENTS3,
2062
+ events: EVENTS4,
1934
2063
  defaultEvent: DEFAULT_EVENT4,
1935
2064
  eventType: "return_prime_return_requested",
1936
2065
  gate: { path: "request.request_type", value: "return" },
1937
- gateFor: (eventName) => ({ path: "request.status", value: eventDef2(eventName).status }),
2066
+ gateFor: (eventName) => ({ path: "request.status", value: eventDef3(eventName).status }),
1938
2067
  signatureScheme: "none documented \u2014 every request is accepted",
1939
2068
  signatureHeader: null,
1940
2069
  fieldMap: fieldMap6,
1941
- fieldMapFor,
2070
+ fieldMapFor: fieldMapFor2,
1942
2071
  editableFields: editableFields6,
1943
2072
  editableFieldsFor: editableFieldsFor3,
1944
- selectionFor: selectionFor3,
1945
- eventFromPayload: eventFromPayload3,
2073
+ selectionFor: selectionFor4,
2074
+ eventFromPayload: eventFromPayload4,
1946
2075
  buildPayload: buildPayload6,
1947
2076
  sign: sign6,
1948
2077
  webhookUrl: webhookUrl6,
@@ -2830,9 +2959,30 @@ function checkAllowedPhone(payload, config) {
2830
2959
  detail: ok ? `${normalized} is an approved test handset` : `${normalized} is NOT in allowedPhones \u2014 refusing to risk messaging a real shopper`
2831
2960
  };
2832
2961
  }
2962
+ function resolveSource(payload, source) {
2963
+ for (const candidate of String(source).split(" / ")) {
2964
+ const path6 = candidate.trim();
2965
+ if (!path6 || path6.startsWith("\u2014")) continue;
2966
+ if (path6.includes("[]")) {
2967
+ const [listPath, ...rest] = path6.split("[]");
2968
+ const list = getPath(payload, listPath);
2969
+ if (!Array.isArray(list)) continue;
2970
+ const leaf = rest.join("").replace(/^\./, "");
2971
+ const hit = list.find((item) => {
2972
+ const v = leaf ? getPath(item, leaf) : item;
2973
+ return v !== void 0 && v !== null && v !== "";
2974
+ });
2975
+ if (hit !== void 0) return leaf ? getPath(hit, leaf) : hit;
2976
+ continue;
2977
+ }
2978
+ const value = getPath(payload, path6);
2979
+ if (value !== void 0 && value !== null && value !== "") return value;
2980
+ }
2981
+ return void 0;
2982
+ }
2833
2983
  function coverage(payload, fieldMap8) {
2834
2984
  return fieldMap8.map((f) => {
2835
- const value = getPath(payload, f.source);
2985
+ const value = resolveSource(payload, f.source);
2836
2986
  const present = value !== void 0 && value !== null && value !== "";
2837
2987
  return {
2838
2988
  field: f.source,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hookwright",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
4
4
  "description": "Build real, signed e-commerce webhooks from a live Shopify catalogue — interactive terminal UI, no backend",
5
5
  "keywords": [
6
6
  "webhook",