@binaryandthread/cli 0.4.11 → 0.4.13
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/bin.js +71 -13
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-T7MFWTB7.js → chunk-IUN3OOYY.js} +6 -2
- package/dist/chunk-IUN3OOYY.js.map +1 -0
- package/dist/{chunk-UTSTDWKZ.js → chunk-ZRHSCMBQ.js} +91 -41
- package/dist/chunk-ZRHSCMBQ.js.map +1 -0
- package/dist/{config-L4X7G563.js → config-PWI2G2O5.js} +2 -2
- package/dist/{interactive-UM5FCZDG.js → interactive-JONAXMTQ.js} +6 -4
- package/dist/interactive-JONAXMTQ.js.map +1 -0
- package/man/bt.1 +1 -1
- package/package.json +1 -1
- package/dist/chunk-T7MFWTB7.js.map +0 -1
- package/dist/chunk-UTSTDWKZ.js.map +0 -1
- package/dist/interactive-UM5FCZDG.js.map +0 -1
- /package/dist/{config-L4X7G563.js.map → config-PWI2G2O5.js.map} +0 -0
|
@@ -31,7 +31,11 @@ function loadConfig() {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
function saveConfig(patch) {
|
|
34
|
-
const
|
|
34
|
+
const raw = patch;
|
|
35
|
+
const merged = { ...loadConfig(), ...sanitize(raw) };
|
|
36
|
+
for (const key of ALLOWED_KEYS) {
|
|
37
|
+
if (raw[key] === null) delete merged[key];
|
|
38
|
+
}
|
|
35
39
|
const dir = configDir();
|
|
36
40
|
fs.mkdirSync(dir, { recursive: true });
|
|
37
41
|
const target = configPath();
|
|
@@ -64,4 +68,4 @@ export {
|
|
|
64
68
|
saveConfig,
|
|
65
69
|
forgetConfig
|
|
66
70
|
};
|
|
67
|
-
//# sourceMappingURL=chunk-
|
|
71
|
+
//# sourceMappingURL=chunk-IUN3OOYY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["// Local config — ~/.config/bt/config.json.\n//\n// Holds ONLY non-sensitive fields (SECURITY G15): cart_id, email, region, defaults.\n// The auth TOKEN is NEVER here — it goes in the OS keychain in Wave 5. Persisting a\n// token in plaintext config would be the exact leak G15 forbids.\n//\n// Writes are ATOMIC (F-B1): write to config.json.tmp then fs.renameSync, so a crash\n// mid-write never corrupts the file (rename is atomic on POSIX + Windows same-volume).\n\nimport * as fs from \"node:fs\"\nimport * as os from \"node:os\"\nimport * as path from \"node:path\"\n\nexport interface BtConfig {\n cart_id?: string\n email?: string\n region?: string\n defaultSize?: string\n defaultColour?: string\n /** CAT-31: \"regular\" | \"oversize\", used when a design comes in both fits and no --fit is given. */\n defaultFit?: string\n defaultBranch?: string\n /** RUNTIME-ONLY (ENG-21): where the effective region came from this run — flag, saved config, or\n * a locale guess. Deliberately NOT in ALLOWED_KEYS, so saveConfig always strips it. */\n region_source?: \"flag\" | \"config\" | \"locale\"\n}\n\n// Persisted keys exclude the RUNTIME-ONLY fields (region_source) — typing ALLOWED_KEYS as the full\n// `keyof BtConfig` wrongly widened `key` to include region_source's literal union, so `out[key] = v`\n// (a string) failed to typecheck. PersistedKey narrows it to the string-valued, persistable fields.\nexport type PersistedKey = Exclude<keyof BtConfig, \"region_source\">\n\n/** A config patch: a string sets the key, `null` DELETES it (TB252: a paid cart's id must go), undefined leaves it. */\nexport type ConfigPatch = { [K in PersistedKey]?: string | null }\n\n// Only these keys are ever persisted — anything else (notably `token`) is dropped (G15).\nconst ALLOWED_KEYS: Array<PersistedKey> = [\n \"cart_id\",\n \"email\",\n \"region\",\n \"defaultSize\",\n \"defaultColour\",\n \"defaultFit\",\n \"defaultBranch\",\n]\n\n/** The config directory: $XDG_CONFIG_HOME/bt or ~/.config/bt. */\nexport function configDir(): string {\n const xdg = process.env.XDG_CONFIG_HOME\n const base = xdg && xdg.trim() ? xdg : path.join(os.homedir(), \".config\")\n return path.join(base, \"bt\")\n}\n\nexport function configPath(): string {\n return path.join(configDir(), \"config.json\")\n}\n\n/** Load the config, or {} if none / unreadable. Never throws. */\nexport function loadConfig(): BtConfig {\n try {\n const raw = fs.readFileSync(configPath(), \"utf8\")\n const parsed = JSON.parse(raw) as Record<string, unknown>\n return sanitize(parsed)\n } catch {\n return {}\n }\n}\n\n/**\n * Merge-and-save the given fields. Strips any non-allowlisted key (G15). Atomic (F-B1). A value of\n * exactly `null` deletes that key (TB252: before this no path could clear cart_id, so a paid cart's\n * id outlived its order and `bt cart` listed the paid items as still staged).\n */\nexport function saveConfig(patch: ConfigPatch | Partial<BtConfig>): BtConfig {\n const raw = patch as Record<string, unknown>\n const merged = { ...loadConfig(), ...sanitize(raw) }\n for (const key of ALLOWED_KEYS) {\n if (raw[key] === null) delete merged[key]\n }\n const dir = configDir()\n fs.mkdirSync(dir, { recursive: true })\n const target = configPath()\n const tmp = target + \".tmp\"\n // write to a temp file then atomically rename (F-B1)\n fs.writeFileSync(tmp, JSON.stringify(merged, null, 2), { encoding: \"utf8\", mode: 0o600 })\n fs.renameSync(tmp, target)\n return merged\n}\n\n/** Wipe the local config file (G15 erasure — `bt forget`). Never throws. */\nexport function forgetConfig(): void {\n try {\n fs.rmSync(configPath(), { force: true })\n } catch {\n /* ignore */\n }\n}\n\n/** Keep only allowlisted, non-sensitive keys (drops `token` and anything unexpected). */\nfunction sanitize(obj: Record<string, unknown>): BtConfig {\n const out: BtConfig = {}\n for (const key of ALLOWED_KEYS) {\n const v = obj[key]\n if (typeof v === \"string\" && v.length > 0) {\n out[key] = v\n }\n }\n return out\n}\n"],"mappings":";;;AASA,YAAY,QAAQ;AACpB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAyBtB,IAAM,eAAoC;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,YAAoB;AAClC,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,OAAO,OAAO,IAAI,KAAK,IAAI,MAAW,UAAQ,WAAQ,GAAG,SAAS;AACxE,SAAY,UAAK,MAAM,IAAI;AAC7B;AAEO,SAAS,aAAqB;AACnC,SAAY,UAAK,UAAU,GAAG,aAAa;AAC7C;AAGO,SAAS,aAAuB;AACrC,MAAI;AACF,UAAM,MAAS,gBAAa,WAAW,GAAG,MAAM;AAChD,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,SAAS,MAAM;AAAA,EACxB,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAOO,SAAS,WAAW,OAAkD;AAC3E,QAAM,MAAM;AACZ,QAAM,SAAS,EAAE,GAAG,WAAW,GAAG,GAAG,SAAS,GAAG,EAAE;AACnD,aAAW,OAAO,cAAc;AAC9B,QAAI,IAAI,GAAG,MAAM,KAAM,QAAO,OAAO,GAAG;AAAA,EAC1C;AACA,QAAM,MAAM,UAAU;AACtB,EAAG,aAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACrC,QAAM,SAAS,WAAW;AAC1B,QAAM,MAAM,SAAS;AAErB,EAAG,iBAAc,KAAK,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AACxF,EAAG,cAAW,KAAK,MAAM;AACzB,SAAO;AACT;AAGO,SAAS,eAAqB;AACnC,MAAI;AACF,IAAG,UAAO,WAAW,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,EACzC,QAAQ;AAAA,EAER;AACF;AAGA,SAAS,SAAS,KAAwC;AACxD,QAAM,MAAgB,CAAC;AACvB,aAAW,OAAO,cAAc;AAC9B,UAAM,IAAI,IAAI,GAAG;AACjB,QAAI,OAAO,MAAM,YAAY,EAAE,SAAS,GAAG;AACzC,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
|
@@ -220,13 +220,19 @@ var StoreClient = class {
|
|
|
220
220
|
} catch {
|
|
221
221
|
parsed = {};
|
|
222
222
|
}
|
|
223
|
-
|
|
223
|
+
const idempotencyConflict = res.status === 409 && parsed.code === "IDEMPOTENCY_CONFLICT";
|
|
224
|
+
const parsedType = String(parsed.type ?? "");
|
|
225
|
+
if (opts.cartRecovery && (res.status === 404 || parsedType === "cart_completed" || parsedType.includes("cart.completed") || /cart.*complete/i.test(String(parsed.message ?? "")))) {
|
|
224
226
|
throw new RecoverableCartError(
|
|
225
227
|
`cart is stale or completed (HTTP ${res.status})`,
|
|
226
228
|
res.status
|
|
227
229
|
);
|
|
228
230
|
}
|
|
229
|
-
if (
|
|
231
|
+
if (idempotencyConflict) {
|
|
232
|
+
retryAfterMs = parseRetryAfterMs(res);
|
|
233
|
+
lastErr = new StoreApiError(`request in flight (HTTP 409)`, 409, path);
|
|
234
|
+
this.logWarn("store.request.idempotency_conflict", { path, attempt: attempt + 1, retryAfterMs });
|
|
235
|
+
} else if (res.status === 429) {
|
|
230
236
|
retryAfterMs = parseRetryAfterMs(res);
|
|
231
237
|
lastErr = new StoreApiError(`rate limited (HTTP 429)`, 429, path);
|
|
232
238
|
this.logWarn("store.request.rate_limited", {
|
|
@@ -626,8 +632,8 @@ function supplierOf(p) {
|
|
|
626
632
|
var PRINTFUL_BLANK_COLOURS = {
|
|
627
633
|
"ascolour-5082": ["black", "white", "coal", "eucalyptus", "bone", "grey", "khaki", "indigo"],
|
|
628
634
|
// TB124, TB126
|
|
629
|
-
"bellacanvas-4711": ["asphalt", "dust", "navy", "maroon", "forest"]
|
|
630
|
-
// TB128: the sweatshirt
|
|
635
|
+
"bellacanvas-4711": ["asphalt", "dust", "navy", "maroon", "forest", "jet"]
|
|
636
|
+
// TB128: the sweatshirt; TB129: + jet (its Black)
|
|
631
637
|
};
|
|
632
638
|
var PRINTFUL_BLANK_SIZES = {
|
|
633
639
|
"ascolour-5082": ["s", "m", "l", "xl", "2xl"],
|
|
@@ -771,7 +777,7 @@ function resolveVariant(p, opts) {
|
|
|
771
777
|
const supplier = supplierOf(p);
|
|
772
778
|
const pb = printfulBlankOf(p);
|
|
773
779
|
const colours = coloursFor(p, fit, opts.countryCode);
|
|
774
|
-
const colour = colourKey(opts.colour ?? "") || (colours[0] ?? null);
|
|
780
|
+
const colour = colourKey(opts.colour ?? "", productColours(p)) || (colours[0] ?? null);
|
|
775
781
|
if (gate && colour && opts.countryCode !== void 0 && !colourBuyable(gate, colour, opts.countryCode, supplier, pb)) {
|
|
776
782
|
return { ok: false, reason: "fit_unavailable", fit: gate, colour };
|
|
777
783
|
}
|
|
@@ -809,15 +815,22 @@ var COLOUR_LABELS = {
|
|
|
809
815
|
bone: "Faded Bone",
|
|
810
816
|
grey: "Faded Grey",
|
|
811
817
|
khaki: "Faded Khaki",
|
|
812
|
-
indigo: "Faded Indigo"
|
|
818
|
+
indigo: "Faded Indigo",
|
|
819
|
+
jet: "Black"
|
|
813
820
|
};
|
|
814
821
|
function colourLabel(colour) {
|
|
815
822
|
const c = colour.trim();
|
|
816
823
|
if (!c) return c;
|
|
817
824
|
return COLOUR_LABELS[c.toLowerCase()] ?? c.charAt(0).toUpperCase() + c.slice(1).toLowerCase();
|
|
818
825
|
}
|
|
819
|
-
function colourKey(colour) {
|
|
826
|
+
function colourKey(colour, available) {
|
|
820
827
|
const c = colour.trim().toLowerCase();
|
|
828
|
+
if (available) {
|
|
829
|
+
const keys = available.map((k) => k.trim().toLowerCase());
|
|
830
|
+
if (keys.includes(c)) return c;
|
|
831
|
+
return keys.find((k) => colourLabel(k).toLowerCase() === c) ?? c;
|
|
832
|
+
}
|
|
833
|
+
if (Object.prototype.hasOwnProperty.call(COLOUR_LABELS, c)) return c;
|
|
821
834
|
const key = Object.entries(COLOUR_LABELS).find(([, label]) => label.toLowerCase() === c)?.[0];
|
|
822
835
|
return key ?? c;
|
|
823
836
|
}
|
|
@@ -878,7 +891,7 @@ function findVariant(p, opts = {}) {
|
|
|
878
891
|
}
|
|
879
892
|
const variants = (p.variants ?? []).filter((v) => fit === null || variantFit(v) === fit);
|
|
880
893
|
const size = opts.size?.trim().toLowerCase();
|
|
881
|
-
const colour = opts.colour ? colourKey(opts.colour) : void 0;
|
|
894
|
+
const colour = opts.colour ? colourKey(opts.colour, productColours(p)) : void 0;
|
|
882
895
|
const sizeMatches = (v) => {
|
|
883
896
|
if (!size) return true;
|
|
884
897
|
return (sizeOfVariant(v) ?? "").toLowerCase() === size || (v.title ?? "").toLowerCase() === size;
|
|
@@ -964,6 +977,31 @@ function firstImageUrl(images) {
|
|
|
964
977
|
function escapeRegex(s) {
|
|
965
978
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
966
979
|
}
|
|
980
|
+
function sizeTokenOf(title) {
|
|
981
|
+
return titleSegments(title).find((s) => SIZE_TOKEN.test(s)) ?? null;
|
|
982
|
+
}
|
|
983
|
+
var SIZE_ORDER = ["xxs", "xs", "s", "m", "l", "xl", "2xl", "xxl", "3xl", "xxxl", "4xl", "5xl"];
|
|
984
|
+
function sortSizes(sizes) {
|
|
985
|
+
const rank = (s) => {
|
|
986
|
+
const i = SIZE_ORDER.indexOf(s.trim().toLowerCase());
|
|
987
|
+
return i === -1 ? SIZE_ORDER.length : i;
|
|
988
|
+
};
|
|
989
|
+
return [...sizes].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
990
|
+
}
|
|
991
|
+
function lineOption(item, titles) {
|
|
992
|
+
const o = item.variant?.options?.find((x) => titles.includes((x.option?.title ?? "").toLowerCase()));
|
|
993
|
+
const v = typeof o?.value === "string" ? o.value.trim() : "";
|
|
994
|
+
return v ? v : null;
|
|
995
|
+
}
|
|
996
|
+
function lineColour(item) {
|
|
997
|
+
const opt = lineOption(item, ["colour", "color"]);
|
|
998
|
+
if (opt) return opt.toLowerCase();
|
|
999
|
+
const vt = (item.variant_title ?? "").toString();
|
|
1000
|
+
return vt ? variantColour({ id: "x", title: vt }) : null;
|
|
1001
|
+
}
|
|
1002
|
+
function lineSize(item) {
|
|
1003
|
+
return lineOption(item, ["size"]) ?? sizeTokenOf(item.variant_title);
|
|
1004
|
+
}
|
|
967
1005
|
function orderStage(o) {
|
|
968
1006
|
const status = (o.status ?? "").trim().toLowerCase();
|
|
969
1007
|
if (status === "canceled" || status === "refunded") return "reverted";
|
|
@@ -1280,7 +1318,7 @@ function contributionBar(style, commits, cap = 20) {
|
|
|
1280
1318
|
function colourSwatch(colour) {
|
|
1281
1319
|
const c = colour.trim().toLowerCase();
|
|
1282
1320
|
if (c === "white") return "\u2B1C";
|
|
1283
|
-
if (c === "black") return "\u2B1B";
|
|
1321
|
+
if (c === "black" || c === "jet") return "\u2B1B";
|
|
1284
1322
|
if (c === "khaki") return "\u{1F7EB}";
|
|
1285
1323
|
return "\u25FC";
|
|
1286
1324
|
}
|
|
@@ -1298,14 +1336,6 @@ function regionLine(rr, style, source) {
|
|
|
1298
1336
|
const nudge = rr.home ? " \xB7 not you? \u2192 bt region" : source === "locale" ? " \xB7 guessed from your locale \u2014 not you? \u2192 bt region" : "";
|
|
1299
1337
|
return dim(style, `${GLYPH.gear} region: ${loc}${rr.region.currency_code}${nudge}`);
|
|
1300
1338
|
}
|
|
1301
|
-
var SIZE_ORDER = ["xxs", "xs", "s", "m", "l", "xl", "2xl", "xxl", "3xl", "xxxl", "4xl", "5xl"];
|
|
1302
|
-
function sortSizes(sizes) {
|
|
1303
|
-
const rank = (s) => {
|
|
1304
|
-
const i = SIZE_ORDER.indexOf(s.trim().toLowerCase());
|
|
1305
|
-
return i === -1 ? SIZE_ORDER.length : i;
|
|
1306
|
-
};
|
|
1307
|
-
return [...sizes].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
1308
|
-
}
|
|
1309
1339
|
function brandOf(args) {
|
|
1310
1340
|
return args?.brand ?? PLAIN;
|
|
1311
1341
|
}
|
|
@@ -1330,21 +1360,6 @@ function displayName(p) {
|
|
|
1330
1360
|
const title = (p.title ?? "").toString().trim() || handle;
|
|
1331
1361
|
return { title, handle };
|
|
1332
1362
|
}
|
|
1333
|
-
function lineOption(item, titles) {
|
|
1334
|
-
const o = item.variant?.options?.find((x) => titles.includes((x.option?.title ?? "").toLowerCase()));
|
|
1335
|
-
const v = typeof o?.value === "string" ? o.value.trim() : "";
|
|
1336
|
-
return v ? v : null;
|
|
1337
|
-
}
|
|
1338
|
-
function lineColour(item) {
|
|
1339
|
-
const opt = lineOption(item, ["colour", "color"]);
|
|
1340
|
-
if (opt) return opt.toLowerCase();
|
|
1341
|
-
const vt = (item.variant_title ?? "").toString();
|
|
1342
|
-
if (vt) {
|
|
1343
|
-
const c = variantColour({ id: "x", title: vt });
|
|
1344
|
-
if (c) return c;
|
|
1345
|
-
}
|
|
1346
|
-
return null;
|
|
1347
|
-
}
|
|
1348
1363
|
function lineFit(item) {
|
|
1349
1364
|
const opt = parseFit(lineOption(item, ["fit"]));
|
|
1350
1365
|
if (opt) return opt;
|
|
@@ -1365,7 +1380,8 @@ function formatCartLine(item, index, currency, brand) {
|
|
|
1365
1380
|
const idTag = item.id ? dimTag(style, ` (#${shortItemId(item.id)})`) : "";
|
|
1366
1381
|
const colour = lineColour(item);
|
|
1367
1382
|
const fit = lineFit(item);
|
|
1368
|
-
const
|
|
1383
|
+
const size = lineSize(item);
|
|
1384
|
+
const tags = [colour ? colourLabel(colour).toLowerCase() : null, fit ? fitLabel(fit).toLowerCase() : null, size].filter(Boolean).join(", ");
|
|
1369
1385
|
const colourTag = tags ? ` \xB7 ${tags}` : "";
|
|
1370
1386
|
const price = item.total != null ? ` ${accentPrice(style, fmtPrice(item.total, currency))}` : "";
|
|
1371
1387
|
return ` ${ref} ${title}${colourTag}${idTag} \xB7 qty ${item.quantity}${price}`;
|
|
@@ -1649,7 +1665,7 @@ async function runView(client, config, args) {
|
|
|
1649
1665
|
}
|
|
1650
1666
|
let imageUrl = product.thumbnail ?? null;
|
|
1651
1667
|
let blackNote = null;
|
|
1652
|
-
const wanted = (args.colour ? colourKey(args.colour) : "") || (fit ? colours[0] : void 0);
|
|
1668
|
+
const wanted = (args.colour ? colourKey(args.colour, [...colours, ...unavailable]) : "") || (fit ? colours[0] : void 0);
|
|
1653
1669
|
if (wanted) {
|
|
1654
1670
|
const resolved = imageFor(product, { fit, colour: wanted }) ?? (fit === null ? imageForColour(product, wanted) : null);
|
|
1655
1671
|
if (resolved) {
|
|
@@ -1721,6 +1737,10 @@ function registerView(program, makeClient, loadConfig) {
|
|
|
1721
1737
|
}
|
|
1722
1738
|
|
|
1723
1739
|
// src/commands/stage.ts
|
|
1740
|
+
function sizeChoice(sizes) {
|
|
1741
|
+
const sorted = sortSizes(sizes);
|
|
1742
|
+
return sorted.length === 1 ? sorted[0] : `<${sorted.join("|")}>`;
|
|
1743
|
+
}
|
|
1724
1744
|
async function runStage(client, config, args) {
|
|
1725
1745
|
const style = brandOf(args);
|
|
1726
1746
|
const rr = await client.resolveRegionResult(config.region);
|
|
@@ -1745,6 +1765,11 @@ async function runStage(client, config, args) {
|
|
|
1745
1765
|
cartId = void 0;
|
|
1746
1766
|
recovered = true;
|
|
1747
1767
|
}
|
|
1768
|
+
if (existing?.completed_at) {
|
|
1769
|
+
existing = null;
|
|
1770
|
+
cartId = void 0;
|
|
1771
|
+
recovered = true;
|
|
1772
|
+
}
|
|
1748
1773
|
if (existing) {
|
|
1749
1774
|
const to = await cartShipTo(existing, () => client.getRegions());
|
|
1750
1775
|
if (!to) {
|
|
@@ -1817,7 +1842,7 @@ async function runStage(client, config, args) {
|
|
|
1817
1842
|
}
|
|
1818
1843
|
const fitFlag = r.fit && productFits(product).length > 1 ? ` --fit ${r.fit}` : "";
|
|
1819
1844
|
const colourAsked = "colour" in r ? r.colour : null;
|
|
1820
|
-
const madeIn = coloursFor(product, r.fit ?? null);
|
|
1845
|
+
const madeIn = coloursFor(product, productFits(product).length > 0 ? r.fit ?? null : null);
|
|
1821
1846
|
if (colourAsked && (r.reason === "fit_unavailable" || r.reason === "not_found") && !madeIn.includes(colourAsked)) {
|
|
1822
1847
|
return failResult({
|
|
1823
1848
|
code: EXIT.DATAERR,
|
|
@@ -1839,7 +1864,7 @@ async function runStage(client, config, args) {
|
|
|
1839
1864
|
code: EXIT.DATAERR,
|
|
1840
1865
|
what: `${r.size} is not made in ${r.fit} where you ship (${countryCode ?? "your region"}).`,
|
|
1841
1866
|
money: "nothing staged, nothing charged.",
|
|
1842
|
-
fix: r.sizes.length ?
|
|
1867
|
+
fix: r.sizes.length ? `${r.sizes.length > 1 ? "ask which size is wanted, don't choose for them, then: " : ""}bt stage ${handleShown} --fit ${r.fit} --size ${sizeChoice(r.sizes)}` : `bt view ${handleShown} --fit ${r.fit} (to see the sizes you can buy)`
|
|
1843
1868
|
});
|
|
1844
1869
|
}
|
|
1845
1870
|
const inColour = r.colour ? ` in ${r.colour}` : "";
|
|
@@ -1848,7 +1873,7 @@ async function runStage(client, config, args) {
|
|
|
1848
1873
|
code: EXIT.DATAERR,
|
|
1849
1874
|
what: `${handleShown} doesn't come in size ${size}${inColour}. sizes: ${r.sizes.join(" ")}.`,
|
|
1850
1875
|
money: "nothing staged, nothing charged.",
|
|
1851
|
-
fix:
|
|
1876
|
+
fix: `${r.sizes.length > 1 ? "ask which size is wanted, don't choose for them, then: " : ""}bt stage ${handleShown}${fitFlag} --size ${sizeChoice(r.sizes)}${r.colour ? ` --colour ${r.colour}` : ""}`
|
|
1852
1877
|
});
|
|
1853
1878
|
}
|
|
1854
1879
|
return failResult({
|
|
@@ -1861,6 +1886,20 @@ async function runStage(client, config, args) {
|
|
|
1861
1886
|
const variant = r.variant;
|
|
1862
1887
|
const colour = r.colour ?? void 0;
|
|
1863
1888
|
const fit = r.fit;
|
|
1889
|
+
if (!size) {
|
|
1890
|
+
const sizes = sortSizes(
|
|
1891
|
+
r.colour ? sizesFor(product, r.fit, r.colour, countryCode) : [...new Set((product.variants ?? []).map((v) => sizeOfVariant(v)).filter((s) => !!s))]
|
|
1892
|
+
);
|
|
1893
|
+
if (sizes.length > 1) {
|
|
1894
|
+
const fitFlag = r.fit && productFits(product).length > 1 ? ` --fit ${r.fit}` : "";
|
|
1895
|
+
return failResult({
|
|
1896
|
+
code: EXIT.DATAERR,
|
|
1897
|
+
what: `${handleShown} needs a size. sizes${r.colour ? ` in ${r.colour}` : ""}: ${sizes.join(" ")}.`,
|
|
1898
|
+
money: "nothing staged, nothing charged.",
|
|
1899
|
+
fix: `ask which size is wanted, don't choose for them, then: bt stage ${handleShown}${fitFlag} --size ${sizeChoice(sizes)}${r.colour ? ` --colour ${r.colour}` : ""}`
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1864
1903
|
const refTags = [colour ? colourLabel(colour).toLowerCase() : null, fit].filter(Boolean).join(", ");
|
|
1865
1904
|
const colourSuffix = refTags ? ` (${refTags})` : "";
|
|
1866
1905
|
if (args.dryRun) {
|
|
@@ -1931,9 +1970,19 @@ async function runStage(client, config, args) {
|
|
|
1931
1970
|
json: args.json ? { cart_id: cartId, recovered, fit: fit ?? null, colour: colour ?? null, items: cart.items ?? [], total: cart.total ?? null } : void 0
|
|
1932
1971
|
};
|
|
1933
1972
|
}
|
|
1973
|
+
function oneDesignPerCommand() {
|
|
1974
|
+
return failResult({
|
|
1975
|
+
code: EXIT.DATAERR,
|
|
1976
|
+
what: 'one design per command. a two-word colour is written --colour coal, or in quotes: --colour "faded coal"'
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1934
1979
|
function registerStage(program, makeClient, loadConfig) {
|
|
1935
|
-
program.command("stage").alias("add").argument("<handle>", "the design: a slug (terminal-velocity), a bt browse number (3), or a fuzzy hint (termnl)").option("--size <size>", "garment size (
|
|
1980
|
+
program.command("stage").alias("add").argument("<handle>", "the design: a slug (terminal-velocity), a bt browse number (3), or a fuzzy hint (termnl)").option("--size <size>", "garment size (required unless bt config set defaultSize)").option("--colour <colour>", "colour, e.g. white / black (default: the product's first colour)").option("--fit <fit>", "regular or oversize (required when a design comes in both; default: your defaultFit)").option("--dry-run", "resolve the variant + show what would be staged, without staging").description("reserve stock \u2014 acquires an inventory lock, reserves nothing permanent.").allowExcessArguments().action(async (handle, opts, cmd) => {
|
|
1936
1981
|
const { json, brand } = resolveOutput(program);
|
|
1982
|
+
if (cmd.args.length > 1) {
|
|
1983
|
+
printResult(oneDesignPerCommand(), json);
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1937
1986
|
const res = await runStage(makeClient(), loadConfig(), {
|
|
1938
1987
|
handle,
|
|
1939
1988
|
size: opts.size,
|
|
@@ -1944,7 +1993,7 @@ function registerStage(program, makeClient, loadConfig) {
|
|
|
1944
1993
|
brand
|
|
1945
1994
|
});
|
|
1946
1995
|
if (res.save) {
|
|
1947
|
-
const { saveConfig } = await import("./config-
|
|
1996
|
+
const { saveConfig } = await import("./config-PWI2G2O5.js");
|
|
1948
1997
|
saveConfig(res.save);
|
|
1949
1998
|
}
|
|
1950
1999
|
printResult(res, json);
|
|
@@ -1967,6 +2016,7 @@ export {
|
|
|
1967
2016
|
labelVariantTitle,
|
|
1968
2017
|
productColours,
|
|
1969
2018
|
findVariant,
|
|
2019
|
+
lineColour,
|
|
1970
2020
|
orderStage,
|
|
1971
2021
|
productLore,
|
|
1972
2022
|
isCartTaxInclusive,
|
|
@@ -1996,7 +2046,6 @@ export {
|
|
|
1996
2046
|
priceLine,
|
|
1997
2047
|
stageMarker,
|
|
1998
2048
|
displayName,
|
|
1999
|
-
lineColour,
|
|
2000
2049
|
formatCartLine,
|
|
2001
2050
|
resolveLineItemRef,
|
|
2002
2051
|
resolveProduct,
|
|
@@ -2007,6 +2056,7 @@ export {
|
|
|
2007
2056
|
runView,
|
|
2008
2057
|
registerView,
|
|
2009
2058
|
runStage,
|
|
2059
|
+
oneDesignPerCommand,
|
|
2010
2060
|
registerStage
|
|
2011
2061
|
};
|
|
2012
|
-
//# sourceMappingURL=chunk-
|
|
2062
|
+
//# sourceMappingURL=chunk-ZRHSCMBQ.js.map
|