ebag 0.1.0 → 0.1.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/cli/index.js CHANGED
@@ -16,7 +16,9 @@ const orders_1 = require("../lib/orders");
16
16
  const products_1 = require("../lib/products");
17
17
  const search_1 = require("../lib/search");
18
18
  const slots_1 = require("../lib/slots");
19
+ const log_1 = require("../lib/log");
19
20
  const format_1 = require("./format");
21
+ const warnings_1 = require("./warnings");
20
22
  function requireSessionCookie() {
21
23
  const session = (0, config_1.loadSession)();
22
24
  if (!session.cookies) {
@@ -35,26 +37,60 @@ function formatError(err) {
35
37
  }
36
38
  async function main() {
37
39
  const program = new commander_1.Command();
40
+ let lastCommand = "unknown";
41
+ let lastArgs = [];
42
+ let lastStart = Date.now();
38
43
  function getPackageVersion() {
39
44
  try {
40
- const packagePath = node_path_1.default.resolve(__dirname, '../../package.json');
41
- const raw = node_fs_1.default.readFileSync(packagePath, 'utf8');
45
+ const packagePath = node_path_1.default.resolve(__dirname, "../../package.json");
46
+ const raw = node_fs_1.default.readFileSync(packagePath, "utf8");
42
47
  const parsed = JSON.parse(raw);
43
- return parsed.version || 'unknown';
48
+ return parsed.version || "unknown";
44
49
  }
45
50
  catch {
46
- return 'unknown';
51
+ return "unknown";
47
52
  }
48
53
  }
49
54
  program
50
- .name('ebag')
51
- .description('CLI for interacting with ebag.bg')
52
- .version(getPackageVersion(), '-v, --version', 'Show CLI version')
53
- .option('--json', 'Output JSON');
55
+ .name("ebag")
56
+ .description("CLI for interacting with ebag.bg")
57
+ .version(getPackageVersion(), "-v, --version", "Show CLI version")
58
+ .option("--json", "Output JSON");
59
+ program.hook("preAction", (_thisCommand, actionCommand) => {
60
+ lastStart = Date.now();
61
+ const commandPath = actionCommand.commandPath?.();
62
+ lastCommand = commandPath || actionCommand.name() || "unknown";
63
+ lastArgs = process.argv.slice(2);
64
+ (0, log_1.appendLog)({
65
+ level: "info",
66
+ event: "command.start",
67
+ command: lastCommand,
68
+ args: lastArgs,
69
+ json: Boolean(program.opts().json),
70
+ pid: process.pid,
71
+ ppid: process.ppid,
72
+ cwd: process.cwd(),
73
+ node: process.version,
74
+ configDir: (0, config_1.getConfigDir)(),
75
+ });
76
+ });
77
+ program.hook("postAction", () => {
78
+ const durationMs = Date.now() - lastStart;
79
+ (0, log_1.appendLog)({
80
+ level: "info",
81
+ event: "command.finish",
82
+ command: lastCommand,
83
+ args: lastArgs,
84
+ json: Boolean(program.opts().json),
85
+ pid: process.pid,
86
+ durationMs,
87
+ configDir: (0, config_1.getConfigDir)(),
88
+ });
89
+ });
54
90
  program
55
- .command('login')
56
- .description('Store session cookie and validate it')
57
- .option('--cookie <cookie>', 'Cookie header value from browser')
91
+ .command("login")
92
+ .description("Store session cookie and validate it")
93
+ .option("--cookie <cookie>", "Cookie header value from browser")
58
94
  .action(async (options) => {
59
95
  const config = (0, config_1.loadConfig)();
60
96
  const json = program.opts().json;
@@ -81,20 +117,20 @@ async function main() {
81
117
  const email = user.email ||
82
118
  user.username;
83
119
  if (!email) {
84
- throw new Error('Session validated but no user email was returned.');
120
+ throw new Error("Session validated but no user email was returned.");
85
121
  }
86
122
  (0, config_1.saveSession)(session);
87
123
  if (json) {
88
- (0, format_1.outputJson)({ status: 'ok', user, email });
124
+ (0, format_1.outputJson)({ status: "ok", user, email });
89
125
  }
90
126
  else {
91
- process.stdout.write('Login session validated and saved.\n');
127
+ process.stdout.write("Login session validated and saved.\n");
92
128
  process.stdout.write(`Logged in as: ${email}\n`);
93
129
  }
94
130
  }
95
131
  catch (err) {
96
132
  if (json) {
97
- (0, format_1.outputJson)({ status: 'error', message: err.message });
133
+ (0, format_1.outputJson)({ status: "error", message: err.message });
98
134
  }
99
135
  else {
100
136
  process.stderr.write(`Login failed: ${err.message}\n`);
@@ -102,29 +138,30 @@ async function main() {
102
138
  }
103
139
  });
104
140
  program
105
- .command('status')
106
- .description('Show current login status')
141
+ .command("status")
142
+ .description("Show current login status")
107
143
  .action(async () => {
108
144
  const config = (0, config_1.loadConfig)();
109
145
  const json = program.opts().json;
110
146
  const session = (0, config_1.loadSession)();
111
147
  if (!session.cookies) {
112
148
  if (json) {
113
- (0, format_1.outputJson)({ status: 'logged_out' });
149
+ (0, format_1.outputJson)({ status: "logged_out" });
114
150
  }
115
151
  else {
116
- process.stdout.write('Logged out (no session cookie).\n');
152
+ process.stdout.write("Logged out (no session cookie).\n");
117
153
  }
118
154
  return;
119
155
  }
120
156
  try {
121
157
  const user = await (0, auth_1.validateSession)(config, session);
122
- const email = user.email || user.username;
158
+ const email = user.email ||
159
+ user.username;
123
160
  if (json) {
124
- (0, format_1.outputJson)({ status: 'logged_in', user, email });
161
+ (0, format_1.outputJson)({ status: "logged_in", user, email });
125
162
  }
126
163
  else {
127
- process.stdout.write('Logged in.\n');
164
+ process.stdout.write("Logged in.\n");
128
165
  if (email) {
129
166
  process.stdout.write(`Email: ${email}\n`);
130
167
  }
@@ -134,10 +171,10 @@ async function main() {
134
171
  const error = err;
135
172
  if (error.status && [401, 403].includes(error.status)) {
136
173
  if (json) {
137
- (0, format_1.outputJson)({ status: 'logged_out' });
174
+ (0, format_1.outputJson)({ status: "logged_out" });
138
175
  }
139
176
  else {
140
- process.stdout.write('Logged out.\n');
177
+ process.stdout.write("Logged out.\n");
141
178
  }
142
179
  return;
143
180
  }
@@ -145,9 +182,9 @@ async function main() {
145
182
  }
146
183
  });
147
184
  program
148
- .command('slots')
149
- .description('Show the next available delivery slots')
150
- .option('--limit <n>', 'Limit number of slots', '10')
185
+ .command("slots")
186
+ .description("Show the next available delivery slots")
187
+ .option("--limit <n>", "Limit number of slots", "10")
151
188
  .action(async (options) => {
152
189
  const config = (0, config_1.loadConfig)();
153
190
  const session = requireSessionCookie();
@@ -162,7 +199,7 @@ async function main() {
162
199
  return;
163
200
  }
164
201
  if (!limited.length) {
165
- process.stdout.write('No available delivery slots.\n');
202
+ process.stdout.write("No available delivery slots.\n");
166
203
  return;
167
204
  }
168
205
  const today = new Date();
@@ -170,7 +207,7 @@ async function main() {
170
207
  const tomorrow = new Date(today);
171
208
  tomorrow.setDate(today.getDate() + 1);
172
209
  const tomorrowDate = tomorrow.toISOString().slice(0, 10);
173
- let currentDate = '';
210
+ let currentDate = "";
174
211
  let printedHeader = false;
175
212
  for (const slot of limited) {
176
213
  if (slot.date !== currentDate) {
@@ -182,27 +219,30 @@ async function main() {
182
219
  else if (currentDate === tomorrowDate) {
183
220
  label = `Tomorrow (${currentDate})`;
184
221
  }
185
- const headerPrefix = printedHeader ? '\n' : '';
222
+ const headerPrefix = printedHeader ? "\n" : "";
186
223
  process.stdout.write(`${headerPrefix}${(0, format_1.formatHeading)(`# ${label}`)}\n`);
187
224
  printedHeader = true;
188
225
  }
189
226
  process.stdout.write(`${(0, slots_1.formatSlotRange)(slot.start, slot.end)} (${(0, slots_1.formatLoadPercent)(slot.loadPercent)})\n`);
190
227
  }
191
228
  });
192
- const product = program.command('product').description('Product operations');
229
+ const product = program.command("product").description("Product operations");
193
230
  product
194
- .command('search')
195
- .description('Search for products')
196
- .argument('<query>', 'Search query')
197
- .option('--limit <n>', 'Limit number of results', '20')
198
- .option('--page <n>', 'Algolia page number (0-based)', '0')
231
+ .command("search")
232
+ .description("Search for products")
233
+ .argument("<query>", "Search query")
234
+ .option("--limit <n>", "Limit number of results", "20")
235
+ .option("--page <n>", "Algolia page number (0-based)", "0")
199
236
  .action(async (query, options) => {
200
237
  const config = (0, config_1.loadConfig)();
201
238
  const session = (0, config_1.loadSession)();
202
239
  const json = program.opts().json;
203
240
  const limit = Number(options.limit);
204
241
  const page = Number(options.page);
205
- const result = await (0, search_1.searchProducts)(config, session, query, { limit, page });
242
+ const result = await (0, search_1.searchProducts)(config, session, query, {
243
+ limit,
244
+ page,
245
+ });
206
246
  if (json) {
207
247
  (0, format_1.outputJson)(result);
208
248
  }
@@ -211,9 +251,9 @@ async function main() {
211
251
  }
212
252
  });
213
253
  product
214
- .command('show')
215
- .description('Get product details by ID')
216
- .argument('<productId>', 'Product ID')
254
+ .command("show")
255
+ .description("Get product details by ID")
256
+ .argument("<productId>", "Product ID")
217
257
  .action(async (productId) => {
218
258
  const config = (0, config_1.loadConfig)();
219
259
  const session = (0, config_1.loadSession)();
@@ -226,14 +266,14 @@ async function main() {
226
266
  (0, format_1.outputProductDetail)(result);
227
267
  }
228
268
  });
229
- const order = program.command('order').description('Order operations');
269
+ const order = program.command("order").description("Order operations");
230
270
  order
231
- .command('list')
232
- .description('List recent orders')
233
- .option('--limit <n>', 'Limit number of results', '10')
234
- .option('--page <n>', 'Page number')
235
- .option('--from <date>', 'Filter from date (YYYY-MM-DD)')
236
- .option('--to <date>', 'Filter to date (YYYY-MM-DD)')
271
+ .command("list")
272
+ .description("List recent orders")
273
+ .option("--limit <n>", "Limit number of results", "10")
274
+ .option("--page <n>", "Page number")
275
+ .option("--from <date>", "Filter from date (YYYY-MM-DD)")
276
+ .option("--to <date>", "Filter to date (YYYY-MM-DD)")
237
277
  .action(async (options) => {
238
278
  const config = (0, config_1.loadConfig)();
239
279
  const session = requireSessionCookie();
@@ -248,25 +288,27 @@ async function main() {
248
288
  from,
249
289
  to,
250
290
  });
291
+ (0, warnings_1.warnUnknownOrderStatuses)(result.results);
251
292
  if (json) {
252
293
  (0, format_1.outputJson)(result);
253
294
  }
254
295
  else if (!result.results.length) {
255
- process.stdout.write('No orders found.\n');
296
+ process.stdout.write("No orders found.\n");
256
297
  }
257
298
  else {
258
299
  (0, format_1.outputOrdersList)(result.results);
259
300
  }
260
301
  });
261
302
  order
262
- .command('show')
263
- .description('Show order details')
264
- .argument('<orderId>', 'Order ID')
303
+ .command("show")
304
+ .description("Show order details")
305
+ .argument("<orderId>", "Order ID")
265
306
  .action(async (orderId) => {
266
307
  const config = (0, config_1.loadConfig)();
267
308
  const session = requireSessionCookie();
268
309
  const json = program.opts().json;
269
310
  const detail = await (0, orders_1.getOrderDetail)(config, session, String(orderId));
311
+ (0, warnings_1.warnUnknownOrderStatuses)([detail]);
270
312
  if (json) {
271
313
  (0, format_1.outputJson)(detail);
272
314
  }
@@ -274,11 +316,11 @@ async function main() {
274
316
  (0, format_1.outputOrderDetail)(detail);
275
317
  }
276
318
  });
277
- const cart = program.command('cart').description('Cart operations');
319
+ const cart = program.command("cart").description("Cart operations");
278
320
  cart
279
- .command('add')
280
- .argument('<productId>', 'Product ID')
281
- .option('--qty <n>', 'Quantity', '1')
321
+ .command("add")
322
+ .argument("<productId>", "Product ID")
323
+ .option("--qty <n>", "Quantity", "1")
282
324
  .action(async (productId, options) => {
283
325
  const config = (0, config_1.loadConfig)();
284
326
  const session = requireSessionCookie();
@@ -289,13 +331,13 @@ async function main() {
289
331
  (0, format_1.outputJson)(result);
290
332
  }
291
333
  else {
292
- process.stdout.write('Added to cart.\n');
334
+ process.stdout.write("Added to cart.\n");
293
335
  }
294
336
  });
295
337
  cart
296
- .command('update')
297
- .argument('<productId>', 'Product ID')
298
- .option('--qty <n>', 'Quantity', '1')
338
+ .command("update")
339
+ .argument("<productId>", "Product ID")
340
+ .option("--qty <n>", "Quantity", "1")
299
341
  .action(async (productId, options) => {
300
342
  const config = (0, config_1.loadConfig)();
301
343
  const session = requireSessionCookie();
@@ -306,12 +348,12 @@ async function main() {
306
348
  (0, format_1.outputJson)(result);
307
349
  }
308
350
  else {
309
- process.stdout.write('Cart updated.\n');
351
+ process.stdout.write("Cart updated.\n");
310
352
  }
311
353
  });
312
354
  cart
313
- .command('show')
314
- .description('Show cart contents')
355
+ .command("show")
356
+ .description("Show cart contents")
315
357
  .action(async () => {
316
358
  const config = (0, config_1.loadConfig)();
317
359
  const session = requireSessionCookie();
@@ -327,8 +369,11 @@ async function main() {
327
369
  const listItems = items
328
370
  .map((item) => {
329
371
  const entry = item;
330
- const id = entry.product?.id ?? entry.product_id ?? entry.productId ?? entry.id;
331
- const name = entry.product?.name ?? entry.name ?? 'Unknown';
372
+ const id = entry.product?.id ??
373
+ entry.product_id ??
374
+ entry.productId ??
375
+ entry.id;
376
+ const name = entry.product?.name ?? entry.name ?? "Unknown";
332
377
  const count = entry.quantity ?? entry.qty;
333
378
  if (!id)
334
379
  return null;
@@ -339,14 +384,14 @@ async function main() {
339
384
  (0, format_1.outputList)(listItems);
340
385
  }
341
386
  else {
342
- process.stdout.write('Cart is empty.\n');
387
+ process.stdout.write("Cart is empty.\n");
343
388
  }
344
389
  });
345
- const list = program.command('list').description('List operations');
390
+ const list = program.command("list").description("List operations");
346
391
  list
347
- .command('show')
348
- .description('Show your lists')
349
- .argument('[listId]', 'List ID')
392
+ .command("show")
393
+ .description("Show your lists")
394
+ .argument("[listId]", "List ID")
350
395
  .action(async (listId) => {
351
396
  const config = (0, config_1.loadConfig)();
352
397
  const session = requireSessionCookie();
@@ -361,7 +406,7 @@ async function main() {
361
406
  const results = Array.isArray(listItems.results)
362
407
  ? listItems.results
363
408
  : [];
364
- const count = typeof listItems.count === 'number'
409
+ const count = typeof listItems.count === "number"
365
410
  ? listItems.count
366
411
  : results.length;
367
412
  if (json) {
@@ -376,7 +421,7 @@ async function main() {
376
421
  else {
377
422
  process.stdout.write(`${listEntry.name} (${listEntry.id})`);
378
423
  if (!count) {
379
- process.stdout.write(' - empty\n');
424
+ process.stdout.write(" - empty\n");
380
425
  return;
381
426
  }
382
427
  process.stdout.write(` - ${count} items\n`);
@@ -385,7 +430,7 @@ async function main() {
385
430
  const entry = item;
386
431
  const product = entry.product;
387
432
  const id = product?.id ?? entry.product_id ?? entry.productId ?? entry.id;
388
- const name = product?.name ?? entry.name ?? 'Unknown';
433
+ const name = product?.name ?? entry.name ?? "Unknown";
389
434
  const itemCount = entry.quantity ?? entry.qty;
390
435
  if (!id)
391
436
  return null;
@@ -407,10 +452,10 @@ async function main() {
407
452
  })));
408
453
  });
409
454
  list
410
- .command('add')
411
- .argument('<listId>', 'List ID')
412
- .argument('<productId>', 'Product ID')
413
- .option('--qty <n>', 'Quantity', '1')
455
+ .command("add")
456
+ .argument("<listId>", "List ID")
457
+ .argument("<productId>", "Product ID")
458
+ .option("--qty <n>", "Quantity", "1")
414
459
  .action(async (listId, productId, options) => {
415
460
  const config = (0, config_1.loadConfig)();
416
461
  const session = requireSessionCookie();
@@ -421,7 +466,7 @@ async function main() {
421
466
  (0, format_1.outputJson)(result);
422
467
  }
423
468
  else {
424
- process.stdout.write('Added to list.\n');
469
+ process.stdout.write("Added to list.\n");
425
470
  }
426
471
  });
427
472
  try {
@@ -429,8 +474,24 @@ async function main() {
429
474
  }
430
475
  catch (err) {
431
476
  const json = program.opts().json;
477
+ const error = err;
478
+ (0, log_1.appendLog)({
479
+ level: "error",
480
+ event: "command.error",
481
+ command: lastCommand,
482
+ args: lastArgs,
483
+ json: Boolean(json),
484
+ pid: process.pid,
485
+ durationMs: Date.now() - lastStart,
486
+ configDir: (0, config_1.getConfigDir)(),
487
+ message: error.message,
488
+ status: error.status,
489
+ body: error.body,
490
+ url: error.url,
491
+ method: error.method,
492
+ });
432
493
  if (json) {
433
- (0, format_1.outputJson)({ status: 'error', ...formatError(err) });
494
+ (0, format_1.outputJson)({ status: "error", ...formatError(err) });
434
495
  }
435
496
  else {
436
497
  const details = formatError(err);
@@ -0,0 +1,2 @@
1
+ import type { OrderDetail, OrderSummary } from "../lib/types";
2
+ export declare function warnUnknownOrderStatuses(orders: Array<OrderSummary | OrderDetail>): void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.warnUnknownOrderStatuses = warnUnknownOrderStatuses;
4
+ const order_status_1 = require("../lib/order-status");
5
+ const ISSUE_URL = "https://github.com/nb/ebag/issues";
6
+ function warnUnknownOrderStatuses(orders) {
7
+ for (const order of orders) {
8
+ const status = order.status;
9
+ if ((0, order_status_1.isKnownOrderStatus)(status) || status === undefined)
10
+ continue;
11
+ const orderId = order.id ? ` for order ${order.id}` : "";
12
+ process.stderr.write(`Warning: Unknown order status${orderId}. Please check this order on the website and file a GitHub issue so we can update the status mapping: ${ISSUE_URL}\n`);
13
+ }
14
+ }
@@ -1,3 +1,3 @@
1
- import type { Config, Session } from './types';
1
+ import type { Config, Session } from "./types";
2
2
  export declare function getLoginInstructions(): string;
3
3
  export declare function validateSession(config: Config, session: Session): Promise<unknown>;
package/dist/lib/auth.js CHANGED
@@ -5,13 +5,13 @@ exports.validateSession = validateSession;
5
5
  const client_1 = require("./client");
6
6
  function getLoginInstructions() {
7
7
  return [
8
- '1) Log in to https://www.ebag.bg in your browser.',
9
- '2) Open DevTools > Network and click any request to https://www.ebag.bg (not a static asset).',
10
- '3) In the request headers, copy the full Cookie header value (semicolon-separated).',
8
+ "1) Log in to https://www.ebag.bg in your browser.",
9
+ "2) Open DevTools > Network and click any request to https://www.ebag.bg (not a static asset).",
10
+ "3) In the request headers, copy the full Cookie header value (semicolon-separated).",
11
11
  '4) Run: ebag login --cookie "<cookie>"',
12
- ].join('\n');
12
+ ].join("\n");
13
13
  }
14
14
  async function validateSession(config, session) {
15
- const result = await (0, client_1.requestEbag)(config, session, '/user/json');
15
+ const result = await (0, client_1.requestEbag)(config, session, "/user/json");
16
16
  return result.data;
17
17
  }
@@ -1,4 +1,4 @@
1
- import type { Config, Session } from './types';
1
+ import type { Config, Session } from "./types";
2
2
  export declare function addToCart(config: Config, session: Session, productId: number, quantity: number, unitTypeOverride?: string): Promise<unknown>;
3
3
  export declare function updateCart(config: Config, session: Session, productId: number, quantity: number, unitTypeOverride?: string): Promise<unknown>;
4
4
  export declare function getCart(config: Config, session: Session): Promise<unknown>;
package/dist/lib/cart.js CHANGED
@@ -4,17 +4,17 @@ exports.addToCart = addToCart;
4
4
  exports.updateCart = updateCart;
5
5
  exports.getCart = getCart;
6
6
  const client_1 = require("./client");
7
- async function addToCart(config, session, productId, quantity, unitTypeOverride = 'false') {
8
- const baseUrl = config.baseUrl || 'https://www.ebag.bg';
7
+ async function addToCart(config, session, productId, quantity, unitTypeOverride = "false") {
8
+ const baseUrl = config.baseUrl || "https://www.ebag.bg";
9
9
  const body = new URLSearchParams({
10
10
  product_id: String(productId),
11
11
  quantity: String(quantity),
12
12
  unit_type_override: unitTypeOverride,
13
13
  });
14
- const result = await (0, client_1.requestEbag)(config, session, '/cart/add', {
15
- method: 'POST',
14
+ const result = await (0, client_1.requestEbag)(config, session, "/cart/add", {
15
+ method: "POST",
16
16
  headers: {
17
- 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
17
+ "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
18
18
  origin: baseUrl,
19
19
  referer: `${baseUrl}/search/`,
20
20
  },
@@ -22,17 +22,17 @@ async function addToCart(config, session, productId, quantity, unitTypeOverride
22
22
  });
23
23
  return result.data;
24
24
  }
25
- async function updateCart(config, session, productId, quantity, unitTypeOverride = 'false') {
26
- const baseUrl = config.baseUrl || 'https://www.ebag.bg';
25
+ async function updateCart(config, session, productId, quantity, unitTypeOverride = "false") {
26
+ const baseUrl = config.baseUrl || "https://www.ebag.bg";
27
27
  const body = new URLSearchParams({
28
28
  product_id: String(productId),
29
29
  quantity: String(quantity),
30
30
  unit_type_override: unitTypeOverride,
31
31
  });
32
- const result = await (0, client_1.requestEbag)(config, session, '/cart/update', {
33
- method: 'POST',
32
+ const result = await (0, client_1.requestEbag)(config, session, "/cart/update", {
33
+ method: "POST",
34
34
  headers: {
35
- 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
35
+ "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
36
36
  origin: baseUrl,
37
37
  referer: `${baseUrl}/search/`,
38
38
  },
@@ -41,6 +41,6 @@ async function updateCart(config, session, productId, quantity, unitTypeOverride
41
41
  return result.data;
42
42
  }
43
43
  async function getCart(config, session) {
44
- const result = await (0, client_1.requestEbag)(config, session, '/cart/json');
44
+ const result = await (0, client_1.requestEbag)(config, session, "/cart/json");
45
45
  return result.data;
46
46
  }
@@ -1,4 +1,4 @@
1
- import type { Config, Session } from './types';
1
+ import type { Config, Session } from "./types";
2
2
  export type RequestResult<T> = {
3
3
  status: number;
4
4
  data: T;