@growupsell/commerce 1.0.0-alpha.1 → 1.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/checkout-CwpFl6qt.d.ts +560 -0
- package/dist/{chunk-YZJTPFCH.js → chunk-AI2CWQ22.js} +149 -56
- package/dist/chunk-AI2CWQ22.js.map +1 -0
- package/dist/chunk-DHBRITDW.js +2980 -0
- package/dist/chunk-DHBRITDW.js.map +1 -0
- package/dist/chunk-JBOZQJ4X.js +472 -0
- package/dist/chunk-JBOZQJ4X.js.map +1 -0
- package/dist/chunk-NNKXIBK4.js +1 -0
- package/dist/chunk-NNKXIBK4.js.map +1 -0
- package/dist/chunk-W2CJSBV4.js +329 -0
- package/dist/chunk-W2CJSBV4.js.map +1 -0
- package/dist/{index-tlJ3s67B.d.ts → client-D4JNDM6j.d.ts} +72 -279
- package/dist/http/index.d.ts +32 -2
- package/dist/http/index.js +2 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +123 -4
- package/dist/rules/index.d.ts +462 -78
- package/dist/rules/index.js +103 -3
- package/dist/sku-C3SGyRqT.d.ts +168 -0
- package/dist/stores/index.d.ts +836 -1
- package/dist/stores/index.js +22 -0
- package/package.json +1 -1
- package/dist/chunk-4PHF3WVQ.js +0 -76
- package/dist/chunk-4PHF3WVQ.js.map +0 -1
- package/dist/chunk-YZJTPFCH.js.map +0 -1
- package/dist/promotions-Ck9FZD7h.d.ts +0 -298
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// src/rules/catalog.ts
|
|
2
|
+
function categoryAndDescendants(tree, id) {
|
|
3
|
+
const found = find(tree, id);
|
|
4
|
+
if (!found) return [id];
|
|
5
|
+
const ids = /* @__PURE__ */ new Set();
|
|
6
|
+
const walk = (category) => {
|
|
7
|
+
if (ids.has(category.id)) return;
|
|
8
|
+
ids.add(category.id);
|
|
9
|
+
category.children.forEach(walk);
|
|
10
|
+
};
|
|
11
|
+
walk(found);
|
|
12
|
+
return [...ids];
|
|
13
|
+
}
|
|
14
|
+
function find(tree, id) {
|
|
15
|
+
for (const category of tree) {
|
|
16
|
+
if (category.id === id) return category;
|
|
17
|
+
const below = find(category.children, id);
|
|
18
|
+
if (below) return below;
|
|
19
|
+
}
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/rules/checkout.ts
|
|
24
|
+
function giftCardBalance(card) {
|
|
25
|
+
return roundMoney(Math.max(card.amount - card.usedAmount, 0));
|
|
26
|
+
}
|
|
27
|
+
function requiresShippingAddress(lines) {
|
|
28
|
+
return lines.length === 0 || lines.some((line) => !line.isDigital);
|
|
29
|
+
}
|
|
30
|
+
function paymentOptions(input) {
|
|
31
|
+
const { cart, features } = input;
|
|
32
|
+
const online = cart.type !== "cod";
|
|
33
|
+
return {
|
|
34
|
+
cashOnDelivery: input.settings.allowCashOnDelivery && !cart.lines.some((l) => l.isDigital),
|
|
35
|
+
giftCards: online && features.includes("giftcard"),
|
|
36
|
+
rewardPoints: online && features.includes("reward-point") && input.rewardPoints.enabled && input.availablePoints > 0 && input.giftCardsSelected !== true
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function checkoutPreview(input) {
|
|
40
|
+
const balances = (input.giftCards ?? []).reduce((sum, card) => sum + giftCardBalance(card), 0);
|
|
41
|
+
const giftCardAmount = Math.min(balances, input.payable);
|
|
42
|
+
const remaining = input.payable - giftCardAmount;
|
|
43
|
+
let pointsUsed = 0;
|
|
44
|
+
const { rewardPoints } = input;
|
|
45
|
+
if (rewardPoints && rewardPoints.currencyPerPoint > 0) {
|
|
46
|
+
const rate = rewardPoints.currencyPerPoint;
|
|
47
|
+
pointsUsed = Math.max(
|
|
48
|
+
Math.min(rewardPoints.available, input.subtotal / rate, remaining / rate),
|
|
49
|
+
0
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const rewardPointsAmount = pointsUsed * (rewardPoints?.currencyPerPoint ?? 0);
|
|
53
|
+
return {
|
|
54
|
+
giftCardAmount: roundMoney(giftCardAmount),
|
|
55
|
+
rewardPointsAmount: roundMoney(rewardPointsAmount),
|
|
56
|
+
pointsUsed: roundMoney(pointsUsed),
|
|
57
|
+
toPay: roundMoney(Math.max(remaining - rewardPointsAmount, 0))
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function roundMoney(value) {
|
|
61
|
+
return Math.round(value * 100) / 100;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/rules/content.ts
|
|
65
|
+
function imagesFor(images, selection) {
|
|
66
|
+
const options = new Set(selection.variantIds);
|
|
67
|
+
const shown = images.filter(
|
|
68
|
+
(image) => image.skuId !== null ? image.skuId === selection.skuId : image.variantId === null || options.has(image.variantId)
|
|
69
|
+
);
|
|
70
|
+
return shown.length > 0 ? shown : [...images];
|
|
71
|
+
}
|
|
72
|
+
function sectionItemsFor(items, selection) {
|
|
73
|
+
const options = new Set(selection.variantIds);
|
|
74
|
+
return items.filter(
|
|
75
|
+
({ target }) => target === "all" ? true : "variantId" in target ? options.has(target.variantId) : target.skuId === selection.skuId
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/rules/listing.ts
|
|
80
|
+
var LISTING_PAGE_SIZE = 20;
|
|
81
|
+
var DEFAULT_LISTING_CRITERIA = Object.freeze({
|
|
82
|
+
categoryId: null,
|
|
83
|
+
search: null,
|
|
84
|
+
brandIds: [],
|
|
85
|
+
variantLabels: [],
|
|
86
|
+
specifications: [],
|
|
87
|
+
minPrice: null,
|
|
88
|
+
maxPrice: null,
|
|
89
|
+
minDiscountPercent: null,
|
|
90
|
+
minRating: null,
|
|
91
|
+
sortBy: "newest",
|
|
92
|
+
page: 1
|
|
93
|
+
});
|
|
94
|
+
var SORTS = [
|
|
95
|
+
"newest",
|
|
96
|
+
"price-low-to-high",
|
|
97
|
+
"price-high-to-low",
|
|
98
|
+
"popularity",
|
|
99
|
+
"category"
|
|
100
|
+
];
|
|
101
|
+
var SPEC_PREFIX = "spec.";
|
|
102
|
+
function listingQuery(criteria) {
|
|
103
|
+
const query = {};
|
|
104
|
+
if (criteria.categoryId !== null) query.category = String(criteria.categoryId);
|
|
105
|
+
const search = criteria.search?.trim();
|
|
106
|
+
if (search) query.q = search;
|
|
107
|
+
if (criteria.brandIds.length > 0) query.brand = criteria.brandIds.join(",");
|
|
108
|
+
if (criteria.variantLabels.length > 0) query.variant = [...criteria.variantLabels];
|
|
109
|
+
for (const { id, values: values2 } of criteria.specifications) {
|
|
110
|
+
if (values2.length > 0) query[SPEC_PREFIX + id] = [...values2];
|
|
111
|
+
}
|
|
112
|
+
if (criteria.minPrice !== null) query.min = String(criteria.minPrice);
|
|
113
|
+
if (criteria.maxPrice !== null) query.max = String(criteria.maxPrice);
|
|
114
|
+
if (criteria.minDiscountPercent !== null) query.discount = String(criteria.minDiscountPercent);
|
|
115
|
+
if (criteria.minRating !== null) query.rating = String(criteria.minRating);
|
|
116
|
+
if (criteria.sortBy !== "newest") query.sort = criteria.sortBy;
|
|
117
|
+
if (criteria.page !== 1) query.page = String(criteria.page);
|
|
118
|
+
return query;
|
|
119
|
+
}
|
|
120
|
+
function parseListingQuery(query) {
|
|
121
|
+
let minPrice = inRange(first(query.min), 0, Infinity);
|
|
122
|
+
let maxPrice = inRange(first(query.max), 0, Infinity);
|
|
123
|
+
if (minPrice !== null && maxPrice !== null && minPrice > maxPrice) {
|
|
124
|
+
[minPrice, maxPrice] = [maxPrice, minPrice];
|
|
125
|
+
}
|
|
126
|
+
const sort = first(query.sort);
|
|
127
|
+
return {
|
|
128
|
+
categoryId: positiveInt(first(query.category)),
|
|
129
|
+
search: first(query.q)?.trim() || null,
|
|
130
|
+
brandIds: [
|
|
131
|
+
...new Set(
|
|
132
|
+
(first(query.brand) ?? "").split(",").map(positiveInt).filter((id) => id !== null)
|
|
133
|
+
)
|
|
134
|
+
].sort((a, b) => a - b),
|
|
135
|
+
variantLabels: values(query.variant),
|
|
136
|
+
specifications: Object.keys(query).flatMap((key) => {
|
|
137
|
+
if (!key.startsWith(SPEC_PREFIX)) return [];
|
|
138
|
+
const id = positiveInt(key.slice(SPEC_PREFIX.length));
|
|
139
|
+
const selected = values(query[key]);
|
|
140
|
+
return id !== null && selected.length > 0 ? [{ id, values: selected }] : [];
|
|
141
|
+
}),
|
|
142
|
+
minPrice,
|
|
143
|
+
maxPrice,
|
|
144
|
+
minDiscountPercent: inRange(first(query.discount), 1, 100),
|
|
145
|
+
minRating: inRange(first(query.rating), 1, 5),
|
|
146
|
+
sortBy: SORTS.find((s) => s === sort) ?? "newest",
|
|
147
|
+
page: positiveInt(first(query.page)) ?? 1
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function withCategory(criteria, categoryId) {
|
|
151
|
+
return { ...criteria, categoryId, brandIds: [], variantLabels: [], specifications: [], page: 1 };
|
|
152
|
+
}
|
|
153
|
+
function listingSearchOptions(criteria, categoryIds, pageSize) {
|
|
154
|
+
const options = {};
|
|
155
|
+
const search = criteria.search?.trim();
|
|
156
|
+
if (search) options.search = search;
|
|
157
|
+
if (categoryIds.length > 0) options.categoryIds = [...categoryIds];
|
|
158
|
+
if (criteria.brandIds.length > 0) options.brandIds = [...criteria.brandIds];
|
|
159
|
+
if (criteria.variantLabels.length > 0) options.variantLabels = [...criteria.variantLabels];
|
|
160
|
+
const specifications = criteria.specifications.filter((s) => s.values.length > 0);
|
|
161
|
+
if (specifications.length > 0) {
|
|
162
|
+
options.specifications = specifications.map(({ id, values: values2 }) => ({ id, values: [...values2] }));
|
|
163
|
+
}
|
|
164
|
+
if (criteria.minPrice !== null) options.minPrice = criteria.minPrice;
|
|
165
|
+
if (criteria.maxPrice !== null) options.maxPrice = criteria.maxPrice;
|
|
166
|
+
if (criteria.minDiscountPercent !== null) {
|
|
167
|
+
options.minDiscountPercent = criteria.minDiscountPercent;
|
|
168
|
+
}
|
|
169
|
+
if (criteria.minRating !== null) options.minRating = criteria.minRating;
|
|
170
|
+
options.sortBy = criteria.sortBy;
|
|
171
|
+
options.limit = pageSize;
|
|
172
|
+
options.offset = (criteria.page - 1) * pageSize;
|
|
173
|
+
return options;
|
|
174
|
+
}
|
|
175
|
+
function parseSelectedVariants(value) {
|
|
176
|
+
if (!value) return [];
|
|
177
|
+
const ids = value.split(",").map(positiveInt).filter((id) => id !== null);
|
|
178
|
+
return [...new Set(ids)];
|
|
179
|
+
}
|
|
180
|
+
function formatSelectedVariants(ids) {
|
|
181
|
+
return ids.join(",");
|
|
182
|
+
}
|
|
183
|
+
function first(value) {
|
|
184
|
+
return typeof value === "string" ? value : value?.[0];
|
|
185
|
+
}
|
|
186
|
+
function values(value) {
|
|
187
|
+
const list = typeof value === "string" ? [value] : value ?? [];
|
|
188
|
+
return [...new Set(list.filter((v) => v !== ""))];
|
|
189
|
+
}
|
|
190
|
+
function toNumber(value) {
|
|
191
|
+
if (value === void 0 || value.trim() === "") return null;
|
|
192
|
+
const n = Number(value);
|
|
193
|
+
return Number.isFinite(n) ? n : null;
|
|
194
|
+
}
|
|
195
|
+
function positiveInt(value) {
|
|
196
|
+
const n = toNumber(value);
|
|
197
|
+
return n !== null && Number.isInteger(n) && n > 0 ? n : null;
|
|
198
|
+
}
|
|
199
|
+
function inRange(value, min, max) {
|
|
200
|
+
const n = toNumber(value);
|
|
201
|
+
return n !== null && n >= min && n <= max ? n : null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/rules/pricing.ts
|
|
205
|
+
function discountPercent({ price, mrp }) {
|
|
206
|
+
if (mrp == null || mrp <= 0 || price <= 0 || price >= mrp) return 0;
|
|
207
|
+
return Math.round((mrp - price) / mrp * 100);
|
|
208
|
+
}
|
|
209
|
+
function productPricing(item) {
|
|
210
|
+
const { price, mrp } = item;
|
|
211
|
+
return {
|
|
212
|
+
price,
|
|
213
|
+
compareAtPrice: mrp != null && price > 0 && mrp > price ? mrp : null,
|
|
214
|
+
discountPercent: discountPercent(item)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/rules/sku.ts
|
|
219
|
+
function sellableSkus(skus) {
|
|
220
|
+
const active = skus.filter((sku) => sku.active);
|
|
221
|
+
const withVariants = active.filter((sku) => sku.variantIds.length > 0);
|
|
222
|
+
return withVariants.length > 0 ? withVariants : active;
|
|
223
|
+
}
|
|
224
|
+
function resolveSku(skus, selectedVariantIds) {
|
|
225
|
+
const wanted = new Set(selectedVariantIds);
|
|
226
|
+
return sellableSkus(skus).find(
|
|
227
|
+
(sku) => sku.variantIds.length === wanted.size && sku.variantIds.every((id) => wanted.has(id))
|
|
228
|
+
) ?? null;
|
|
229
|
+
}
|
|
230
|
+
function groupIndexOf(groups) {
|
|
231
|
+
const index = /* @__PURE__ */ new Map();
|
|
232
|
+
groups.forEach((group, i) => group.options.forEach((option) => index.set(option.id, i)));
|
|
233
|
+
return index;
|
|
234
|
+
}
|
|
235
|
+
function cleanSelection(groups, ids) {
|
|
236
|
+
const index = groupIndexOf(groups);
|
|
237
|
+
const byGroup = [];
|
|
238
|
+
for (const id of ids) {
|
|
239
|
+
const group = index.get(id);
|
|
240
|
+
if (group !== void 0 && byGroup[group] === void 0) byGroup[group] = id;
|
|
241
|
+
}
|
|
242
|
+
return byGroup.filter((id) => id !== void 0);
|
|
243
|
+
}
|
|
244
|
+
function isSelectionComplete(groups, selectedVariantIds) {
|
|
245
|
+
const index = groupIndexOf(groups);
|
|
246
|
+
const counts = groups.map(() => 0);
|
|
247
|
+
for (const id of selectedVariantIds) {
|
|
248
|
+
const group = index.get(id);
|
|
249
|
+
if (group !== void 0) counts[group] += 1;
|
|
250
|
+
}
|
|
251
|
+
return counts.every((count) => count === 1);
|
|
252
|
+
}
|
|
253
|
+
function selectOption(groups, selectedVariantIds, optionId) {
|
|
254
|
+
const index = groupIndexOf(groups);
|
|
255
|
+
const group = index.get(optionId);
|
|
256
|
+
if (group === void 0) return cleanSelection(groups, selectedVariantIds);
|
|
257
|
+
const others = selectedVariantIds.filter((id) => index.get(id) !== group);
|
|
258
|
+
return cleanSelection(groups, [optionId, ...others]);
|
|
259
|
+
}
|
|
260
|
+
function defaultSelection(product, requested = []) {
|
|
261
|
+
const groups = product.variantGroups;
|
|
262
|
+
if (groups.length === 0) return [];
|
|
263
|
+
const valid = cleanSelection(groups, requested);
|
|
264
|
+
if (isSelectionComplete(groups, valid) && resolveSku(product.skus, valid)) return valid;
|
|
265
|
+
const sellable = sellableSkus(product.skus);
|
|
266
|
+
const candidates = sellable.filter((sku) => valid.every((id) => sku.variantIds.includes(id)));
|
|
267
|
+
const inStock = (skus) => skus.find((sku) => sku.stockQty > 0);
|
|
268
|
+
const pick = inStock(candidates) ?? candidates[0] ?? inStock(sellable) ?? sellable[0];
|
|
269
|
+
return pick ? cleanSelection(groups, pick.variantIds) : valid;
|
|
270
|
+
}
|
|
271
|
+
function variantChoices(product, selectedVariantIds) {
|
|
272
|
+
const groups = product.variantGroups;
|
|
273
|
+
const selection = cleanSelection(groups, selectedVariantIds);
|
|
274
|
+
const sellable = sellableSkus(product.skus);
|
|
275
|
+
return groups.map((group) => {
|
|
276
|
+
const ownIds = new Set(group.options.map((option) => option.id));
|
|
277
|
+
const others = selection.filter((id) => !ownIds.has(id));
|
|
278
|
+
return {
|
|
279
|
+
groupId: group.id,
|
|
280
|
+
name: group.name,
|
|
281
|
+
selectedOptionId: selection.find((id) => ownIds.has(id)) ?? null,
|
|
282
|
+
options: group.options.map((option) => ({
|
|
283
|
+
...option,
|
|
284
|
+
selectable: sellable.some(
|
|
285
|
+
(sku) => sku.variantIds.includes(option.id) && others.every((id) => sku.variantIds.includes(id))
|
|
286
|
+
)
|
|
287
|
+
}))
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/rules/specifications.ts
|
|
293
|
+
function specificationsFor(groups, selection) {
|
|
294
|
+
const options = new Set(selection.variantIds);
|
|
295
|
+
return groups.map((group) => ({
|
|
296
|
+
...group,
|
|
297
|
+
items: group.items.filter(
|
|
298
|
+
({ appliesTo }) => appliesTo.type === "all" ? true : appliesTo.type === "variant" ? options.has(appliesTo.variantId) : appliesTo.skuId === selection.skuId
|
|
299
|
+
)
|
|
300
|
+
})).filter((group) => group.items.length > 0);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export {
|
|
304
|
+
categoryAndDescendants,
|
|
305
|
+
giftCardBalance,
|
|
306
|
+
requiresShippingAddress,
|
|
307
|
+
paymentOptions,
|
|
308
|
+
checkoutPreview,
|
|
309
|
+
imagesFor,
|
|
310
|
+
sectionItemsFor,
|
|
311
|
+
LISTING_PAGE_SIZE,
|
|
312
|
+
DEFAULT_LISTING_CRITERIA,
|
|
313
|
+
listingQuery,
|
|
314
|
+
parseListingQuery,
|
|
315
|
+
withCategory,
|
|
316
|
+
listingSearchOptions,
|
|
317
|
+
parseSelectedVariants,
|
|
318
|
+
formatSelectedVariants,
|
|
319
|
+
discountPercent,
|
|
320
|
+
productPricing,
|
|
321
|
+
sellableSkus,
|
|
322
|
+
resolveSku,
|
|
323
|
+
isSelectionComplete,
|
|
324
|
+
selectOption,
|
|
325
|
+
defaultSelection,
|
|
326
|
+
variantChoices,
|
|
327
|
+
specificationsFor
|
|
328
|
+
};
|
|
329
|
+
//# sourceMappingURL=chunk-W2CJSBV4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rules/catalog.ts","../src/rules/checkout.ts","../src/rules/content.ts","../src/rules/listing.ts","../src/rules/pricing.ts","../src/rules/sku.ts","../src/rules/specifications.ts"],"sourcesContent":["/** Catalog rules: working with the category tree. */\nimport type { CategoryTree } from '../types/catalog';\n\n/**\n * The category and every category below it, depth first in tree order: `[id, ...descendants]`.\n * A listing filters by all of them, because the backend expands only one level (W71). An id\n * missing from the tree gives `[id]`, so the listing still filters by it. Each id appears once.\n */\nexport function categoryAndDescendants(tree: readonly CategoryTree[], id: number): number[] {\n const found = find(tree, id);\n if (!found) return [id];\n const ids = new Set<number>();\n const walk = (category: CategoryTree): void => {\n if (ids.has(category.id)) return;\n ids.add(category.id);\n category.children.forEach(walk);\n };\n walk(found);\n return [...ids];\n}\n\nfunction find(tree: readonly CategoryTree[], id: number): CategoryTree | undefined {\n for (const category of tree) {\n if (category.id === id) return category;\n const below = find(category.children, id);\n if (below) return below;\n }\n return undefined;\n}\n","/**\n * Checkout previews: what the shopper sees before placing the order. The backend calculates\n * the cart totals and charges gift cards and reward points itself (ADR-001); these rules only\n * preview the result. The confirmed amount is what `checkout.placeOrder()` returns.\n */\nimport type { CartLine, OrderType } from '../types/cart';\nimport type { GiftCard, RewardPointSettings } from '../types/promotions';\n\ntype GiftCardAmounts = Pick<GiftCard, 'amount' | 'usedAmount'>;\n\n/** What is left on a gift card: amount minus used, never negative. */\nexport function giftCardBalance(card: GiftCardAmounts): number {\n return roundMoney(Math.max(card.amount - card.usedAmount, 0));\n}\n\n/**\n * Whether checkout needs a shipping address: always, unless the cart has lines and every line\n * is digital (the backend's rule, `Order.is_all_digital_order`).\n */\nexport function requiresShippingAddress(lines: readonly Pick<CartLine, 'isDigital'>[]): boolean {\n return lines.length === 0 || lines.some((line) => !line.isDigital);\n}\n\nexport interface PaymentOptionsInput {\n /** A `Cart`, or just its type and the lines' `isDigital`. */\n cart: { type: OrderType; lines: readonly Pick<CartLine, 'isDigital'>[] };\n /** From `checkout.settings()`. */\n settings: { allowCashOnDelivery: boolean };\n rewardPoints: Pick<RewardPointSettings, 'enabled'>;\n /** The customer's reward point balance. */\n availablePoints: number;\n /** The business's enabled features (`giftcard`, `reward-point`, …). */\n features: readonly string[];\n /** Gift cards are selected for this order. */\n giftCardsSelected?: boolean;\n}\n\nexport interface PaymentOptions {\n cashOnDelivery: boolean;\n giftCards: boolean;\n rewardPoints: boolean;\n}\n\n/**\n * Which payment options to offer:\n * - cash on delivery when the business allows it and no line is digital (the backend accepts\n * both anyway, W39);\n * - gift cards and reward points only for online payment (`delivery` carts; the backend refuses\n * them with COD) and when the business has the feature;\n * - reward points also need the setting, a positive balance, and no gift cards selected: the\n * backend over-charges gift cards when both are used (W36; ADR-023).\n */\nexport function paymentOptions(input: PaymentOptionsInput): PaymentOptions {\n const { cart, features } = input;\n const online = cart.type !== 'cod';\n return {\n cashOnDelivery: input.settings.allowCashOnDelivery && !cart.lines.some((l) => l.isDigital),\n giftCards: online && features.includes('giftcard'),\n rewardPoints:\n online &&\n features.includes('reward-point') &&\n input.rewardPoints.enabled &&\n input.availablePoints > 0 &&\n input.giftCardsSelected !== true,\n };\n}\n\nexport interface CheckoutPreviewInput {\n /** `Cart.payable`. */\n payable: number;\n /** `Cart.subtotal`: the backend caps reward points at its value. */\n subtotal: number;\n /** The selected gift cards (from `promotions.validateGiftCard()`). */\n giftCards?: readonly GiftCardAmounts[];\n /** To spend reward points: the balance and `RewardPointSettings.currencyPerPoint`. */\n rewardPoints?: { available: number; currencyPerPoint: number };\n}\n\nexport interface CheckoutPreview {\n giftCardAmount: number;\n rewardPointsAmount: number;\n pointsUsed: number;\n /** What is left to pay online; never negative. */\n toPay: number;\n}\n\n/**\n * Previews gift cards, then reward points, against the payable. Gift cards cover at most the\n * payable; points at most what is left, the subtotal and the balance. Unlike the old\n * storefront, the result never goes below zero.\n */\nexport function checkoutPreview(input: CheckoutPreviewInput): CheckoutPreview {\n const balances = (input.giftCards ?? []).reduce((sum, card) => sum + giftCardBalance(card), 0);\n const giftCardAmount = Math.min(balances, input.payable);\n const remaining = input.payable - giftCardAmount;\n\n let pointsUsed = 0;\n const { rewardPoints } = input;\n if (rewardPoints && rewardPoints.currencyPerPoint > 0) {\n const rate = rewardPoints.currencyPerPoint;\n // The first two caps are the backend's (`calculate_reward_points_debit`); the last keeps\n // the preview from spending points on nothing.\n pointsUsed = Math.max(\n Math.min(rewardPoints.available, input.subtotal / rate, remaining / rate),\n 0,\n );\n }\n const rewardPointsAmount = pointsUsed * (rewardPoints?.currencyPerPoint ?? 0);\n\n return {\n giftCardAmount: roundMoney(giftCardAmount),\n rewardPointsAmount: roundMoney(rewardPointsAmount),\n pointsUsed: roundMoney(pointsUsed),\n toPay: roundMoney(Math.max(remaining - rewardPointsAmount, 0)),\n };\n}\n\nfunction roundMoney(value: number): number {\n return Math.round(value * 100) / 100;\n}\n","/**\n * Variant-dependent content: which images and product-page section items to show for the\n * shopper's selection.\n */\nimport type { ProductImage } from '../types/product';\nimport type { ProductSectionItem } from '../types/productPage';\n\n/** What the shopper has selected: the resolved SKU (null while incomplete) and the options. */\nexport interface VariantSelection {\n skuId: number | null;\n variantIds: readonly number[];\n}\n\n/**\n * The images to show for a selection: images of the resolved SKU, images of a selected variant\n * option, and untagged images, in their order. When none match, all images (the old storefront\n * showed an empty gallery). Without a resolved SKU, SKU images are hidden.\n */\nexport function imagesFor(\n images: readonly ProductImage[],\n selection: VariantSelection,\n): ProductImage[] {\n const options = new Set(selection.variantIds);\n const shown = images.filter((image) =>\n image.skuId !== null\n ? image.skuId === selection.skuId\n : image.variantId === null || options.has(image.variantId),\n );\n return shown.length > 0 ? shown : [...images];\n}\n\n/**\n * The section items to show for a selection: items for everyone, items of a selected variant\n * option, and items of the resolved SKU (hidden without one). Showing everything in an editor\n * is the app's choice.\n */\nexport function sectionItemsFor(\n items: readonly ProductSectionItem[],\n selection: VariantSelection,\n): ProductSectionItem[] {\n const options = new Set(selection.variantIds);\n return items.filter(({ target }) =>\n target === 'all'\n ? true\n : 'variantId' in target\n ? options.has(target.variantId)\n : target.skuId === selection.skuId,\n );\n}\n","/**\n * Listing rules: the listing criteria, their URL form and the `products.search()` options they\n * map to. Pure functions over plain query records (no `URL`, no `URLSearchParams`), so any\n * router (Next.js, React Router, Expo Router, Angular) can use them.\n */\nimport type { ListingCriteria, ListingQuery } from '../types/listing';\nimport type { ProductSearchOptions, ProductSort } from '../types/product';\n\n/** URL query values as routers hand them over: one string, several, or missing. */\ntype QueryInput = Readonly<Record<string, string | readonly string[] | undefined>>;\n\n/** Products per listing page (ADR-025). */\nexport const LISTING_PAGE_SIZE = 20;\n\n/** Newest first (the old storefront's default), page 1, no filters. */\nexport const DEFAULT_LISTING_CRITERIA: Readonly<ListingCriteria> = Object.freeze({\n categoryId: null,\n search: null,\n brandIds: [],\n variantLabels: [],\n specifications: [],\n minPrice: null,\n maxPrice: null,\n minDiscountPercent: null,\n minRating: null,\n sortBy: 'newest',\n page: 1,\n});\n\nconst SORTS: readonly ProductSort[] = [\n 'newest',\n 'price-low-to-high',\n 'price-high-to-low',\n 'popularity',\n 'category',\n];\nconst SPEC_PREFIX = 'spec.';\n\n/**\n * Criteria as URL query values. Keys: `category`, `q`, `brand` (comma-joined ids), `variant`\n * (one entry per label: labels can contain commas), `spec.<id>` (one entry per value), `min`,\n * `max`, `discount`, `rating`, `sort` and `page`. Defaults are left out. The app may move\n * `category` into its path (`/products/:name/:id`).\n */\nexport function listingQuery(criteria: ListingCriteria): ListingQuery {\n const query: ListingQuery = {};\n if (criteria.categoryId !== null) query.category = String(criteria.categoryId);\n const search = criteria.search?.trim();\n if (search) query.q = search;\n if (criteria.brandIds.length > 0) query.brand = criteria.brandIds.join(',');\n if (criteria.variantLabels.length > 0) query.variant = [...criteria.variantLabels];\n for (const { id, values } of criteria.specifications) {\n if (values.length > 0) query[SPEC_PREFIX + id] = [...values];\n }\n if (criteria.minPrice !== null) query.min = String(criteria.minPrice);\n if (criteria.maxPrice !== null) query.max = String(criteria.maxPrice);\n if (criteria.minDiscountPercent !== null) query.discount = String(criteria.minDiscountPercent);\n if (criteria.minRating !== null) query.rating = String(criteria.minRating);\n if (criteria.sortBy !== 'newest') query.sort = criteria.sortBy;\n if (criteria.page !== 1) query.page = String(criteria.page);\n return query;\n}\n\n/**\n * Criteria from URL query values (deep links, back/forward). Never throws: invalid values are\n * dropped, a single string is accepted where a list is expected, the first value is taken\n * where one is expected, and a minimum price above the maximum is swapped.\n */\nexport function parseListingQuery(query: QueryInput): ListingCriteria {\n let minPrice = inRange(first(query.min), 0, Infinity);\n let maxPrice = inRange(first(query.max), 0, Infinity);\n if (minPrice !== null && maxPrice !== null && minPrice > maxPrice) {\n [minPrice, maxPrice] = [maxPrice, minPrice];\n }\n const sort = first(query.sort);\n return {\n categoryId: positiveInt(first(query.category)),\n search: first(query.q)?.trim() || null,\n brandIds: [\n ...new Set(\n (first(query.brand) ?? '')\n .split(',')\n .map(positiveInt)\n .filter((id) => id !== null),\n ),\n ].sort((a, b) => a - b),\n variantLabels: values(query.variant),\n specifications: Object.keys(query).flatMap((key) => {\n if (!key.startsWith(SPEC_PREFIX)) return [];\n const id = positiveInt(key.slice(SPEC_PREFIX.length));\n const selected = values(query[key]);\n return id !== null && selected.length > 0 ? [{ id, values: selected }] : [];\n }),\n minPrice,\n maxPrice,\n minDiscountPercent: inRange(first(query.discount), 1, 100),\n minRating: inRange(first(query.rating), 1, 5),\n sortBy: SORTS.find((s) => s === sort) ?? 'newest',\n page: positiveInt(first(query.page)) ?? 1,\n };\n}\n\n/**\n * The criteria for another category (or all products with null): brands, variants,\n * specifications and the page are reset, because the new category offers other ones; search,\n * sort, price, rating and discount are kept (ADR-025).\n */\nexport function withCategory(\n criteria: ListingCriteria,\n categoryId: number | null,\n): ListingCriteria {\n return { ...criteria, categoryId, brandIds: [], variantLabels: [], specifications: [], page: 1 };\n}\n\n/**\n * The `products.search()` options for the criteria. `categoryIds` must already include the\n * descendants (`categoryAndDescendants`: the backend expands only one level, W71). Empty\n * filters and unset prices are left out.\n */\nexport function listingSearchOptions(\n criteria: ListingCriteria,\n categoryIds: readonly number[],\n pageSize: number,\n): ProductSearchOptions {\n const options: ProductSearchOptions = {};\n const search = criteria.search?.trim();\n if (search) options.search = search;\n if (categoryIds.length > 0) options.categoryIds = [...categoryIds];\n if (criteria.brandIds.length > 0) options.brandIds = [...criteria.brandIds];\n if (criteria.variantLabels.length > 0) options.variantLabels = [...criteria.variantLabels];\n const specifications = criteria.specifications.filter((s) => s.values.length > 0);\n if (specifications.length > 0) {\n options.specifications = specifications.map(({ id, values }) => ({ id, values: [...values] }));\n }\n if (criteria.minPrice !== null) options.minPrice = criteria.minPrice;\n if (criteria.maxPrice !== null) options.maxPrice = criteria.maxPrice;\n if (criteria.minDiscountPercent !== null) {\n options.minDiscountPercent = criteria.minDiscountPercent;\n }\n if (criteria.minRating !== null) options.minRating = criteria.minRating;\n options.sortBy = criteria.sortBy;\n options.limit = pageSize;\n options.offset = (criteria.page - 1) * pageSize;\n return options;\n}\n\n/**\n * Option ids from a product link's `?selected_variants=` value, e.g. \"31,33\" → [31, 33].\n * Anything that is not a positive whole number is ignored; repeats are dropped.\n */\nexport function parseSelectedVariants(value: string | null | undefined): number[] {\n if (!value) return [];\n const ids = value\n .split(',')\n .map(positiveInt)\n .filter((id) => id !== null);\n return [...new Set(ids)];\n}\n\n/** The `?selected_variants=` value for option ids, e.g. [31, 33] → \"31,33\". */\nexport function formatSelectedVariants(ids: readonly number[]): string {\n return ids.join(',');\n}\n\nfunction first(value: string | readonly string[] | undefined): string | undefined {\n return typeof value === 'string' ? value : value?.[0];\n}\n\nfunction values(value: string | readonly string[] | undefined): string[] {\n const list = typeof value === 'string' ? [value] : (value ?? []);\n return [...new Set(list.filter((v) => v !== ''))];\n}\n\nfunction toNumber(value: string | undefined): number | null {\n if (value === undefined || value.trim() === '') return null;\n const n = Number(value);\n return Number.isFinite(n) ? n : null;\n}\n\nfunction positiveInt(value: string | undefined): number | null {\n const n = toNumber(value);\n return n !== null && Number.isInteger(n) && n > 0 ? n : null;\n}\n\nfunction inRange(value: string | undefined, min: number, max: number): number | null {\n const n = toNumber(value);\n return n !== null && n >= min && n <= max ? n : null;\n}\n","/**\n * Pricing rules. The one place a discount is calculated, for every app (ADR-001).\n */\n\n/**\n * Whole-percent discount of `price` against `mrp`, e.g. 58999 vs 60000 → 2.\n * 0 when there is no MRP, the item is free, or the price is at or above the MRP (the backend\n * reports a negative discount in that case; a negative discount is never shown).\n */\nexport function discountPercent({ price, mrp }: { price: number; mrp: number | null }): number {\n if (mrp == null || mrp <= 0 || price <= 0 || price >= mrp) return 0;\n return Math.round(((mrp - price) / mrp) * 100);\n}\n\n/** What to show for a price: the price, the MRP struck through (or null), the discount. */\nexport interface ProductPricing {\n price: number;\n /** The MRP to strike through: only when it is above a positive price. */\n compareAtPrice: number | null;\n /** Whole percent, from `discountPercent()`. */\n discountPercent: number;\n}\n\n/**\n * The price to show and the MRP to strike through: the MRP only when it is above a price above\n * zero (the old storefront fell back to the price, compared strings, and struck through a\n * missing or lower MRP). Works for a `ProductSku`, a `ProductListItem`, or a `CartLine` mapped\n * as `{ price: line.unitPrice, mrp: line.mrp }`. The price is returned as the backend sent it.\n */\nexport function productPricing(item: { price: number; mrp: number | null }): ProductPricing {\n const { price, mrp } = item;\n return {\n price,\n compareAtPrice: mrp != null && price > 0 && mrp > price ? mrp : null,\n discountPercent: discountPercent(item),\n };\n}\n","import type { ProductDetail, ProductSku, VariantGroup, VariantOption } from '../types/product';\n\n/**\n * The SKUs a shopper can buy: active SKUs, and when any of them is made of variant options,\n * only those (the base SKU of a product with variants is never offered, even while the\n * backend keeps it active). Order is kept.\n */\nexport function sellableSkus(skus: readonly ProductSku[]): ProductSku[] {\n const active = skus.filter((sku) => sku.active);\n const withVariants = active.filter((sku) => sku.variantIds.length > 0);\n return withVariants.length > 0 ? withVariants : active;\n}\n\n/**\n * The SKU a customer is buying: the sellable SKU made of exactly the selected variant options\n * (order does not matter). A product without variants has one SKU, chosen with no selection.\n * Returns null while the selection is incomplete or matches nothing: never falls back to\n * another SKU (the old storefront's `sku[0]` showed a deleted SKU's price), and never picks the\n * base SKU of a product with variants.\n */\nexport function resolveSku(\n skus: readonly ProductSku[],\n selectedVariantIds: readonly number[],\n): ProductSku | null {\n const wanted = new Set(selectedVariantIds);\n return (\n sellableSkus(skus).find(\n (sku) =>\n sku.variantIds.length === wanted.size && sku.variantIds.every((id) => wanted.has(id)),\n ) ?? null\n );\n}\n\n/** Option id → index of its group, for every option of every group. */\nfunction groupIndexOf(groups: readonly VariantGroup[]): Map<number, number> {\n const index = new Map<number, number>();\n groups.forEach((group, i) => group.options.forEach((option) => index.set(option.id, i)));\n return index;\n}\n\n/**\n * One option per group, the first given winning, in group order; ids that belong to no group\n * are dropped.\n */\nfunction cleanSelection(groups: readonly VariantGroup[], ids: readonly number[]): number[] {\n const index = groupIndexOf(groups);\n const byGroup: Array<number | undefined> = [];\n for (const id of ids) {\n const group = index.get(id);\n if (group !== undefined && byGroup[group] === undefined) byGroup[group] = id;\n }\n return byGroup.filter((id): id is number => id !== undefined);\n}\n\n/**\n * Whether the selection names exactly one option of every variant group (ids of no group are\n * ignored). Always true for a product without variant groups.\n */\nexport function isSelectionComplete(\n groups: readonly VariantGroup[],\n selectedVariantIds: readonly number[],\n): boolean {\n const index = groupIndexOf(groups);\n const counts = groups.map(() => 0);\n for (const id of selectedVariantIds) {\n const group = index.get(id);\n if (group !== undefined) counts[group]! += 1;\n }\n return counts.every((count) => count === 1);\n}\n\n/**\n * The selection after the shopper picks `optionId`: it replaces the option of the same group,\n * or is added. The result keeps one option per group, in group order, and drops ids of no\n * group; an unknown `optionId` returns that cleaned selection unchanged.\n */\nexport function selectOption(\n groups: readonly VariantGroup[],\n selectedVariantIds: readonly number[],\n optionId: number,\n): number[] {\n const index = groupIndexOf(groups);\n const group = index.get(optionId);\n if (group === undefined) return cleanSelection(groups, selectedVariantIds);\n const others = selectedVariantIds.filter((id) => index.get(id) !== group);\n return cleanSelection(groups, [optionId, ...others]);\n}\n\n/**\n * The selection a product page opens with: the requested options (e.g. from the URL) when they\n * name a sellable SKU; otherwise the first in-stock sellable SKU containing the valid requested\n * options, else the first such SKU, else the first sellable SKU (in stock first). With nothing\n * sellable, the valid part of the request. `[]` for a product without variant groups. Fixes the\n * old storefront, which took the first option of every group (possibly a combination that does\n * not exist or is out of stock) and left a partial URL selection partial.\n */\nexport function defaultSelection(\n product: Pick<ProductDetail, 'variantGroups' | 'skus'>,\n requested: readonly number[] = [],\n): number[] {\n const groups = product.variantGroups;\n if (groups.length === 0) return [];\n const valid = cleanSelection(groups, requested);\n if (isSelectionComplete(groups, valid) && resolveSku(product.skus, valid)) return valid;\n\n const sellable = sellableSkus(product.skus);\n const candidates = sellable.filter((sku) => valid.every((id) => sku.variantIds.includes(id)));\n const inStock = (skus: ProductSku[]) => skus.find((sku) => sku.stockQty > 0);\n const pick = inStock(candidates) ?? candidates[0] ?? inStock(sellable) ?? sellable[0];\n return pick ? cleanSelection(groups, pick.variantIds) : valid;\n}\n\n/** A variant group as a product page shows it: what is selected and what can be picked. */\nexport interface VariantChoice {\n groupId: number;\n name: string;\n /** The selected option of this group, or null. */\n selectedOptionId: number | null;\n /**\n * `selectable`: some sellable SKU has this option together with the other groups' current\n * selections. Picking an option that is not selectable leads to no SKU (no price).\n */\n options: Array<VariantOption & { selectable: boolean }>;\n}\n\n/**\n * The variant groups with the selected option and, per option, whether picking it (keeping the\n * other groups' selections) leads to a sellable SKU. Stock does not matter here: an\n * out-of-stock SKU is still shown, with its price, as out of stock.\n */\nexport function variantChoices(\n product: Pick<ProductDetail, 'variantGroups' | 'skus'>,\n selectedVariantIds: readonly number[],\n): VariantChoice[] {\n const groups = product.variantGroups;\n const selection = cleanSelection(groups, selectedVariantIds);\n const sellable = sellableSkus(product.skus);\n return groups.map((group) => {\n const ownIds = new Set(group.options.map((option) => option.id));\n const others = selection.filter((id) => !ownIds.has(id));\n return {\n groupId: group.id,\n name: group.name,\n selectedOptionId: selection.find((id) => ownIds.has(id)) ?? null,\n options: group.options.map((option) => ({\n ...option,\n selectable: sellable.some(\n (sku) =>\n sku.variantIds.includes(option.id) && others.every((id) => sku.variantIds.includes(id)),\n ),\n })),\n };\n });\n}\n","import type { ProductSpecificationGroup } from '../types/product';\nimport type { VariantSelection } from './content';\n\n/**\n * The specification rows that apply to what the customer has selected: rows for every\n * variant, rows for a selected option, and rows for the resolved SKU. Groups left empty are\n * dropped. Without a resolved SKU (incomplete selection), SKU-specific rows are hidden.\n */\nexport function specificationsFor(\n groups: readonly ProductSpecificationGroup[],\n selection: VariantSelection,\n): ProductSpecificationGroup[] {\n const options = new Set(selection.variantIds);\n return groups\n .map((group) => ({\n ...group,\n items: group.items.filter(({ appliesTo }) =>\n appliesTo.type === 'all'\n ? true\n : appliesTo.type === 'variant'\n ? options.has(appliesTo.variantId)\n : appliesTo.skuId === selection.skuId,\n ),\n }))\n .filter((group) => group.items.length > 0);\n}\n"],"mappings":"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","names":["values"]}
|