hookwright 2.0.1 → 2.2.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 +60 -22
  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: "2.0.1",
27
+ version: "2.2.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",
@@ -1792,8 +1792,12 @@ var EVENTS4 = ["return", "exchange"].flatMap(
1792
1792
  status,
1793
1793
  type: `return_prime_${requestType}_${status}`,
1794
1794
  label: `${titleCase(requestType)} ${titleCase(status)}`,
1795
- selection: requestType === "exchange" ? "exchange" : "product",
1796
- subject: requestType === "exchange" ? "the returned product and its replacement" : "the returned product"
1795
+ // A return is one or more products sent back, so it is a multi pick, not the
1796
+ // single "which product was viewed" a browsing event asks for. An exchange is
1797
+ // the same list, followed by the replacement chosen for each line.
1798
+ selection: requestType === "exchange" ? "exchange" : "cart",
1799
+ subject: requestType === "exchange" ? "the returned products and their replacements" : "the returned products",
1800
+ pickPrompt: requestType === "exchange" ? "Select the products being returned for exchange" : "Select the products being returned"
1797
1801
  }))
1798
1802
  );
1799
1803
  var DEFAULT_EVENT4 = "return_requested";
@@ -1806,6 +1810,9 @@ function eventFromPayload4(payload) {
1806
1810
  function selectionFor4(eventName) {
1807
1811
  return eventDef3(eventName).selection;
1808
1812
  }
1813
+ function pickPromptFor(eventName) {
1814
+ return eventDef3(eventName).pickPrompt;
1815
+ }
1809
1816
  var COMMON_FIELDS3 = [
1810
1817
  { source: "request.request_type", target: "\xABgate\xBB + requestType", required: true, note: "return or exchange" },
1811
1818
  { source: "request.status", target: "\xABgate\xBB + status", required: true, note: "requested, approved, received, inspected or rejected" },
@@ -1868,7 +1875,7 @@ function editableFieldsFor3(eventName) {
1868
1875
  }
1869
1876
  var editableFields6 = editableFieldsFor3(DEFAULT_EVENT4);
1870
1877
  function statusBlock(reached, at, comment = null) {
1871
- return { status: reached, comment, created_at: reached ? at : null };
1878
+ return { status: reached, comment: comment ?? "quickreply test", created_at: at };
1872
1879
  }
1873
1880
  function buildPayload6(ctx) {
1874
1881
  const { config, items, phone } = ctx;
@@ -1891,14 +1898,14 @@ function buildPayload6(ctx) {
1891
1898
  id: Number(sel?.variant?.id ?? sel?.product?.id ?? 0),
1892
1899
  refund: {
1893
1900
  requested_mode: "store_credit",
1894
- actual_mode: def.status === "rejected" ? "store_credit" : null,
1895
- meta: def.status === "rejected" && ctx.couponCode ? { discount_code: ctx.couponCode } : null,
1896
- status: def.status === "rejected" ? "refunded" : "pending",
1901
+ actual_mode: "store_credit",
1902
+ meta: { discount_code: ctx.couponCode ?? `RP${String(now.getTime()).slice(-7)}` },
1903
+ status: def.status === "requested" ? "pending" : "refunded",
1897
1904
  refunded_amount: {
1898
- shop_money: { ...def.status === "rejected" ? { amount: originalPrice } : {}, currency_code: currency },
1899
- presentment_money: { ...def.status === "rejected" ? { amount: originalPrice } : {}, currency_code: currency }
1905
+ shop_money: { amount: originalPrice, currency_code: currency },
1906
+ presentment_money: { amount: originalPrice, currency_code: currency }
1900
1907
  },
1901
- refunded_at: def.status === "rejected" ? at : null,
1908
+ refunded_at: at,
1902
1909
  comment: null
1903
1910
  },
1904
1911
  return_fee: {
@@ -1910,8 +1917,8 @@ function buildPayload6(ctx) {
1910
1917
  },
1911
1918
  exchange_fee: {
1912
1919
  price_set: {
1913
- shop_money: { amount: null, currency_code: null },
1914
- presentment_money: { amount: null, currency_code: null }
1920
+ shop_money: { amount: ctx.exchangeFee ?? 0, currency_code: currency },
1921
+ presentment_money: { amount: ctx.exchangeFee ?? 0, currency_code: currency }
1915
1922
  }
1916
1923
  },
1917
1924
  exchange: { order: null },
@@ -1940,7 +1947,7 @@ function buildPayload6(ctx) {
1940
1947
  }
1941
1948
  } : {},
1942
1949
  shipping: [
1943
- def.status === "requested" ? { tracking_available: false, labels: [] } : {
1950
+ {
1944
1951
  shipping_company: "bluedartV2",
1945
1952
  tracking_available: true,
1946
1953
  awb: String(now.getTime()).slice(-11),
@@ -1973,13 +1980,13 @@ function buildPayload6(ctx) {
1973
1980
  do_not_carry_forward_discount: true
1974
1981
  },
1975
1982
  shopify_order_fulfillment_location: null,
1976
- return_location: def.status === "requested" ? null : {
1983
+ return_location: {
1977
1984
  id: "rp-location-1",
1978
1985
  fullName: config.shopify.shop?.name ?? config.shopify.domain,
1979
1986
  mobileNumber: nationalNumber(phone, cust.countryCode),
1980
1987
  pinCode: cust.zip,
1981
1988
  streetAddress1: cust.address1,
1982
- streetAddress2: cust.address2 ?? "",
1989
+ streetAddress2: cust.address2 || "Second line",
1983
1990
  landmark: "",
1984
1991
  city: cust.city,
1985
1992
  state: cust.province,
@@ -2032,7 +2039,7 @@ function buildPayload6(ctx) {
2032
2039
  country_code: cust.countryCode,
2033
2040
  province_code: cust.provinceCode,
2034
2041
  address_line_1: cust.address1,
2035
- address_line_2: cust.address2 ?? "",
2042
+ address_line_2: cust.address2 || "Second line",
2036
2043
  country: cust.country,
2037
2044
  address_line_3: ""
2038
2045
  },
@@ -2092,6 +2099,7 @@ var return_prime_default = {
2092
2099
  editableFields: editableFields6,
2093
2100
  editableFieldsFor: editableFieldsFor3,
2094
2101
  selectionFor: selectionFor4,
2102
+ pickPromptFor,
2095
2103
  eventFromPayload: eventFromPayload4,
2096
2104
  buildPayload: buildPayload6,
2097
2105
  sign: sign6,
@@ -3571,7 +3579,7 @@ function loadPayload(file) {
3571
3579
  }
3572
3580
 
3573
3581
  // src/core/build.mjs
3574
- function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName, collection, envelope, live, delta }) {
3582
+ function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName, collection, envelope, live, delta, exchangeItems }) {
3575
3583
  const provider = getProvider(providerId);
3576
3584
  const event = eventName ?? provider.defaultEvent;
3577
3585
  const resolvedPhone = phone ?? cfg.customer.phone;
@@ -3585,6 +3593,7 @@ function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, p
3585
3593
  envelope,
3586
3594
  live,
3587
3595
  delta,
3596
+ exchangeItems,
3588
3597
  phone: parsed.ok ? parsed.value : String(resolvedPhone ?? "")
3589
3598
  });
3590
3599
  const editable = provider.editableFieldsFor ? provider.editableFieldsFor(event) : provider.editableFields;
@@ -3600,7 +3609,7 @@ function applyEdits(payload, fields, edits) {
3600
3609
  }
3601
3610
  return payload;
3602
3611
  }
3603
- async function buildPayload10({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope, live, delta }) {
3612
+ async function buildPayload10({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope, live, delta, exchangeItems }) {
3604
3613
  const provider0 = getProvider(providerId);
3605
3614
  const needsItems = !provider0.selectionFor || ["cart", "product"].includes(provider0.selectionFor(eventName ?? provider0.defaultEvent));
3606
3615
  if (needsItems && !items.length) throw new ConfigError("no products selected");
@@ -3609,7 +3618,7 @@ async function buildPayload10({ cfg, providerId = "cashfree-occ", items = [], di
3609
3618
  const parsed = toE164(resolvedPhone, cfg.customer.countryCode);
3610
3619
  if (!parsed.ok) throw new ConfigError(`phone is unusable: ${parsed.reason}`);
3611
3620
  const event = eventName ?? provider.defaultEvent;
3612
- const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event, collection, envelope, live, delta });
3621
+ const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event, collection, envelope, live, delta, exchangeItems });
3613
3622
  const body = JSON.stringify(payload);
3614
3623
  const report = await runAll({ payload, body, provider, config: cfg, checkImageUrls, eventName: event });
3615
3624
  const summary = provider.summarize ? provider.summarize(payload) : {};
@@ -3905,6 +3914,7 @@ var STEP = {
3905
3914
  LOADING: "loading",
3906
3915
  COLLECTION: "collection",
3907
3916
  PICK: "pick",
3917
+ REPLACEMENT: "replacement",
3908
3918
  VARIANT: "variant",
3909
3919
  QUANTITY: "quantity",
3910
3920
  DELTA: "delta",
@@ -3932,6 +3942,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
3932
3942
  const [cursor, setCursor] = useState(0);
3933
3943
  const [items, setItems] = useState([]);
3934
3944
  const [pendingVariant, setPendingVariant] = useState(null);
3945
+ const [exchangeItems, setExchangeItems] = useState([]);
3935
3946
  const [delta, setDelta] = useState([]);
3936
3947
  const isDeltaEvent = providerId === "nitro" && (eventName === "addtocart" || eventName === "removefromcart");
3937
3948
  const [draft, setDraft] = useState(null);
@@ -4054,10 +4065,18 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
4054
4065
  alive = false;
4055
4066
  };
4056
4067
  }, [step === STEP.EVENT, eventName]);
4068
+ const onReplacementsPicked = (ids) => {
4069
+ const picked = ids.map((id) => products.find((p) => p.id === id)).filter(Boolean);
4070
+ if (!picked.length) return;
4071
+ const pairs = picked.map((p) => ({ product: p, variant: p.variants[0], quantity: 1 }));
4072
+ setExchangeItems(pairs);
4073
+ startReview(0, collection, items, delta, pairs);
4074
+ };
4057
4075
  const advance = (nextItems, nextCursor) => {
4058
4076
  if (nextCursor >= chosen.length) {
4059
4077
  setItems(nextItems);
4060
4078
  if (isDeltaEvent) setStep(STEP.DELTA);
4079
+ else if (selection === "exchange") setStep(STEP.REPLACEMENT);
4061
4080
  else if (selection === "cart") setStep(STEP.DISCOUNT);
4062
4081
  else startReview(0, collection, nextItems);
4063
4082
  return;
@@ -4114,7 +4133,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
4114
4133
  const nextItems = [...items, { product, variant: pendingVariant, quantity: qty }];
4115
4134
  advance(nextItems, cursor + 1);
4116
4135
  };
4117
- const startReview = async (discount, chosenCollection = collection, chosenItems = items, chosenDelta = delta) => {
4136
+ const startReview = async (discount, chosenCollection = collection, chosenItems = items, chosenDelta = delta, chosenExchange = exchangeItems) => {
4118
4137
  try {
4119
4138
  let live = null;
4120
4139
  if (needsLive(providerId, eventName)) {
@@ -4129,7 +4148,8 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
4129
4148
  eventName,
4130
4149
  collection: chosenCollection,
4131
4150
  live,
4132
- delta: chosenDelta
4151
+ delta: chosenDelta,
4152
+ exchangeItems: chosenExchange
4133
4153
  });
4134
4154
  setDraft({ ...next, discount, live, delta: chosenDelta });
4135
4155
  setStep(STEP.MODE);
@@ -4252,7 +4272,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
4252
4272
  ] }),
4253
4273
  step === STEP.PICK && selection !== "product" && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
4254
4274
  /* @__PURE__ */ jsxs8(Text8, { children: [
4255
- `Select the products in the cart `,
4275
+ `${provider.pickPromptFor?.(eventName) ?? "Select the products in the cart"} `,
4256
4276
  /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "(space toggles \xB7 enter confirms)" })
4257
4277
  ] }),
4258
4278
  /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(
@@ -4267,6 +4287,24 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
4267
4287
  }
4268
4288
  ) })
4269
4289
  ] }),
4290
+ step === STEP.REPLACEMENT && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
4291
+ /* @__PURE__ */ jsxs8(Text8, { children: [
4292
+ "What is each returned line being exchanged for? ",
4293
+ /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "(space toggles \xB7 enter confirms)" })
4294
+ ] }),
4295
+ /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: `Picked in order, paired with the ${items.length} line${items.length === 1 ? "" : "s"} coming back.` }),
4296
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(
4297
+ MultiSelect,
4298
+ {
4299
+ visibleOptionCount: 10,
4300
+ options: products.map((p) => ({
4301
+ label: `${truncate(p.title, 44).padEnd(45)} ${money(p.variants[0]?.price, currency)}`,
4302
+ value: p.id
4303
+ })),
4304
+ onSubmit: onReplacementsPicked
4305
+ }
4306
+ ) })
4307
+ ] }),
4270
4308
  step === STEP.DELTA && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
4271
4309
  /* @__PURE__ */ jsxs8(Text8, { children: [
4272
4310
  eventName === "addtocart" ? "Which of those lines is the shopper adding now? " : "Which of those lines is the shopper removing? ",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hookwright",
3
- "version": "2.0.1",
3
+ "version": "2.2.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",