hookwright 1.2.0 → 1.2.2
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/README.md +2 -2
- package/dist/cli.js +25 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ A generated payload is useless if the receiver silently drops it, so every build
|
|
|
94
94
|
- **JSON round-trip** — receivers that verify `HMAC(secret, timestamp + JSON.stringify(parsedBody))` can only match if `JSON.stringify(JSON.parse(raw)) === raw`. If it doesn't, you're told the exact byte offset.
|
|
95
95
|
- **Phone** — parsed to E.164 using the shipping `country_code`, the same way the consumer does.
|
|
96
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.
|
|
97
|
-
- **Product images** — every `image_url` is `HEAD`-checked
|
|
97
|
+
- **Product images** — every `image_url` is `HEAD`-checked. A product with no image warns rather than blocking: the message simply renders without one.
|
|
98
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`.
|
|
99
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.
|
|
100
100
|
|
|
@@ -142,7 +142,7 @@ Adding one is a single file in `src/providers/` exporting `buildPayload`, `sign`
|
|
|
142
142
|
```bash
|
|
143
143
|
npm install
|
|
144
144
|
npm run dev # esbuild watch
|
|
145
|
-
npm test #
|
|
145
|
+
npm test # 190 tests, no network required
|
|
146
146
|
npm start # build + run
|
|
147
147
|
```
|
|
148
148
|
|
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.2.
|
|
27
|
+
version: "1.2.2",
|
|
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",
|
|
@@ -449,7 +449,7 @@ var fieldMap = [
|
|
|
449
449
|
{ source: "data.customer.shipping_address.zip", target: "customer.zip" },
|
|
450
450
|
{ source: "data.line_items[0].name", target: "cart.productName", required: true },
|
|
451
451
|
{ source: "data.line_items[0].currency", target: "pricing.currency", required: true, note: "currency is read off the FIRST line item, not the root" },
|
|
452
|
-
{ source: "data.line_items[0].image_url", target: "cart.image",
|
|
452
|
+
{ source: "data.line_items[0].image_url", target: "cart.image", note: "first line item with an image wins; blank if the product has none" },
|
|
453
453
|
{ source: "data.line_items[0].quantity", target: "cart.quantity" },
|
|
454
454
|
{ source: "data.utm_parameters.utm_source", target: "utm.source" },
|
|
455
455
|
{ source: "data.utm_parameters.utm_medium", target: "utm.medium" },
|
|
@@ -621,7 +621,7 @@ var fieldMap2 = [
|
|
|
621
621
|
{ source: "customer.Shipping_address.Country_code", target: "customer.countryCode", required: true, note: "region used to parse the phone" },
|
|
622
622
|
{ source: "customer.Shipping_address.Zip", target: "customer.zip" },
|
|
623
623
|
{ source: "line_items[0].name", target: "cart.productName", required: true },
|
|
624
|
-
{ source: "line_items[0].image_url", target: "cart.image",
|
|
624
|
+
{ source: "line_items[0].image_url", target: "cart.image", note: "blank if the product has no image" },
|
|
625
625
|
{ source: "line_items[0].quantity", target: "cart.quantity" }
|
|
626
626
|
];
|
|
627
627
|
var editableFields2 = [
|
|
@@ -776,6 +776,9 @@ var EVENTS = [
|
|
|
776
776
|
{ eventName: "orders/create", type: "nitro_orders_create", label: "Order Created", selection: "cart", subject: "ordered items" },
|
|
777
777
|
{ eventName: "orders/updated", type: "nitro_orders_updated", label: "Order Updated", selection: "cart", subject: "ordered items" }
|
|
778
778
|
];
|
|
779
|
+
function eventFromPayload(payload) {
|
|
780
|
+
return payload?.eventName;
|
|
781
|
+
}
|
|
779
782
|
function selectionFor(eventName) {
|
|
780
783
|
return EVENTS.find((e) => e.eventName === eventName)?.selection ?? "cart";
|
|
781
784
|
}
|
|
@@ -805,7 +808,7 @@ var FIELD_MAPS = {
|
|
|
805
808
|
...COMMON_FIELDS,
|
|
806
809
|
...PAGE_FIELDS,
|
|
807
810
|
{ source: "eventVal.title", target: "eventVal.title", required: true },
|
|
808
|
-
{ source: "eventVal.image", target: "eventVal.image",
|
|
811
|
+
{ source: "eventVal.image", target: "eventVal.image", note: "blank if the product has no image" },
|
|
809
812
|
{ source: "eventVal.price", target: "eventVal.price", required: true },
|
|
810
813
|
{ source: "eventVal.resource_id", target: "eventVal.resource_id", required: true },
|
|
811
814
|
{ source: "eventVal.resource", target: "eventVal.resource" },
|
|
@@ -1061,6 +1064,7 @@ var nitro_default = {
|
|
|
1061
1064
|
editableFields: editableFields3,
|
|
1062
1065
|
editableFieldsFor,
|
|
1063
1066
|
selectionFor,
|
|
1067
|
+
eventFromPayload,
|
|
1064
1068
|
buildPayload: buildPayload3,
|
|
1065
1069
|
sign: sign3,
|
|
1066
1070
|
webhookUrl: webhookUrl3,
|
|
@@ -1773,10 +1777,11 @@ async function checkImages(payload, { timeoutMs = 1e4 } = {}) {
|
|
|
1773
1777
|
}));
|
|
1774
1778
|
}
|
|
1775
1779
|
async function runAll({ payload, body, provider, config, checkImageUrls = true, checkEndpoint = true, eventName }) {
|
|
1776
|
-
const
|
|
1780
|
+
const event = eventName ?? (provider.eventFromPayload ? provider.eventFromPayload(payload) : void 0);
|
|
1781
|
+
const checks = [checkGate(payload, provider, event), checkRoundTrip(body), checkPhone(payload), checkAllowedPhone(payload, config)];
|
|
1777
1782
|
if (checkImageUrls) checks.push(...await checkImages(payload));
|
|
1778
1783
|
if (checkEndpoint) checks.push(await checkEndpointExists(provider.webhookUrl(config)));
|
|
1779
|
-
const fields =
|
|
1784
|
+
const fields = event && provider.fieldMapFor ? provider.fieldMapFor(event) : provider.fieldMap;
|
|
1780
1785
|
const cov = coverage(payload, fields);
|
|
1781
1786
|
const missing = cov.filter((c2) => c2.status === "MISSING");
|
|
1782
1787
|
checks.push({
|
|
@@ -1918,7 +1923,8 @@ async function buildPayload4({ cfg, providerId = "cashfree-occ", items = [], dis
|
|
|
1918
1923
|
};
|
|
1919
1924
|
const file = savePayload({ provider: provider.id, payload, body, meta });
|
|
1920
1925
|
audit("payload.built", { provider: provider.id, valid: report.ok, file });
|
|
1921
|
-
|
|
1926
|
+
const record = { provider: provider.id, payload, body, meta };
|
|
1927
|
+
return { provider, payload, body, report, file, meta, summary, record };
|
|
1922
1928
|
}
|
|
1923
1929
|
|
|
1924
1930
|
// src/core/send.mjs
|
|
@@ -2212,7 +2218,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2212
2218
|
setStep(STEP.COLLECTION);
|
|
2213
2219
|
}).catch((err) => {
|
|
2214
2220
|
if (alive) {
|
|
2215
|
-
setError({ message: err.message, hint: err.hint });
|
|
2221
|
+
setError({ message: err.message, hint: err.hint, failures: err.failures });
|
|
2216
2222
|
setStep(STEP.ERROR);
|
|
2217
2223
|
}
|
|
2218
2224
|
});
|
|
@@ -2231,7 +2237,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2231
2237
|
setStep(STEP.PICK);
|
|
2232
2238
|
}).catch((err) => {
|
|
2233
2239
|
if (!alive) return;
|
|
2234
|
-
setError({ message: err.message, hint: err.hint });
|
|
2240
|
+
setError({ message: err.message, hint: err.hint, failures: err.failures });
|
|
2235
2241
|
setStep(STEP.ERROR);
|
|
2236
2242
|
});
|
|
2237
2243
|
return () => {
|
|
@@ -2310,7 +2316,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2310
2316
|
setDraft({ ...next, discount });
|
|
2311
2317
|
setStep(STEP.MODE);
|
|
2312
2318
|
} catch (err) {
|
|
2313
|
-
setError({ message: err.message, hint: err.hint });
|
|
2319
|
+
setError({ message: err.message, hint: err.hint, failures: err.failures });
|
|
2314
2320
|
setStep(STEP.ERROR);
|
|
2315
2321
|
}
|
|
2316
2322
|
};
|
|
@@ -2334,7 +2340,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2334
2340
|
}
|
|
2335
2341
|
setStep(STEP.REPORT);
|
|
2336
2342
|
}).catch((err) => {
|
|
2337
|
-
setError({ message: err.message, hint: err.hint });
|
|
2343
|
+
setError({ message: err.message, hint: err.hint, failures: err.failures });
|
|
2338
2344
|
setStep(STEP.ERROR);
|
|
2339
2345
|
});
|
|
2340
2346
|
};
|
|
@@ -2356,11 +2362,11 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2356
2362
|
};
|
|
2357
2363
|
const doSend = () => {
|
|
2358
2364
|
setStep(STEP.SENDING);
|
|
2359
|
-
dispatch({ cfg: config, record:
|
|
2365
|
+
dispatch({ cfg: config, record: built.record }).then((result) => {
|
|
2360
2366
|
setSendResult(result);
|
|
2361
2367
|
setStep(STEP.RESULT);
|
|
2362
2368
|
}).catch((err) => {
|
|
2363
|
-
setError({ message: err.message, hint: err.hint });
|
|
2369
|
+
setError({ message: err.message, hint: err.hint, failures: err.failures });
|
|
2364
2370
|
setStep(STEP.ERROR);
|
|
2365
2371
|
});
|
|
2366
2372
|
};
|
|
@@ -2594,6 +2600,11 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2594
2600
|
] }),
|
|
2595
2601
|
step === STEP.ERROR && error && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2596
2602
|
/* @__PURE__ */ jsx8(Alert, { variant: "error", children: error.message }),
|
|
2603
|
+
error.failures?.length ? /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", marginTop: 1, children: error.failures.map((f, i) => /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
2604
|
+
/* @__PURE__ */ jsx8(Text8, { color: palette.bad, children: ` ${glyph.cross} ` }),
|
|
2605
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, children: `${f.name}: ` }),
|
|
2606
|
+
/* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: f.detail })
|
|
2607
|
+
] }, i)) }) : null,
|
|
2597
2608
|
error.hint ? /* @__PURE__ */ jsx8(Text8, { color: palette.warn, children: ` ${error.hint}` }) : null,
|
|
2598
2609
|
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "press enter to return to the menu" }) })
|
|
2599
2610
|
] })
|
|
@@ -3581,7 +3592,7 @@ async function cmdBuild(args, config) {
|
|
|
3581
3592
|
async function cmdSend(args, config, built) {
|
|
3582
3593
|
const bad = requireConfig(config);
|
|
3583
3594
|
if (bad) return bad;
|
|
3584
|
-
let record = built
|
|
3595
|
+
let record = built?.record ?? null;
|
|
3585
3596
|
if (!record && typeof args.flags.file === "string") record = loadPayload(args.flags.file);
|
|
3586
3597
|
if (!record) record = loadLast();
|
|
3587
3598
|
if (!record) {
|