@binaryandthread/cli 0.3.2 → 0.4.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/README.md +4 -4
- package/dist/bin.js +38 -25
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-RGFMXWD5.js → chunk-T7MFWTB7.js} +2 -1
- package/dist/chunk-T7MFWTB7.js.map +1 -0
- package/dist/{chunk-BFMH7VCE.js → chunk-X3RWS3AG.js} +298 -73
- package/dist/chunk-X3RWS3AG.js.map +1 -0
- package/dist/{config-WV4DTQXU.js → config-L4X7G563.js} +2 -2
- package/dist/{interactive-BOS6VMRJ.js → interactive-VBATIHRD.js} +29 -11
- package/dist/interactive-VBATIHRD.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-BFMH7VCE.js.map +0 -1
- package/dist/chunk-RGFMXWD5.js.map +0 -1
- package/dist/interactive-BOS6VMRJ.js.map +0 -1
- /package/dist/{config-WV4DTQXU.js.map → config-L4X7G563.js.map} +0 -0
|
@@ -10,6 +10,7 @@ var ALLOWED_KEYS = [
|
|
|
10
10
|
"region",
|
|
11
11
|
"defaultSize",
|
|
12
12
|
"defaultColour",
|
|
13
|
+
"defaultFit",
|
|
13
14
|
"defaultBranch"
|
|
14
15
|
];
|
|
15
16
|
function configDir() {
|
|
@@ -63,4 +64,4 @@ export {
|
|
|
63
64
|
saveConfig,
|
|
64
65
|
forgetConfig
|
|
65
66
|
};
|
|
66
|
-
//# sourceMappingURL=chunk-
|
|
67
|
+
//# sourceMappingURL=chunk-T7MFWTB7.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.\ntype PersistedKey = Exclude<keyof BtConfig, \"region_source\">\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/** Merge-and-save the given fields. Strips any non-allowlisted key (G15). Atomic (F-B1). */\nexport function saveConfig(patch: Partial<BtConfig>): BtConfig {\n const merged = { ...loadConfig(), ...sanitize(patch as Record<string, unknown>) }\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;AAsBtB,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;AAGO,SAAS,WAAW,OAAoC;AAC7D,QAAM,SAAS,EAAE,GAAG,WAAW,GAAG,GAAG,SAAS,KAAgC,EAAE;AAChF,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":[]}
|
|
@@ -545,6 +545,116 @@ function isAllowedImageHost(url) {
|
|
|
545
545
|
(allowed) => host === allowed || host.endsWith("." + allowed)
|
|
546
546
|
);
|
|
547
547
|
}
|
|
548
|
+
var FITS = ["regular", "oversize"];
|
|
549
|
+
function parseFit(v) {
|
|
550
|
+
const s = typeof v === "string" ? v.trim().toLowerCase() : "";
|
|
551
|
+
return s === "regular" || s === "oversize" ? s : null;
|
|
552
|
+
}
|
|
553
|
+
function fitLabel(f) {
|
|
554
|
+
return f === "oversize" ? "Oversize" : "Regular";
|
|
555
|
+
}
|
|
556
|
+
function variantFit(v) {
|
|
557
|
+
const opt = (v.options ?? []).find((o) => (o.option?.title ?? "").toLowerCase() === "fit");
|
|
558
|
+
return opt ? parseFit(opt.value) : null;
|
|
559
|
+
}
|
|
560
|
+
function productFits(p) {
|
|
561
|
+
const seen = /* @__PURE__ */ new Set();
|
|
562
|
+
for (const v of p.variants ?? []) {
|
|
563
|
+
const f = variantFit(v);
|
|
564
|
+
if (f) seen.add(f);
|
|
565
|
+
}
|
|
566
|
+
return FITS.filter((f) => seen.has(f));
|
|
567
|
+
}
|
|
568
|
+
var BLANK_COLOURS = {
|
|
569
|
+
"gildan-5000": ["black", "white"],
|
|
570
|
+
"sols-03996": ["black", "white"],
|
|
571
|
+
"shaka-shmhss": ["black", "white", "khaki"]
|
|
572
|
+
};
|
|
573
|
+
function blankFor(fit, countryCode) {
|
|
574
|
+
if (fit === "regular") return "gildan-5000";
|
|
575
|
+
return (countryCode ?? "").trim().toLowerCase() === "us" ? "shaka-shmhss" : "sols-03996";
|
|
576
|
+
}
|
|
577
|
+
function colourBuyable(fit, colour, countryCode) {
|
|
578
|
+
return BLANK_COLOURS[blankFor(fit, countryCode)].includes(colour.trim().toLowerCase());
|
|
579
|
+
}
|
|
580
|
+
function variantsOfFit(p, fit) {
|
|
581
|
+
const all = p.variants ?? [];
|
|
582
|
+
return fit === null ? all : all.filter((v) => variantFit(v) === fit);
|
|
583
|
+
}
|
|
584
|
+
function coloursFor(p, fit, countryCode) {
|
|
585
|
+
const out = [];
|
|
586
|
+
for (const v of variantsOfFit(p, fit)) {
|
|
587
|
+
const c = variantColour(v);
|
|
588
|
+
if (!c || out.includes(c)) continue;
|
|
589
|
+
if (fit && countryCode !== void 0 && !colourBuyable(fit, c, countryCode)) continue;
|
|
590
|
+
out.push(c);
|
|
591
|
+
}
|
|
592
|
+
return out;
|
|
593
|
+
}
|
|
594
|
+
function sizesFor(p, fit, colour) {
|
|
595
|
+
const target = colour.trim().toLowerCase();
|
|
596
|
+
const out = [];
|
|
597
|
+
for (const v of variantsOfFit(p, fit)) {
|
|
598
|
+
if (variantColour(v) !== target) continue;
|
|
599
|
+
const s = sizeOfVariant(v);
|
|
600
|
+
if (s && !out.includes(s)) out.push(s);
|
|
601
|
+
}
|
|
602
|
+
return out;
|
|
603
|
+
}
|
|
604
|
+
function cheapest(vs) {
|
|
605
|
+
const priced = vs.filter((v2) => typeof v2.calculated_price?.calculated_amount === "number").sort(
|
|
606
|
+
(a, b) => a.calculated_price.calculated_amount - b.calculated_price.calculated_amount
|
|
607
|
+
);
|
|
608
|
+
const v = priced[0];
|
|
609
|
+
return {
|
|
610
|
+
amount: v?.calculated_price?.calculated_amount ?? null,
|
|
611
|
+
currency: v?.calculated_price?.currency_code ?? null,
|
|
612
|
+
taxInclusive: v?.calculated_price?.is_calculated_price_tax_inclusive ?? null
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
function priceFrom(p) {
|
|
616
|
+
const fits = productFits(p);
|
|
617
|
+
const base = cheapest(p.variants ?? []);
|
|
618
|
+
const amounts = new Set(fits.map((f) => cheapest(variantsOfFit(p, f)).amount));
|
|
619
|
+
return { ...base, from: fits.length > 1 && amounts.size > 1 };
|
|
620
|
+
}
|
|
621
|
+
function priceFor(p, fit) {
|
|
622
|
+
return cheapest(variantsOfFit(p, fit));
|
|
623
|
+
}
|
|
624
|
+
function resolveVariant(p, opts) {
|
|
625
|
+
const fits = productFits(p);
|
|
626
|
+
const requested = parseFit(opts.fit);
|
|
627
|
+
const askedFor = typeof opts.fit === "string" ? opts.fit.trim() : "";
|
|
628
|
+
let fit = null;
|
|
629
|
+
if (fits.length > 0) {
|
|
630
|
+
if (requested && fits.includes(requested)) fit = requested;
|
|
631
|
+
else if (askedFor) return { ok: false, reason: "fit_not_offered", requested: askedFor, fits };
|
|
632
|
+
else if (fits.length === 1) fit = fits[0];
|
|
633
|
+
else return { ok: false, reason: "ambiguous_fit", fits: fits.map((f) => ({ fit: f, ...priceFor(p, f) })) };
|
|
634
|
+
}
|
|
635
|
+
const pool = variantsOfFit(p, fit);
|
|
636
|
+
const colours = coloursFor(p, fit, opts.countryCode);
|
|
637
|
+
const colour = (opts.colour ?? "").trim().toLowerCase() || (colours[0] ?? null);
|
|
638
|
+
if (fit && colour && opts.countryCode !== void 0 && !colourBuyable(fit, colour, opts.countryCode)) {
|
|
639
|
+
return { ok: false, reason: "fit_unavailable", fit, colour };
|
|
640
|
+
}
|
|
641
|
+
const size = (opts.size ?? "").trim() || null;
|
|
642
|
+
const sizeLc = size?.toLowerCase();
|
|
643
|
+
const v = pool.find((x) => {
|
|
644
|
+
const colourOk = !colour || variantColour(x) === colour;
|
|
645
|
+
const sizeOk = !sizeLc || (sizeOfVariant(x) ?? "").toLowerCase() === sizeLc || (x.title ?? "").toLowerCase() === sizeLc;
|
|
646
|
+
return colourOk && sizeOk;
|
|
647
|
+
});
|
|
648
|
+
if (!v) {
|
|
649
|
+
return { ok: false, reason: "not_found", fit, colour, size, colours, sizes: colour ? sizesFor(p, fit, colour) : [] };
|
|
650
|
+
}
|
|
651
|
+
return { ok: true, variant: v, fit, colour: variantColour(v), size: sizeOfVariant(v) };
|
|
652
|
+
}
|
|
653
|
+
var SIZE_TOKEN = /^(xxs|xs|s|m|l|xl|xxl|2xl|3xl|xxxl|4xl|5xl)$/i;
|
|
654
|
+
var FIT_TOKEN = /^(regular|oversize)$/i;
|
|
655
|
+
function titleSegments(title) {
|
|
656
|
+
return (title ?? "").split("/").map((s) => s.trim()).filter(Boolean);
|
|
657
|
+
}
|
|
548
658
|
function displayColour(colour) {
|
|
549
659
|
const c = colour.trim();
|
|
550
660
|
if (!c) return c;
|
|
@@ -557,11 +667,10 @@ function variantColour(v) {
|
|
|
557
667
|
if (opt && typeof opt.value === "string" && opt.value.trim()) {
|
|
558
668
|
return opt.value.trim().toLowerCase();
|
|
559
669
|
}
|
|
560
|
-
const
|
|
561
|
-
if (
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
if (tail) return tail.toLowerCase();
|
|
670
|
+
const segs = titleSegments(v.title);
|
|
671
|
+
if (segs.length >= 2) {
|
|
672
|
+
const seg = segs.find((s) => !SIZE_TOKEN.test(s) && !FIT_TOKEN.test(s));
|
|
673
|
+
if (seg) return seg.toLowerCase();
|
|
565
674
|
}
|
|
566
675
|
return null;
|
|
567
676
|
}
|
|
@@ -575,32 +684,28 @@ function productColours(p) {
|
|
|
575
684
|
}
|
|
576
685
|
return seen;
|
|
577
686
|
}
|
|
578
|
-
function sizesForColour(p, colour) {
|
|
579
|
-
const target = colour.trim().toLowerCase();
|
|
580
|
-
const sizes = [];
|
|
581
|
-
for (const v of p.variants ?? []) {
|
|
582
|
-
if (variantColour(v) !== target) continue;
|
|
583
|
-
const size = sizeOfVariant(v);
|
|
584
|
-
if (size && !sizes.includes(size)) {
|
|
585
|
-
sizes.push(size);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return sizes;
|
|
589
|
-
}
|
|
590
687
|
function sizeOfVariant(v) {
|
|
591
688
|
const opt = (v.options ?? []).find((o) => (o.option?.title ?? "").toLowerCase() === "size");
|
|
592
689
|
if (opt && typeof opt.value === "string" && opt.value.trim()) {
|
|
593
690
|
return opt.value.trim();
|
|
594
691
|
}
|
|
595
|
-
const
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
600
|
-
return title || null;
|
|
692
|
+
const segs = titleSegments(v.title);
|
|
693
|
+
const token = segs.find((s) => SIZE_TOKEN.test(s));
|
|
694
|
+
if (token) return token;
|
|
695
|
+
return segs.length === 1 ? segs[0] : segs[0] ?? null;
|
|
601
696
|
}
|
|
602
697
|
function findVariant(p, opts = {}) {
|
|
603
|
-
const
|
|
698
|
+
const fits = productFits(p);
|
|
699
|
+
let fit = null;
|
|
700
|
+
if (fits.length > 0) {
|
|
701
|
+
const wanted = parseFit(opts.fit);
|
|
702
|
+
const askedFor = typeof opts.fit === "string" ? opts.fit.trim() : "";
|
|
703
|
+
if (wanted && fits.includes(wanted)) fit = wanted;
|
|
704
|
+
else if (askedFor) return void 0;
|
|
705
|
+
else if (fits.length === 1) fit = fits[0];
|
|
706
|
+
else return void 0;
|
|
707
|
+
}
|
|
708
|
+
const variants = (p.variants ?? []).filter((v) => fit === null || variantFit(v) === fit);
|
|
604
709
|
const size = opts.size?.trim().toLowerCase();
|
|
605
710
|
const colour = opts.colour?.trim().toLowerCase();
|
|
606
711
|
const sizeMatches = (v) => {
|
|
@@ -660,6 +765,24 @@ function imageForColour(p, colour) {
|
|
|
660
765
|
}
|
|
661
766
|
return null;
|
|
662
767
|
}
|
|
768
|
+
function imageFor(p, sel) {
|
|
769
|
+
const colour = sel.colour.trim().toLowerCase();
|
|
770
|
+
if (!colour) return null;
|
|
771
|
+
const metas = (p.images ?? []).map((img) => ({
|
|
772
|
+
url: (img?.url ?? "").trim(),
|
|
773
|
+
scene: typeof img?.metadata?.["scene"] === "string" ? String(img.metadata["scene"]) : null,
|
|
774
|
+
fit: parseFit(img?.metadata?.["fit"]),
|
|
775
|
+
colour: typeof img?.metadata?.["colour"] === "string" ? String(img.metadata["colour"]).toLowerCase() : null
|
|
776
|
+
}));
|
|
777
|
+
const tagged = metas.filter((m) => m.url && m.colour === colour && m.scene === PREFERRED_SCENE);
|
|
778
|
+
const exact = tagged.find((m) => sel.fit !== null && m.fit === sel.fit);
|
|
779
|
+
if (exact) return exact.url;
|
|
780
|
+
const neutral = tagged.find((m) => m.fit === null);
|
|
781
|
+
if (neutral) return neutral.url;
|
|
782
|
+
const otherFitOnly = tagged.length > 0 && sel.fit !== null;
|
|
783
|
+
if (otherFitOnly) return null;
|
|
784
|
+
return imageForColour(p, colour);
|
|
785
|
+
}
|
|
663
786
|
function firstImageUrl(images) {
|
|
664
787
|
for (const img of images ?? []) {
|
|
665
788
|
const u = (img?.url ?? "").trim();
|
|
@@ -673,15 +796,19 @@ function escapeRegex(s) {
|
|
|
673
796
|
function str(v, fallback) {
|
|
674
797
|
return typeof v === "string" && v.length > 0 ? v : fallback;
|
|
675
798
|
}
|
|
676
|
-
function productSpecs(p) {
|
|
799
|
+
function productSpecs(p, fit) {
|
|
677
800
|
const md = p.metadata ?? {};
|
|
801
|
+
const byFit = md.specs_by_fit ?? null;
|
|
802
|
+
const fitKey = fit ?? (byFit && Object.keys(byFit).length === 1 ? Object.keys(byFit)[0] : void 0);
|
|
803
|
+
const src = fitKey && byFit?.[fitKey] || md;
|
|
804
|
+
const pick = (k, fallback) => str(src[k] ?? md[k], fallback);
|
|
678
805
|
return [
|
|
679
|
-
{ key: "material", value:
|
|
680
|
-
{ key: "weight", value:
|
|
681
|
-
{ key: "fit", value:
|
|
682
|
-
{ key: "print", value:
|
|
683
|
-
{ key: "delivery", value:
|
|
684
|
-
{ key: "care", value:
|
|
806
|
+
{ key: "material", value: pick("material", "100% cotton") },
|
|
807
|
+
{ key: "weight", value: pick("weight", "180 g/m\xB2") },
|
|
808
|
+
{ key: "fit", value: pick("fit", "Unisex heavyweight crewneck") },
|
|
809
|
+
{ key: "print", value: pick("print", "Front print") },
|
|
810
|
+
{ key: "delivery", value: pick("delivery", "Ships in 2\u20135 business days + transit") },
|
|
811
|
+
{ key: "care", value: pick("care", "Cold wash, line dry") }
|
|
685
812
|
];
|
|
686
813
|
}
|
|
687
814
|
function productLore(p) {
|
|
@@ -891,6 +1018,7 @@ function colourSwatch(colour) {
|
|
|
891
1018
|
const c = colour.trim().toLowerCase();
|
|
892
1019
|
if (c === "white") return "\u2B1C";
|
|
893
1020
|
if (c === "black") return "\u2B1B";
|
|
1021
|
+
if (c === "khaki") return "\u{1F7EB}";
|
|
894
1022
|
return "\u25FC";
|
|
895
1023
|
}
|
|
896
1024
|
function colourMarker(colours) {
|
|
@@ -928,13 +1056,8 @@ function fmtPrice(amount, currency) {
|
|
|
928
1056
|
function taxSuffix(isTaxInclusive) {
|
|
929
1057
|
return isTaxInclusive ? " incl. VAT" : "";
|
|
930
1058
|
}
|
|
931
|
-
function
|
|
932
|
-
|
|
933
|
-
return {
|
|
934
|
-
amount: v?.calculated_price?.calculated_amount ?? null,
|
|
935
|
-
currency: v?.calculated_price?.currency_code ?? null,
|
|
936
|
-
taxInclusive: v?.calculated_price?.is_calculated_price_tax_inclusive ?? null
|
|
937
|
-
};
|
|
1059
|
+
function priceLine(p) {
|
|
1060
|
+
return priceFrom(p);
|
|
938
1061
|
}
|
|
939
1062
|
function stageMarker(channelStage) {
|
|
940
1063
|
return channelStage === "next" ? "\u25CB on next" : "\u25CF in stock";
|
|
@@ -944,7 +1067,14 @@ function displayName(p) {
|
|
|
944
1067
|
const title = (p.title ?? "").toString().trim() || handle;
|
|
945
1068
|
return { title, handle };
|
|
946
1069
|
}
|
|
1070
|
+
function lineOption(item, titles) {
|
|
1071
|
+
const o = item.variant?.options?.find((x) => titles.includes((x.option?.title ?? "").toLowerCase()));
|
|
1072
|
+
const v = typeof o?.value === "string" ? o.value.trim() : "";
|
|
1073
|
+
return v ? v : null;
|
|
1074
|
+
}
|
|
947
1075
|
function lineColour(item) {
|
|
1076
|
+
const opt = lineOption(item, ["colour", "color"]);
|
|
1077
|
+
if (opt) return opt.toLowerCase();
|
|
948
1078
|
const vt = (item.variant_title ?? "").toString();
|
|
949
1079
|
if (vt) {
|
|
950
1080
|
const c = variantColour({ id: "x", title: vt });
|
|
@@ -952,6 +1082,15 @@ function lineColour(item) {
|
|
|
952
1082
|
}
|
|
953
1083
|
return null;
|
|
954
1084
|
}
|
|
1085
|
+
function lineFit(item) {
|
|
1086
|
+
const opt = parseFit(lineOption(item, ["fit"]));
|
|
1087
|
+
if (opt) return opt;
|
|
1088
|
+
for (const seg of (item.variant_title ?? "").toString().split("/")) {
|
|
1089
|
+
const f = parseFit(seg);
|
|
1090
|
+
if (f) return f;
|
|
1091
|
+
}
|
|
1092
|
+
return null;
|
|
1093
|
+
}
|
|
955
1094
|
function shortItemId(id) {
|
|
956
1095
|
const s = (id ?? "").toString();
|
|
957
1096
|
return s.length <= 6 ? s : s.slice(-6);
|
|
@@ -962,7 +1101,9 @@ function formatCartLine(item, index, currency, brand) {
|
|
|
962
1101
|
const title = (item.title ?? item.product_title ?? "item").toString();
|
|
963
1102
|
const idTag = item.id ? dimTag(style, ` (#${shortItemId(item.id)})`) : "";
|
|
964
1103
|
const colour = lineColour(item);
|
|
965
|
-
const
|
|
1104
|
+
const fit = lineFit(item);
|
|
1105
|
+
const tags = [colour, fit ? fitLabel(fit).toLowerCase() : null].filter(Boolean).join(", ");
|
|
1106
|
+
const colourTag = tags ? ` \xB7 ${tags}` : "";
|
|
966
1107
|
const price = item.total != null ? ` ${accentPrice(style, fmtPrice(item.total, currency))}` : "";
|
|
967
1108
|
return ` ${ref} ${title}${colourTag}${idTag} \xB7 qty ${item.quantity}${price}`;
|
|
968
1109
|
}
|
|
@@ -1032,6 +1173,17 @@ async function resolveProduct(client, region, selector) {
|
|
|
1032
1173
|
if (exact) {
|
|
1033
1174
|
return { product: exact };
|
|
1034
1175
|
}
|
|
1176
|
+
const m = needle.match(/^(.+)-oversize$/i);
|
|
1177
|
+
if (m && m[1]) {
|
|
1178
|
+
const base = await client.getProduct(m[1], { region });
|
|
1179
|
+
if (base) {
|
|
1180
|
+
return {
|
|
1181
|
+
product: base,
|
|
1182
|
+
impliedFit: "oversize",
|
|
1183
|
+
aliasNote: `"${needle}" is a retired handle \xB7 resolved to ${base.handle ?? m[1]} --fit oversize`
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1035
1187
|
}
|
|
1036
1188
|
const products = await client.listProducts({ region });
|
|
1037
1189
|
const r = resolveProductSelector(products, needle);
|
|
@@ -1172,47 +1324,70 @@ async function runView(client, config, args) {
|
|
|
1172
1324
|
});
|
|
1173
1325
|
}
|
|
1174
1326
|
const { title, handle } = displayName(product);
|
|
1175
|
-
const
|
|
1327
|
+
const fits = productFits(product);
|
|
1328
|
+
const requestedFit = parseFit(args.fit ?? picked.impliedFit);
|
|
1329
|
+
if (args.fit && (!requestedFit || !fits.includes(requestedFit))) {
|
|
1330
|
+
return failResult({
|
|
1331
|
+
code: EXIT.DATAERR,
|
|
1332
|
+
what: fits.length ? `no fit called "${args.fit}" for ${handle}.` : `${handle} comes in one fit; --fit does not apply.`,
|
|
1333
|
+
fix: fits.length ? `bt view ${handle} --fit ${fits.join(" | ")}` : `bt view ${handle}`
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
const fit = fits.length ? requestedFit ?? (fits.includes("regular") ? "regular" : fits[0]) : null;
|
|
1337
|
+
const countryCode = (rr.requested ?? region.countries?.[0]?.iso_2 ?? "").toLowerCase() || null;
|
|
1338
|
+
const colours = coloursFor(product, fit, countryCode);
|
|
1339
|
+
const unavailable = coloursFor(product, fit).filter((c) => !colours.includes(c));
|
|
1176
1340
|
const lines = [];
|
|
1177
1341
|
if (picked.fuzzy && handle !== args.handle.trim()) {
|
|
1178
1342
|
lines.push(working(`matched "${handle}" from "${args.handle}"`));
|
|
1179
1343
|
}
|
|
1344
|
+
if (picked.aliasNote) {
|
|
1345
|
+
lines.push(working(picked.aliasNote));
|
|
1346
|
+
}
|
|
1180
1347
|
lines.push(header(style, title));
|
|
1181
1348
|
lines.push(` ${dim(style, handle)}`);
|
|
1182
1349
|
if (product.description) {
|
|
1183
1350
|
lines.push(empty(` ${product.description}`));
|
|
1184
1351
|
}
|
|
1185
1352
|
const fabric = product.metadata?.["fabric"] ?? null;
|
|
1186
|
-
|
|
1353
|
+
if (fit && fits.length > 1) {
|
|
1354
|
+
const other = fits.find((f) => f !== fit);
|
|
1355
|
+
const op = priceFor(product, other);
|
|
1356
|
+
lines.push(` fits ${fits.map((f) => fitLabel(f)).join(" \xB7 ")} \u2192 viewing ${fitLabel(fit)} \xB7 the other \u2192 bt view ${handle} --fit ${other} (${fmtPrice(op.amount, op.currency)})`);
|
|
1357
|
+
}
|
|
1358
|
+
const specs = productSpecs(product, fit);
|
|
1187
1359
|
for (const s of specs) {
|
|
1188
1360
|
lines.push(` ${s.key.padEnd(9)}${s.value}`);
|
|
1189
1361
|
}
|
|
1190
|
-
if (colours.length > 0) {
|
|
1362
|
+
if (colours.length > 0 || unavailable.length > 0) {
|
|
1191
1363
|
lines.push(` colours ${colours.map((c) => `${colourSwatch(c)} ${c}`).join(" \xB7 ")}`);
|
|
1192
1364
|
for (const c of colours) {
|
|
1193
|
-
const sizes = sortSizes(
|
|
1365
|
+
const sizes = sortSizes(sizesFor(product, fit, c));
|
|
1194
1366
|
lines.push(` ${colourSwatch(c)} ${c.padEnd(6)} ${sizes.join(" ")}`);
|
|
1195
1367
|
}
|
|
1368
|
+
for (const c of unavailable) {
|
|
1369
|
+
lines.push(dim(style, ` ${colourSwatch(c)} ${c.padEnd(6)} not available where you ship (${countryCode ?? "your region"})`));
|
|
1370
|
+
}
|
|
1196
1371
|
} else {
|
|
1197
1372
|
const sizes = sortSizes((product.variants ?? []).map((v) => v.title ?? "?"));
|
|
1198
1373
|
lines.push(` sizes ${sizes.join(" ")}`);
|
|
1199
1374
|
}
|
|
1200
|
-
const
|
|
1201
|
-
if (
|
|
1375
|
+
const pr = fit ? { ...priceFor(product, fit), from: false } : priceLine(product);
|
|
1376
|
+
if (pr.amount != null) {
|
|
1202
1377
|
lines.push(
|
|
1203
|
-
` price ${cyan(style, fmtPrice(
|
|
1378
|
+
` price ${cyan(style, `${pr.from ? "from " : ""}${fmtPrice(pr.amount, pr.currency)}`)}${taxSuffix(pr.taxInclusive)}`
|
|
1204
1379
|
);
|
|
1205
1380
|
lines.push(` ${regionLine(rr, style, config.region_source)}`);
|
|
1206
1381
|
}
|
|
1207
1382
|
let imageUrl = product.thumbnail ?? null;
|
|
1208
1383
|
let blackNote = null;
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
const resolved = imageForColour(product, wanted);
|
|
1384
|
+
const wanted = args.colour?.trim().toLowerCase() || (fit ? colours[0] : void 0);
|
|
1385
|
+
if (wanted) {
|
|
1386
|
+
const resolved = imageFor(product, { fit, colour: wanted }) ?? (fit === null ? imageForColour(product, wanted) : null);
|
|
1212
1387
|
if (resolved) {
|
|
1213
1388
|
imageUrl = resolved;
|
|
1214
|
-
} else {
|
|
1215
|
-
blackNote = ` ${displayColour(wanted)} available \u2014 showing the default mockup (no ${wanted} render on file).`;
|
|
1389
|
+
} else if (args.colour) {
|
|
1390
|
+
blackNote = unavailable.includes(wanted) ? ` ${displayColour(wanted)} isn't available where you ship \u2014 showing the default mockup.` : ` ${displayColour(wanted)} available \u2014 showing the default mockup (no ${wanted} render on file).`;
|
|
1216
1391
|
}
|
|
1217
1392
|
}
|
|
1218
1393
|
if (imageUrl) {
|
|
@@ -1224,7 +1399,7 @@ async function runView(client, config, args) {
|
|
|
1224
1399
|
}
|
|
1225
1400
|
if (colours.length > 1 && !args.colour) {
|
|
1226
1401
|
const others = colours.filter((c) => c !== colours[0]).join(", ");
|
|
1227
|
-
lines.push(` see another colour \u2192 bt view ${handle} --colour ${others.split(",")[0]?.trim()}`);
|
|
1402
|
+
lines.push(` see another colour \u2192 bt view ${handle} --colour ${others.split(",")[0]?.trim()}${fit && fits.length > 1 ? ` --fit ${fit}` : ""}`);
|
|
1228
1403
|
}
|
|
1229
1404
|
if (productLore(product).text) {
|
|
1230
1405
|
lines.push(` the story \u2192 bt blame ${handle}`);
|
|
@@ -1237,11 +1412,17 @@ async function runView(client, config, args) {
|
|
|
1237
1412
|
title: product.title,
|
|
1238
1413
|
fabric,
|
|
1239
1414
|
specs,
|
|
1415
|
+
fits,
|
|
1416
|
+
fit,
|
|
1240
1417
|
colours,
|
|
1418
|
+
unavailable_colours: unavailable,
|
|
1241
1419
|
thumbnail: product.thumbnail,
|
|
1420
|
+
// CAT-31: `size` is the REAL size (never the variant title); fit/colour from the options.
|
|
1242
1421
|
variants: (product.variants ?? []).map((v) => ({
|
|
1243
1422
|
id: v.id,
|
|
1244
|
-
|
|
1423
|
+
fit: variantFit(v),
|
|
1424
|
+
colour: variantColour(v),
|
|
1425
|
+
size: sizeOfVariant(v),
|
|
1245
1426
|
price: v.calculated_price?.calculated_amount ?? null,
|
|
1246
1427
|
currency: v.calculated_price?.currency_code ?? null
|
|
1247
1428
|
}))
|
|
@@ -1249,12 +1430,13 @@ async function runView(client, config, args) {
|
|
|
1249
1430
|
};
|
|
1250
1431
|
}
|
|
1251
1432
|
function registerView(program, makeClient, loadConfig) {
|
|
1252
|
-
program.command("view").alias("show").argument("<handle>", "the design: a slug, a bt browse number (3), or a fuzzy hint (termnl)").option("--colour <colour>", "render a specific colour's mockup (e.g. black)").description("render a design: specs + image (always with the link as the floor).").action(async (handle, opts) => {
|
|
1433
|
+
program.command("view").alias("show").argument("<handle>", "the design: a slug, a bt browse number (3), or a fuzzy hint (termnl)").option("--colour <colour>", "render a specific colour's mockup (e.g. black)").option("--fit <fit>", "regular or oversize: which fit's sizes, price and image to show (default: regular)").description("render a design: specs + image (always with the link as the floor).").action(async (handle, opts) => {
|
|
1253
1434
|
const { json, brand } = resolveOutput(program);
|
|
1254
1435
|
const noColor = program.opts().color === false;
|
|
1255
1436
|
const res = await runView(makeClient(), loadConfig(), {
|
|
1256
1437
|
handle,
|
|
1257
1438
|
colour: opts.colour,
|
|
1439
|
+
fit: opts.fit,
|
|
1258
1440
|
json,
|
|
1259
1441
|
noColor,
|
|
1260
1442
|
brand
|
|
@@ -1279,38 +1461,74 @@ async function runStage(client, config, args) {
|
|
|
1279
1461
|
const resolved = await resolveProduct(client, region, args.handle);
|
|
1280
1462
|
if (!resolved.product) {
|
|
1281
1463
|
if (resolved.candidates && resolved.candidates.length > 1) {
|
|
1282
|
-
const
|
|
1283
|
-
|
|
1284
|
-
return
|
|
1464
|
+
const r2 = failResult({ code: EXIT.DATAERR, what: `"${args.handle}" matches ${resolved.candidates.length} designs \u2014 pick one:` });
|
|
1465
|
+
r2.lines.push(...resolved.candidates.map((p) => ` ${p.handle ?? p.id}`), " or be exact \u2192 bt stage <slug>");
|
|
1466
|
+
return r2;
|
|
1285
1467
|
}
|
|
1286
1468
|
return failResult({ code: EXIT.UNAVAILABLE, what: `no design here called "${args.handle}".`, fix: "bt browse" });
|
|
1287
1469
|
}
|
|
1288
1470
|
const product = resolved.product;
|
|
1289
1471
|
const matchNote = resolved.fuzzy && (product.handle ?? "") !== args.handle.trim() ? working(`matched "${product.handle}" from "${args.handle}"`) : null;
|
|
1290
1472
|
const size = args.size ?? config.defaultSize;
|
|
1291
|
-
const
|
|
1292
|
-
const
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1295
|
-
|
|
1473
|
+
const countryCode = (rr.requested ?? region.countries?.[0]?.iso_2 ?? "").toLowerCase() || null;
|
|
1474
|
+
const explicitFit = args.fit ?? resolved.impliedFit;
|
|
1475
|
+
const fitArg = explicitFit ?? (productFits(product).length > 1 ? config.defaultFit : void 0);
|
|
1476
|
+
const r = resolveVariant(product, { fit: fitArg, size, colour: args.colour ?? config.defaultColour, countryCode });
|
|
1477
|
+
const handleShown = product.handle ?? args.handle;
|
|
1478
|
+
if (!r.ok) {
|
|
1479
|
+
if (r.reason === "ambiguous_fit") {
|
|
1480
|
+
const fitList = r.fits.map((f) => `${f.fit} (${fmtPrice(f.amount, f.currency)})`).join(" \xB7 ");
|
|
1481
|
+
const res = failResult({
|
|
1482
|
+
code: EXIT.DATAERR,
|
|
1483
|
+
what: `${handleShown} comes in two fits: ${fitList}.`,
|
|
1484
|
+
money: "nothing staged, nothing charged.",
|
|
1485
|
+
fix: `bt stage ${handleShown} --fit ${r.fits[0].fit}${size ? ` --size ${size}` : ""}`
|
|
1486
|
+
});
|
|
1487
|
+
res.lines.push(` or set yours once \u2192 bt config set defaultFit ${r.fits[0].fit}`);
|
|
1488
|
+
return res;
|
|
1489
|
+
}
|
|
1490
|
+
if (r.reason === "fit_not_offered") {
|
|
1491
|
+
return failResult({
|
|
1492
|
+
code: EXIT.DATAERR,
|
|
1493
|
+
what: `${handleShown} isn't sold in "${r.requested}"${r.fits.length ? ` \u2014 it comes in ${r.fits.join(" \xB7 ")}` : ""}.`,
|
|
1494
|
+
money: "nothing staged, nothing charged.",
|
|
1495
|
+
fix: r.fits.length ? `bt stage ${handleShown} --fit ${r.fits[0]}${size ? ` --size ${size}` : ""}` : `bt stage ${handleShown}${size ? ` --size ${size}` : ""} (this design comes in one fit)`
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
if (r.reason === "fit_unavailable") {
|
|
1499
|
+
return failResult({
|
|
1500
|
+
code: EXIT.DATAERR,
|
|
1501
|
+
what: `${r.colour} is not available in ${r.fit} where you ship (${countryCode ?? "your region"}).`,
|
|
1502
|
+
money: "nothing staged, nothing charged.",
|
|
1503
|
+
fix: `bt view ${handleShown} --fit ${r.fit} (to see the colours you can buy)`
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
const have = r.colours.length ? ` available colours: ${r.colours.join(", ")}.` : "";
|
|
1507
|
+
const haveSizes = r.sizes.length ? ` sizes: ${r.sizes.join(" ")}.` : "";
|
|
1296
1508
|
return failResult({
|
|
1297
1509
|
code: EXIT.DATAERR,
|
|
1298
|
-
what: `no variant for ${args.handle}${size ? `#size=${size}` : ""}${colour ? ` (${colour})` : ""}.`,
|
|
1299
|
-
fix: `bt view ${args.handle} (to see available sizes + colours)${have}`
|
|
1510
|
+
what: `no variant for ${args.handle}${r.fit ? ` --fit ${r.fit}` : ""}${size ? `#size=${size}` : ""}${r.colour ? ` (${r.colour})` : ""}.`,
|
|
1511
|
+
fix: `bt view ${args.handle}${r.fit ? ` --fit ${r.fit}` : ""} (to see available sizes + colours)${have}${haveSizes}`
|
|
1300
1512
|
});
|
|
1301
1513
|
}
|
|
1514
|
+
const variant = r.variant;
|
|
1515
|
+
const colour = r.colour ?? void 0;
|
|
1516
|
+
const fit = r.fit;
|
|
1517
|
+
const refTags = [colour, fit].filter(Boolean).join(", ");
|
|
1518
|
+
const colourSuffix = refTags ? ` (${refTags})` : "";
|
|
1302
1519
|
if (args.dryRun) {
|
|
1303
|
-
const colourSuffix2 = colour ? ` (${colour})` : "";
|
|
1304
1520
|
return {
|
|
1305
1521
|
lines: [
|
|
1306
1522
|
...matchNote ? [matchNote] : [],
|
|
1307
|
-
|
|
1523
|
+
...resolved.aliasNote ? [warn(resolved.aliasNote)] : [],
|
|
1524
|
+
ok(`dry-run \u2014 would stage ${product.handle ?? args.handle}${size ? `#${size}` : ""}${colourSuffix} (nothing staged).`),
|
|
1308
1525
|
" run without --dry-run to acquire the inventory lock."
|
|
1309
1526
|
],
|
|
1310
1527
|
json: args.json ? {
|
|
1311
1528
|
dry_run: true,
|
|
1312
1529
|
would_stage: {
|
|
1313
1530
|
handle: product.handle ?? args.handle,
|
|
1531
|
+
fit: fit ?? null,
|
|
1314
1532
|
size: size ?? null,
|
|
1315
1533
|
colour: colour ?? null,
|
|
1316
1534
|
variant_id: variant.id
|
|
@@ -1322,6 +1540,9 @@ async function runStage(client, config, args) {
|
|
|
1322
1540
|
if (matchNote) {
|
|
1323
1541
|
lines.push(matchNote);
|
|
1324
1542
|
}
|
|
1543
|
+
if (resolved.aliasNote) {
|
|
1544
|
+
lines.push(warn(resolved.aliasNote));
|
|
1545
|
+
}
|
|
1325
1546
|
let cartId = config.cart_id;
|
|
1326
1547
|
let recovered = false;
|
|
1327
1548
|
if (!cartId) {
|
|
@@ -1348,7 +1569,6 @@ async function runStage(client, config, args) {
|
|
|
1348
1569
|
)
|
|
1349
1570
|
);
|
|
1350
1571
|
}
|
|
1351
|
-
const colourSuffix = colour ? ` (${colour})` : "";
|
|
1352
1572
|
lines.push(
|
|
1353
1573
|
ok(
|
|
1354
1574
|
`staged ${product.handle ?? args.handle}${size ? `#${size}` : ""}${colourSuffix} \xB7 inventory lock acquired (held 10m)`
|
|
@@ -1363,22 +1583,23 @@ async function runStage(client, config, args) {
|
|
|
1363
1583
|
// persist the cart_id atomically BEFORE returning (the router calls config.save)
|
|
1364
1584
|
save: { cart_id: cartId },
|
|
1365
1585
|
lines,
|
|
1366
|
-
json: args.json ? { cart_id: cartId, recovered, colour: colour ?? null, items: cart.items ?? [], total: cart.total ?? null } : void 0
|
|
1586
|
+
json: args.json ? { cart_id: cartId, recovered, fit: fit ?? null, colour: colour ?? null, items: cart.items ?? [], total: cart.total ?? null } : void 0
|
|
1367
1587
|
};
|
|
1368
1588
|
}
|
|
1369
1589
|
function registerStage(program, makeClient, loadConfig) {
|
|
1370
|
-
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 (default: your last commit's size)").option("--colour <colour>", "colour, e.g. white / black (default: the product's first colour)").option("--dry-run", "resolve the variant + show what would be staged, without staging").description("reserve stock \u2014 acquires an inventory lock, reserves nothing permanent.").action(async (handle, opts) => {
|
|
1590
|
+
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 (default: your last commit's size)").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.").action(async (handle, opts) => {
|
|
1371
1591
|
const { json, brand } = resolveOutput(program);
|
|
1372
1592
|
const res = await runStage(makeClient(), loadConfig(), {
|
|
1373
1593
|
handle,
|
|
1374
1594
|
size: opts.size,
|
|
1375
1595
|
colour: opts.colour,
|
|
1596
|
+
fit: opts.fit,
|
|
1376
1597
|
dryRun: opts.dryRun,
|
|
1377
1598
|
json,
|
|
1378
1599
|
brand
|
|
1379
1600
|
});
|
|
1380
1601
|
if (res.save) {
|
|
1381
|
-
const { saveConfig } = await import("./config-
|
|
1602
|
+
const { saveConfig } = await import("./config-L4X7G563.js");
|
|
1382
1603
|
saveConfig(res.save);
|
|
1383
1604
|
}
|
|
1384
1605
|
printResult(res, json);
|
|
@@ -1392,8 +1613,12 @@ export {
|
|
|
1392
1613
|
StoreClient,
|
|
1393
1614
|
BT_DEFAULT_STORE_URL,
|
|
1394
1615
|
BT_DEFAULT_PUBLISHABLE_KEY,
|
|
1616
|
+
fitLabel,
|
|
1617
|
+
productFits,
|
|
1618
|
+
coloursFor,
|
|
1619
|
+
sizesFor,
|
|
1620
|
+
priceFor,
|
|
1395
1621
|
productColours,
|
|
1396
|
-
sizesForColour,
|
|
1397
1622
|
findVariant,
|
|
1398
1623
|
productLore,
|
|
1399
1624
|
isCartTaxInclusive,
|
|
@@ -1419,7 +1644,7 @@ export {
|
|
|
1419
1644
|
brandOf,
|
|
1420
1645
|
fmtPrice,
|
|
1421
1646
|
taxSuffix,
|
|
1422
|
-
|
|
1647
|
+
priceLine,
|
|
1423
1648
|
stageMarker,
|
|
1424
1649
|
displayName,
|
|
1425
1650
|
lineColour,
|
|
@@ -1434,4 +1659,4 @@ export {
|
|
|
1434
1659
|
runStage,
|
|
1435
1660
|
registerStage
|
|
1436
1661
|
};
|
|
1437
|
-
//# sourceMappingURL=chunk-
|
|
1662
|
+
//# sourceMappingURL=chunk-X3RWS3AG.js.map
|