hookwright 1.0.0 → 1.1.1

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 (3) hide show
  1. package/README.md +34 -8
  2. package/dist/cli.js +1154 -345
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -38,7 +38,7 @@ hookwright # interactive UI — starts in the setup wizard on first
38
38
  | Setup | asks for |
39
39
  |---|---|
40
40
  | **Shopify** | store domain + Admin API access token (needs `read_products`). Verifies immediately and picks up currency, country, province and contact details from the store record |
41
- | **Webhook** | just the destination URL, plus the signing secret |
41
+ | **Webhook** | one destination URL + signing secret **per integration** — Cashfree, Razorpay Magic and Nitro each get their own |
42
42
  | **Customer** | the shopper the abandoned cart belongs to |
43
43
  | **Defaults** | currency, platform |
44
44
 
@@ -50,14 +50,31 @@ Then **Integrations → Cashfree One Click Checkout** builds the payload:
50
50
  2. Variant and quantity per product
51
51
  3. Cart discount
52
52
  4. Either accept the suggested values, or **review every field** — each one is pre-filled with the generated suggestion, enter accepts it, typing replaces it
53
- 5. Pre-flight report, then send
53
+ 5. Pre-flight report — every check, the full field coverage table, the signed request and the complete payload
54
+ 6. A **ready-to-paste `curl` is copied to your clipboard** automatically, then send
55
+
56
+ The report page is a compact status board with a command bar — `s` send · `c` copy curl · `j` copy json · `u` curl · `p` payload · `f` fields · `v` checks · `r` request. Panels are collapsed by default, so you see the verdict first and the detail only when you ask for it.
57
+
58
+ `esc` walks back one step at a time through the whole flow (report → mode → discount → quantity → variant → products → event), and only leaves the integration from the first step.
59
+
60
+ ### The curl is built for pasting
61
+
62
+ The command is emitted in the exact shape Postman itself exports — `curl --location --request POST` with unindented `--header` / `--data` lines — and the body is inlined, never `@file`. Drop it straight into **Postman → Import → Raw text**, a colleague's terminal, or a bug report.
63
+
64
+ Single quotes inside product titles (`Levi's 501`) are escaped, and the signature travels with the exact body it signs, so a pasted command verifies precisely as the original request would.
65
+
66
+ Two things keep this honest: a test that executes the generated command verbatim against a receiver doing real HMAC verification, and a test that runs it through **`curl-to-postmanv2`** — the same parser Postman uses for Raw-text import — asserting the URL, method, every header and a byte-identical body all survive.
67
+
68
+ In the report screen: `c` re-copies the curl · `j` copies the raw payload JSON · `p` toggles the full payload view.
54
69
 
55
70
  ## Headless usage
56
71
 
57
72
  Every command works without a TTY, for CI or scripting:
58
73
 
59
74
  ```bash
60
- hookwright build --items 2 # build from the 2 first products
75
+ hookwright build --items 2 # cashfree-occ by default
76
+ hookwright build --provider razorpay-magic --items 2
77
+ hookwright build --provider nitro --event orders/create
61
78
  hookwright build --search "cold brew" # only products matching a title
62
79
  hookwright build --items 3 --discount 250 --send
63
80
  hookwright send --dry-run # print the request + curl, send nothing
@@ -78,6 +95,7 @@ A generated payload is useless if the receiver silently drops it, so every build
78
95
  - **Phone** — parsed to E.164 using the shipping `country_code`, the same way the consumer does.
79
96
  - **Phone allow-list** — refuses to build for a number that isn't an approved test handset, so a live shopper can never be messaged by a test run.
80
97
  - **Product images** — every `image_url` is `HEAD`-checked; unreachable images fail the build.
98
+ - **Endpoint exists** — before a signed payload is handed over, the destination is verified: the hostname must resolve, something must accept a TCP connection on the port, and the path must not 404. A typo'd host is caught here instead of looking like a silent delivery failure. The probe is `OPTIONS`, falling back to `HEAD` — **never a POST and never a body**, so it cannot create data on the receiving system. Timeouts and servers that reject `OPTIONS` warn rather than block. Skip with `--no-endpoint-check`, override with `--force`.
81
99
  - **Field coverage** — a table of every field the consumer reads, what it maps to, and its value. Missing required fields are flagged in red.
82
100
 
83
101
  Sending is also blocked when the target looks like production, unless `--force`.
@@ -94,18 +112,26 @@ Override both locations with `HOOKWRIGHT_HOME`, or point at one config with `--c
94
112
 
95
113
  ## Providers
96
114
 
97
- | provider | signature | headers |
98
- |---|---|---|
99
- | `cashfree-occ` | base64 HMAC-SHA256 over `timestamp + body` | `x-webhook-signature`, `x-webhook-timestamp` |
115
+ | provider | signature | headers | events |
116
+ |---|---|---|---|
117
+ | `cashfree-occ` | base64 HMAC-SHA256 over `timestamp + body` | `x-webhook-signature`, `x-webhook-timestamp` | abandoned checkout |
118
+ | `razorpay-magic` | hex HMAC-SHA256 over the body | `x-razorpay-signature` | abandoned checkout |
119
+ | `nitro` | static bearer token, no body signature | `authorization` | 8 — page view, category view, product view, add to cart, remove from cart, checkout, order created, order updated |
120
+
121
+ Each provider owns its gate, signature scheme, field map, payload shape and summary, so nothing is assumed across them:
122
+
123
+ - **Cashfree OCC** nests everything under `data`; currency is read from `line_items[0]`, not the root.
124
+ - **Razorpay Magic** is root-level, uses capitalised shipping keys (`Shipping_address.Address1`) and sends `line_items_total` as a **string in paise** — matching [Razorpay's abandoned cart webhook docs](https://razorpay.com/docs/payments/magic-checkout/abandoned-cart/). Its gate is "non-empty `abandoned_checkout_url`" rather than an exact value.
125
+ - **Nitro** keys off `eventName` and requires `eventVal.customer.phone` on *every* event, parsed against root `country`. Choose the event with `--event`, or from the menu in the UI.
100
126
 
101
- Adding one is a single file in `src/providers/` exporting `buildPayload`, `sign`, `webhookUrl`, a `gate` and a `fieldMap` — see [docs/cashfree-occ.md](docs/cashfree-occ.md).
127
+ Adding one is a single file in `src/providers/` exporting `buildPayload`, `sign`, `webhookUrl`, `summarize`, a `gate` and a `fieldMap` — see [docs/cashfree-occ.md](docs/cashfree-occ.md).
102
128
 
103
129
  ## Development
104
130
 
105
131
  ```bash
106
132
  npm install
107
133
  npm run dev # esbuild watch
108
- npm test # 54 tests, no network required
134
+ npm test # 179 tests, no network required
109
135
  npm start # build + run
110
136
  ```
111
137