@asteby/storefront 0.3.0 → 0.5.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 +82 -1
- package/dist/chunk-45R47HYO.js +144 -0
- package/dist/chunk-45R47HYO.js.map +1 -0
- package/dist/geo.cjs +154 -0
- package/dist/geo.cjs.map +1 -0
- package/dist/geo.d.cts +82 -0
- package/dist/geo.d.ts +82 -0
- package/dist/geo.js +3 -0
- package/dist/geo.js.map +1 -0
- package/dist/index.cjs +47 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -3
- package/dist/index.d.ts +44 -3
- package/dist/index.js +44 -2
- package/dist/index.js.map +1 -1
- package/dist/mock.cjs +115 -7
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.d.cts +8 -2
- package/dist/mock.d.ts +8 -2
- package/dist/mock.js +93 -8
- package/dist/mock.js.map +1 -1
- package/dist/mockup.cjs +218 -0
- package/dist/mockup.cjs.map +1 -0
- package/dist/mockup.d.cts +142 -0
- package/dist/mockup.d.ts +142 -0
- package/dist/mockup.js +205 -0
- package/dist/mockup.js.map +1 -0
- package/dist/{types-CTXB8Dnl.d.cts → types-BmC-RDo8.d.ts} +51 -1
- package/dist/{types-CTXB8Dnl.d.ts → types-D300wdPF.d.cts} +51 -1
- package/package.json +21 -1
package/dist/mockup.cjs
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/mockup.ts
|
|
4
|
+
var BUILTIN_MOCKUP_KINDS = ["tire", "part", "supplement", "generic"];
|
|
5
|
+
var MOCKUP_TEXT_LIMITS = {
|
|
6
|
+
title: 80,
|
|
7
|
+
brand: 32,
|
|
8
|
+
model: 40,
|
|
9
|
+
size: 24,
|
|
10
|
+
service: 12,
|
|
11
|
+
partNumber: 32,
|
|
12
|
+
fitment: 90,
|
|
13
|
+
position: 24,
|
|
14
|
+
category: 40,
|
|
15
|
+
flavor: 32,
|
|
16
|
+
presentation: 32,
|
|
17
|
+
monogram: 2
|
|
18
|
+
};
|
|
19
|
+
var CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g;
|
|
20
|
+
function cleanMockupText(value, max = MOCKUP_TEXT_LIMITS.title) {
|
|
21
|
+
if (value === null || value === void 0 || typeof value === "boolean") return null;
|
|
22
|
+
if (typeof value === "number" && !Number.isFinite(value)) return null;
|
|
23
|
+
const text = String(value).replace(CONTROL_CHARS, " ").replace(/\s+/g, " ").trim();
|
|
24
|
+
if (!text) return null;
|
|
25
|
+
const chars = Array.from(text);
|
|
26
|
+
if (chars.length <= max) return text;
|
|
27
|
+
if (max <= 1) return "\u2026";
|
|
28
|
+
return chars.slice(0, max - 1).join("").trimEnd() + "\u2026";
|
|
29
|
+
}
|
|
30
|
+
function mockupMonogram(text) {
|
|
31
|
+
const words = (cleanMockupText(text, 200) ?? "").split(" ").map((w) => w.replace(/[^\p{L}\p{N}]/gu, "")).filter(Boolean);
|
|
32
|
+
if (!words.length) return null;
|
|
33
|
+
const letters = words.length === 1 ? Array.from(words[0]).slice(0, 2) : [Array.from(words[0])[0], Array.from(words[1])[0]];
|
|
34
|
+
return letters.join("").toLocaleUpperCase("es");
|
|
35
|
+
}
|
|
36
|
+
function attr(attrs, keys) {
|
|
37
|
+
if (!attrs) return void 0;
|
|
38
|
+
const lower = new Map(Object.entries(attrs).map(([k, v]) => [k.toLowerCase(), v]));
|
|
39
|
+
for (const key of keys) {
|
|
40
|
+
const v = lower.get(key);
|
|
41
|
+
if (v === void 0 || v === null) continue;
|
|
42
|
+
if (typeof v === "string" && !v.trim()) continue;
|
|
43
|
+
return v;
|
|
44
|
+
}
|
|
45
|
+
return void 0;
|
|
46
|
+
}
|
|
47
|
+
function attrText(attrs, keys, max) {
|
|
48
|
+
const v = attr(attrs, keys);
|
|
49
|
+
return typeof v === "boolean" ? null : cleanMockupText(v, max);
|
|
50
|
+
}
|
|
51
|
+
function attrNumber(attrs, keys) {
|
|
52
|
+
const v = attr(attrs, keys);
|
|
53
|
+
if (typeof v === "number") return Number.isFinite(v) && v > 0 ? v : null;
|
|
54
|
+
if (typeof v === "string" && /^\s*\d+(\.\d+)?\s*$/.test(v)) return Number.parseFloat(v);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
function attrFlag(attrs, keys) {
|
|
58
|
+
const v = attr(attrs, keys);
|
|
59
|
+
return v === true || typeof v === "string" && /^(true|s[ií]|yes|1)$/i.test(v.trim());
|
|
60
|
+
}
|
|
61
|
+
function optionValue(product, variant, names, max) {
|
|
62
|
+
const selected = variant?.selected_options?.find((o) => names.test(o.name.trim()));
|
|
63
|
+
if (selected) return cleanMockupText(selected.value, max);
|
|
64
|
+
const option = product.options?.find((o) => names.test(o.name.trim()));
|
|
65
|
+
return option && option.values.length === 1 ? cleanMockupText(option.values[0], max) : null;
|
|
66
|
+
}
|
|
67
|
+
var fmtNumber = (n) => Number.isInteger(n) ? String(n) : String(Number(n.toFixed(2)));
|
|
68
|
+
function common(product) {
|
|
69
|
+
return {
|
|
70
|
+
title: cleanMockupText(product.title, MOCKUP_TEXT_LIMITS.title) ?? "",
|
|
71
|
+
brand: cleanMockupText(product.vendor, MOCKUP_TEXT_LIMITS.brand)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
var TIRE_KEYS = {
|
|
75
|
+
size: ["size_key", "size", "tire_size", "medida"],
|
|
76
|
+
width: ["section_width_mm", "section_width", "width", "ancho"],
|
|
77
|
+
aspect: ["aspect_ratio", "aspect", "profile", "perfil"],
|
|
78
|
+
rim: ["rim_diameter_in", "rim_diameter", "rim", "rin"],
|
|
79
|
+
model: ["model", "modelo", "tread", "pattern", "line", "linea", "l\xEDnea"],
|
|
80
|
+
loadService: ["load_service", "service_description"],
|
|
81
|
+
load: ["load_index", "indice_carga", "\xEDndice_de_carga"],
|
|
82
|
+
speed: ["speed_rating", "speed_index", "indice_velocidad", "\xEDndice_de_velocidad"],
|
|
83
|
+
lightTruck: ["light_truck", "lt"],
|
|
84
|
+
runFlat: ["runflat", "run_flat"]
|
|
85
|
+
};
|
|
86
|
+
function tireMockupData(product) {
|
|
87
|
+
const a = product.attributes;
|
|
88
|
+
const w = attrNumber(a, TIRE_KEYS.width);
|
|
89
|
+
const ar = attrNumber(a, TIRE_KEYS.aspect);
|
|
90
|
+
const r = attrNumber(a, TIRE_KEYS.rim);
|
|
91
|
+
const split = w !== null && ar !== null && r !== null;
|
|
92
|
+
const written = attrText(a, TIRE_KEYS.size, MOCKUP_TEXT_LIMITS.size);
|
|
93
|
+
const size = written ?? (split ? `${fmtNumber(w)}/${fmtNumber(ar)}R${fmtNumber(r)}` : null);
|
|
94
|
+
const loadService = attrText(a, TIRE_KEYS.loadService, MOCKUP_TEXT_LIMITS.service);
|
|
95
|
+
const load = attrText(a, TIRE_KEYS.load, 7);
|
|
96
|
+
const speed = attrText(a, TIRE_KEYS.speed, 3);
|
|
97
|
+
const service = cleanMockupText(loadService ? loadService + (speed && !loadService.endsWith(speed) ? speed : "") : (load ?? "") + (speed ?? ""), MOCKUP_TEXT_LIMITS.service);
|
|
98
|
+
return {
|
|
99
|
+
kind: "tire",
|
|
100
|
+
...common(product),
|
|
101
|
+
size,
|
|
102
|
+
width: split ? fmtNumber(w) : null,
|
|
103
|
+
aspect: split ? fmtNumber(ar) : null,
|
|
104
|
+
rim: split ? fmtNumber(r) : null,
|
|
105
|
+
model: attrText(a, TIRE_KEYS.model, MOCKUP_TEXT_LIMITS.model),
|
|
106
|
+
service,
|
|
107
|
+
lightTruck: attrFlag(a, TIRE_KEYS.lightTruck),
|
|
108
|
+
runFlat: attrFlag(a, TIRE_KEYS.runFlat)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
var PART_KEYS = {
|
|
112
|
+
partNumber: ["part_number", "mpn", "numero_de_parte", "n\xFAmero_de_parte", "no_parte"],
|
|
113
|
+
oem: ["oem_number", "oem", "oem_part_number"],
|
|
114
|
+
fitment: ["fitment", "fitment_summary", "compatibility", "compatible_with", "compatibilidad", "aplicaciones", "vehicle"],
|
|
115
|
+
position: ["position", "posicion", "posici\xF3n"]
|
|
116
|
+
};
|
|
117
|
+
function partMockupData(product) {
|
|
118
|
+
const a = product.attributes;
|
|
119
|
+
return {
|
|
120
|
+
kind: "part",
|
|
121
|
+
...common(product),
|
|
122
|
+
partNumber: attrText(a, PART_KEYS.partNumber, MOCKUP_TEXT_LIMITS.partNumber),
|
|
123
|
+
oemNumber: attrText(a, PART_KEYS.oem, MOCKUP_TEXT_LIMITS.partNumber),
|
|
124
|
+
fitment: attrText(a, PART_KEYS.fitment, MOCKUP_TEXT_LIMITS.fitment),
|
|
125
|
+
position: attrText(a, PART_KEYS.position, MOCKUP_TEXT_LIMITS.position),
|
|
126
|
+
category: cleanMockupText(product.category?.title, MOCKUP_TEXT_LIMITS.category)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
var SUPPLEMENT_KEYS = {
|
|
130
|
+
flavor: ["flavor", "flavour", "sabor"],
|
|
131
|
+
presentation: ["presentation", "presentacion", "presentaci\xF3n", "net_content", "contenido_neto", "net_weight", "peso_neto"],
|
|
132
|
+
servings: ["servings", "porciones", "servicios"],
|
|
133
|
+
form: ["container", "envase", "form", "forma", "presentation_form"]
|
|
134
|
+
};
|
|
135
|
+
function supplementShape(product) {
|
|
136
|
+
const form = String(attr(product.attributes, SUPPLEMENT_KEYS.form) ?? "").toLowerCase();
|
|
137
|
+
if (/bottle|frasco|c[aá]psula|capsule|tableta|tablet|softgel|gomita|gummy|l[ií]quido|liquid/.test(form)) return "bottle";
|
|
138
|
+
if (/bag|bolsa|doypack|pouch|sobre/.test(form)) return "bag";
|
|
139
|
+
return "tub";
|
|
140
|
+
}
|
|
141
|
+
function supplementMockupData(product, variant) {
|
|
142
|
+
const a = product.attributes;
|
|
143
|
+
const servings = attrNumber(a, SUPPLEMENT_KEYS.servings);
|
|
144
|
+
return {
|
|
145
|
+
kind: "supplement",
|
|
146
|
+
...common(product),
|
|
147
|
+
flavor: optionValue(product, variant, /^(sabor|flavou?r)$/i, MOCKUP_TEXT_LIMITS.flavor) ?? attrText(a, SUPPLEMENT_KEYS.flavor, MOCKUP_TEXT_LIMITS.flavor),
|
|
148
|
+
presentation: optionValue(product, variant, /^(presentaci[oó]n|tama[nñ]o|size|contenido|peso)$/i, MOCKUP_TEXT_LIMITS.presentation) ?? attrText(a, SUPPLEMENT_KEYS.presentation, MOCKUP_TEXT_LIMITS.presentation),
|
|
149
|
+
servings: servings !== null && Number.isInteger(servings) ? servings : null,
|
|
150
|
+
shape: supplementShape(product)
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function genericMockupData(product) {
|
|
154
|
+
return {
|
|
155
|
+
kind: "generic",
|
|
156
|
+
...common(product),
|
|
157
|
+
monogram: mockupMonogram(product.title),
|
|
158
|
+
category: cleanMockupText(product.category?.title ?? product.product_type, MOCKUP_TEXT_LIMITS.category)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function mockupDataFromProduct(kind, product, variant) {
|
|
162
|
+
switch (kind) {
|
|
163
|
+
case "tire":
|
|
164
|
+
return tireMockupData(product);
|
|
165
|
+
case "part":
|
|
166
|
+
return partMockupData(product);
|
|
167
|
+
case "supplement":
|
|
168
|
+
return supplementMockupData(product, variant);
|
|
169
|
+
default:
|
|
170
|
+
return genericMockupData(product);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
var MOCKUP_KIND_ATTRIBUTES = ["mockup_kind", "mockup"];
|
|
174
|
+
var DEFAULT_MOCKUP_KIND_RULES = [
|
|
175
|
+
{ kind: "tire", attributes: ["size_key", "section_width_mm", "rim_diameter_in", "aspect_ratio", "speed_rating", "load_index"] },
|
|
176
|
+
{ kind: "part", attributes: ["part_number", "oem_number", "mpn"] },
|
|
177
|
+
{ kind: "tire", text: /\b(llantas?|neum[aá]ticos?|tires?|tyres?)\b/i },
|
|
178
|
+
{ kind: "part", text: /\b(refacci[oó]n(es)?|autopartes?|repuestos?|spare[ -]?parts?|auto[ -]?parts?)\b/i },
|
|
179
|
+
{
|
|
180
|
+
kind: "supplement",
|
|
181
|
+
text: /\b(suplementos?|supplements?|prote[ií]nas?|whey|creatina|creatine|amino[aá]cidos?|bcaa|pre-?entrenos?|pre-?workout|col[aá]geno|vitaminas?|multivitam[ií]nicos?|omega[ -]?3|electrolitos?|termog[eé]nicos?|ganadores? de masa)\b/i
|
|
182
|
+
}
|
|
183
|
+
];
|
|
184
|
+
function hasAttr(attrs, keys) {
|
|
185
|
+
if (!attrs) return false;
|
|
186
|
+
return keys.some((k) => {
|
|
187
|
+
const v = attrs[k];
|
|
188
|
+
return v !== void 0 && v !== null && v !== false && !(typeof v === "string" && !v.trim());
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function resolveMockupKind(product, options = {}) {
|
|
192
|
+
const explicit = attr(product.attributes, MOCKUP_KIND_ATTRIBUTES);
|
|
193
|
+
if (typeof explicit === "string") {
|
|
194
|
+
const k = explicit.trim().toLowerCase();
|
|
195
|
+
if (/^[a-z][a-z0-9_-]{0,31}$/.test(k) && (!options.kinds || options.kinds.includes(k))) return k;
|
|
196
|
+
}
|
|
197
|
+
const haystack = [product.product_type, product.category?.title, product.category?.handle?.replace(/-/g, " "), ...product.tags ?? []].filter((s) => typeof s === "string" && s.length > 0).join(" \xB7 ");
|
|
198
|
+
for (const rule of options.rules ?? DEFAULT_MOCKUP_KIND_RULES) {
|
|
199
|
+
if (rule.attributes && hasAttr(product.attributes, rule.attributes)) return rule.kind;
|
|
200
|
+
if (rule.text && haystack && rule.text.test(haystack)) return rule.kind;
|
|
201
|
+
}
|
|
202
|
+
return options.fallback ?? "generic";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
exports.BUILTIN_MOCKUP_KINDS = BUILTIN_MOCKUP_KINDS;
|
|
206
|
+
exports.DEFAULT_MOCKUP_KIND_RULES = DEFAULT_MOCKUP_KIND_RULES;
|
|
207
|
+
exports.MOCKUP_KIND_ATTRIBUTES = MOCKUP_KIND_ATTRIBUTES;
|
|
208
|
+
exports.MOCKUP_TEXT_LIMITS = MOCKUP_TEXT_LIMITS;
|
|
209
|
+
exports.cleanMockupText = cleanMockupText;
|
|
210
|
+
exports.genericMockupData = genericMockupData;
|
|
211
|
+
exports.mockupDataFromProduct = mockupDataFromProduct;
|
|
212
|
+
exports.mockupMonogram = mockupMonogram;
|
|
213
|
+
exports.partMockupData = partMockupData;
|
|
214
|
+
exports.resolveMockupKind = resolveMockupKind;
|
|
215
|
+
exports.supplementMockupData = supplementMockupData;
|
|
216
|
+
exports.tireMockupData = tireMockupData;
|
|
217
|
+
//# sourceMappingURL=mockup.cjs.map
|
|
218
|
+
//# sourceMappingURL=mockup.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mockup.ts"],"names":[],"mappings":"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","file":"mockup.cjs","sourcesContent":["/**\n * Fallback product mockups: the pure, framework-agnostic half.\n *\n * When a product from OPS has no image, a store can draw an illustration\n * (tire, part, supplement or a generic box) from the product's own data. This\n * module decides WHICH illustration (`resolveMockupKind`) and WHAT it may show\n * (`mockupDataFromProduct`). Rendering lives in `@asteby/storefront-react/mockups`.\n *\n * Rules every extractor follows:\n * - Only data that is in the product (attributes, vendor, title, options, the\n * selected variant). Nothing is guessed: a missing size stays missing.\n * - Never prices.\n * - Every string is cleaned (control characters removed, whitespace collapsed)\n * and capped by `MOCKUP_TEXT_LIMITS`. Escaping is the renderer's job (React\n * escapes text and attributes); these values are plain text, never markup.\n */\nimport type { Product, ProductAttributeValue, ProductVariant } from './types.js'\n\n// ---------------------------------------------------------------------------\n// Kinds\n// ---------------------------------------------------------------------------\n\n/** Built-in kinds. Stores can register more (any string). */\nexport type BuiltinMockupKind = 'tire' | 'part' | 'supplement' | 'generic'\n\n/** A mockup kind: a built-in one or any kind a store registers. */\nexport type MockupKind = BuiltinMockupKind | (string & {})\n\nexport const BUILTIN_MOCKUP_KINDS: readonly BuiltinMockupKind[] = ['tire', 'part', 'supplement', 'generic']\n\n/** The product fields mockups read. A full `Product` satisfies it. */\nexport type MockupProduct = Pick<Product, 'title'> &\n Partial<Pick<Product, 'handle' | 'vendor' | 'product_type' | 'category' | 'tags' | 'attributes' | 'options'>>\n\n/** The variant fields mockups read (selected flavor, presentation...). */\nexport type MockupVariant = Partial<Pick<ProductVariant, 'selected_options' | 'title'>>\n\n// ---------------------------------------------------------------------------\n// Text\n// ---------------------------------------------------------------------------\n\n/** Maximum characters per field (counted in code points, ellipsis included). */\nexport const MOCKUP_TEXT_LIMITS = {\n title: 80,\n brand: 32,\n model: 40,\n size: 24,\n service: 12,\n partNumber: 32,\n fitment: 90,\n position: 24,\n category: 40,\n flavor: 32,\n presentation: 32,\n monogram: 2,\n} as const\n\nconst CONTROL_CHARS = /[\\u0000-\\u001f\\u007f-\\u009f\\u200b-\\u200f\\u2028-\\u202e\\u2066-\\u2069\\ufeff]/g\n\n/**\n * Plain, single-line text capped at `max` code points (with `…` when cut).\n * Returns `null` for empty values, so callers can skip the line entirely.\n */\nexport function cleanMockupText(value: unknown, max: number = MOCKUP_TEXT_LIMITS.title): string | null {\n if (value === null || value === undefined || typeof value === 'boolean') return null\n if (typeof value === 'number' && !Number.isFinite(value)) return null\n const text = String(value).replace(CONTROL_CHARS, ' ').replace(/\\s+/g, ' ').trim()\n if (!text) return null\n const chars = Array.from(text)\n if (chars.length <= max) return text\n if (max <= 1) return '…'\n return chars.slice(0, max - 1).join('').trimEnd() + '…'\n}\n\n/** Up to two letters taken from the words of `text` (e.g. \"Casa Textil\" → \"CT\"). */\nexport function mockupMonogram(text: string | null | undefined): string | null {\n const words = (cleanMockupText(text, 200) ?? '').split(' ').map((w) => w.replace(/[^\\p{L}\\p{N}]/gu, '')).filter(Boolean)\n if (!words.length) return null\n const letters = words.length === 1 ? Array.from(words[0]!).slice(0, 2) : [Array.from(words[0]!)[0]!, Array.from(words[1]!)[0]!]\n return letters.join('').toLocaleUpperCase('es')\n}\n\n// ---------------------------------------------------------------------------\n// Attribute helpers\n// ---------------------------------------------------------------------------\n\ntype Attrs = Record<string, ProductAttributeValue>\n\n/** First attribute (by key, case-insensitive) that has a usable value. */\nfunction attr(attrs: Attrs | undefined, keys: readonly string[]): ProductAttributeValue | undefined {\n if (!attrs) return undefined\n const lower = new Map(Object.entries(attrs).map(([k, v]) => [k.toLowerCase(), v]))\n for (const key of keys) {\n const v = lower.get(key)\n if (v === undefined || v === null) continue\n if (typeof v === 'string' && !v.trim()) continue\n return v\n }\n return undefined\n}\n\nfunction attrText(attrs: Attrs | undefined, keys: readonly string[], max: number): string | null {\n const v = attr(attrs, keys)\n return typeof v === 'boolean' ? null : cleanMockupText(v, max)\n}\n\nfunction attrNumber(attrs: Attrs | undefined, keys: readonly string[]): number | null {\n const v = attr(attrs, keys)\n if (typeof v === 'number') return Number.isFinite(v) && v > 0 ? v : null\n if (typeof v === 'string' && /^\\s*\\d+(\\.\\d+)?\\s*$/.test(v)) return Number.parseFloat(v)\n return null\n}\n\nfunction attrFlag(attrs: Attrs | undefined, keys: readonly string[]): boolean {\n const v = attr(attrs, keys)\n return v === true || (typeof v === 'string' && /^(true|s[ií]|yes|1)$/i.test(v.trim()))\n}\n\n/** Value of an option: from the selected variant, or the product's only value. */\nfunction optionValue(product: MockupProduct, variant: MockupVariant | null | undefined, names: RegExp, max: number): string | null {\n const selected = variant?.selected_options?.find((o) => names.test(o.name.trim()))\n if (selected) return cleanMockupText(selected.value, max)\n const option = product.options?.find((o) => names.test(o.name.trim()))\n return option && option.values.length === 1 ? cleanMockupText(option.values[0], max) : null\n}\n\nconst fmtNumber = (n: number) => (Number.isInteger(n) ? String(n) : String(Number(n.toFixed(2))))\n\n// ---------------------------------------------------------------------------\n// Data per kind\n// ---------------------------------------------------------------------------\n\ntype MockupCommon = {\n /** Product title, always present (the accessible name is built from it). */\n title: string\n /** Brand (`vendor`). */\n brand: string | null\n}\n\nexport type TireMockupData = MockupCommon & {\n kind: 'tire'\n /** Size as written in OPS (`size_key`/`size`) or built from width/aspect/rim when all three exist. */\n size: string | null\n /** Split size when width, aspect and rim are all known (for the big digits). */\n width: string | null\n aspect: string | null\n rim: string | null\n /** Tread line / model (`model`, `line`, `tread`...). */\n model: string | null\n /** Service description, e.g. `91V` or `121/118S` (load index + speed rating). */\n service: string | null\n lightTruck: boolean\n runFlat: boolean\n}\n\nexport type PartMockupData = MockupCommon & {\n kind: 'part'\n partNumber: string | null\n oemNumber: string | null\n /** Compatibility as sent by OPS (`fitment`, `compatibility`...). */\n fitment: string | null\n position: string | null\n category: string | null\n}\n\nexport type SupplementShape = 'tub' | 'bottle' | 'bag'\n\nexport type SupplementMockupData = MockupCommon & {\n kind: 'supplement'\n flavor: string | null\n /** Net content / presentation, e.g. `5 lb`, `120 cápsulas`, or `servings` (number). */\n presentation: string | null\n /** Number of servings when OPS sends it as a number (the label adds the unit). */\n servings: number | null\n shape: SupplementShape\n}\n\nexport type GenericMockupData = MockupCommon & {\n kind: 'generic'\n monogram: string | null\n category: string | null\n}\n\nexport type MockupDataMap = {\n tire: TireMockupData\n part: PartMockupData\n supplement: SupplementMockupData\n generic: GenericMockupData\n}\n\n/** Data of any built-in kind. */\nexport type BuiltinMockupData = MockupDataMap[BuiltinMockupKind]\n\nfunction common(product: MockupProduct): MockupCommon {\n return {\n title: cleanMockupText(product.title, MOCKUP_TEXT_LIMITS.title) ?? '',\n brand: cleanMockupText(product.vendor, MOCKUP_TEXT_LIMITS.brand),\n }\n}\n\nconst TIRE_KEYS = {\n size: ['size_key', 'size', 'tire_size', 'medida'],\n width: ['section_width_mm', 'section_width', 'width', 'ancho'],\n aspect: ['aspect_ratio', 'aspect', 'profile', 'perfil'],\n rim: ['rim_diameter_in', 'rim_diameter', 'rim', 'rin'],\n model: ['model', 'modelo', 'tread', 'pattern', 'line', 'linea', 'línea'],\n loadService: ['load_service', 'service_description'],\n load: ['load_index', 'indice_carga', 'índice_de_carga'],\n speed: ['speed_rating', 'speed_index', 'indice_velocidad', 'índice_de_velocidad'],\n lightTruck: ['light_truck', 'lt'],\n runFlat: ['runflat', 'run_flat'],\n} as const\n\n/** Tire data from the product attributes (`size_key`, `section_width_mm`, `load_index`, `speed_rating`...). */\nexport function tireMockupData(product: MockupProduct): TireMockupData {\n const a = product.attributes\n const w = attrNumber(a, TIRE_KEYS.width)\n const ar = attrNumber(a, TIRE_KEYS.aspect)\n const r = attrNumber(a, TIRE_KEYS.rim)\n const split = w !== null && ar !== null && r !== null\n const written = attrText(a, TIRE_KEYS.size, MOCKUP_TEXT_LIMITS.size)\n const size = written ?? (split ? `${fmtNumber(w)}/${fmtNumber(ar)}R${fmtNumber(r)}` : null)\n const loadService = attrText(a, TIRE_KEYS.loadService, MOCKUP_TEXT_LIMITS.service)\n const load = attrText(a, TIRE_KEYS.load, 7)\n const speed = attrText(a, TIRE_KEYS.speed, 3)\n const service = cleanMockupText(loadService ? loadService + (speed && !loadService.endsWith(speed) ? speed : '') : (load ?? '') + (speed ?? ''), MOCKUP_TEXT_LIMITS.service)\n return {\n kind: 'tire',\n ...common(product),\n size,\n width: split ? fmtNumber(w) : null,\n aspect: split ? fmtNumber(ar) : null,\n rim: split ? fmtNumber(r) : null,\n model: attrText(a, TIRE_KEYS.model, MOCKUP_TEXT_LIMITS.model),\n service,\n lightTruck: attrFlag(a, TIRE_KEYS.lightTruck),\n runFlat: attrFlag(a, TIRE_KEYS.runFlat),\n }\n}\n\nconst PART_KEYS = {\n partNumber: ['part_number', 'mpn', 'numero_de_parte', 'número_de_parte', 'no_parte'],\n oem: ['oem_number', 'oem', 'oem_part_number'],\n fitment: ['fitment', 'fitment_summary', 'compatibility', 'compatible_with', 'compatibilidad', 'aplicaciones', 'vehicle'],\n position: ['position', 'posicion', 'posición'],\n} as const\n\n/** Part data: part number, OEM number, compatibility and position (attributes), category title. */\nexport function partMockupData(product: MockupProduct): PartMockupData {\n const a = product.attributes\n return {\n kind: 'part',\n ...common(product),\n partNumber: attrText(a, PART_KEYS.partNumber, MOCKUP_TEXT_LIMITS.partNumber),\n oemNumber: attrText(a, PART_KEYS.oem, MOCKUP_TEXT_LIMITS.partNumber),\n fitment: attrText(a, PART_KEYS.fitment, MOCKUP_TEXT_LIMITS.fitment),\n position: attrText(a, PART_KEYS.position, MOCKUP_TEXT_LIMITS.position),\n category: cleanMockupText(product.category?.title, MOCKUP_TEXT_LIMITS.category),\n }\n}\n\nconst SUPPLEMENT_KEYS = {\n flavor: ['flavor', 'flavour', 'sabor'],\n presentation: ['presentation', 'presentacion', 'presentación', 'net_content', 'contenido_neto', 'net_weight', 'peso_neto'],\n servings: ['servings', 'porciones', 'servicios'],\n form: ['container', 'envase', 'form', 'forma', 'presentation_form'],\n} as const\n\nfunction supplementShape(product: MockupProduct): SupplementShape {\n const form = String(attr(product.attributes, SUPPLEMENT_KEYS.form) ?? '').toLowerCase()\n if (/bottle|frasco|c[aá]psula|capsule|tableta|tablet|softgel|gomita|gummy|l[ií]quido|liquid/.test(form)) return 'bottle'\n if (/bag|bolsa|doypack|pouch|sobre/.test(form)) return 'bag'\n return 'tub'\n}\n\n/** Supplement data: flavor and presentation (selected variant, single-valued option or attribute). */\nexport function supplementMockupData(product: MockupProduct, variant?: MockupVariant | null): SupplementMockupData {\n const a = product.attributes\n const servings = attrNumber(a, SUPPLEMENT_KEYS.servings)\n return {\n kind: 'supplement',\n ...common(product),\n flavor: optionValue(product, variant, /^(sabor|flavou?r)$/i, MOCKUP_TEXT_LIMITS.flavor) ?? attrText(a, SUPPLEMENT_KEYS.flavor, MOCKUP_TEXT_LIMITS.flavor),\n presentation:\n optionValue(product, variant, /^(presentaci[oó]n|tama[nñ]o|size|contenido|peso)$/i, MOCKUP_TEXT_LIMITS.presentation) ??\n attrText(a, SUPPLEMENT_KEYS.presentation, MOCKUP_TEXT_LIMITS.presentation),\n servings: servings !== null && Number.isInteger(servings) ? servings : null,\n shape: supplementShape(product),\n }\n}\n\n/** Generic data: title, brand, a monogram of the title and the category. */\nexport function genericMockupData(product: MockupProduct): GenericMockupData {\n return {\n kind: 'generic',\n ...common(product),\n monogram: mockupMonogram(product.title),\n category: cleanMockupText(product.category?.title ?? product.product_type, MOCKUP_TEXT_LIMITS.category),\n }\n}\n\n/** Data for a built-in kind; unknown kinds get the generic data. */\nexport function mockupDataFromProduct<K extends BuiltinMockupKind>(kind: K, product: MockupProduct, variant?: MockupVariant | null): MockupDataMap[K]\nexport function mockupDataFromProduct(kind: MockupKind, product: MockupProduct, variant?: MockupVariant | null): BuiltinMockupData\nexport function mockupDataFromProduct(kind: MockupKind, product: MockupProduct, variant?: MockupVariant | null): BuiltinMockupData {\n switch (kind) {\n case 'tire':\n return tireMockupData(product)\n case 'part':\n return partMockupData(product)\n case 'supplement':\n return supplementMockupData(product, variant)\n default:\n return genericMockupData(product)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Kind resolution\n// ---------------------------------------------------------------------------\n\n/** Attribute a merchant can set in OPS to pick the mockup explicitly (`mockup`, `mockup_kind`). */\nexport const MOCKUP_KIND_ATTRIBUTES = ['mockup_kind', 'mockup'] as const\n\nexport type MockupKindRule = {\n kind: MockupKind\n /** Matched against `product_type`, `category.title/handle` and `tags`. */\n text?: RegExp\n /** Any of these attribute keys present (with a value) selects the kind. */\n attributes?: readonly string[]\n}\n\n/** Default rules, checked in order: attributes first (precise), then words. */\nexport const DEFAULT_MOCKUP_KIND_RULES: readonly MockupKindRule[] = [\n { kind: 'tire', attributes: ['size_key', 'section_width_mm', 'rim_diameter_in', 'aspect_ratio', 'speed_rating', 'load_index'] },\n { kind: 'part', attributes: ['part_number', 'oem_number', 'mpn'] },\n { kind: 'tire', text: /\\b(llantas?|neum[aá]ticos?|tires?|tyres?)\\b/i },\n { kind: 'part', text: /\\b(refacci[oó]n(es)?|autopartes?|repuestos?|spare[ -]?parts?|auto[ -]?parts?)\\b/i },\n {\n kind: 'supplement',\n text: /\\b(suplementos?|supplements?|prote[ií]nas?|whey|creatina|creatine|amino[aá]cidos?|bcaa|pre-?entrenos?|pre-?workout|col[aá]geno|vitaminas?|multivitam[ií]nicos?|omega[ -]?3|electrolitos?|termog[eé]nicos?|ganadores? de masa)\\b/i,\n },\n]\n\nexport type ResolveMockupKindOptions = {\n /** Rules to use instead of `DEFAULT_MOCKUP_KIND_RULES`. */\n rules?: readonly MockupKindRule[]\n /** When set, an explicit `mockup_kind` attribute is only honored if it is in this list. */\n kinds?: readonly MockupKind[]\n /** Kind when nothing matches (default `generic`). */\n fallback?: MockupKind\n}\n\nfunction hasAttr(attrs: Attrs | undefined, keys: readonly string[]): boolean {\n if (!attrs) return false\n return keys.some((k) => {\n const v = attrs[k]\n return v !== undefined && v !== null && v !== false && !(typeof v === 'string' && !v.trim())\n })\n}\n\n/**\n * Picks a mockup kind from the product: the explicit `mockup_kind` attribute,\n * then attribute rules (tire specs, part numbers), then words in the product\n * type, category and tags. Falls back to `generic`.\n */\nexport function resolveMockupKind(product: MockupProduct, options: ResolveMockupKindOptions = {}): MockupKind {\n const explicit = attr(product.attributes, MOCKUP_KIND_ATTRIBUTES)\n if (typeof explicit === 'string') {\n const k = explicit.trim().toLowerCase()\n if (/^[a-z][a-z0-9_-]{0,31}$/.test(k) && (!options.kinds || options.kinds.includes(k))) return k\n }\n const haystack = [product.product_type, product.category?.title, product.category?.handle?.replace(/-/g, ' '), ...(product.tags ?? [])]\n .filter((s): s is string => typeof s === 'string' && s.length > 0)\n .join(' · ')\n for (const rule of options.rules ?? DEFAULT_MOCKUP_KIND_RULES) {\n if (rule.attributes && hasAttr(product.attributes, rule.attributes)) return rule.kind\n if (rule.text && haystack && rule.text.test(haystack)) return rule.kind\n }\n return options.fallback ?? 'generic'\n}\n"]}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { W as Product, a0 as ProductVariant } from './types-D300wdPF.cjs';
|
|
2
|
+
import './geo.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Fallback product mockups: the pure, framework-agnostic half.
|
|
6
|
+
*
|
|
7
|
+
* When a product from OPS has no image, a store can draw an illustration
|
|
8
|
+
* (tire, part, supplement or a generic box) from the product's own data. This
|
|
9
|
+
* module decides WHICH illustration (`resolveMockupKind`) and WHAT it may show
|
|
10
|
+
* (`mockupDataFromProduct`). Rendering lives in `@asteby/storefront-react/mockups`.
|
|
11
|
+
*
|
|
12
|
+
* Rules every extractor follows:
|
|
13
|
+
* - Only data that is in the product (attributes, vendor, title, options, the
|
|
14
|
+
* selected variant). Nothing is guessed: a missing size stays missing.
|
|
15
|
+
* - Never prices.
|
|
16
|
+
* - Every string is cleaned (control characters removed, whitespace collapsed)
|
|
17
|
+
* and capped by `MOCKUP_TEXT_LIMITS`. Escaping is the renderer's job (React
|
|
18
|
+
* escapes text and attributes); these values are plain text, never markup.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** Built-in kinds. Stores can register more (any string). */
|
|
22
|
+
type BuiltinMockupKind = 'tire' | 'part' | 'supplement' | 'generic';
|
|
23
|
+
/** A mockup kind: a built-in one or any kind a store registers. */
|
|
24
|
+
type MockupKind = BuiltinMockupKind | (string & {});
|
|
25
|
+
declare const BUILTIN_MOCKUP_KINDS: readonly BuiltinMockupKind[];
|
|
26
|
+
/** The product fields mockups read. A full `Product` satisfies it. */
|
|
27
|
+
type MockupProduct = Pick<Product, 'title'> & Partial<Pick<Product, 'handle' | 'vendor' | 'product_type' | 'category' | 'tags' | 'attributes' | 'options'>>;
|
|
28
|
+
/** The variant fields mockups read (selected flavor, presentation...). */
|
|
29
|
+
type MockupVariant = Partial<Pick<ProductVariant, 'selected_options' | 'title'>>;
|
|
30
|
+
/** Maximum characters per field (counted in code points, ellipsis included). */
|
|
31
|
+
declare const MOCKUP_TEXT_LIMITS: {
|
|
32
|
+
readonly title: 80;
|
|
33
|
+
readonly brand: 32;
|
|
34
|
+
readonly model: 40;
|
|
35
|
+
readonly size: 24;
|
|
36
|
+
readonly service: 12;
|
|
37
|
+
readonly partNumber: 32;
|
|
38
|
+
readonly fitment: 90;
|
|
39
|
+
readonly position: 24;
|
|
40
|
+
readonly category: 40;
|
|
41
|
+
readonly flavor: 32;
|
|
42
|
+
readonly presentation: 32;
|
|
43
|
+
readonly monogram: 2;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Plain, single-line text capped at `max` code points (with `…` when cut).
|
|
47
|
+
* Returns `null` for empty values, so callers can skip the line entirely.
|
|
48
|
+
*/
|
|
49
|
+
declare function cleanMockupText(value: unknown, max?: number): string | null;
|
|
50
|
+
/** Up to two letters taken from the words of `text` (e.g. "Casa Textil" → "CT"). */
|
|
51
|
+
declare function mockupMonogram(text: string | null | undefined): string | null;
|
|
52
|
+
type MockupCommon = {
|
|
53
|
+
/** Product title, always present (the accessible name is built from it). */
|
|
54
|
+
title: string;
|
|
55
|
+
/** Brand (`vendor`). */
|
|
56
|
+
brand: string | null;
|
|
57
|
+
};
|
|
58
|
+
type TireMockupData = MockupCommon & {
|
|
59
|
+
kind: 'tire';
|
|
60
|
+
/** Size as written in OPS (`size_key`/`size`) or built from width/aspect/rim when all three exist. */
|
|
61
|
+
size: string | null;
|
|
62
|
+
/** Split size when width, aspect and rim are all known (for the big digits). */
|
|
63
|
+
width: string | null;
|
|
64
|
+
aspect: string | null;
|
|
65
|
+
rim: string | null;
|
|
66
|
+
/** Tread line / model (`model`, `line`, `tread`...). */
|
|
67
|
+
model: string | null;
|
|
68
|
+
/** Service description, e.g. `91V` or `121/118S` (load index + speed rating). */
|
|
69
|
+
service: string | null;
|
|
70
|
+
lightTruck: boolean;
|
|
71
|
+
runFlat: boolean;
|
|
72
|
+
};
|
|
73
|
+
type PartMockupData = MockupCommon & {
|
|
74
|
+
kind: 'part';
|
|
75
|
+
partNumber: string | null;
|
|
76
|
+
oemNumber: string | null;
|
|
77
|
+
/** Compatibility as sent by OPS (`fitment`, `compatibility`...). */
|
|
78
|
+
fitment: string | null;
|
|
79
|
+
position: string | null;
|
|
80
|
+
category: string | null;
|
|
81
|
+
};
|
|
82
|
+
type SupplementShape = 'tub' | 'bottle' | 'bag';
|
|
83
|
+
type SupplementMockupData = MockupCommon & {
|
|
84
|
+
kind: 'supplement';
|
|
85
|
+
flavor: string | null;
|
|
86
|
+
/** Net content / presentation, e.g. `5 lb`, `120 cápsulas`, or `servings` (number). */
|
|
87
|
+
presentation: string | null;
|
|
88
|
+
/** Number of servings when OPS sends it as a number (the label adds the unit). */
|
|
89
|
+
servings: number | null;
|
|
90
|
+
shape: SupplementShape;
|
|
91
|
+
};
|
|
92
|
+
type GenericMockupData = MockupCommon & {
|
|
93
|
+
kind: 'generic';
|
|
94
|
+
monogram: string | null;
|
|
95
|
+
category: string | null;
|
|
96
|
+
};
|
|
97
|
+
type MockupDataMap = {
|
|
98
|
+
tire: TireMockupData;
|
|
99
|
+
part: PartMockupData;
|
|
100
|
+
supplement: SupplementMockupData;
|
|
101
|
+
generic: GenericMockupData;
|
|
102
|
+
};
|
|
103
|
+
/** Data of any built-in kind. */
|
|
104
|
+
type BuiltinMockupData = MockupDataMap[BuiltinMockupKind];
|
|
105
|
+
/** Tire data from the product attributes (`size_key`, `section_width_mm`, `load_index`, `speed_rating`...). */
|
|
106
|
+
declare function tireMockupData(product: MockupProduct): TireMockupData;
|
|
107
|
+
/** Part data: part number, OEM number, compatibility and position (attributes), category title. */
|
|
108
|
+
declare function partMockupData(product: MockupProduct): PartMockupData;
|
|
109
|
+
/** Supplement data: flavor and presentation (selected variant, single-valued option or attribute). */
|
|
110
|
+
declare function supplementMockupData(product: MockupProduct, variant?: MockupVariant | null): SupplementMockupData;
|
|
111
|
+
/** Generic data: title, brand, a monogram of the title and the category. */
|
|
112
|
+
declare function genericMockupData(product: MockupProduct): GenericMockupData;
|
|
113
|
+
/** Data for a built-in kind; unknown kinds get the generic data. */
|
|
114
|
+
declare function mockupDataFromProduct<K extends BuiltinMockupKind>(kind: K, product: MockupProduct, variant?: MockupVariant | null): MockupDataMap[K];
|
|
115
|
+
declare function mockupDataFromProduct(kind: MockupKind, product: MockupProduct, variant?: MockupVariant | null): BuiltinMockupData;
|
|
116
|
+
/** Attribute a merchant can set in OPS to pick the mockup explicitly (`mockup`, `mockup_kind`). */
|
|
117
|
+
declare const MOCKUP_KIND_ATTRIBUTES: readonly ["mockup_kind", "mockup"];
|
|
118
|
+
type MockupKindRule = {
|
|
119
|
+
kind: MockupKind;
|
|
120
|
+
/** Matched against `product_type`, `category.title/handle` and `tags`. */
|
|
121
|
+
text?: RegExp;
|
|
122
|
+
/** Any of these attribute keys present (with a value) selects the kind. */
|
|
123
|
+
attributes?: readonly string[];
|
|
124
|
+
};
|
|
125
|
+
/** Default rules, checked in order: attributes first (precise), then words. */
|
|
126
|
+
declare const DEFAULT_MOCKUP_KIND_RULES: readonly MockupKindRule[];
|
|
127
|
+
type ResolveMockupKindOptions = {
|
|
128
|
+
/** Rules to use instead of `DEFAULT_MOCKUP_KIND_RULES`. */
|
|
129
|
+
rules?: readonly MockupKindRule[];
|
|
130
|
+
/** When set, an explicit `mockup_kind` attribute is only honored if it is in this list. */
|
|
131
|
+
kinds?: readonly MockupKind[];
|
|
132
|
+
/** Kind when nothing matches (default `generic`). */
|
|
133
|
+
fallback?: MockupKind;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Picks a mockup kind from the product: the explicit `mockup_kind` attribute,
|
|
137
|
+
* then attribute rules (tire specs, part numbers), then words in the product
|
|
138
|
+
* type, category and tags. Falls back to `generic`.
|
|
139
|
+
*/
|
|
140
|
+
declare function resolveMockupKind(product: MockupProduct, options?: ResolveMockupKindOptions): MockupKind;
|
|
141
|
+
|
|
142
|
+
export { BUILTIN_MOCKUP_KINDS, type BuiltinMockupData, type BuiltinMockupKind, DEFAULT_MOCKUP_KIND_RULES, type GenericMockupData, MOCKUP_KIND_ATTRIBUTES, MOCKUP_TEXT_LIMITS, type MockupDataMap, type MockupKind, type MockupKindRule, type MockupProduct, type MockupVariant, type PartMockupData, type ResolveMockupKindOptions, type SupplementMockupData, type SupplementShape, type TireMockupData, cleanMockupText, genericMockupData, mockupDataFromProduct, mockupMonogram, partMockupData, resolveMockupKind, supplementMockupData, tireMockupData };
|
package/dist/mockup.d.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { W as Product, a0 as ProductVariant } from './types-BmC-RDo8.js';
|
|
2
|
+
import './geo.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Fallback product mockups: the pure, framework-agnostic half.
|
|
6
|
+
*
|
|
7
|
+
* When a product from OPS has no image, a store can draw an illustration
|
|
8
|
+
* (tire, part, supplement or a generic box) from the product's own data. This
|
|
9
|
+
* module decides WHICH illustration (`resolveMockupKind`) and WHAT it may show
|
|
10
|
+
* (`mockupDataFromProduct`). Rendering lives in `@asteby/storefront-react/mockups`.
|
|
11
|
+
*
|
|
12
|
+
* Rules every extractor follows:
|
|
13
|
+
* - Only data that is in the product (attributes, vendor, title, options, the
|
|
14
|
+
* selected variant). Nothing is guessed: a missing size stays missing.
|
|
15
|
+
* - Never prices.
|
|
16
|
+
* - Every string is cleaned (control characters removed, whitespace collapsed)
|
|
17
|
+
* and capped by `MOCKUP_TEXT_LIMITS`. Escaping is the renderer's job (React
|
|
18
|
+
* escapes text and attributes); these values are plain text, never markup.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** Built-in kinds. Stores can register more (any string). */
|
|
22
|
+
type BuiltinMockupKind = 'tire' | 'part' | 'supplement' | 'generic';
|
|
23
|
+
/** A mockup kind: a built-in one or any kind a store registers. */
|
|
24
|
+
type MockupKind = BuiltinMockupKind | (string & {});
|
|
25
|
+
declare const BUILTIN_MOCKUP_KINDS: readonly BuiltinMockupKind[];
|
|
26
|
+
/** The product fields mockups read. A full `Product` satisfies it. */
|
|
27
|
+
type MockupProduct = Pick<Product, 'title'> & Partial<Pick<Product, 'handle' | 'vendor' | 'product_type' | 'category' | 'tags' | 'attributes' | 'options'>>;
|
|
28
|
+
/** The variant fields mockups read (selected flavor, presentation...). */
|
|
29
|
+
type MockupVariant = Partial<Pick<ProductVariant, 'selected_options' | 'title'>>;
|
|
30
|
+
/** Maximum characters per field (counted in code points, ellipsis included). */
|
|
31
|
+
declare const MOCKUP_TEXT_LIMITS: {
|
|
32
|
+
readonly title: 80;
|
|
33
|
+
readonly brand: 32;
|
|
34
|
+
readonly model: 40;
|
|
35
|
+
readonly size: 24;
|
|
36
|
+
readonly service: 12;
|
|
37
|
+
readonly partNumber: 32;
|
|
38
|
+
readonly fitment: 90;
|
|
39
|
+
readonly position: 24;
|
|
40
|
+
readonly category: 40;
|
|
41
|
+
readonly flavor: 32;
|
|
42
|
+
readonly presentation: 32;
|
|
43
|
+
readonly monogram: 2;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Plain, single-line text capped at `max` code points (with `…` when cut).
|
|
47
|
+
* Returns `null` for empty values, so callers can skip the line entirely.
|
|
48
|
+
*/
|
|
49
|
+
declare function cleanMockupText(value: unknown, max?: number): string | null;
|
|
50
|
+
/** Up to two letters taken from the words of `text` (e.g. "Casa Textil" → "CT"). */
|
|
51
|
+
declare function mockupMonogram(text: string | null | undefined): string | null;
|
|
52
|
+
type MockupCommon = {
|
|
53
|
+
/** Product title, always present (the accessible name is built from it). */
|
|
54
|
+
title: string;
|
|
55
|
+
/** Brand (`vendor`). */
|
|
56
|
+
brand: string | null;
|
|
57
|
+
};
|
|
58
|
+
type TireMockupData = MockupCommon & {
|
|
59
|
+
kind: 'tire';
|
|
60
|
+
/** Size as written in OPS (`size_key`/`size`) or built from width/aspect/rim when all three exist. */
|
|
61
|
+
size: string | null;
|
|
62
|
+
/** Split size when width, aspect and rim are all known (for the big digits). */
|
|
63
|
+
width: string | null;
|
|
64
|
+
aspect: string | null;
|
|
65
|
+
rim: string | null;
|
|
66
|
+
/** Tread line / model (`model`, `line`, `tread`...). */
|
|
67
|
+
model: string | null;
|
|
68
|
+
/** Service description, e.g. `91V` or `121/118S` (load index + speed rating). */
|
|
69
|
+
service: string | null;
|
|
70
|
+
lightTruck: boolean;
|
|
71
|
+
runFlat: boolean;
|
|
72
|
+
};
|
|
73
|
+
type PartMockupData = MockupCommon & {
|
|
74
|
+
kind: 'part';
|
|
75
|
+
partNumber: string | null;
|
|
76
|
+
oemNumber: string | null;
|
|
77
|
+
/** Compatibility as sent by OPS (`fitment`, `compatibility`...). */
|
|
78
|
+
fitment: string | null;
|
|
79
|
+
position: string | null;
|
|
80
|
+
category: string | null;
|
|
81
|
+
};
|
|
82
|
+
type SupplementShape = 'tub' | 'bottle' | 'bag';
|
|
83
|
+
type SupplementMockupData = MockupCommon & {
|
|
84
|
+
kind: 'supplement';
|
|
85
|
+
flavor: string | null;
|
|
86
|
+
/** Net content / presentation, e.g. `5 lb`, `120 cápsulas`, or `servings` (number). */
|
|
87
|
+
presentation: string | null;
|
|
88
|
+
/** Number of servings when OPS sends it as a number (the label adds the unit). */
|
|
89
|
+
servings: number | null;
|
|
90
|
+
shape: SupplementShape;
|
|
91
|
+
};
|
|
92
|
+
type GenericMockupData = MockupCommon & {
|
|
93
|
+
kind: 'generic';
|
|
94
|
+
monogram: string | null;
|
|
95
|
+
category: string | null;
|
|
96
|
+
};
|
|
97
|
+
type MockupDataMap = {
|
|
98
|
+
tire: TireMockupData;
|
|
99
|
+
part: PartMockupData;
|
|
100
|
+
supplement: SupplementMockupData;
|
|
101
|
+
generic: GenericMockupData;
|
|
102
|
+
};
|
|
103
|
+
/** Data of any built-in kind. */
|
|
104
|
+
type BuiltinMockupData = MockupDataMap[BuiltinMockupKind];
|
|
105
|
+
/** Tire data from the product attributes (`size_key`, `section_width_mm`, `load_index`, `speed_rating`...). */
|
|
106
|
+
declare function tireMockupData(product: MockupProduct): TireMockupData;
|
|
107
|
+
/** Part data: part number, OEM number, compatibility and position (attributes), category title. */
|
|
108
|
+
declare function partMockupData(product: MockupProduct): PartMockupData;
|
|
109
|
+
/** Supplement data: flavor and presentation (selected variant, single-valued option or attribute). */
|
|
110
|
+
declare function supplementMockupData(product: MockupProduct, variant?: MockupVariant | null): SupplementMockupData;
|
|
111
|
+
/** Generic data: title, brand, a monogram of the title and the category. */
|
|
112
|
+
declare function genericMockupData(product: MockupProduct): GenericMockupData;
|
|
113
|
+
/** Data for a built-in kind; unknown kinds get the generic data. */
|
|
114
|
+
declare function mockupDataFromProduct<K extends BuiltinMockupKind>(kind: K, product: MockupProduct, variant?: MockupVariant | null): MockupDataMap[K];
|
|
115
|
+
declare function mockupDataFromProduct(kind: MockupKind, product: MockupProduct, variant?: MockupVariant | null): BuiltinMockupData;
|
|
116
|
+
/** Attribute a merchant can set in OPS to pick the mockup explicitly (`mockup`, `mockup_kind`). */
|
|
117
|
+
declare const MOCKUP_KIND_ATTRIBUTES: readonly ["mockup_kind", "mockup"];
|
|
118
|
+
type MockupKindRule = {
|
|
119
|
+
kind: MockupKind;
|
|
120
|
+
/** Matched against `product_type`, `category.title/handle` and `tags`. */
|
|
121
|
+
text?: RegExp;
|
|
122
|
+
/** Any of these attribute keys present (with a value) selects the kind. */
|
|
123
|
+
attributes?: readonly string[];
|
|
124
|
+
};
|
|
125
|
+
/** Default rules, checked in order: attributes first (precise), then words. */
|
|
126
|
+
declare const DEFAULT_MOCKUP_KIND_RULES: readonly MockupKindRule[];
|
|
127
|
+
type ResolveMockupKindOptions = {
|
|
128
|
+
/** Rules to use instead of `DEFAULT_MOCKUP_KIND_RULES`. */
|
|
129
|
+
rules?: readonly MockupKindRule[];
|
|
130
|
+
/** When set, an explicit `mockup_kind` attribute is only honored if it is in this list. */
|
|
131
|
+
kinds?: readonly MockupKind[];
|
|
132
|
+
/** Kind when nothing matches (default `generic`). */
|
|
133
|
+
fallback?: MockupKind;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Picks a mockup kind from the product: the explicit `mockup_kind` attribute,
|
|
137
|
+
* then attribute rules (tire specs, part numbers), then words in the product
|
|
138
|
+
* type, category and tags. Falls back to `generic`.
|
|
139
|
+
*/
|
|
140
|
+
declare function resolveMockupKind(product: MockupProduct, options?: ResolveMockupKindOptions): MockupKind;
|
|
141
|
+
|
|
142
|
+
export { BUILTIN_MOCKUP_KINDS, type BuiltinMockupData, type BuiltinMockupKind, DEFAULT_MOCKUP_KIND_RULES, type GenericMockupData, MOCKUP_KIND_ATTRIBUTES, MOCKUP_TEXT_LIMITS, type MockupDataMap, type MockupKind, type MockupKindRule, type MockupProduct, type MockupVariant, type PartMockupData, type ResolveMockupKindOptions, type SupplementMockupData, type SupplementShape, type TireMockupData, cleanMockupText, genericMockupData, mockupDataFromProduct, mockupMonogram, partMockupData, resolveMockupKind, supplementMockupData, tireMockupData };
|