@binaryandthread/cli 0.4.10 → 0.4.12
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-PVKB77IE.js → chunk-YNBNKCCN.js} +132 -51
- package/dist/chunk-YNBNKCCN.js.map +1 -0
- package/dist/{config-L4X7G563.js → config-PWI2G2O5.js} +2 -2
- package/dist/{interactive-B5TALADG.js → interactive-JDUPAMLW.js} +3 -3
- package/dist/interactive-JDUPAMLW.js.map +1 -0
- package/man/bt.1 +1 -1
- package/package.json +1 -1
- package/dist/chunk-PVKB77IE.js.map +0 -1
- package/dist/chunk-T7MFWTB7.js.map +0 -1
- package/dist/interactive-B5TALADG.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", {
|
|
@@ -624,12 +630,20 @@ function supplierOf(p) {
|
|
|
624
630
|
return v === "gelato" || v === "printful" ? v : null;
|
|
625
631
|
}
|
|
626
632
|
var PRINTFUL_BLANK_COLOURS = {
|
|
627
|
-
"ascolour-5082": ["black", "white", "coal", "eucalyptus", "bone", "grey", "khaki", "indigo"]
|
|
633
|
+
"ascolour-5082": ["black", "white", "coal", "eucalyptus", "bone", "grey", "khaki", "indigo"],
|
|
628
634
|
// TB124, TB126
|
|
635
|
+
"bellacanvas-4711": ["asphalt", "dust", "navy", "maroon", "forest"]
|
|
636
|
+
// TB128: the sweatshirt
|
|
629
637
|
};
|
|
630
638
|
var PRINTFUL_BLANK_SIZES = {
|
|
631
|
-
"ascolour-5082": ["s", "m", "l", "xl", "2xl"]
|
|
639
|
+
"ascolour-5082": ["s", "m", "l", "xl", "2xl"],
|
|
640
|
+
"bellacanvas-4711": ["s", "m", "l", "xl", "2xl"]
|
|
632
641
|
};
|
|
642
|
+
function printfulBlankOf(p) {
|
|
643
|
+
const v = (p.metadata ?? {})["bt_blank"];
|
|
644
|
+
if (v === void 0 || v === null) return "ascolour-5082";
|
|
645
|
+
return typeof v === "string" && Object.prototype.hasOwnProperty.call(PRINTFUL_BLANK_COLOURS, v) ? v : null;
|
|
646
|
+
}
|
|
633
647
|
var PRINTFUL_SERVED_COUNTRIES = [
|
|
634
648
|
"se",
|
|
635
649
|
"us",
|
|
@@ -660,29 +674,30 @@ var PRINTFUL_SERVED_COUNTRIES = [
|
|
|
660
674
|
"si",
|
|
661
675
|
"es"
|
|
662
676
|
];
|
|
663
|
-
function printfulBlankFor(fit, countryCode) {
|
|
677
|
+
function printfulBlankFor(fit, countryCode, productBlank = "ascolour-5082") {
|
|
664
678
|
if (!PRINTFUL_SERVED_COUNTRIES.includes((countryCode ?? "").trim().toLowerCase())) return null;
|
|
665
|
-
|
|
679
|
+
if (productBlank === null) return null;
|
|
680
|
+
return fit === "regular" ? productBlank : null;
|
|
666
681
|
}
|
|
667
682
|
function sizeKey(size) {
|
|
668
683
|
const s = size.trim().toLowerCase();
|
|
669
684
|
return s === "xxl" ? "2xl" : s === "xxxl" ? "3xl" : s;
|
|
670
685
|
}
|
|
671
|
-
function blankRule(fit, countryCode, supplier) {
|
|
686
|
+
function blankRule(fit, countryCode, supplier, productBlank) {
|
|
672
687
|
if (supplier === null) return null;
|
|
673
688
|
if (supplier === "printful") {
|
|
674
|
-
const pb = printfulBlankFor(fit, countryCode);
|
|
689
|
+
const pb = printfulBlankFor(fit, countryCode, productBlank);
|
|
675
690
|
return pb ? { colours: PRINTFUL_BLANK_COLOURS[pb], sizes: PRINTFUL_BLANK_SIZES[pb] } : null;
|
|
676
691
|
}
|
|
677
692
|
const blank = blankFor(fit, countryCode);
|
|
678
693
|
return blank ? { colours: BLANK_COLOURS[blank], sizes: BLANK_SIZES[blank] } : null;
|
|
679
694
|
}
|
|
680
|
-
function colourBuyable(fit, colour, countryCode, supplier = "gelato") {
|
|
681
|
-
const rule2 = blankRule(fit, countryCode, supplier);
|
|
695
|
+
function colourBuyable(fit, colour, countryCode, supplier = "gelato", printfulBlank = "ascolour-5082") {
|
|
696
|
+
const rule2 = blankRule(fit, countryCode, supplier, printfulBlank);
|
|
682
697
|
return !!rule2 && rule2.colours.includes(colour.trim().toLowerCase());
|
|
683
698
|
}
|
|
684
|
-
function sizeBuyable(fit, size, countryCode, supplier = "gelato") {
|
|
685
|
-
const rule2 = blankRule(fit, countryCode, supplier);
|
|
699
|
+
function sizeBuyable(fit, size, countryCode, supplier = "gelato", printfulBlank = "ascolour-5082") {
|
|
700
|
+
const rule2 = blankRule(fit, countryCode, supplier, printfulBlank);
|
|
686
701
|
return !!rule2 && rule2.sizes.includes(sizeKey(size));
|
|
687
702
|
}
|
|
688
703
|
function legacyFit(p) {
|
|
@@ -701,11 +716,12 @@ function variantsOfFit(p, fit) {
|
|
|
701
716
|
function coloursFor(p, fit, countryCode) {
|
|
702
717
|
const gate = gateFit(p, fit);
|
|
703
718
|
const supplier = supplierOf(p);
|
|
719
|
+
const pb = printfulBlankOf(p);
|
|
704
720
|
const out = [];
|
|
705
721
|
for (const v of variantsOfFit(p, fit)) {
|
|
706
722
|
const c = variantColour(v);
|
|
707
723
|
if (!c || out.includes(c)) continue;
|
|
708
|
-
if (gate && countryCode !== void 0 && !colourBuyable(gate, c, countryCode, supplier)) continue;
|
|
724
|
+
if (gate && countryCode !== void 0 && !colourBuyable(gate, c, countryCode, supplier, pb)) continue;
|
|
709
725
|
out.push(c);
|
|
710
726
|
}
|
|
711
727
|
return out;
|
|
@@ -713,13 +729,14 @@ function coloursFor(p, fit, countryCode) {
|
|
|
713
729
|
function sizesFor(p, fit, colour, countryCode) {
|
|
714
730
|
const gate = gateFit(p, fit);
|
|
715
731
|
const supplier = supplierOf(p);
|
|
732
|
+
const pb = printfulBlankOf(p);
|
|
716
733
|
const target = colour.trim().toLowerCase();
|
|
717
734
|
const out = [];
|
|
718
735
|
for (const v of variantsOfFit(p, fit)) {
|
|
719
736
|
if (variantColour(v) !== target) continue;
|
|
720
737
|
const s = sizeOfVariant(v);
|
|
721
738
|
if (!s || out.includes(s)) continue;
|
|
722
|
-
if (gate && countryCode !== void 0 && !sizeBuyable(gate, s, countryCode, supplier)) continue;
|
|
739
|
+
if (gate && countryCode !== void 0 && !sizeBuyable(gate, s, countryCode, supplier, pb)) continue;
|
|
723
740
|
out.push(s);
|
|
724
741
|
}
|
|
725
742
|
return out;
|
|
@@ -758,21 +775,22 @@ function resolveVariant(p, opts) {
|
|
|
758
775
|
const pool = variantsOfFit(p, fit);
|
|
759
776
|
const gate = gateFit(p, fit);
|
|
760
777
|
const supplier = supplierOf(p);
|
|
778
|
+
const pb = printfulBlankOf(p);
|
|
761
779
|
const colours = coloursFor(p, fit, opts.countryCode);
|
|
762
780
|
const colour = colourKey(opts.colour ?? "") || (colours[0] ?? null);
|
|
763
|
-
if (gate && colour && opts.countryCode !== void 0 && !colourBuyable(gate, colour, opts.countryCode, supplier)) {
|
|
781
|
+
if (gate && colour && opts.countryCode !== void 0 && !colourBuyable(gate, colour, opts.countryCode, supplier, pb)) {
|
|
764
782
|
return { ok: false, reason: "fit_unavailable", fit: gate, colour };
|
|
765
783
|
}
|
|
766
784
|
const size = (opts.size ?? "").trim() || null;
|
|
767
785
|
const sizeLc = size?.toLowerCase();
|
|
768
786
|
const buyableSizes = colour ? sizesFor(p, fit, colour, opts.countryCode) : [];
|
|
769
787
|
const sizeExists = !!size && pool.some((x) => (sizeOfVariant(x) ?? x.title ?? "").toLowerCase() === size.toLowerCase());
|
|
770
|
-
if (gate && size && sizeExists && opts.countryCode !== void 0 && !sizeBuyable(gate, size, opts.countryCode, supplier)) {
|
|
788
|
+
if (gate && size && sizeExists && opts.countryCode !== void 0 && !sizeBuyable(gate, size, opts.countryCode, supplier, pb)) {
|
|
771
789
|
return { ok: false, reason: "size_unavailable", fit: gate, size, sizes: buyableSizes };
|
|
772
790
|
}
|
|
773
791
|
const sellable = gate && opts.countryCode !== void 0 ? pool.filter((x) => {
|
|
774
792
|
const s = sizeOfVariant(x);
|
|
775
|
-
return !s || sizeBuyable(gate, s, opts.countryCode, supplier);
|
|
793
|
+
return !s || sizeBuyable(gate, s, opts.countryCode, supplier, pb);
|
|
776
794
|
}) : pool;
|
|
777
795
|
const v = sellable.find((x) => {
|
|
778
796
|
const colourOk = !colour || variantColour(x) === colour;
|
|
@@ -952,6 +970,31 @@ function firstImageUrl(images) {
|
|
|
952
970
|
function escapeRegex(s) {
|
|
953
971
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
954
972
|
}
|
|
973
|
+
function sizeTokenOf(title) {
|
|
974
|
+
return titleSegments(title).find((s) => SIZE_TOKEN.test(s)) ?? null;
|
|
975
|
+
}
|
|
976
|
+
var SIZE_ORDER = ["xxs", "xs", "s", "m", "l", "xl", "2xl", "xxl", "3xl", "xxxl", "4xl", "5xl"];
|
|
977
|
+
function sortSizes(sizes) {
|
|
978
|
+
const rank = (s) => {
|
|
979
|
+
const i = SIZE_ORDER.indexOf(s.trim().toLowerCase());
|
|
980
|
+
return i === -1 ? SIZE_ORDER.length : i;
|
|
981
|
+
};
|
|
982
|
+
return [...sizes].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
983
|
+
}
|
|
984
|
+
function lineOption(item, titles) {
|
|
985
|
+
const o = item.variant?.options?.find((x) => titles.includes((x.option?.title ?? "").toLowerCase()));
|
|
986
|
+
const v = typeof o?.value === "string" ? o.value.trim() : "";
|
|
987
|
+
return v ? v : null;
|
|
988
|
+
}
|
|
989
|
+
function lineColour(item) {
|
|
990
|
+
const opt = lineOption(item, ["colour", "color"]);
|
|
991
|
+
if (opt) return opt.toLowerCase();
|
|
992
|
+
const vt = (item.variant_title ?? "").toString();
|
|
993
|
+
return vt ? variantColour({ id: "x", title: vt }) : null;
|
|
994
|
+
}
|
|
995
|
+
function lineSize(item) {
|
|
996
|
+
return lineOption(item, ["size"]) ?? sizeTokenOf(item.variant_title);
|
|
997
|
+
}
|
|
955
998
|
function orderStage(o) {
|
|
956
999
|
const status = (o.status ?? "").trim().toLowerCase();
|
|
957
1000
|
if (status === "canceled" || status === "refunded") return "reverted";
|
|
@@ -1286,14 +1329,6 @@ function regionLine(rr, style, source) {
|
|
|
1286
1329
|
const nudge = rr.home ? " \xB7 not you? \u2192 bt region" : source === "locale" ? " \xB7 guessed from your locale \u2014 not you? \u2192 bt region" : "";
|
|
1287
1330
|
return dim(style, `${GLYPH.gear} region: ${loc}${rr.region.currency_code}${nudge}`);
|
|
1288
1331
|
}
|
|
1289
|
-
var SIZE_ORDER = ["xxs", "xs", "s", "m", "l", "xl", "2xl", "xxl", "3xl", "xxxl", "4xl", "5xl"];
|
|
1290
|
-
function sortSizes(sizes) {
|
|
1291
|
-
const rank = (s) => {
|
|
1292
|
-
const i = SIZE_ORDER.indexOf(s.trim().toLowerCase());
|
|
1293
|
-
return i === -1 ? SIZE_ORDER.length : i;
|
|
1294
|
-
};
|
|
1295
|
-
return [...sizes].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
1296
|
-
}
|
|
1297
1332
|
function brandOf(args) {
|
|
1298
1333
|
return args?.brand ?? PLAIN;
|
|
1299
1334
|
}
|
|
@@ -1318,21 +1353,6 @@ function displayName(p) {
|
|
|
1318
1353
|
const title = (p.title ?? "").toString().trim() || handle;
|
|
1319
1354
|
return { title, handle };
|
|
1320
1355
|
}
|
|
1321
|
-
function lineOption(item, titles) {
|
|
1322
|
-
const o = item.variant?.options?.find((x) => titles.includes((x.option?.title ?? "").toLowerCase()));
|
|
1323
|
-
const v = typeof o?.value === "string" ? o.value.trim() : "";
|
|
1324
|
-
return v ? v : null;
|
|
1325
|
-
}
|
|
1326
|
-
function lineColour(item) {
|
|
1327
|
-
const opt = lineOption(item, ["colour", "color"]);
|
|
1328
|
-
if (opt) return opt.toLowerCase();
|
|
1329
|
-
const vt = (item.variant_title ?? "").toString();
|
|
1330
|
-
if (vt) {
|
|
1331
|
-
const c = variantColour({ id: "x", title: vt });
|
|
1332
|
-
if (c) return c;
|
|
1333
|
-
}
|
|
1334
|
-
return null;
|
|
1335
|
-
}
|
|
1336
1356
|
function lineFit(item) {
|
|
1337
1357
|
const opt = parseFit(lineOption(item, ["fit"]));
|
|
1338
1358
|
if (opt) return opt;
|
|
@@ -1353,7 +1373,8 @@ function formatCartLine(item, index, currency, brand) {
|
|
|
1353
1373
|
const idTag = item.id ? dimTag(style, ` (#${shortItemId(item.id)})`) : "";
|
|
1354
1374
|
const colour = lineColour(item);
|
|
1355
1375
|
const fit = lineFit(item);
|
|
1356
|
-
const
|
|
1376
|
+
const size = lineSize(item);
|
|
1377
|
+
const tags = [colour ? colourLabel(colour).toLowerCase() : null, fit ? fitLabel(fit).toLowerCase() : null, size].filter(Boolean).join(", ");
|
|
1357
1378
|
const colourTag = tags ? ` \xB7 ${tags}` : "";
|
|
1358
1379
|
const price = item.total != null ? ` ${accentPrice(style, fmtPrice(item.total, currency))}` : "";
|
|
1359
1380
|
return ` ${ref} ${title}${colourTag}${idTag} \xB7 qty ${item.quantity}${price}`;
|
|
@@ -1619,7 +1640,7 @@ async function runView(client, config, args) {
|
|
|
1619
1640
|
lines.push(` colours ${colours.map((c) => `${colourSwatch(c)} ${colourLabel(c)}`).join(" \xB7 ")}`);
|
|
1620
1641
|
for (const c of colours) {
|
|
1621
1642
|
const sizes = sortSizes(sizesFor(product, fit, c, countryCode));
|
|
1622
|
-
lines.push(` ${colourSwatch(c)} ${colourLabel(c).padEnd(12)} ${sizes.join(" ")}`);
|
|
1643
|
+
lines.push(` ${colourSwatch(c)} ${colourLabel(c).padEnd(12)} ${sizes.join(" ").padEnd(22)} ${dim(style, `--colour ${c}`)}`);
|
|
1623
1644
|
}
|
|
1624
1645
|
for (const c of unavailable) {
|
|
1625
1646
|
lines.push(dim(style, ` ${colourSwatch(c)} ${colourLabel(c).padEnd(12)} not available where you ship (${countryCode ?? "your region"})`));
|
|
@@ -1653,6 +1674,13 @@ async function runView(client, config, args) {
|
|
|
1653
1674
|
if (blackNote) {
|
|
1654
1675
|
lines.push(blackNote);
|
|
1655
1676
|
}
|
|
1677
|
+
if (colours.length > 0) {
|
|
1678
|
+
const c0 = colours[0];
|
|
1679
|
+
const sz = sortSizes(sizesFor(product, fit, c0, countryCode));
|
|
1680
|
+
const pick = sz.find((s) => s.toUpperCase() === "M") ?? sz[0] ?? "M";
|
|
1681
|
+
const fitFlag = fit && fits.length > 1 ? ` --fit ${fit}` : "";
|
|
1682
|
+
lines.push(` stage one \u2192 bt stage ${handle}${fitFlag} --size ${pick} --colour ${c0} (colour is the one word shown on its row)`);
|
|
1683
|
+
}
|
|
1656
1684
|
if (colours.length > 1 && !args.colour) {
|
|
1657
1685
|
const others = colours.filter((c) => c !== colours[0]).join(", ");
|
|
1658
1686
|
lines.push(` see another colour \u2192 bt view ${handle} --colour ${others.split(",")[0]?.trim()}${fit && fits.length > 1 ? ` --fit ${fit}` : ""}`);
|
|
@@ -1702,6 +1730,10 @@ function registerView(program, makeClient, loadConfig) {
|
|
|
1702
1730
|
}
|
|
1703
1731
|
|
|
1704
1732
|
// src/commands/stage.ts
|
|
1733
|
+
function sizeChoice(sizes) {
|
|
1734
|
+
const sorted = sortSizes(sizes);
|
|
1735
|
+
return sorted.length === 1 ? sorted[0] : `<${sorted.join("|")}>`;
|
|
1736
|
+
}
|
|
1705
1737
|
async function runStage(client, config, args) {
|
|
1706
1738
|
const style = brandOf(args);
|
|
1707
1739
|
const rr = await client.resolveRegionResult(config.region);
|
|
@@ -1726,6 +1758,11 @@ async function runStage(client, config, args) {
|
|
|
1726
1758
|
cartId = void 0;
|
|
1727
1759
|
recovered = true;
|
|
1728
1760
|
}
|
|
1761
|
+
if (existing?.completed_at) {
|
|
1762
|
+
existing = null;
|
|
1763
|
+
cartId = void 0;
|
|
1764
|
+
recovered = true;
|
|
1765
|
+
}
|
|
1729
1766
|
if (existing) {
|
|
1730
1767
|
const to = await cartShipTo(existing, () => client.getRegions());
|
|
1731
1768
|
if (!to) {
|
|
@@ -1796,6 +1833,17 @@ async function runStage(client, config, args) {
|
|
|
1796
1833
|
fix: r.fits.length ? `${r.fits.length > 1 ? "ask which fit is wanted, then: " : ""}${r.fits.map((f) => `bt stage ${handleShown} --fit ${f}${size ? ` --size ${size}` : ""}`).join(" | ")}` : `bt stage ${handleShown}${size ? ` --size ${size}` : ""} (this design comes in one fit)`
|
|
1797
1834
|
});
|
|
1798
1835
|
}
|
|
1836
|
+
const fitFlag = r.fit && productFits(product).length > 1 ? ` --fit ${r.fit}` : "";
|
|
1837
|
+
const colourAsked = "colour" in r ? r.colour : null;
|
|
1838
|
+
const madeIn = coloursFor(product, productFits(product).length > 0 ? r.fit ?? null : null);
|
|
1839
|
+
if (colourAsked && (r.reason === "fit_unavailable" || r.reason === "not_found") && !madeIn.includes(colourAsked)) {
|
|
1840
|
+
return failResult({
|
|
1841
|
+
code: EXIT.DATAERR,
|
|
1842
|
+
what: `${handleShown} isn't made in ${colourAsked}. colours: ${madeIn.join(" \xB7 ")}.`,
|
|
1843
|
+
money: "nothing staged, nothing charged.",
|
|
1844
|
+
fix: madeIn.length ? `bt stage ${handleShown}${fitFlag}${size ? ` --size ${size}` : ""} --colour ${madeIn[0]} (one word, as listed)` : `bt view ${handleShown}`
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1799
1847
|
if (r.reason === "fit_unavailable") {
|
|
1800
1848
|
return failResult({
|
|
1801
1849
|
code: EXIT.DATAERR,
|
|
@@ -1809,20 +1857,42 @@ async function runStage(client, config, args) {
|
|
|
1809
1857
|
code: EXIT.DATAERR,
|
|
1810
1858
|
what: `${r.size} is not made in ${r.fit} where you ship (${countryCode ?? "your region"}).`,
|
|
1811
1859
|
money: "nothing staged, nothing charged.",
|
|
1812
|
-
fix: r.sizes.length ?
|
|
1860
|
+
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)`
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
const inColour = r.colour ? ` in ${r.colour}` : "";
|
|
1864
|
+
if (size && r.sizes.length) {
|
|
1865
|
+
return failResult({
|
|
1866
|
+
code: EXIT.DATAERR,
|
|
1867
|
+
what: `${handleShown} doesn't come in size ${size}${inColour}. sizes: ${r.sizes.join(" ")}.`,
|
|
1868
|
+
money: "nothing staged, nothing charged.",
|
|
1869
|
+
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}` : ""}`
|
|
1813
1870
|
});
|
|
1814
1871
|
}
|
|
1815
|
-
const have = r.colours.length ? ` available colours: ${r.colours.map((c) => colourLabel(c)).join(", ")}.` : "";
|
|
1816
|
-
const haveSizes = r.sizes.length ? ` sizes: ${r.sizes.join(" ")}.` : "";
|
|
1817
1872
|
return failResult({
|
|
1818
1873
|
code: EXIT.DATAERR,
|
|
1819
|
-
what: `no variant for ${
|
|
1820
|
-
|
|
1874
|
+
what: `no variant for ${handleShown}${fitFlag}${size ? ` --size ${size}` : ""}${r.colour ? ` --colour ${r.colour}` : ""}.`,
|
|
1875
|
+
money: "nothing staged, nothing charged.",
|
|
1876
|
+
fix: `bt view ${handleShown}${fitFlag} (to see the sizes + colours you can buy)`
|
|
1821
1877
|
});
|
|
1822
1878
|
}
|
|
1823
1879
|
const variant = r.variant;
|
|
1824
1880
|
const colour = r.colour ?? void 0;
|
|
1825
1881
|
const fit = r.fit;
|
|
1882
|
+
if (!size) {
|
|
1883
|
+
const sizes = sortSizes(
|
|
1884
|
+
r.colour ? sizesFor(product, r.fit, r.colour, countryCode) : [...new Set((product.variants ?? []).map((v) => sizeOfVariant(v)).filter((s) => !!s))]
|
|
1885
|
+
);
|
|
1886
|
+
if (sizes.length > 1) {
|
|
1887
|
+
const fitFlag = r.fit && productFits(product).length > 1 ? ` --fit ${r.fit}` : "";
|
|
1888
|
+
return failResult({
|
|
1889
|
+
code: EXIT.DATAERR,
|
|
1890
|
+
what: `${handleShown} needs a size. sizes${r.colour ? ` in ${r.colour}` : ""}: ${sizes.join(" ")}.`,
|
|
1891
|
+
money: "nothing staged, nothing charged.",
|
|
1892
|
+
fix: `ask which size is wanted, don't choose for them, then: bt stage ${handleShown}${fitFlag} --size ${sizeChoice(sizes)}${r.colour ? ` --colour ${r.colour}` : ""}`
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1826
1896
|
const refTags = [colour ? colourLabel(colour).toLowerCase() : null, fit].filter(Boolean).join(", ");
|
|
1827
1897
|
const colourSuffix = refTags ? ` (${refTags})` : "";
|
|
1828
1898
|
if (args.dryRun) {
|
|
@@ -1893,9 +1963,19 @@ async function runStage(client, config, args) {
|
|
|
1893
1963
|
json: args.json ? { cart_id: cartId, recovered, fit: fit ?? null, colour: colour ?? null, items: cart.items ?? [], total: cart.total ?? null } : void 0
|
|
1894
1964
|
};
|
|
1895
1965
|
}
|
|
1966
|
+
function oneDesignPerCommand() {
|
|
1967
|
+
return failResult({
|
|
1968
|
+
code: EXIT.DATAERR,
|
|
1969
|
+
what: 'one design per command. a two-word colour is written --colour coal, or in quotes: --colour "faded coal"'
|
|
1970
|
+
});
|
|
1971
|
+
}
|
|
1896
1972
|
function registerStage(program, makeClient, loadConfig) {
|
|
1897
|
-
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 (
|
|
1973
|
+
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) => {
|
|
1898
1974
|
const { json, brand } = resolveOutput(program);
|
|
1975
|
+
if (cmd.args.length > 1) {
|
|
1976
|
+
printResult(oneDesignPerCommand(), json);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1899
1979
|
const res = await runStage(makeClient(), loadConfig(), {
|
|
1900
1980
|
handle,
|
|
1901
1981
|
size: opts.size,
|
|
@@ -1906,7 +1986,7 @@ function registerStage(program, makeClient, loadConfig) {
|
|
|
1906
1986
|
brand
|
|
1907
1987
|
});
|
|
1908
1988
|
if (res.save) {
|
|
1909
|
-
const { saveConfig } = await import("./config-
|
|
1989
|
+
const { saveConfig } = await import("./config-PWI2G2O5.js");
|
|
1910
1990
|
saveConfig(res.save);
|
|
1911
1991
|
}
|
|
1912
1992
|
printResult(res, json);
|
|
@@ -1929,6 +2009,7 @@ export {
|
|
|
1929
2009
|
labelVariantTitle,
|
|
1930
2010
|
productColours,
|
|
1931
2011
|
findVariant,
|
|
2012
|
+
lineColour,
|
|
1932
2013
|
orderStage,
|
|
1933
2014
|
productLore,
|
|
1934
2015
|
isCartTaxInclusive,
|
|
@@ -1958,7 +2039,6 @@ export {
|
|
|
1958
2039
|
priceLine,
|
|
1959
2040
|
stageMarker,
|
|
1960
2041
|
displayName,
|
|
1961
|
-
lineColour,
|
|
1962
2042
|
formatCartLine,
|
|
1963
2043
|
resolveLineItemRef,
|
|
1964
2044
|
resolveProduct,
|
|
@@ -1969,6 +2049,7 @@ export {
|
|
|
1969
2049
|
runView,
|
|
1970
2050
|
registerView,
|
|
1971
2051
|
runStage,
|
|
2052
|
+
oneDesignPerCommand,
|
|
1972
2053
|
registerStage
|
|
1973
2054
|
};
|
|
1974
|
-
//# sourceMappingURL=chunk-
|
|
2055
|
+
//# sourceMappingURL=chunk-YNBNKCCN.js.map
|