hookwright 1.12.1 → 1.13.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 +453 -8
  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.12.1",
27
+ version: "1.13.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",
@@ -178,7 +178,9 @@ var DEFAULT_CONFIG = {
178
178
  shopflo: { webhookUrl: "", webhookSecret: "" },
179
179
  flexype: { webhookUrl: "", webhookSecret: "" },
180
180
  "return-prime": { webhookUrl: "", webhookSecret: "" },
181
- fastrr: { webhookUrl: "", webhookSecret: "" }
181
+ fastrr: { webhookUrl: "", webhookSecret: "" },
182
+ gokwik: { webhookUrl: "", webhookSecret: "" },
183
+ ecom360: { webhookUrl: "", webhookSecret: "" }
182
184
  },
183
185
  customer: {
184
186
  firstName: "",
@@ -381,6 +383,7 @@ function normalizeProduct(product) {
381
383
  title: v.title,
382
384
  sku: v.sku || "",
383
385
  price: v.price != null ? Number(v.price) : null,
386
+ compareAtPrice: v.compare_at_price != null ? Number(v.compare_at_price) : null,
384
387
  available: v.inventory_quantity == null ? true : v.inventory_quantity > 0,
385
388
  imageId: v.image_id ? String(v.image_id) : null
386
389
  }));
@@ -2257,6 +2260,423 @@ var fastrr_default = {
2257
2260
  summarize: summarize7
2258
2261
  };
2259
2262
 
2263
+ // src/providers/gokwik.mjs
2264
+ var GATE_PATH5 = "abc_url";
2265
+ var GATE_VALUE4 = "";
2266
+ var fieldMap8 = [
2267
+ { source: "abc_url", target: "\xABgate\xBB + checkout_link", required: true, note: "absent and the event is dropped; its origin builds every other link" },
2268
+ { source: "customer.phone / address.phone", target: "customer.phone", required: true },
2269
+ { source: "sa_localization", target: "phone region", required: true, note: 'address.country says "India", which the parser cannot use' },
2270
+ { source: "customer.firstname", target: "customer.firstName" },
2271
+ { source: "customer.lastname", target: "customer.lastName" },
2272
+ { source: "customer.email / address.email", target: "customer.email" },
2273
+ { source: "items[].title", target: "product_details", required: true, note: "the first PAID line wins; freebies rank last" },
2274
+ { source: "items[].product_title", target: "product_name" },
2275
+ { source: "items[].image", target: "product_image_url", note: "read off the featured line only" },
2276
+ { source: "items[].url", target: "product_url", note: "relative \u2014 resolved against the abc_url origin" },
2277
+ { source: "items[].final_price", target: "product_price", note: "PAISE \u2014 the consumer divides by 100" },
2278
+ { source: "items[].sku", target: "product_sku" },
2279
+ { source: "items[].product_type", target: "product_type" },
2280
+ { source: "items[].variant_id", target: "cart_link", note: "joined as variant:quantity" },
2281
+ { source: "items[].quantity", target: "total_quantity", note: "summed across the cart" },
2282
+ { source: "item_count", target: "item_count", note: "gokwik's own count is kept over the line count" },
2283
+ { source: "total_price", target: "total_price", note: "RUPEE string, unlike the line prices" },
2284
+ { source: "original_total_price", target: "original_total_price" },
2285
+ { source: "items_subtotal_price", target: "subtotal_price" },
2286
+ { source: "total_discount", target: "total_discount" },
2287
+ { source: "currency", target: "currency" },
2288
+ { source: "request_id", target: "request_id" },
2289
+ { source: "token", target: "token" },
2290
+ { source: "drop_stage", target: "drop_stage" },
2291
+ { source: "rto_risk_flag", target: "rto_risk_flag" },
2292
+ { source: "brand_order_count", target: "brand_order_count" },
2293
+ { source: "mkt_source", target: "mkt_source" },
2294
+ { source: "mkt_medium", target: "mkt_medium" },
2295
+ { source: "mkt_campaign", target: "mkt_campaign" },
2296
+ { source: "mkt_content", target: "mkt_content" },
2297
+ { source: "mkt_term", target: "mkt_term" },
2298
+ { source: "mkt_fbclid", target: "mkt_fbclid" },
2299
+ { source: "landing_page", target: "landing_page" },
2300
+ { source: "orig_referrer", target: "orig_referrer" },
2301
+ { source: "address.city", target: "address.city" },
2302
+ { source: "address.state", target: "address.state" },
2303
+ { source: "address.pincode", target: "address.pincode" },
2304
+ { source: "address.country", target: "address.country" },
2305
+ { source: "address.address", target: "address.address" },
2306
+ { source: "shipping.price", target: "shipping.price" },
2307
+ { source: "city", target: "city", note: "the ip city, not the delivery city" },
2308
+ { source: "isp", target: "isp" },
2309
+ { source: "user_agent", target: "user_agent" },
2310
+ { source: "updated_at", target: "eventTime" }
2311
+ ];
2312
+ var editableFields8 = [
2313
+ { group: "Checkout", path: "request_id", label: "Request id" },
2314
+ { group: "Checkout", path: "token", label: "Token" },
2315
+ { group: "Checkout", path: "abc_url", label: "Recovery URL", hint: "gates the event; its origin builds the product and cart links" },
2316
+ { group: "Checkout", path: "drop_stage", label: "Drop stage" },
2317
+ { group: "Checkout", path: "rto_risk_flag", label: "RTO risk flag" },
2318
+ { group: "Checkout", path: "gokwik_source_channel", label: "Source channel" },
2319
+ { group: "Pricing", path: "total_price", label: "Total price", hint: "rupee string, unlike the line prices" },
2320
+ { group: "Pricing", path: "total_discount", label: "Discount" },
2321
+ { group: "Pricing", path: "currency", label: "Currency" },
2322
+ { group: "Customer", path: "customer.firstname", label: "First name" },
2323
+ { group: "Customer", path: "customer.lastname", label: "Last name" },
2324
+ { group: "Customer", path: "customer.email", label: "Email" },
2325
+ { group: "Customer", path: "customer.phone", label: "Phone" },
2326
+ { group: "Customer", path: "brand_order_count", label: "Brand order count", type: "number" },
2327
+ { group: "Customer", path: "sa_localization", label: "Localization", hint: "two letter region used to parse the phone" },
2328
+ { group: "Shipping", path: "address.address", label: "Address line" },
2329
+ { group: "Shipping", path: "address.city", label: "City" },
2330
+ { group: "Shipping", path: "address.state", label: "State" },
2331
+ { group: "Shipping", path: "address.pincode", label: "Pincode", type: "number" },
2332
+ { group: "Attribution", path: "mkt_source", label: "UTM source" },
2333
+ { group: "Attribution", path: "mkt_medium", label: "UTM medium" },
2334
+ { group: "Attribution", path: "mkt_campaign", label: "UTM campaign" },
2335
+ { group: "Attribution", path: "landing_page", label: "Landing page" },
2336
+ { group: "Attribution", path: "orig_referrer", label: "Referrer" }
2337
+ ];
2338
+ function paise(rupees) {
2339
+ return Math.round(money2(rupees) * 100);
2340
+ }
2341
+ function buildPayload8(ctx) {
2342
+ const { config, items, phone } = ctx;
2343
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
2344
+ const currency = ctx.currency || config.defaults.currency;
2345
+ const domain = config.shopify.domain;
2346
+ const cust = config.customer;
2347
+ const utm = config.defaults.utm ?? {};
2348
+ const lineItems = (items ?? []).map(({ product, variant, quantity }) => {
2349
+ const unit = money2(variant?.price ?? 0);
2350
+ return {
2351
+ id: Number(variant?.id ?? 0),
2352
+ sku: variant?.sku || "",
2353
+ url: `/products/${product.handle}?variant=${variant?.id ?? ""}`,
2354
+ grams: 500,
2355
+ image: imageForVariant(product, variant),
2356
+ price: paise(unit),
2357
+ title: `${product.title}${variant?.title ? ` - ${variant.title}` : ""}`,
2358
+ vendor: product.vendor ?? "",
2359
+ taxable: true,
2360
+ quantity,
2361
+ gift_card: false,
2362
+ line_price: paise(unit * quantity),
2363
+ product_id: Number(product.id),
2364
+ variant_id: Number(variant?.id ?? 0),
2365
+ final_price: paise(unit),
2366
+ product_type: product.productType ?? "",
2367
+ product_title: product.title,
2368
+ original_price: paise(unit),
2369
+ total_discount: 0,
2370
+ final_line_price: paise(unit * quantity),
2371
+ requires_shipping: true
2372
+ };
2373
+ });
2374
+ const subtotal = money2(lineItems.reduce((sum, li) => sum + li.final_line_price / 100, 0));
2375
+ const discount = money2(Math.min(ctx.discount ?? 0, subtotal));
2376
+ const total = money2(subtotal - discount);
2377
+ const requestId = ctx.requestId ?? `gk-${now.getTime()}`;
2378
+ const abcUrl = `https://${domain}?mrid=${requestId}`;
2379
+ return {
2380
+ request_id: requestId,
2381
+ token: `${requestId}?key=${now.getTime().toString(36)}`,
2382
+ note: null,
2383
+ attributes: "{}",
2384
+ original_total_price: subtotal.toFixed(4),
2385
+ total_price: total.toFixed(4),
2386
+ total_discount: discount.toFixed(4),
2387
+ total_weight: "500.0000",
2388
+ is_abandoned: true,
2389
+ item_count: lineItems.length,
2390
+ items: lineItems,
2391
+ requires_shipping: true,
2392
+ currency,
2393
+ items_subtotal_price: String(subtotal),
2394
+ cart_level_discount_applications: [],
2395
+ discount: null,
2396
+ shipping: {
2397
+ id: 1,
2398
+ name: "Shipping",
2399
+ price: ctx.shippingPrice ?? 0,
2400
+ title: "Shipping",
2401
+ base_price: 0,
2402
+ cod_charges: 0,
2403
+ prepaid_price: 0,
2404
+ payment_options: "all",
2405
+ discounted_price: 0
2406
+ },
2407
+ abc_url: abcUrl,
2408
+ created_at: now.toISOString(),
2409
+ updated_at: now.toISOString(),
2410
+ city: cust.city,
2411
+ isp: "Test ISP",
2412
+ ip: "203.0.113.10",
2413
+ mkt_source: utm.utm_source ?? "direct",
2414
+ mkt_medium: utm.utm_medium ?? null,
2415
+ mkt_campaign: utm.utm_campaign ?? null,
2416
+ mkt_content: utm.utm_content ?? null,
2417
+ mkt_term: utm.utm_term ?? null,
2418
+ mkt_fbclid: utm.fbclid ?? null,
2419
+ gift_card_total: "0",
2420
+ payment_methods: ["upi", "cc", "dc", "netbanking"],
2421
+ drop_stage: "Payment Page",
2422
+ checkout_url: null,
2423
+ note_text: "",
2424
+ landing_page: "/",
2425
+ orig_referrer: `https://${domain}/`,
2426
+ gokwik_source_channel: "shopify_checkout",
2427
+ store_source: "web",
2428
+ store_platform: null,
2429
+ rto_risk_flag: "Control",
2430
+ sa_localization: cust.countryCode,
2431
+ shopifysessionid: `gk-session-${now.getTime()}`,
2432
+ user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/151.0.0.0 Safari/537.36",
2433
+ notify_customer: false,
2434
+ brand_order_count: 2,
2435
+ address: {
2436
+ firstname: cust.firstName,
2437
+ lastname: cust.lastName,
2438
+ city: cust.city,
2439
+ pincode: Number(cust.zip) || 0,
2440
+ state: cust.province,
2441
+ country: cust.country,
2442
+ email: cust.email,
2443
+ phone,
2444
+ default_address: 0,
2445
+ addresstype: "home",
2446
+ partial_address: false,
2447
+ total_order_count: 1,
2448
+ deliver_order_count: 0,
2449
+ address: cust.address1
2450
+ },
2451
+ customer: {
2452
+ firstname: cust.firstName,
2453
+ lastname: cust.lastName,
2454
+ email: cust.email,
2455
+ phone
2456
+ }
2457
+ };
2458
+ }
2459
+ function sign8() {
2460
+ return { signature: null, timestamp: null, headers: { "content-type": "application/json" } };
2461
+ }
2462
+ function webhookUrl8(config) {
2463
+ return String(targetFor(config, "gokwik").webhookUrl ?? "").trim();
2464
+ }
2465
+ function webhookSecret8(config) {
2466
+ return String(targetFor(config, "gokwik").webhookSecret ?? "");
2467
+ }
2468
+ function summarize8(payload) {
2469
+ const items = Array.isArray(payload?.items) ? payload.items : [];
2470
+ let store;
2471
+ try {
2472
+ store = payload?.abc_url ? new URL(payload.abc_url).host : void 0;
2473
+ } catch {
2474
+ store = payload?.abc_url;
2475
+ }
2476
+ return {
2477
+ event: "abandoned checkout",
2478
+ store,
2479
+ customer: `${payload?.customer?.firstname ?? ""} ${payload?.customer?.lastname ?? ""} \xB7 ${payload?.customer?.phone ?? ""}`.trim(),
2480
+ items: items.map((i) => `${i.title} x${i.quantity}`).join(", "),
2481
+ total: payload?.total_price,
2482
+ currency: payload?.currency,
2483
+ extra: `stage ${payload?.drop_stage}, rto ${payload?.rto_risk_flag}`,
2484
+ link: payload?.abc_url
2485
+ };
2486
+ }
2487
+ var gokwik_default = {
2488
+ id: "gokwik",
2489
+ urlKey: "gokwik",
2490
+ label: "GoKwik",
2491
+ eventType: "gokwik_abandoned_checkout",
2492
+ gate: { path: GATE_PATH5, value: GATE_VALUE4 },
2493
+ signatureScheme: "none documented \u2014 every request is accepted",
2494
+ signatureHeader: null,
2495
+ fieldMap: fieldMap8,
2496
+ editableFields: editableFields8,
2497
+ buildPayload: buildPayload8,
2498
+ sign: sign8,
2499
+ webhookUrl: webhookUrl8,
2500
+ webhookSecret: webhookSecret8,
2501
+ summarize: summarize8
2502
+ };
2503
+
2504
+ // src/providers/ecom360.mjs
2505
+ import crypto4 from "crypto";
2506
+ var GATE_PATH6 = "event";
2507
+ var GATE_VALUE5 = "ABANDONED_CHECKOUT";
2508
+ var fieldMap9 = [
2509
+ { source: "event", target: "\xABgate\xBB", required: true, note: "must be ABANDONED_CHECKOUT or the event is dropped" },
2510
+ { source: "data.phone / data.customer.shipping_address.phone", target: "customer.phone", required: true },
2511
+ { source: "data.customer.shipping_address.country_code", target: "phone region" },
2512
+ { source: "data.customer.first_name", target: "customer.firstName" },
2513
+ { source: "data.customer.last_name", target: "customer.lastName" },
2514
+ { source: "data.email / data.customer.email", target: "customer.email" },
2515
+ { source: "data.abandoned_checkout_url", target: "checkout_link", required: true },
2516
+ { source: "data.store_url", target: "store_url + cart_link", required: true, note: "the cart link is built from it; no lookup needed" },
2517
+ { source: "data.line_items[].title", target: "product_details", required: true, note: "the first PAID line wins; freebies rank last" },
2518
+ { source: "data.line_items[].sku", target: "product_sku" },
2519
+ { source: "data.line_items[].price", target: "product_price", note: "also ranks the lines; a zero marks a freebie" },
2520
+ { source: "data.line_items[].variant_id", target: "cart_link", note: "joined as variant:quantity" },
2521
+ { source: "data.line_items[].quantity", target: "total_quantity", note: "summed across the cart" },
2522
+ { source: "data.line_items[]", target: "item_count", note: "the line count" },
2523
+ { source: "\u2014 (no handle is sent)", target: "product_url", note: "the consumer builds none, by design" },
2524
+ { source: "data.total_price", target: "total_price" },
2525
+ { source: "data.original_total_price", target: "original_total_price" },
2526
+ { source: "data.total_discount", target: "total_discount" },
2527
+ { source: "data.promotions[0].code", target: "coupon_code" },
2528
+ { source: "data.promotions[0].value", target: "coupon_value" },
2529
+ { source: "data.cart_id", target: "cart_id" },
2530
+ { source: "data.cart_token", target: "cart_token" },
2531
+ { source: "data.platform", target: "platform" },
2532
+ { source: "data.utm_parameters.utm_source", target: "utm_parameters.utm_source" },
2533
+ { source: "data.utm_parameters.utm_medium", target: "utm_parameters.utm_medium" },
2534
+ { source: "data.utm_parameters.utm_campaign", target: "utm_parameters.utm_campaign" },
2535
+ { source: "data.utm_parameters.fbclid", target: "utm_parameters.fbclid" },
2536
+ { source: "data.customer.shipping_address.city", target: "shipping_address.city" },
2537
+ { source: "data.customer.shipping_address.province", target: "shipping_address.province" },
2538
+ { source: "data.customer.shipping_address.zip", target: "shipping_address.zip" },
2539
+ { source: "data.customer.shipping_address.country", target: "shipping_address.country" }
2540
+ ];
2541
+ var editableFields9 = [
2542
+ { group: "Checkout", path: "data.cart_id", label: "Cart id" },
2543
+ { group: "Checkout", path: "data.cart_token", label: "Cart token" },
2544
+ { group: "Checkout", path: "data.abandoned_checkout_url", label: "Checkout URL", hint: "the link the shopper is sent back to" },
2545
+ { group: "Checkout", path: "data.store_url", label: "Store domain", hint: "the cart link is built from this" },
2546
+ { group: "Checkout", path: "data.platform", label: "Platform" },
2547
+ { group: "Pricing", path: "data.total_price", label: "Total price", type: "number" },
2548
+ { group: "Pricing", path: "data.original_total_price", label: "Original total", type: "number" },
2549
+ { group: "Pricing", path: "data.total_discount", label: "Discount", type: "number" },
2550
+ { group: "Customer", path: "data.customer.first_name", label: "First name" },
2551
+ { group: "Customer", path: "data.customer.last_name", label: "Last name" },
2552
+ { group: "Customer", path: "data.email", label: "Email" },
2553
+ { group: "Customer", path: "data.phone", label: "Phone" },
2554
+ { group: "Shipping", path: "data.customer.shipping_address.address1", label: "Address line 1" },
2555
+ { group: "Shipping", path: "data.customer.shipping_address.city", label: "City" },
2556
+ { group: "Shipping", path: "data.customer.shipping_address.province", label: "State" },
2557
+ { group: "Shipping", path: "data.customer.shipping_address.zip", label: "Zip / postcode" },
2558
+ { group: "Shipping", path: "data.customer.shipping_address.country_code", label: "Country code", hint: "region used to parse the phone" },
2559
+ { group: "Attribution", path: "data.utm_parameters.utm_source", label: "UTM source" },
2560
+ { group: "Attribution", path: "data.utm_parameters.utm_campaign", label: "UTM campaign" },
2561
+ { group: "Attribution", path: "data.utm_parameters.fbclid", label: "FBCLID" }
2562
+ ];
2563
+ function buildPayload9(ctx) {
2564
+ const { config, items, phone } = ctx;
2565
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
2566
+ const domain = config.shopify.domain;
2567
+ const cust = config.customer;
2568
+ const utm = config.defaults.utm ?? {};
2569
+ const lineItems = (items ?? []).map(({ product, variant, quantity }) => ({
2570
+ product_id: String(product.id),
2571
+ variant_id: String(variant?.id ?? ""),
2572
+ sku: variant?.sku || "",
2573
+ price: money2(variant?.price ?? 0),
2574
+ quantity,
2575
+ gram: 200,
2576
+ title: `${product.title}${variant?.title ? ` - ${variant.title}` : ""}`
2577
+ }));
2578
+ const subtotal = money2(lineItems.reduce((sum, li) => sum + li.price * li.quantity, 0));
2579
+ const discount = money2(Math.min(ctx.discount ?? 0, subtotal));
2580
+ const total = money2(subtotal - discount);
2581
+ const cartToken = ctx.cartToken ?? `ec-${now.getTime()}`;
2582
+ const shippingAddress = {
2583
+ first_name: cust.firstName,
2584
+ last_name: cust.lastName,
2585
+ address1: cust.address1,
2586
+ address2: "",
2587
+ city: cust.city,
2588
+ province: cust.province,
2589
+ country: cust.country,
2590
+ zip: cust.zip,
2591
+ phone,
2592
+ name: `${cust.firstName} ${cust.lastName}`.trim(),
2593
+ province_code: cust.provinceCode ?? "",
2594
+ country_code: cust.countryCode
2595
+ };
2596
+ return {
2597
+ event: GATE_VALUE5,
2598
+ data: {
2599
+ cart_id: `cart_${now.getTime()}`,
2600
+ store_url: domain,
2601
+ platform: "SHOPIFY",
2602
+ cart_token: cartToken,
2603
+ email: cust.email,
2604
+ phone,
2605
+ abandoned_checkout_url: `https://${domain}/checkouts/${cartToken}`,
2606
+ original_total_price: subtotal,
2607
+ total_price: total,
2608
+ total_discount: discount,
2609
+ utm_parameters: {
2610
+ fbclid: utm.fbclid ?? void 0,
2611
+ utm_source: utm.utm_source ?? void 0,
2612
+ utm_medium: utm.utm_medium ?? void 0,
2613
+ utm_campaign: utm.utm_campaign ?? void 0,
2614
+ utm_content: utm.utm_content ?? void 0,
2615
+ utm_term: utm.utm_term ?? void 0
2616
+ },
2617
+ line_items: lineItems,
2618
+ promotions: ctx.couponCode ? [{ code: ctx.couponCode, value: discount }] : [],
2619
+ customer: {
2620
+ email: cust.email,
2621
+ first_name: cust.firstName,
2622
+ last_name: cust.lastName,
2623
+ shipping_address: shippingAddress
2624
+ }
2625
+ }
2626
+ };
2627
+ }
2628
+ function sign9({ body, secret }) {
2629
+ const signature = crypto4.createHmac("sha256", String(secret)).update(body).digest("base64");
2630
+ return {
2631
+ timestamp: null,
2632
+ signature,
2633
+ headers: {
2634
+ "content-type": "application/json",
2635
+ "x-webhook-signature": signature,
2636
+ "x-webhook-topic": "abandonedCheckout",
2637
+ "x-webhook-version": "v1",
2638
+ "user-agent": "Ecom360-v1"
2639
+ }
2640
+ };
2641
+ }
2642
+ function webhookUrl9(config) {
2643
+ return String(targetFor(config, "ecom360").webhookUrl ?? "").trim();
2644
+ }
2645
+ function webhookSecret9(config) {
2646
+ return String(targetFor(config, "ecom360").webhookSecret ?? "");
2647
+ }
2648
+ function summarize9(payload) {
2649
+ const d = payload?.data ?? {};
2650
+ const items = Array.isArray(d.line_items) ? d.line_items : [];
2651
+ const c2 = d.customer ?? {};
2652
+ return {
2653
+ event: "abandoned checkout",
2654
+ store: d.store_url,
2655
+ customer: `${c2.first_name ?? ""} ${c2.last_name ?? ""} \xB7 ${d.phone ?? ""}`.trim(),
2656
+ items: items.map((i) => `${i.title} x${i.quantity}`).join(", "),
2657
+ total: d.total_price,
2658
+ currency: void 0,
2659
+ extra: `platform ${d.platform}`,
2660
+ link: d.abandoned_checkout_url
2661
+ };
2662
+ }
2663
+ var ecom360_default = {
2664
+ id: "ecom360",
2665
+ urlKey: "ecom360",
2666
+ label: "Ecom 360",
2667
+ eventType: "ecom360_abandoned_checkout",
2668
+ gate: { path: GATE_PATH6, value: GATE_VALUE5 },
2669
+ signatureScheme: "base64 HMAC-SHA256 over the body",
2670
+ signatureHeader: "x-webhook-signature",
2671
+ fieldMap: fieldMap9,
2672
+ editableFields: editableFields9,
2673
+ buildPayload: buildPayload9,
2674
+ sign: sign9,
2675
+ webhookUrl: webhookUrl9,
2676
+ webhookSecret: webhookSecret9,
2677
+ summarize: summarize9
2678
+ };
2679
+
2260
2680
  // src/providers/index.mjs
2261
2681
  var PROVIDER_META = [
2262
2682
  {
@@ -2310,6 +2730,20 @@ var PROVIDER_META = [
2310
2730
  signature: "none documented \u2014 every request is accepted",
2311
2731
  header: null,
2312
2732
  available: true
2733
+ },
2734
+ {
2735
+ id: "gokwik",
2736
+ label: "GoKwik",
2737
+ signature: "none documented \u2014 every request is accepted",
2738
+ header: null,
2739
+ available: true
2740
+ },
2741
+ {
2742
+ id: "ecom360",
2743
+ label: "Ecom 360",
2744
+ signature: "base64 HMAC-SHA256 over the body",
2745
+ header: "x-webhook-signature",
2746
+ available: true
2313
2747
  }
2314
2748
  ];
2315
2749
  var providers = {
@@ -2319,7 +2753,9 @@ var providers = {
2319
2753
  [shopflo_default.id]: shopflo_default,
2320
2754
  [flexype_default.id]: flexype_default,
2321
2755
  [return_prime_default.id]: return_prime_default,
2322
- [fastrr_default.id]: fastrr_default
2756
+ [fastrr_default.id]: fastrr_default,
2757
+ [gokwik_default.id]: gokwik_default,
2758
+ [ecom360_default.id]: ecom360_default
2323
2759
  };
2324
2760
  function getProvider(id) {
2325
2761
  const provider = providers[id];
@@ -2979,8 +3415,8 @@ function resolveSource(payload, source) {
2979
3415
  }
2980
3416
  return void 0;
2981
3417
  }
2982
- function coverage(payload, fieldMap8) {
2983
- return fieldMap8.map((f) => {
3418
+ function coverage(payload, fieldMap10) {
3419
+ return fieldMap10.map((f) => {
2984
3420
  const value = resolveSource(payload, f.source);
2985
3421
  const present = value !== void 0 && value !== null && value !== "";
2986
3422
  return {
@@ -3032,6 +3468,15 @@ async function runAll({ payload, body, provider, config, checkImageUrls = true,
3032
3468
  ok: missing.length === 0,
3033
3469
  detail: missing.length === 0 ? `all ${cov.length} mapped fields resolved` : `missing: ${missing.map((m) => m.field).join(", ")}`
3034
3470
  });
3471
+ const empty = cov.filter((c2) => c2.status === "empty");
3472
+ if (empty.length) {
3473
+ checks.push({
3474
+ name: "Empty optional fields",
3475
+ ok: true,
3476
+ warn: true,
3477
+ detail: `${empty.map((e) => e.maps_to).join(", ")} \u2014 a journey param bound to one of these needs a fallback, or the send fails`
3478
+ });
3479
+ }
3035
3480
  return {
3036
3481
  checks,
3037
3482
  coverage: cov,
@@ -3136,7 +3581,7 @@ function applyEdits(payload, fields, edits) {
3136
3581
  }
3137
3582
  return payload;
3138
3583
  }
3139
- async function buildPayload8({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope, live, delta }) {
3584
+ async function buildPayload10({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope, live, delta }) {
3140
3585
  const provider0 = getProvider(providerId);
3141
3586
  const needsItems = !provider0.selectionFor || ["cart", "product"].includes(provider0.selectionFor(eventName ?? provider0.defaultEvent));
3142
3587
  if (needsItems && !items.length) throw new ConfigError("no products selected");
@@ -3683,7 +4128,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
3683
4128
  const onDiscount = (raw) => startReview(Math.max(0, Number.parseFloat(raw) || 0));
3684
4129
  const finalise = (payload) => {
3685
4130
  setStep(STEP.BUILDING);
3686
- buildPayload8({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection, live: draft.live, delta: draft.delta }).then(async (result) => {
4131
+ buildPayload10({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection, live: draft.live, delta: draft.delta }).then(async (result) => {
3687
4132
  setBuilt(result);
3688
4133
  let req = null;
3689
4134
  let command = "";
@@ -4954,7 +5399,7 @@ async function cmdBuild(args, config) {
4954
5399
  else if (live?.kind === "order") log.ok(`order ${live.name} (${live.id})`);
4955
5400
  else log.warn("no live record available \u2014 falling back to a generated one");
4956
5401
  }
4957
- const built = await buildPayload8({
5402
+ const built = await buildPayload10({
4958
5403
  cfg: config,
4959
5404
  providerId,
4960
5405
  eventName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hookwright",
3
- "version": "1.12.1",
3
+ "version": "1.13.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",