hookwright 1.4.0 → 1.6.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 +196 -19
- 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.6.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",
|
|
@@ -911,7 +911,7 @@ function editableFieldsFor(eventName) {
|
|
|
911
911
|
}
|
|
912
912
|
var editableFields3 = editableFieldsFor(DEFAULT_EVENT);
|
|
913
913
|
function buildPayload3(ctx) {
|
|
914
|
-
const { config, items, phone } = ctx;
|
|
914
|
+
const { config, items, phone, live } = ctx;
|
|
915
915
|
const eventName = ctx.eventName ?? DEFAULT_EVENT;
|
|
916
916
|
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
917
917
|
const currency = ctx.currency || config.defaults.currency;
|
|
@@ -919,15 +919,55 @@ function buildPayload3(ctx) {
|
|
|
919
919
|
const cust = config.customer;
|
|
920
920
|
const session = crypto3.randomBytes(8).toString("hex");
|
|
921
921
|
const first = items[0];
|
|
922
|
-
const
|
|
922
|
+
const cartLine = ({ product, variant, quantity }) => ({
|
|
923
923
|
quantity,
|
|
924
924
|
title: product.title,
|
|
925
925
|
line_price: money2((variant?.price ?? 0) * quantity).toFixed(2),
|
|
926
926
|
id: Number(variant?.id ?? product.id),
|
|
927
927
|
product_id: Number(product.id),
|
|
928
928
|
image_url: imageForVariant(product, variant)
|
|
929
|
-
})
|
|
930
|
-
const
|
|
929
|
+
});
|
|
930
|
+
const removedLine = ({ product, variant, quantity }) => {
|
|
931
|
+
const unit = money2(variant?.price ?? 0).toFixed(2);
|
|
932
|
+
const line = money2((variant?.price ?? 0) * quantity).toFixed(2);
|
|
933
|
+
const priceSet = (amount) => {
|
|
934
|
+
const value = Number(amount).toFixed(1);
|
|
935
|
+
return {
|
|
936
|
+
shop_money: { amount: value, currency_code: currency },
|
|
937
|
+
presentment_money: { amount: value, currency_code: currency }
|
|
938
|
+
};
|
|
939
|
+
};
|
|
940
|
+
return {
|
|
941
|
+
id: Number(variant?.id ?? product.id),
|
|
942
|
+
properties: null,
|
|
943
|
+
quantity,
|
|
944
|
+
variant_id: Number(variant?.id ?? product.id),
|
|
945
|
+
key: `${variant?.id ?? product.id}:${crypto3.randomBytes(16).toString("hex")}`,
|
|
946
|
+
discounted_price: unit,
|
|
947
|
+
discounts: [],
|
|
948
|
+
gift_card: false,
|
|
949
|
+
grams: 0,
|
|
950
|
+
line_price: line,
|
|
951
|
+
original_line_price: line,
|
|
952
|
+
original_price: unit,
|
|
953
|
+
price: unit,
|
|
954
|
+
product_id: Number(product.id),
|
|
955
|
+
sku: variant?.sku || "",
|
|
956
|
+
taxable: true,
|
|
957
|
+
title: product.title,
|
|
958
|
+
total_discount: "0.00",
|
|
959
|
+
vendor: config.shopify.shop?.name ?? config.shopify.domain,
|
|
960
|
+
discounted_price_set: priceSet(unit),
|
|
961
|
+
line_price_set: priceSet(line),
|
|
962
|
+
original_line_price_set: priceSet(line),
|
|
963
|
+
price_set: priceSet(unit),
|
|
964
|
+
total_discount_set: priceSet(0),
|
|
965
|
+
parent_relationship: null
|
|
966
|
+
};
|
|
967
|
+
};
|
|
968
|
+
const lineItems = items.map(cartLine);
|
|
969
|
+
const valueOf = (list) => money2(list.reduce((sum, i) => sum + money2(i.variant?.price ?? 0) * i.quantity, 0));
|
|
970
|
+
const cartValue = valueOf(items);
|
|
931
971
|
const customer = {
|
|
932
972
|
email: cust.email,
|
|
933
973
|
phone,
|
|
@@ -982,14 +1022,16 @@ function buildPayload3(ctx) {
|
|
|
982
1022
|
case "addtocart":
|
|
983
1023
|
case "removefromcart": {
|
|
984
1024
|
const added = eventName === "addtocart";
|
|
1025
|
+
const removed = added ? [] : items.slice(-1);
|
|
1026
|
+
const remaining = added ? items : items.slice(0, -1);
|
|
985
1027
|
eventVal = {
|
|
986
|
-
line_items: added ? lineItems :
|
|
987
|
-
cart_value: added ? cartValue :
|
|
1028
|
+
line_items: added ? lineItems : remaining.map(cartLine),
|
|
1029
|
+
cart_value: added ? cartValue : valueOf(remaining),
|
|
988
1030
|
recent_product_image: added ? lineItems[0]?.image_url ?? null : null,
|
|
989
1031
|
init_cart: false,
|
|
990
|
-
empty_cart: !added,
|
|
1032
|
+
empty_cart: !added && remaining.length === 0,
|
|
991
1033
|
items_added: added ? lineItems : [],
|
|
992
|
-
items_removed:
|
|
1034
|
+
items_removed: removed.map(removedLine),
|
|
993
1035
|
customer,
|
|
994
1036
|
bik_customer_id: null
|
|
995
1037
|
};
|
|
@@ -997,8 +1039,8 @@ function buildPayload3(ctx) {
|
|
|
997
1039
|
}
|
|
998
1040
|
case "checkout":
|
|
999
1041
|
eventVal = {
|
|
1000
|
-
checkout: cartPermalink(domain, items.map(({ variant, quantity }) => ({ variantId: variant?.id, quantity }))),
|
|
1001
|
-
cart_value: cartValue.toFixed(2),
|
|
1042
|
+
checkout: live?.kind === "checkout" ? live.url : cartPermalink(domain, items.map(({ variant, quantity }) => ({ variantId: variant?.id, quantity }))),
|
|
1043
|
+
cart_value: live?.kind === "checkout" ? live.cartValue : cartValue.toFixed(2),
|
|
1002
1044
|
customer,
|
|
1003
1045
|
bik_customer_id: null
|
|
1004
1046
|
};
|
|
@@ -1006,7 +1048,17 @@ function buildPayload3(ctx) {
|
|
|
1006
1048
|
case "orders/create":
|
|
1007
1049
|
case "orders/updated": {
|
|
1008
1050
|
const orderNumber = 1e3 + Math.floor(Math.random() * 9e3);
|
|
1009
|
-
eventVal = {
|
|
1051
|
+
eventVal = live?.kind === "order" ? {
|
|
1052
|
+
url: live.statusUrl,
|
|
1053
|
+
order_id: live.id,
|
|
1054
|
+
order_number: live.number,
|
|
1055
|
+
order_name: live.name,
|
|
1056
|
+
price: live.price,
|
|
1057
|
+
currency: live.currency ?? currency,
|
|
1058
|
+
order_created_at: live.createdAt ?? now.toISOString(),
|
|
1059
|
+
customer,
|
|
1060
|
+
bik_customer_id: null
|
|
1061
|
+
} : {
|
|
1010
1062
|
url: `https://${domain}/${crypto3.randomBytes(5).toString("hex")}/orders/${crypto3.randomBytes(16).toString("hex")}/authenticate?key=${crypto3.randomBytes(16).toString("hex")}`,
|
|
1011
1063
|
order_id: Number(`${Date.now()}`.slice(-13)),
|
|
1012
1064
|
order_number: orderNumber,
|
|
@@ -2430,6 +2482,38 @@ var ShopifyClient = class {
|
|
|
2430
2482
|
const { json } = await this.request("/products/count.json");
|
|
2431
2483
|
return json?.count ?? 0;
|
|
2432
2484
|
}
|
|
2485
|
+
/**
|
|
2486
|
+
* Abandoned checkouts, newest first. Nitro's checkout webhook carries a real
|
|
2487
|
+
* recovery link, and the cart token inside it is what the receiving side looks
|
|
2488
|
+
* the checkout up by — so it has to come from a checkout that actually exists.
|
|
2489
|
+
*/
|
|
2490
|
+
async abandonedCheckouts({ limit = 10 } = {}) {
|
|
2491
|
+
const { json } = await this.request("/checkouts.json", { query: { limit } });
|
|
2492
|
+
return json?.checkouts ?? [];
|
|
2493
|
+
}
|
|
2494
|
+
/** Orders, newest first. */
|
|
2495
|
+
async orders({ limit = 10, status = "any" } = {}) {
|
|
2496
|
+
const { json } = await this.request("/orders.json", { query: { limit, status } });
|
|
2497
|
+
return json?.orders ?? [];
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* Creates a real order. Receipts and inventory are suppressed: this exists to
|
|
2501
|
+
* give an order webhook something true to point at, not to trade.
|
|
2502
|
+
*/
|
|
2503
|
+
async createOrder(order) {
|
|
2504
|
+
const { json } = await this.request("/orders.json", {
|
|
2505
|
+
method: "POST",
|
|
2506
|
+
body: {
|
|
2507
|
+
order: {
|
|
2508
|
+
...order,
|
|
2509
|
+
send_receipt: false,
|
|
2510
|
+
send_fulfillment_receipt: false,
|
|
2511
|
+
inventory_behaviour: "bypass"
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
});
|
|
2515
|
+
return json?.order ?? null;
|
|
2516
|
+
}
|
|
2433
2517
|
/**
|
|
2434
2518
|
* Collections, from both endpoints Shopify splits them across: custom
|
|
2435
2519
|
* (manually curated) and smart (rule based). Either can back a category page.
|
|
@@ -2824,7 +2908,7 @@ function loadPayload(file) {
|
|
|
2824
2908
|
}
|
|
2825
2909
|
|
|
2826
2910
|
// src/core/build.mjs
|
|
2827
|
-
function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName, collection, envelope }) {
|
|
2911
|
+
function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName, collection, envelope, live }) {
|
|
2828
2912
|
const provider = getProvider(providerId);
|
|
2829
2913
|
const event = eventName ?? provider.defaultEvent;
|
|
2830
2914
|
const resolvedPhone = phone ?? cfg.customer.phone;
|
|
@@ -2836,6 +2920,7 @@ function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, p
|
|
|
2836
2920
|
eventName: event,
|
|
2837
2921
|
collection,
|
|
2838
2922
|
envelope,
|
|
2923
|
+
live,
|
|
2839
2924
|
phone: parsed.ok ? parsed.value : String(resolvedPhone ?? "")
|
|
2840
2925
|
});
|
|
2841
2926
|
const editable = provider.editableFieldsFor ? provider.editableFieldsFor(event) : provider.editableFields;
|
|
@@ -2851,7 +2936,7 @@ function applyEdits(payload, fields, edits) {
|
|
|
2851
2936
|
}
|
|
2852
2937
|
return payload;
|
|
2853
2938
|
}
|
|
2854
|
-
async function buildPayload8({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope }) {
|
|
2939
|
+
async function buildPayload8({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope, live }) {
|
|
2855
2940
|
const provider0 = getProvider(providerId);
|
|
2856
2941
|
const needsItems = !provider0.selectionFor || ["cart", "product"].includes(provider0.selectionFor(eventName ?? provider0.defaultEvent));
|
|
2857
2942
|
if (needsItems && !items.length) throw new ConfigError("no products selected");
|
|
@@ -2860,7 +2945,7 @@ async function buildPayload8({ cfg, providerId = "cashfree-occ", items = [], dis
|
|
|
2860
2945
|
const parsed = toE164(resolvedPhone, cfg.customer.countryCode);
|
|
2861
2946
|
if (!parsed.ok) throw new ConfigError(`phone is unusable: ${parsed.reason}`);
|
|
2862
2947
|
const event = eventName ?? provider.defaultEvent;
|
|
2863
|
-
const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event, collection, envelope });
|
|
2948
|
+
const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event, collection, envelope, live });
|
|
2864
2949
|
const body = JSON.stringify(payload);
|
|
2865
2950
|
const report = await runAll({ payload, body, provider, config: cfg, checkImageUrls, eventName: event });
|
|
2866
2951
|
const summary = provider.summarize ? provider.summarize(payload) : {};
|
|
@@ -2888,6 +2973,80 @@ async function buildPayload8({ cfg, providerId = "cashfree-occ", items = [], dis
|
|
|
2888
2973
|
return { provider, payload, body, report, file, meta, summary, record };
|
|
2889
2974
|
}
|
|
2890
2975
|
|
|
2976
|
+
// src/shopify/live.mjs
|
|
2977
|
+
var LIVE_EVENTS = {
|
|
2978
|
+
nitro: /* @__PURE__ */ new Set(["checkout", "orders/create", "orders/updated"])
|
|
2979
|
+
};
|
|
2980
|
+
function needsLive(providerId, eventName) {
|
|
2981
|
+
return LIVE_EVENTS[providerId]?.has(eventName) ?? false;
|
|
2982
|
+
}
|
|
2983
|
+
function clientFor(config) {
|
|
2984
|
+
return new ShopifyClient({
|
|
2985
|
+
domain: config.shopify.domain,
|
|
2986
|
+
accessToken: config.shopify.accessToken,
|
|
2987
|
+
apiVersion: config.shopify.apiVersion
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2990
|
+
async function liveCheckout({ config, items = [], client = clientFor(config) }) {
|
|
2991
|
+
const checkouts = await client.abandonedCheckouts({ limit: 20 });
|
|
2992
|
+
if (checkouts.length === 0) return null;
|
|
2993
|
+
const wanted = new Set(items.map(({ variant }) => Number(variant?.id)).filter(Boolean));
|
|
2994
|
+
const matching = checkouts.find((c2) => (c2.line_items ?? []).some((li) => wanted.has(Number(li.variant_id))));
|
|
2995
|
+
const checkout = matching ?? checkouts[0];
|
|
2996
|
+
return {
|
|
2997
|
+
kind: "checkout",
|
|
2998
|
+
url: checkout.abandoned_checkout_url,
|
|
2999
|
+
cartValue: money2(checkout.total_price ?? 0).toFixed(2),
|
|
3000
|
+
cartToken: checkout.cart_token,
|
|
3001
|
+
lineItems: checkout.line_items ?? []
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
async function liveOrder({ config, items = [], phone, client = clientFor(config) }) {
|
|
3005
|
+
const cust = config.customer ?? {};
|
|
3006
|
+
const lineItems = items.map(({ variant, product, quantity }) => ({
|
|
3007
|
+
variant_id: Number(variant?.id ?? product?.id),
|
|
3008
|
+
quantity: Number(quantity) || 1
|
|
3009
|
+
})).filter((li) => Number.isFinite(li.variant_id) && li.variant_id > 0);
|
|
3010
|
+
if (lineItems.length === 0) return null;
|
|
3011
|
+
const order = await client.createOrder({
|
|
3012
|
+
line_items: lineItems,
|
|
3013
|
+
financial_status: "paid",
|
|
3014
|
+
email: cust.email || void 0,
|
|
3015
|
+
phone: phone || cust.phone || void 0,
|
|
3016
|
+
customer: cust.email || cust.firstName ? { first_name: cust.firstName, last_name: cust.lastName, email: cust.email } : void 0,
|
|
3017
|
+
shipping_address: cust.address1 ? {
|
|
3018
|
+
first_name: cust.firstName,
|
|
3019
|
+
last_name: cust.lastName,
|
|
3020
|
+
address1: cust.address1,
|
|
3021
|
+
address2: cust.address2 || void 0,
|
|
3022
|
+
city: cust.city,
|
|
3023
|
+
province: cust.province,
|
|
3024
|
+
country_code: cust.countryCode,
|
|
3025
|
+
zip: cust.zip,
|
|
3026
|
+
phone: phone || cust.phone || void 0
|
|
3027
|
+
} : void 0,
|
|
3028
|
+
tags: "hookwright",
|
|
3029
|
+
note: "created by hookwright for webhook testing"
|
|
3030
|
+
});
|
|
3031
|
+
if (!order) return null;
|
|
3032
|
+
return {
|
|
3033
|
+
kind: "order",
|
|
3034
|
+
id: Number(order.id),
|
|
3035
|
+
number: Number(order.order_number),
|
|
3036
|
+
name: order.name,
|
|
3037
|
+
price: String(order.total_price ?? "0.00"),
|
|
3038
|
+
currency: order.currency,
|
|
3039
|
+
createdAt: order.created_at,
|
|
3040
|
+
statusUrl: order.order_status_url
|
|
3041
|
+
};
|
|
3042
|
+
}
|
|
3043
|
+
async function resolveLive({ config, providerId, eventName, items, phone, client }) {
|
|
3044
|
+
if (!needsLive(providerId, eventName)) return null;
|
|
3045
|
+
if (!config?.shopify?.domain || !config?.shopify?.accessToken) return null;
|
|
3046
|
+
if (eventName === "checkout") return await liveCheckout({ config, items, ...client ? { client } : {} });
|
|
3047
|
+
return await liveOrder({ config, items, phone, ...client ? { client } : {} });
|
|
3048
|
+
}
|
|
3049
|
+
|
|
2891
3050
|
// src/core/send.mjs
|
|
2892
3051
|
import fs4 from "node:fs";
|
|
2893
3052
|
import path4 from "node:path";
|
|
@@ -3066,6 +3225,7 @@ var STEP = {
|
|
|
3066
3225
|
VARIANT: "variant",
|
|
3067
3226
|
QUANTITY: "quantity",
|
|
3068
3227
|
DISCOUNT: "discount",
|
|
3228
|
+
LIVE: "live",
|
|
3069
3229
|
MODE: "mode",
|
|
3070
3230
|
FIELDS: "fields",
|
|
3071
3231
|
BUILDING: "building",
|
|
@@ -3121,6 +3281,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
3121
3281
|
setItems(items.slice(0, -1));
|
|
3122
3282
|
return stepInto(last);
|
|
3123
3283
|
}
|
|
3284
|
+
case STEP.LIVE:
|
|
3124
3285
|
case STEP.MODE:
|
|
3125
3286
|
return setStep(STEP.DISCOUNT);
|
|
3126
3287
|
case STEP.FIELDS:
|
|
@@ -3264,17 +3425,23 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
3264
3425
|
const nextItems = [...items, { product, variant: pendingVariant, quantity: qty }];
|
|
3265
3426
|
advance(nextItems, cursor + 1);
|
|
3266
3427
|
};
|
|
3267
|
-
const startReview = (discount, chosenCollection = collection, chosenItems = items) => {
|
|
3428
|
+
const startReview = async (discount, chosenCollection = collection, chosenItems = items) => {
|
|
3268
3429
|
try {
|
|
3430
|
+
let live = null;
|
|
3431
|
+
if (needsLive(providerId, eventName)) {
|
|
3432
|
+
setStep(STEP.LIVE);
|
|
3433
|
+
live = await resolveLive({ config, providerId, eventName, items: chosenItems, phone: config.customer.phone });
|
|
3434
|
+
}
|
|
3269
3435
|
const next = draftPayload({
|
|
3270
3436
|
cfg: config,
|
|
3271
3437
|
providerId,
|
|
3272
3438
|
items: chosenItems,
|
|
3273
3439
|
discount,
|
|
3274
3440
|
eventName,
|
|
3275
|
-
collection: chosenCollection
|
|
3441
|
+
collection: chosenCollection,
|
|
3442
|
+
live
|
|
3276
3443
|
});
|
|
3277
|
-
setDraft({ ...next, discount });
|
|
3444
|
+
setDraft({ ...next, discount, live });
|
|
3278
3445
|
setStep(STEP.MODE);
|
|
3279
3446
|
} catch (err) {
|
|
3280
3447
|
setError({ message: err.message, hint: err.hint, failures: err.failures });
|
|
@@ -3284,7 +3451,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
3284
3451
|
const onDiscount = (raw) => startReview(Math.max(0, Number.parseFloat(raw) || 0));
|
|
3285
3452
|
const finalise = (payload) => {
|
|
3286
3453
|
setStep(STEP.BUILDING);
|
|
3287
|
-
buildPayload8({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection }).then(async (result) => {
|
|
3454
|
+
buildPayload8({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection, live: draft.live }).then(async (result) => {
|
|
3288
3455
|
setBuilt(result);
|
|
3289
3456
|
let req = null;
|
|
3290
3457
|
let command = "";
|
|
@@ -3358,6 +3525,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
3358
3525
|
) })
|
|
3359
3526
|
] }),
|
|
3360
3527
|
step === STEP.LOADING && /* @__PURE__ */ jsx8(Spinner, { label: `fetching live products from ${store}\u2026` }),
|
|
3528
|
+
step === STEP.LIVE && /* @__PURE__ */ jsx8(Spinner, { label: eventName === "checkout" ? `finding a real abandoned checkout in ${store}\u2026` : `creating a real order in ${store}\u2026` }),
|
|
3361
3529
|
step === STEP.COLLECTION && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
3362
3530
|
/* @__PURE__ */ jsx8(Text8, { children: "Which collection was viewed?" }),
|
|
3363
3531
|
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(
|
|
@@ -4528,10 +4696,19 @@ async function cmdBuild(args, config) {
|
|
|
4528
4696
|
} else {
|
|
4529
4697
|
log.ok(`${eventName} needs no product or collection`);
|
|
4530
4698
|
}
|
|
4699
|
+
let live = null;
|
|
4700
|
+
if (needsLive(providerId, eventName)) {
|
|
4701
|
+
log.step(eventName === "checkout" ? `finding a real abandoned checkout in ${config.shopify.domain}\u2026` : `creating a real order in ${config.shopify.domain}\u2026`);
|
|
4702
|
+
live = await resolveLive({ config, providerId, eventName, items, phone: typeof args.flags.phone === "string" ? args.flags.phone : void 0 });
|
|
4703
|
+
if (live?.kind === "checkout") log.ok(`checkout cart token ${live.cartToken}`);
|
|
4704
|
+
else if (live?.kind === "order") log.ok(`order ${live.name} (${live.id})`);
|
|
4705
|
+
else log.warn("no live record available \u2014 falling back to a generated one");
|
|
4706
|
+
}
|
|
4531
4707
|
const built = await buildPayload8({
|
|
4532
4708
|
cfg: config,
|
|
4533
4709
|
providerId,
|
|
4534
4710
|
eventName,
|
|
4711
|
+
live,
|
|
4535
4712
|
collection,
|
|
4536
4713
|
envelope: typeof args.flags.envelope === "string" ? args.flags.envelope : void 0,
|
|
4537
4714
|
items,
|