hookwright 1.9.0 → 1.10.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/cli.js +119 -28
- 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.
|
|
27
|
+
version: "1.10.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
|
];
|
|
@@ -1173,16 +1172,20 @@ var BROWSING_FIELDS = [
|
|
|
1173
1172
|
{ source: "session_id", target: "sessionId" }
|
|
1174
1173
|
];
|
|
1175
1174
|
var CART_FIELDS = [
|
|
1176
|
-
{ source: "line_items[].title", target: "cart.
|
|
1177
|
-
{ source: "line_items[].
|
|
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: "
|
|
1182
|
-
{ source: "subtotal_price", target: "
|
|
1183
|
-
{ source: "total_discount", target: "
|
|
1184
|
-
{ source: "currency", target: "
|
|
1185
|
-
{ source: "
|
|
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
|
-
|
|
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:
|
|
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
|
-
//
|
|
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: "
|
|
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?.
|
|
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" },
|
|
@@ -1541,12 +1576,64 @@ var editableFields5 = [
|
|
|
1541
1576
|
{ group: "Attribution", path: "payload.metadata.landing_page", label: "Landing page", optional: true }
|
|
1542
1577
|
];
|
|
1543
1578
|
function buildPayload5(ctx) {
|
|
1544
|
-
const { config, items, phone, live } = ctx;
|
|
1579
|
+
const { config, items, phone, live, collection } = ctx;
|
|
1580
|
+
const eventName = ctx.eventName ?? DEFAULT_EVENT3;
|
|
1545
1581
|
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
1546
1582
|
const currency = ctx.currency || config.defaults.currency;
|
|
1547
1583
|
const domain = config.shopify.domain;
|
|
1548
1584
|
const cust = config.customer;
|
|
1549
1585
|
const { dialCode, national } = splitPhone(phone, cust.countryCode);
|
|
1586
|
+
const envelope = {
|
|
1587
|
+
event_id: `fx-${now.getTime()}`,
|
|
1588
|
+
event_type: eventName,
|
|
1589
|
+
event_time: now.toISOString(),
|
|
1590
|
+
store: {
|
|
1591
|
+
id: 1,
|
|
1592
|
+
name: config.shopify.shop?.name ?? config.shopify.domain,
|
|
1593
|
+
shopify_domain: config.shopify.domain,
|
|
1594
|
+
main_domain: config.shopify.domain
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
const user = {
|
|
1598
|
+
email: config.customer.email,
|
|
1599
|
+
full_name: `${config.customer.firstName} ${config.customer.lastName}`.trim(),
|
|
1600
|
+
first_name: config.customer.firstName,
|
|
1601
|
+
last_name: config.customer.lastName,
|
|
1602
|
+
dial_code: splitPhone(phone, config.customer.countryCode).dialCode,
|
|
1603
|
+
phone: splitPhone(phone, config.customer.countryCode).national,
|
|
1604
|
+
phone_with_dial_code: phone
|
|
1605
|
+
};
|
|
1606
|
+
if (eventName === "COLLECTION_VIEWED") {
|
|
1607
|
+
return {
|
|
1608
|
+
...envelope,
|
|
1609
|
+
payload: {
|
|
1610
|
+
user,
|
|
1611
|
+
collection_viewed: {
|
|
1612
|
+
collection_id: Number(collection?.id) || 0,
|
|
1613
|
+
name: collection?.title ?? "",
|
|
1614
|
+
handle: collection?.handle ?? "",
|
|
1615
|
+
image: collection?.image?.src ?? "",
|
|
1616
|
+
timestamp: now.toISOString()
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
if (eventName === "PRODUCT_VIEWED") {
|
|
1622
|
+
const first = items?.[0];
|
|
1623
|
+
return {
|
|
1624
|
+
...envelope,
|
|
1625
|
+
payload: {
|
|
1626
|
+
user,
|
|
1627
|
+
product_viewed: {
|
|
1628
|
+
product_id: Number(first?.product?.id) || 0,
|
|
1629
|
+
title: first?.product?.title ?? "",
|
|
1630
|
+
handle: first?.product?.handle ?? "",
|
|
1631
|
+
image: imageForVariant(first?.product, first?.variant) ?? "",
|
|
1632
|
+
timestamp: now.toISOString()
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1550
1637
|
const cartItems = (items ?? []).map(({ product, variant, quantity }) => {
|
|
1551
1638
|
const unit = money2(variant?.price ?? 0);
|
|
1552
1639
|
return {
|
|
@@ -1638,7 +1725,11 @@ var flexype_default = {
|
|
|
1638
1725
|
urlKey: "flexype",
|
|
1639
1726
|
label: "FlexyPe",
|
|
1640
1727
|
eventType: "flexype_abandoned_checkout",
|
|
1728
|
+
events: EVENTS3,
|
|
1641
1729
|
defaultEvent: DEFAULT_EVENT3,
|
|
1730
|
+
gateFor: (eventName) => ({ path: GATE_PATH3, value: eventName }),
|
|
1731
|
+
selectionFor: selectionFor3,
|
|
1732
|
+
eventFromPayload: eventFromPayload3,
|
|
1642
1733
|
gate: { path: GATE_PATH3, value: GATE_VALUE2 },
|
|
1643
1734
|
signatureScheme: "static shared secret in the header (no body signature)",
|
|
1644
1735
|
signatureHeader: "x-flexype-authorization",
|
|
@@ -1654,7 +1745,7 @@ var flexype_default = {
|
|
|
1654
1745
|
// src/providers/return-prime.mjs
|
|
1655
1746
|
var STATUSES = ["requested", "approved", "received", "inspected", "rejected"];
|
|
1656
1747
|
var titleCase = (v) => v.charAt(0).toUpperCase() + v.slice(1);
|
|
1657
|
-
var
|
|
1748
|
+
var EVENTS4 = ["return", "exchange"].flatMap(
|
|
1658
1749
|
(requestType) => STATUSES.map((status) => ({
|
|
1659
1750
|
eventName: `${requestType}_${status}`,
|
|
1660
1751
|
requestType,
|
|
@@ -1666,14 +1757,14 @@ var EVENTS3 = ["return", "exchange"].flatMap(
|
|
|
1666
1757
|
}))
|
|
1667
1758
|
);
|
|
1668
1759
|
var DEFAULT_EVENT4 = "return_requested";
|
|
1669
|
-
var
|
|
1670
|
-
function
|
|
1760
|
+
var eventDef3 = (eventName) => EVENTS4.find((e) => e.eventName === eventName) ?? EVENTS4.find((e) => e.eventName === DEFAULT_EVENT4);
|
|
1761
|
+
function eventFromPayload4(payload) {
|
|
1671
1762
|
const request = payload?.request ?? payload ?? {};
|
|
1672
1763
|
if (!request.request_type || !request.status) return void 0;
|
|
1673
1764
|
return `${request.request_type}_${request.status}`;
|
|
1674
1765
|
}
|
|
1675
|
-
function
|
|
1676
|
-
return
|
|
1766
|
+
function selectionFor4(eventName) {
|
|
1767
|
+
return eventDef3(eventName).selection;
|
|
1677
1768
|
}
|
|
1678
1769
|
var COMMON_FIELDS3 = [
|
|
1679
1770
|
{ source: "request_type", target: "\xABgate\xBB + requestType", required: true, note: "return or exchange" },
|
|
@@ -1699,10 +1790,10 @@ var EXCHANGE_FIELDS = [
|
|
|
1699
1790
|
];
|
|
1700
1791
|
var fieldMap6 = COMMON_FIELDS3;
|
|
1701
1792
|
function fieldMapFor(eventName) {
|
|
1702
|
-
return
|
|
1793
|
+
return eventDef3(eventName).requestType === "exchange" ? [...COMMON_FIELDS3, ...EXCHANGE_FIELDS] : COMMON_FIELDS3;
|
|
1703
1794
|
}
|
|
1704
1795
|
function editableFieldsFor3(eventName) {
|
|
1705
|
-
const def =
|
|
1796
|
+
const def = eventDef3(eventName);
|
|
1706
1797
|
const p = (path6) => def.envelope === "root" ? path6 : `request.${path6}`;
|
|
1707
1798
|
return [
|
|
1708
1799
|
{ group: "Request", path: p("request_number"), label: "Request number" },
|
|
@@ -1728,7 +1819,7 @@ function statusBlock(reached, at, comment = null) {
|
|
|
1728
1819
|
function buildPayload6(ctx) {
|
|
1729
1820
|
const { config, items, phone } = ctx;
|
|
1730
1821
|
const eventName = ctx.eventName ?? DEFAULT_EVENT4;
|
|
1731
|
-
const def =
|
|
1822
|
+
const def = eventDef3(eventName);
|
|
1732
1823
|
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
1733
1824
|
const currency = ctx.currency || config.defaults.currency;
|
|
1734
1825
|
const cust = config.customer;
|
|
@@ -1930,19 +2021,19 @@ var return_prime_default = {
|
|
|
1930
2021
|
id: "return-prime",
|
|
1931
2022
|
urlKey: "return_prime",
|
|
1932
2023
|
label: "Return Prime",
|
|
1933
|
-
events:
|
|
2024
|
+
events: EVENTS4,
|
|
1934
2025
|
defaultEvent: DEFAULT_EVENT4,
|
|
1935
2026
|
eventType: "return_prime_return_requested",
|
|
1936
2027
|
gate: { path: "request.request_type", value: "return" },
|
|
1937
|
-
gateFor: (eventName) => ({ path: "request.status", value:
|
|
2028
|
+
gateFor: (eventName) => ({ path: "request.status", value: eventDef3(eventName).status }),
|
|
1938
2029
|
signatureScheme: "none documented \u2014 every request is accepted",
|
|
1939
2030
|
signatureHeader: null,
|
|
1940
2031
|
fieldMap: fieldMap6,
|
|
1941
2032
|
fieldMapFor,
|
|
1942
2033
|
editableFields: editableFields6,
|
|
1943
2034
|
editableFieldsFor: editableFieldsFor3,
|
|
1944
|
-
selectionFor:
|
|
1945
|
-
eventFromPayload:
|
|
2035
|
+
selectionFor: selectionFor4,
|
|
2036
|
+
eventFromPayload: eventFromPayload4,
|
|
1946
2037
|
buildPayload: buildPayload6,
|
|
1947
2038
|
sign: sign6,
|
|
1948
2039
|
webhookUrl: webhookUrl6,
|