ebag 0.0.2 → 0.1.1
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 +26 -10
- package/dist/cli/format.d.ts +1 -1
- package/dist/cli/format.js +136 -115
- package/dist/cli/index.js +138 -79
- package/dist/lib/auth.d.ts +1 -1
- package/dist/lib/auth.js +5 -5
- package/dist/lib/cart.d.ts +1 -1
- package/dist/lib/cart.js +11 -11
- package/dist/lib/client.d.ts +1 -1
- package/dist/lib/client.js +30 -20
- package/dist/lib/config.d.ts +2 -1
- package/dist/lib/config.js +22 -13
- package/dist/lib/cookies.js +10 -7
- package/dist/lib/index.d.ts +9 -9
- package/dist/lib/lists.d.ts +1 -1
- package/dist/lib/lists.js +4 -4
- package/dist/lib/log.d.ts +8 -0
- package/dist/lib/log.js +115 -0
- package/dist/lib/orders.d.ts +1 -1
- package/dist/lib/orders.js +47 -29
- package/dist/lib/products.d.ts +1 -1
- package/dist/lib/search.d.ts +1 -1
- package/dist/lib/search.js +26 -20
- package/dist/lib/slots.d.ts +1 -1
- package/dist/lib/slots.js +3 -3
- package/dist/lib/types.d.ts +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -23,15 +23,17 @@ ebag login --cookie "<cookie>"
|
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Notes:
|
|
26
|
+
|
|
26
27
|
- Session data is stored in plain text at `~/.config/ebag/session.json` with owner-only permissions (`0600`).
|
|
27
28
|
- List-based search caches product details in `~/.config/ebag/cache.json` for up to 6 hours.
|
|
28
29
|
- Completed orders may be cached locally for faster access.
|
|
29
30
|
|
|
30
|
-
##
|
|
31
|
+
## Products
|
|
31
32
|
|
|
32
33
|
```bash
|
|
33
|
-
ebag search "lindt"
|
|
34
|
-
ebag search "lindt" --json
|
|
34
|
+
ebag product search "lindt"
|
|
35
|
+
ebag product search "lindt" --json
|
|
36
|
+
ebag product show 5128
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
## Delivery slots
|
|
@@ -72,18 +74,32 @@ ebag list show 753250
|
|
|
72
74
|
npm run build
|
|
73
75
|
```
|
|
74
76
|
|
|
75
|
-
###
|
|
77
|
+
### Unit tests
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
```bash
|
|
80
|
+
npm run test:unit
|
|
81
|
+
```
|
|
79
82
|
|
|
80
|
-
|
|
83
|
+
### End-to-end tests
|
|
84
|
+
|
|
85
|
+
Requires a valid session in `tests/.config/ebag/session.json` (gitignored):
|
|
81
86
|
|
|
82
87
|
```bash
|
|
83
|
-
|
|
88
|
+
npm run test:e2e
|
|
84
89
|
```
|
|
85
|
-
|
|
90
|
+
|
|
91
|
+
### All tests
|
|
86
92
|
|
|
87
93
|
```bash
|
|
88
|
-
npm
|
|
94
|
+
npm test
|
|
89
95
|
```
|
|
96
|
+
|
|
97
|
+
## Changelog
|
|
98
|
+
|
|
99
|
+
### 0.1.1
|
|
100
|
+
|
|
101
|
+
- Add logging in the config directory
|
|
102
|
+
|
|
103
|
+
### 0.1.0
|
|
104
|
+
|
|
105
|
+
- Initial release
|
package/dist/cli/format.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OrderDetail, OrderSummary, ProductSummary } from
|
|
1
|
+
import type { OrderDetail, OrderSummary, ProductSummary } from "../lib/types";
|
|
2
2
|
export declare function outputJson(data: unknown): void;
|
|
3
3
|
export declare function formatHeading(text: string): string;
|
|
4
4
|
export declare function outputProducts(products: ProductSummary[]): void;
|
package/dist/cli/format.js
CHANGED
|
@@ -23,84 +23,95 @@ function formatPrice(product) {
|
|
|
23
23
|
return product.pricePromo;
|
|
24
24
|
if (product.price)
|
|
25
25
|
return product.price;
|
|
26
|
-
return
|
|
26
|
+
return "";
|
|
27
27
|
}
|
|
28
28
|
function outputProducts(products) {
|
|
29
29
|
for (const product of products) {
|
|
30
30
|
const price = formatPrice(product);
|
|
31
|
-
let source =
|
|
32
|
-
if (product.source ===
|
|
31
|
+
let source = "";
|
|
32
|
+
if (product.source === "list") {
|
|
33
33
|
const listNames = (product.listNames || []).filter(Boolean);
|
|
34
34
|
if (listNames.length) {
|
|
35
|
-
source = ` [${listNames.join(
|
|
35
|
+
source = ` [${listNames.join(", ")}]`;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
const line = `${product.id} ${product.name}${price ? ` - ${price}` :
|
|
38
|
+
const line = `${product.id} ${product.name}${price ? ` - ${price}` : ""}${source}`;
|
|
39
39
|
process.stdout.write(`${line}\n`);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
function outputList(items) {
|
|
43
43
|
for (const item of items) {
|
|
44
|
-
const count = item.count !== undefined ? ` (${item.count})` :
|
|
44
|
+
const count = item.count !== undefined ? ` (${item.count})` : "";
|
|
45
45
|
process.stdout.write(`${item.id} ${item.name}${count}\n`);
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
function outputProductDetail(data) {
|
|
49
|
-
const id = data.id ? String(data.id) :
|
|
50
|
-
const name = data.name ? String(data.name) :
|
|
51
|
-
const nameEn = data.name_en ? String(data.name_en) :
|
|
52
|
-
const brand = typeof data.brand ===
|
|
49
|
+
const id = data.id ? String(data.id) : "";
|
|
50
|
+
const name = data.name ? String(data.name) : "";
|
|
51
|
+
const nameEn = data.name_en ? String(data.name_en) : "";
|
|
52
|
+
const brand = typeof data.brand === "string"
|
|
53
53
|
? data.brand
|
|
54
54
|
: data.brand?.name ||
|
|
55
|
-
data.brand
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
'';
|
|
60
|
-
const unit = data.unit_weight_text ? String(data.unit_weight_text) : '';
|
|
55
|
+
data.brand?.name_bg ||
|
|
56
|
+
data.brand?.name_en ||
|
|
57
|
+
"";
|
|
58
|
+
const unit = data.unit_weight_text ? String(data.unit_weight_text) : "";
|
|
61
59
|
const price = data.current_price_eur ?? data.price_promo_eur ?? data.price_eur;
|
|
62
|
-
const priceText = price ? String(price) :
|
|
63
|
-
const currency = priceText ?
|
|
64
|
-
const availability = data.is_available === false
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
const priceText = price ? String(price) : "";
|
|
61
|
+
const currency = priceText ? "EUR" : "";
|
|
62
|
+
const availability = data.is_available === false
|
|
63
|
+
? "No"
|
|
64
|
+
: data.is_available === true
|
|
65
|
+
? "Yes"
|
|
66
|
+
: "";
|
|
67
|
+
const urlSlug = data.url_slug ? String(data.url_slug) : "";
|
|
68
|
+
const origin = data.country_of_origin ? String(data.country_of_origin) : "";
|
|
69
|
+
const expiryRaw = data.expiry_date ? String(data.expiry_date) : "";
|
|
68
70
|
const expiry = formatDate(expiryRaw);
|
|
69
|
-
const description = data.description ? String(data.description) :
|
|
71
|
+
const description = data.description ? String(data.description) : "";
|
|
70
72
|
const kvPairs = [
|
|
71
|
-
[
|
|
72
|
-
[
|
|
73
|
-
[
|
|
74
|
-
[
|
|
75
|
-
[
|
|
76
|
-
[
|
|
77
|
-
[
|
|
78
|
-
[
|
|
73
|
+
["ID", id],
|
|
74
|
+
["Brand", brand],
|
|
75
|
+
["Unit", unit],
|
|
76
|
+
["Price", priceText ? `${priceText}${currency ? ` ${currency}` : ""}` : ""],
|
|
77
|
+
["Available", availability],
|
|
78
|
+
["Origin", origin],
|
|
79
|
+
["Expiry", expiry],
|
|
80
|
+
["Slug", urlSlug],
|
|
79
81
|
];
|
|
80
82
|
const energyPairs = formatEnergyValues(data.energy_values);
|
|
81
|
-
const kvLines = [
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const descriptionMd = description ? htmlToMarkdown(description) :
|
|
83
|
+
const kvLines = [...kvPairs.filter(([, value]) => value), ...energyPairs].map(([key, value]) => `${key}: ${value}`);
|
|
84
|
+
const headerLines = [
|
|
85
|
+
`# ${name || "Product"}`,
|
|
86
|
+
nameEn ? `*${nameEn}*` : "",
|
|
87
|
+
].filter(Boolean);
|
|
88
|
+
const descriptionMd = description ? htmlToMarkdown(description) : "";
|
|
87
89
|
const { descriptionText, ingredientsText } = splitIngredients(descriptionMd);
|
|
88
|
-
const descriptionBlock = descriptionText
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
90
|
+
const descriptionBlock = descriptionText
|
|
91
|
+
? `# Description\n\n${descriptionText}`
|
|
92
|
+
: "";
|
|
93
|
+
const ingredientsBlock = ingredientsText
|
|
94
|
+
? `\n# Ingredients\n\n${ingredientsText}`
|
|
95
|
+
: "";
|
|
96
|
+
const kvBlock = kvLines.length ? ["---", ...kvLines, "---"].join("\n") : "";
|
|
97
|
+
const output = [
|
|
98
|
+
headerLines.join("\n"),
|
|
99
|
+
kvBlock,
|
|
100
|
+
descriptionBlock,
|
|
101
|
+
ingredientsBlock,
|
|
102
|
+
]
|
|
92
103
|
.filter(Boolean)
|
|
93
|
-
.join(
|
|
104
|
+
.join("\n");
|
|
94
105
|
process.stdout.write(`${output}\n`);
|
|
95
106
|
}
|
|
96
107
|
function formatOrderAmount(order) {
|
|
97
|
-
if (
|
|
108
|
+
if ("finalAmountEur" in order && order.finalAmountEur) {
|
|
98
109
|
return `${order.finalAmountEur} EUR`;
|
|
99
110
|
}
|
|
100
|
-
if (
|
|
111
|
+
if ("finalAmount" in order && order.finalAmount) {
|
|
101
112
|
return order.finalAmount;
|
|
102
113
|
}
|
|
103
|
-
if (
|
|
114
|
+
if ("totals" in order) {
|
|
104
115
|
if (order.totals.totalPaidEur)
|
|
105
116
|
return `${order.totals.totalPaidEur} EUR`;
|
|
106
117
|
if (order.totals.totalEur)
|
|
@@ -110,46 +121,46 @@ function formatOrderAmount(order) {
|
|
|
110
121
|
if (order.totals.total)
|
|
111
122
|
return order.totals.total;
|
|
112
123
|
}
|
|
113
|
-
return
|
|
124
|
+
return "";
|
|
114
125
|
}
|
|
115
126
|
function formatDateInTimeZone(date, timeZone) {
|
|
116
|
-
const parts = new Intl.DateTimeFormat(
|
|
127
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
117
128
|
timeZone,
|
|
118
|
-
year:
|
|
119
|
-
month:
|
|
120
|
-
day:
|
|
129
|
+
year: "numeric",
|
|
130
|
+
month: "2-digit",
|
|
131
|
+
day: "2-digit",
|
|
121
132
|
}).formatToParts(date);
|
|
122
133
|
const lookup = new Map(parts.map((part) => [part.type, part.value]));
|
|
123
|
-
const year = lookup.get(
|
|
124
|
-
const month = lookup.get(
|
|
125
|
-
const day = lookup.get(
|
|
134
|
+
const year = lookup.get("year") || "";
|
|
135
|
+
const month = lookup.get("month") || "";
|
|
136
|
+
const day = lookup.get("day") || "";
|
|
126
137
|
return `${year}-${month}-${day}`;
|
|
127
138
|
}
|
|
128
139
|
function formatOrderStatus(order) {
|
|
129
140
|
const status = order.status;
|
|
130
141
|
if (status === 3)
|
|
131
|
-
return
|
|
142
|
+
return "Отказана";
|
|
132
143
|
if (status === 4) {
|
|
133
|
-
const date = order.shippingDate ? formatDate(order.shippingDate) :
|
|
134
|
-
const todayDate = formatDateInTimeZone(new Date(),
|
|
144
|
+
const date = order.shippingDate ? formatDate(order.shippingDate) : "";
|
|
145
|
+
const todayDate = formatDateInTimeZone(new Date(), "Europe/Sofia");
|
|
135
146
|
if (date && date >= todayDate)
|
|
136
|
-
return
|
|
137
|
-
return
|
|
147
|
+
return "Нова";
|
|
148
|
+
return "Завършена";
|
|
138
149
|
}
|
|
139
150
|
if (status !== undefined) {
|
|
140
151
|
return `Status ${status}`;
|
|
141
152
|
}
|
|
142
|
-
return
|
|
153
|
+
return "";
|
|
143
154
|
}
|
|
144
155
|
function outputOrdersList(orders) {
|
|
145
156
|
for (const order of orders) {
|
|
146
|
-
const date = order.shippingDate ? formatDate(order.shippingDate) :
|
|
147
|
-
const slot = order.timeSlotDisplay ||
|
|
157
|
+
const date = order.shippingDate ? formatDate(order.shippingDate) : "";
|
|
158
|
+
const slot = order.timeSlotDisplay || "";
|
|
148
159
|
const status = formatOrderStatus(order);
|
|
149
160
|
const total = formatOrderAmount(order);
|
|
150
161
|
const parts = [order.id, date, slot].filter(Boolean);
|
|
151
|
-
const suffix = [status, total].filter(Boolean).join(
|
|
152
|
-
const line = `${parts.join(
|
|
162
|
+
const suffix = [status, total].filter(Boolean).join(" - ");
|
|
163
|
+
const line = `${parts.join(" ")}${suffix ? ` - ${suffix}` : ""}`;
|
|
153
164
|
process.stdout.write(`${line}\n`);
|
|
154
165
|
}
|
|
155
166
|
}
|
|
@@ -171,57 +182,67 @@ function outputOrderItems(items) {
|
|
|
171
182
|
for (const [group, groupItems] of groupEntries) {
|
|
172
183
|
process.stdout.write(`## ${group}\n`);
|
|
173
184
|
for (const item of groupItems) {
|
|
174
|
-
const qty = item.quantity ? ` x${item.quantity}` :
|
|
175
|
-
const unit = item.unit ? ` (${item.unit})` :
|
|
176
|
-
const price = item.priceEur ? `${item.priceEur} EUR` : item.price ||
|
|
177
|
-
const line = `- ${item.name}${unit}${qty}${price ? ` - ${price}` :
|
|
185
|
+
const qty = item.quantity ? ` x${item.quantity}` : "";
|
|
186
|
+
const unit = item.unit ? ` (${item.unit})` : "";
|
|
187
|
+
const price = item.priceEur ? `${item.priceEur} EUR` : item.price || "";
|
|
188
|
+
const line = `- ${item.name}${unit}${qty}${price ? ` - ${price}` : ""}`;
|
|
178
189
|
process.stdout.write(`${line}\n`);
|
|
179
190
|
}
|
|
180
|
-
process.stdout.write(
|
|
191
|
+
process.stdout.write("\n");
|
|
181
192
|
}
|
|
182
193
|
}
|
|
183
194
|
if (ungrouped.length) {
|
|
184
195
|
for (const item of ungrouped) {
|
|
185
|
-
const qty = item.quantity ? ` x${item.quantity}` :
|
|
186
|
-
const unit = item.unit ? ` (${item.unit})` :
|
|
187
|
-
const price = item.priceEur ? `${item.priceEur} EUR` : item.price ||
|
|
188
|
-
const line = `- ${item.name}${unit}${qty}${price ? ` - ${price}` :
|
|
196
|
+
const qty = item.quantity ? ` x${item.quantity}` : "";
|
|
197
|
+
const unit = item.unit ? ` (${item.unit})` : "";
|
|
198
|
+
const price = item.priceEur ? `${item.priceEur} EUR` : item.price || "";
|
|
199
|
+
const line = `- ${item.name}${unit}${qty}${price ? ` - ${price}` : ""}`;
|
|
189
200
|
process.stdout.write(`${line}\n`);
|
|
190
201
|
}
|
|
191
|
-
process.stdout.write(
|
|
202
|
+
process.stdout.write("\n");
|
|
192
203
|
}
|
|
193
204
|
}
|
|
194
205
|
function outputOrderDetail(detail) {
|
|
195
|
-
const header = `# Order ${detail.id ||
|
|
206
|
+
const header = `# Order ${detail.id || ""}`.trim();
|
|
196
207
|
const status = formatOrderStatus(detail);
|
|
197
|
-
const date = detail.shippingDate ? formatDate(detail.shippingDate) :
|
|
198
|
-
const address = detail.address ||
|
|
208
|
+
const date = detail.shippingDate ? formatDate(detail.shippingDate) : "";
|
|
209
|
+
const address = detail.address || "";
|
|
199
210
|
const total = formatOrderAmount(detail);
|
|
200
211
|
const kvPairs = [
|
|
201
|
-
[
|
|
202
|
-
[
|
|
203
|
-
[
|
|
204
|
-
[
|
|
205
|
-
[
|
|
206
|
-
[
|
|
207
|
-
[
|
|
208
|
-
|
|
212
|
+
["ID", detail.id],
|
|
213
|
+
["Status", status],
|
|
214
|
+
["Date", date],
|
|
215
|
+
["Timeslot", detail.timeSlotDisplay || ""],
|
|
216
|
+
["Address", address],
|
|
217
|
+
["Total", total],
|
|
218
|
+
[
|
|
219
|
+
"Discount",
|
|
220
|
+
detail.totals.discountEur
|
|
221
|
+
? `${detail.totals.discountEur} EUR`
|
|
222
|
+
: detail.totals.discount || "",
|
|
223
|
+
],
|
|
224
|
+
[
|
|
225
|
+
"Tip",
|
|
226
|
+
detail.totals.tipEur
|
|
227
|
+
? `${detail.totals.tipEur} EUR`
|
|
228
|
+
: detail.totals.tip || "",
|
|
229
|
+
],
|
|
209
230
|
].filter(([, value]) => value);
|
|
210
231
|
const kvLines = kvPairs.map(([key, value]) => `${key}: ${value}`);
|
|
211
|
-
const kvBlock = kvLines.length ? [
|
|
232
|
+
const kvBlock = kvLines.length ? ["---", ...kvLines, "---"].join("\n") : "";
|
|
212
233
|
process.stdout.write(`${header}\n`);
|
|
213
234
|
if (kvBlock) {
|
|
214
235
|
process.stdout.write(`${kvBlock}\n`);
|
|
215
236
|
}
|
|
216
|
-
process.stdout.write(
|
|
237
|
+
process.stdout.write("# Items\n");
|
|
217
238
|
if (detail.items.length) {
|
|
218
239
|
outputOrderItems(detail.items);
|
|
219
240
|
}
|
|
220
241
|
else {
|
|
221
|
-
process.stdout.write(
|
|
242
|
+
process.stdout.write("No items found.\n\n");
|
|
222
243
|
}
|
|
223
244
|
if (detail.additionalOrders && detail.additionalOrders.length) {
|
|
224
|
-
process.stdout.write(
|
|
245
|
+
process.stdout.write("# Additional Orders\n");
|
|
225
246
|
for (const additional of detail.additionalOrders) {
|
|
226
247
|
process.stdout.write(`## Order ${additional.id}\n`);
|
|
227
248
|
const additionalTotal = formatOrderAmount(additional);
|
|
@@ -232,14 +253,14 @@ function outputOrderDetail(detail) {
|
|
|
232
253
|
outputOrderItems(additional.items);
|
|
233
254
|
}
|
|
234
255
|
else {
|
|
235
|
-
process.stdout.write(
|
|
256
|
+
process.stdout.write("No items found.\n\n");
|
|
236
257
|
}
|
|
237
258
|
}
|
|
238
259
|
}
|
|
239
260
|
}
|
|
240
261
|
function formatDate(value) {
|
|
241
262
|
if (!value)
|
|
242
|
-
return
|
|
263
|
+
return "";
|
|
243
264
|
const isoMatch = value.match(/(\d{4})-(\d{2})-(\d{2})/);
|
|
244
265
|
if (isoMatch)
|
|
245
266
|
return `${isoMatch[1]}-${isoMatch[2]}-${isoMatch[3]}`;
|
|
@@ -252,7 +273,7 @@ function formatDate(value) {
|
|
|
252
273
|
const parsed = new Date(value);
|
|
253
274
|
if (Number.isNaN(parsed.getTime()))
|
|
254
275
|
return value;
|
|
255
|
-
const pad = (n) => String(n).padStart(2,
|
|
276
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
256
277
|
return `${parsed.getFullYear()}-${pad(parsed.getMonth() + 1)}-${pad(parsed.getDate())}`;
|
|
257
278
|
}
|
|
258
279
|
function formatEnergyValues(value) {
|
|
@@ -261,7 +282,7 @@ function formatEnergyValues(value) {
|
|
|
261
282
|
if (Array.isArray(value)) {
|
|
262
283
|
const pairs = [];
|
|
263
284
|
for (const item of value) {
|
|
264
|
-
if (!item || typeof item !==
|
|
285
|
+
if (!item || typeof item !== "object")
|
|
265
286
|
continue;
|
|
266
287
|
const entry = item;
|
|
267
288
|
const label = entry.name_bg ||
|
|
@@ -279,7 +300,7 @@ function formatEnergyValues(value) {
|
|
|
279
300
|
}
|
|
280
301
|
return pairs;
|
|
281
302
|
}
|
|
282
|
-
if (typeof value ===
|
|
303
|
+
if (typeof value === "object") {
|
|
283
304
|
return Object.entries(value)
|
|
284
305
|
.filter(([, entry]) => entry !== null && entry !== undefined)
|
|
285
306
|
.flatMap(([key, entry]) => splitEnergyValues(`Energy ${key}`, String(entry)));
|
|
@@ -292,7 +313,7 @@ function splitEnergyValues(label, raw) {
|
|
|
292
313
|
const kjMatch = normalized.match(/(\d+(?:\.\d+)?)\s*kJ/i);
|
|
293
314
|
const hasKcalKjLabel = /kcal/i.test(label) && /kJ/i.test(label);
|
|
294
315
|
const splitMatch = normalized.match(/^(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)$/);
|
|
295
|
-
const baseLabel = label.replace(/\(?\s*kcal\s*\/\s*kJ\s*\)?/i,
|
|
316
|
+
const baseLabel = label.replace(/\(?\s*kcal\s*\/\s*kJ\s*\)?/i, "").trim();
|
|
296
317
|
const energyLabel = baseLabel || label;
|
|
297
318
|
if (kcalMatch || kjMatch) {
|
|
298
319
|
const items = [];
|
|
@@ -311,40 +332,40 @@ function splitEnergyValues(label, raw) {
|
|
|
311
332
|
return [[label, normalized]];
|
|
312
333
|
}
|
|
313
334
|
function normalizeNumberString(value) {
|
|
314
|
-
if (typeof value ===
|
|
335
|
+
if (typeof value === "number")
|
|
315
336
|
return value.toString();
|
|
316
|
-
if (typeof value !==
|
|
337
|
+
if (typeof value !== "string")
|
|
317
338
|
return String(value);
|
|
318
|
-
const trimmed = value.trim().replace(/\u00a0/g,
|
|
319
|
-
const noSpaces = trimmed.replace(/\s+/g,
|
|
320
|
-
if (noSpaces.includes(
|
|
321
|
-
return noSpaces.replace(
|
|
339
|
+
const trimmed = value.trim().replace(/\u00a0/g, " ");
|
|
340
|
+
const noSpaces = trimmed.replace(/\s+/g, "");
|
|
341
|
+
if (noSpaces.includes(",")) {
|
|
342
|
+
return noSpaces.replace(",", ".");
|
|
322
343
|
}
|
|
323
344
|
return noSpaces;
|
|
324
345
|
}
|
|
325
346
|
function htmlToMarkdown(html) {
|
|
326
347
|
let text = html;
|
|
327
|
-
text = text.replace(/<\s*br\s*\/?\s*>/gi,
|
|
328
|
-
text = text.replace(/<\s*\/p\s*>/gi,
|
|
329
|
-
text = text.replace(/<\s*p[^>]*>/gi,
|
|
330
|
-
text = text.replace(/<\s*li[^>]*>/gi,
|
|
331
|
-
text = text.replace(/<\s*\/li\s*>/gi,
|
|
332
|
-
text = text.replace(/<\s*ul[^>]*>/gi,
|
|
333
|
-
text = text.replace(/<\s*\/ul\s*>/gi,
|
|
334
|
-
text = text.replace(/<[^>]+>/g,
|
|
335
|
-
text = text.replace(/ /g,
|
|
336
|
-
text = text.replace(/&/g,
|
|
337
|
-
text = text.replace(/</g,
|
|
338
|
-
text = text.replace(/>/g,
|
|
339
|
-
text = text.replace(/\n{3,}/g,
|
|
348
|
+
text = text.replace(/<\s*br\s*\/?\s*>/gi, "\n");
|
|
349
|
+
text = text.replace(/<\s*\/p\s*>/gi, "\n\n");
|
|
350
|
+
text = text.replace(/<\s*p[^>]*>/gi, "");
|
|
351
|
+
text = text.replace(/<\s*li[^>]*>/gi, "- ");
|
|
352
|
+
text = text.replace(/<\s*\/li\s*>/gi, "\n");
|
|
353
|
+
text = text.replace(/<\s*ul[^>]*>/gi, "\n");
|
|
354
|
+
text = text.replace(/<\s*\/ul\s*>/gi, "\n");
|
|
355
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
356
|
+
text = text.replace(/ /g, " ");
|
|
357
|
+
text = text.replace(/&/g, "&");
|
|
358
|
+
text = text.replace(/</g, "<");
|
|
359
|
+
text = text.replace(/>/g, ">");
|
|
360
|
+
text = text.replace(/\n{3,}/g, "\n\n");
|
|
340
361
|
return text.trim();
|
|
341
362
|
}
|
|
342
363
|
function splitIngredients(text) {
|
|
343
364
|
if (!text)
|
|
344
|
-
return { descriptionText:
|
|
365
|
+
return { descriptionText: "", ingredientsText: "" };
|
|
345
366
|
const match = text.match(/(?:^|\n)\s*Съставки\s*:?\s*/i);
|
|
346
367
|
if (!match || match.index === undefined) {
|
|
347
|
-
return { descriptionText: text, ingredientsText:
|
|
368
|
+
return { descriptionText: text, ingredientsText: "" };
|
|
348
369
|
}
|
|
349
370
|
const start = match.index;
|
|
350
371
|
const end = start + match[0].length;
|