@growupsell/commerce 0.0.0-stage → 1.0.0-alpha.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.
@@ -0,0 +1,2789 @@
1
+ // src/http/convert.ts
2
+ var text = (value) => value == null || value.trim() === "" ? null : value;
3
+ var amount = (value) => {
4
+ if (value == null || value === "") return null;
5
+ const n = typeof value === "number" ? value : Number(value);
6
+ return Number.isFinite(n) ? n : null;
7
+ };
8
+
9
+ // src/http/blog.ts
10
+ var PUBLIC = { anonymous: true, business: "required" };
11
+ var MERCHANT = { merchant: true, anonymous: true, business: "required" };
12
+ function createBlogApi(request) {
13
+ return {
14
+ async list(options = {}) {
15
+ const categorySlug = options.categorySlug?.trim();
16
+ const rows = await request("GET", "/blog/list", {
17
+ ...PUBLIC,
18
+ query: categorySlug ? { category_slug: categorySlug } : void 0
19
+ });
20
+ return rows.map(toBlogPostSummary);
21
+ },
22
+ async get(slug) {
23
+ return toBlogPost(await request("GET", `/blog/get/${path(slug)}`, PUBLIC));
24
+ },
25
+ async updateBody(slug, body) {
26
+ const json = { body };
27
+ await request("PATCH", `/blog/update/${path(slug)}`, { ...MERCHANT, json });
28
+ }
29
+ };
30
+ }
31
+ function path(slug) {
32
+ if (!slug.trim()) throw new Error("blog: slug is required.");
33
+ return encodeURIComponent(slug.trim());
34
+ }
35
+ function toBlogPostSummary(wire) {
36
+ return summary(wire, wire.user);
37
+ }
38
+ function toBlogPost(wire) {
39
+ return {
40
+ ...summary(wire, wire.created_by),
41
+ body: Array.isArray(wire.body) ? wire.body.filter((s) => typeof s === "string") : [],
42
+ editor: EDITORS.includes(wire.editor_type) ? wire.editor_type : "quill"
43
+ };
44
+ }
45
+ var EDITORS = ["quill", "ckeditor", "froala"];
46
+ function summary(wire, author) {
47
+ return {
48
+ id: wire.id,
49
+ slug: wire.slug,
50
+ title: wire.title,
51
+ shortDescription: text(wire.short_description),
52
+ image: text(wire.image),
53
+ category: wire.category ? toCategory(wire.category) : null,
54
+ tags: Array.isArray(wire.tags) ? wire.tags.filter((t) => typeof t === "string" && t.trim() !== "") : [],
55
+ author: author ? toAuthor(author) : null,
56
+ isPublished: wire.is_published ?? false,
57
+ createdAt: wire.created_at,
58
+ updatedAt: wire.updated_at
59
+ };
60
+ }
61
+ function toCategory(wire) {
62
+ return {
63
+ id: wire.id,
64
+ name: text(wire.name),
65
+ slug: wire.slug,
66
+ image: text(wire.image),
67
+ seoDescription: text(wire.seo_description)
68
+ };
69
+ }
70
+ function toAuthor(wire) {
71
+ return { id: wire.id, name: text(wire.name.trim()), photo: text(wire.photo) };
72
+ }
73
+
74
+ // src/http/business.ts
75
+ var PUBLIC2 = { business: "required", anonymous: true };
76
+ var MERCHANT2 = { merchant: true, anonymous: true };
77
+ function createBusinessApi(request) {
78
+ return {
79
+ async detail(domain) {
80
+ const trimmed = domain.trim();
81
+ if (!trimmed) throw new Error("business.detail: domain is required.");
82
+ const wire = await request(
83
+ "GET",
84
+ `/business/detail/${encodeURIComponent(trimmed)}`,
85
+ { anonymous: true }
86
+ );
87
+ return toBusiness(wire);
88
+ },
89
+ async settings() {
90
+ return toBusinessSettings(
91
+ await request("GET", "/business/setting/get", PUBLIC2)
92
+ );
93
+ },
94
+ async media() {
95
+ return toBusinessMedia(
96
+ await request("GET", "/business/media/get", PUBLIC2)
97
+ );
98
+ },
99
+ async socialLinks() {
100
+ return toSocialLinks(
101
+ await request(
102
+ "GET",
103
+ "/business/social-media-link/get",
104
+ PUBLIC2
105
+ )
106
+ );
107
+ },
108
+ async bankTransferOptions() {
109
+ const wire = await request(
110
+ "GET",
111
+ "/business/direct/transfer/list",
112
+ {
113
+ business: "required"
114
+ }
115
+ );
116
+ return wire.filter((o) => !o.deleted).map(toBankTransferOption);
117
+ },
118
+ async themeSettings() {
119
+ return toThemeSettings(
120
+ await request("GET", "/business/theme/setting/get", PUBLIC2)
121
+ );
122
+ },
123
+ async updateThemeSettings(changes) {
124
+ const wire = await request(
125
+ "PATCH",
126
+ "/business/theme/setting/update",
127
+ { ...MERCHANT2, json: toThemeSettingWrite(changes) }
128
+ );
129
+ return toThemeSettings(wire);
130
+ }
131
+ };
132
+ }
133
+ function toBusiness(wire) {
134
+ const tag = wire.website_tag;
135
+ return {
136
+ id: wire.id,
137
+ name: wire.name ?? null,
138
+ description: wire.description ?? null,
139
+ legalName: wire.legal_name ?? null,
140
+ gstNumber: wire.gst_number ?? null,
141
+ panNumber: wire.pan_number ?? null,
142
+ isTemplate: wire.is_template ?? false,
143
+ category: wire.category ? { id: wire.category.id, name: wire.category.name, type: wire.category.type } : null,
144
+ legalForm: wire.legal_forms ? { id: wire.legal_forms.id, name: wire.legal_forms.name } : null,
145
+ features: wire.features.filter((f) => f.is_active !== false).map((f) => f.name),
146
+ googleTagManagerId: tag?.is_active && tag.gtm_container_id ? tag.gtm_container_id : null,
147
+ createdAt: wire.created_at,
148
+ updatedAt: wire.updated_at
149
+ };
150
+ }
151
+ function toBusinessSettings(wire) {
152
+ return {
153
+ currency: wire.currency ? { code: wire.currency.currency_code, symbol: wire.currency.currency_symbol } : null,
154
+ domain: text(wire.domain)
155
+ };
156
+ }
157
+ function toBusinessMedia(wire) {
158
+ return { logoSquare: text(wire.logo_square), logoRectangle: text(wire.logo_rectangle) };
159
+ }
160
+ function toSocialLinks(wire) {
161
+ return {
162
+ facebook: text(wire.facebook),
163
+ instagram: text(wire.instagram),
164
+ twitter: text(wire.twitter),
165
+ telegram: text(wire.telegram),
166
+ whatsapp: text(wire.whatsapp),
167
+ linkedin: text(wire.linkedin)
168
+ };
169
+ }
170
+ function toThemeSettings(wire) {
171
+ return {
172
+ id: wire.id,
173
+ // Required by the model; '#CCC' is its default.
174
+ primaryColor: text(wire.primary_color) ?? "#CCC",
175
+ accentColor: text(wire.accent_color),
176
+ fontFamily: text(wire.font_family),
177
+ websiteThemeId: themeId(wire.website_theme),
178
+ appThemeId: themeId(wire.app_theme)
179
+ };
180
+ }
181
+ var themeId = (theme) => typeof theme === "number" ? theme : theme?.id ?? null;
182
+ function toThemeSettingWrite(changes) {
183
+ const body = {};
184
+ if (changes.primaryColor !== void 0) body.primary_color = changes.primaryColor;
185
+ if (changes.accentColor !== void 0) body.accent_color = changes.accentColor;
186
+ if (changes.fontFamily !== void 0) body.font_family = changes.fontFamily;
187
+ if (changes.websiteThemeId !== void 0) body.website_theme = changes.websiteThemeId;
188
+ if (changes.appThemeId !== void 0) body.app_theme = changes.appThemeId;
189
+ return body;
190
+ }
191
+ function toBankTransferOption(wire) {
192
+ return {
193
+ id: wire.id,
194
+ type: text(wire.type),
195
+ description: text(wire.description),
196
+ photo: text(wire.photo),
197
+ paymentMethodId: wire.payment_method ?? null
198
+ };
199
+ }
200
+
201
+ // src/http/cart.ts
202
+ var SHOPPER = { business: "required", customerOrSession: "required" };
203
+ function createCartApi(request) {
204
+ async function removeItem(lineId) {
205
+ await request("DELETE", `/order/cart/product/delete/${lineId}`, SHOPPER);
206
+ }
207
+ return {
208
+ async get() {
209
+ return toCart(await request("GET", "/order/cart/get", SHOPPER));
210
+ },
211
+ async addItem({ skuId, quantity, customData }) {
212
+ checkQuantity(quantity, 1);
213
+ const json = { sku: skuId, qty: quantity };
214
+ if (customData) json.custom_data = customData;
215
+ const wire = await request("POST", "/order/cart/product/create", {
216
+ ...SHOPPER,
217
+ json
218
+ });
219
+ return { lineId: wire.id };
220
+ },
221
+ async setQuantity(lineId, quantity) {
222
+ checkQuantity(quantity, 0);
223
+ if (quantity === 0) return removeItem(lineId);
224
+ await request("PATCH", `/order/cart/product/update/${lineId}`, {
225
+ ...SHOPPER,
226
+ json: { qty: quantity }
227
+ });
228
+ },
229
+ removeItem,
230
+ async setOrderType(type) {
231
+ const wire = await request("PATCH", "/order/cart/type/update", {
232
+ ...SHOPPER,
233
+ json: { type }
234
+ });
235
+ return {
236
+ subtotal: amount(wire.total_amount) ?? 0,
237
+ deliveryCharge: amount(wire.delivery_charge) ?? 0,
238
+ payable: amount(wire.final_payable_amount) ?? 0
239
+ };
240
+ },
241
+ async setAddresses({ shippingAddressId, billingAddressId }) {
242
+ if (shippingAddressId == null && billingAddressId == null) {
243
+ throw new TypeError("setAddresses: pass shippingAddressId, billingAddressId or both.");
244
+ }
245
+ const json = {};
246
+ if (shippingAddressId != null) json.shipping_address = shippingAddressId;
247
+ if (billingAddressId != null) json.billing_address = billingAddressId;
248
+ const wire = await request("PATCH", "/order/cart/address/update", {
249
+ business: "required",
250
+ customer: "required",
251
+ json
252
+ });
253
+ return {
254
+ shippingAddressId: wire.shipping_address ?? null,
255
+ billingAddressId: wire.billing_address ?? null
256
+ };
257
+ },
258
+ async applyOffer(offerId) {
259
+ const json = { offer: offerId };
260
+ await request("PATCH", "/order/cart/offer/update", { ...SHOPPER, json });
261
+ },
262
+ async applyPromoCode(promoCodeId) {
263
+ const json = { promo: promoCodeId };
264
+ await request("PATCH", "/order/cart/promo/update", { ...SHOPPER, json });
265
+ },
266
+ async setTaxDetails({ legalName, taxNumber }) {
267
+ const json = { legal_name: legalName, tax_number: taxNumber };
268
+ const wire = await request("PATCH", "/order/cart/tax/update", {
269
+ business: "required",
270
+ session: "required",
271
+ json
272
+ });
273
+ return { legalName: wire.legal_name, taxNumber: wire.tax_number };
274
+ },
275
+ async deliveryEstimate(cartId, options = {}) {
276
+ const wire = await request(
277
+ "GET",
278
+ `/order/cart/delivery/charge/info/get/${cartId}`,
279
+ // The backend calls Shiprocket synchronously; a blank pincode is left out.
280
+ { ...SHOPPER, timeoutMs: 3e4, query: { pincode: options.pincode?.trim() } }
281
+ );
282
+ return {
283
+ deliveryCharge: amount(wire.delivery_charge) ?? 0,
284
+ payable: amount(wire.final_payable_amount) ?? 0,
285
+ estimatedDelivery: text(wire.estimate_delivery_day)
286
+ };
287
+ }
288
+ };
289
+ }
290
+ function checkQuantity(quantity, min) {
291
+ if (!Number.isInteger(quantity) || quantity < min) {
292
+ throw new RangeError(`Cart quantity must be a whole number of at least ${min}: ${quantity}.`);
293
+ }
294
+ }
295
+ function toCart(wire) {
296
+ const lines = (wire.products ?? []).map(toCartLine);
297
+ const orderOffer = amount(wire.offer_redeem_amount) ?? 0;
298
+ return {
299
+ id: wire.id,
300
+ type: wire.type,
301
+ lines,
302
+ subtotal: amount(wire.total_amount) ?? 0,
303
+ // The backend stores the offer amount on the order for order-level offers, or on each line
304
+ // for product, category and brand offers (never both).
305
+ offerDiscount: orderOffer > 0 ? orderOffer : sum(lines.map((line) => line.offerDiscount)),
306
+ promoDiscount: amount(wire.promo_redeem_amount) ?? 0,
307
+ deliveryCharge: amount(wire.delivery_charge) ?? 0,
308
+ payable: amount(wire.final_payable_amount) ?? 0,
309
+ deliveryChargeType: wire.delivery_charge_type ?? null,
310
+ offer: wire.offers ? { id: wire.offers.id, name: wire.offers.name, couponCode: text(wire.offers.coupon_code) } : null,
311
+ promoCode: wire.promo ? toAppliedPromoCode(wire.promo) : null,
312
+ shippingAddressId: idOf(wire.shipping_address),
313
+ billingAddressId: idOf(wire.billing_address),
314
+ advanceAmount: amount(wire.advance_amount)
315
+ };
316
+ }
317
+ function toCartLine(wire) {
318
+ const { sku } = wire;
319
+ const mrp = sku ? amount(sku.mrp) : null;
320
+ return {
321
+ id: wire.id,
322
+ skuId: sku?.id ?? null,
323
+ productId: sku?.product.id ?? null,
324
+ name: wire.label ?? wire.name ?? "",
325
+ photo: text(wire.photo),
326
+ unitPrice: amount(wire.price) ?? 0,
327
+ // An MRP of 0 means "not set", as in products.
328
+ mrp: mrp && mrp > 0 ? mrp : null,
329
+ quantity: wire.qty,
330
+ lineTotal: amount(wire.total) ?? 0,
331
+ offerDiscount: amount(wire.offer_redeem_amount) ?? 0,
332
+ taxPercent: amount(wire.tax),
333
+ maxQuantity: sku ? wire.max_qty : null,
334
+ isDigital: wire.is_digital_product,
335
+ fulfillmentMode: text(wire.fulfillment_mode) ?? "none",
336
+ customData: wire.custom_data ?? null,
337
+ variantIds: sku?.selected_variants ?? []
338
+ };
339
+ }
340
+ function toAppliedPromoCode(wire) {
341
+ return {
342
+ id: wire.id,
343
+ code: wire.code ?? "",
344
+ type: wire.type ?? "percentage",
345
+ value: amount(wire.value) ?? 0
346
+ };
347
+ }
348
+ function idOf(value) {
349
+ if (value == null) return null;
350
+ return typeof value === "number" ? value : value.id;
351
+ }
352
+ function sum(values) {
353
+ return Math.round(values.reduce((total, value) => total + value, 0) * 100) / 100;
354
+ }
355
+
356
+ // src/http/catalog.ts
357
+ var PUBLIC3 = { business: "required", anonymous: true };
358
+ function createCatalogApi(request) {
359
+ return {
360
+ async categories(options = {}) {
361
+ const search = options.search?.trim();
362
+ const wire = await request("GET", "/product/category/list", {
363
+ ...PUBLIC3,
364
+ query: { search }
365
+ });
366
+ return wire.map(toCategoryTree);
367
+ },
368
+ async category(id) {
369
+ return toCategory2(
370
+ await request("GET", `/product/category/get/${id}`, {
371
+ anonymous: true
372
+ })
373
+ );
374
+ },
375
+ async categoriesByIds(ids) {
376
+ if (ids.length === 0) return [];
377
+ const wire = await request("GET", "/product/category/list/filter", {
378
+ ...PUBLIC3,
379
+ query: { ids }
380
+ });
381
+ return wire.map(toCategory2);
382
+ },
383
+ async brands(options = {}) {
384
+ const wire = await request("GET", "/product/brand/list", {
385
+ ...PUBLIC3,
386
+ query: { category_ids: options.categoryIds }
387
+ });
388
+ return wire.map(toBrand);
389
+ },
390
+ async settings() {
391
+ return toCatalogSettings(
392
+ await request("GET", "/product/business/setting/get", PUBLIC3)
393
+ );
394
+ }
395
+ };
396
+ }
397
+ function toCategory2(wire) {
398
+ return {
399
+ id: wire.id,
400
+ name: wire.name,
401
+ description: text(wire.description),
402
+ photo: text(wire.photo),
403
+ parentId: wire.parent ?? null,
404
+ productCount: wire.product_count
405
+ };
406
+ }
407
+ function toCategoryTree(wire) {
408
+ return { ...toCategory2(wire), children: wire.child.map(toCategoryTree) };
409
+ }
410
+ function toBrand(wire) {
411
+ return {
412
+ id: wire.id,
413
+ name: wire.name,
414
+ description: text(wire.description),
415
+ photo: text(wire.photo),
416
+ productCount: wire.product_count
417
+ };
418
+ }
419
+ function toCatalogSettings(wire) {
420
+ return {
421
+ reviews: wire.is_active_review ?? true,
422
+ brands: wire.is_active_brand ?? true,
423
+ categories: wire.is_active_category ?? true,
424
+ subscriptions: wire.is_active_subscription ?? true,
425
+ digitalDelivery: wire.enable_digital_delivery ?? false,
426
+ variants: wire.is_active_variants ?? true,
427
+ specifications: wire.is_active_specification ?? true,
428
+ unitTracking: wire.has_product_unit_tracking_info ?? false,
429
+ manufactureAndExpiryDates: wire.has_mfg_expiry_date ?? false
430
+ };
431
+ }
432
+
433
+ // src/http/faq.ts
434
+ var PUBLIC4 = { business: "required", anonymous: true };
435
+ var MERCHANT3 = { business: "required", merchant: true, anonymous: true };
436
+ function createFaqApi(request) {
437
+ return {
438
+ async list() {
439
+ return (await request("GET", "/content/faq/list", PUBLIC4)).map(toFaq);
440
+ },
441
+ async create(input) {
442
+ const json = { question: input.question, answer: input.answer };
443
+ return toFaq(await request("POST", "/content/faq/create", { ...MERCHANT3, json }));
444
+ },
445
+ async update(id, changes) {
446
+ const json = {};
447
+ if (changes.question !== void 0) json.question = changes.question;
448
+ if (changes.answer !== void 0) json.answer = changes.answer;
449
+ return toFaq(
450
+ await request("PATCH", `/content/faq/update/${id}`, { ...MERCHANT3, json })
451
+ );
452
+ },
453
+ async move(id, direction) {
454
+ const json = { direction };
455
+ await request("PATCH", `/content/faq/update/${id}`, { ...MERCHANT3, json });
456
+ },
457
+ async remove(id) {
458
+ await request("DELETE", `/content/faq/delete/${id}`, MERCHANT3);
459
+ }
460
+ };
461
+ }
462
+ function toFaq(wire) {
463
+ return {
464
+ id: wire.id,
465
+ question: text(wire.question),
466
+ answer: text(wire.answer),
467
+ displayOrder: wire.display_order
468
+ };
469
+ }
470
+
471
+ // src/http/forms.ts
472
+ var PUBLIC5 = { anonymous: true, business: "required" };
473
+ var SUBMIT = { business: "required" };
474
+ var MERCHANT4 = { merchant: true, anonymous: true, business: "required" };
475
+ function createFormsApi(request) {
476
+ return {
477
+ async get(slug) {
478
+ return toPublicForm(
479
+ await request("GET", `/forms/public/${path2(slug)}`, PUBLIC5)
480
+ );
481
+ },
482
+ async submit(slug, values) {
483
+ const json = { data: values };
484
+ await request("POST", `/forms/submit/${path2(slug)}`, { ...SUBMIT, json });
485
+ },
486
+ async listForMerchant() {
487
+ const rows = await request("GET", "/forms/list", {
488
+ ...MERCHANT4,
489
+ query: { is_active: true }
490
+ });
491
+ return rows.map(toFormSummary);
492
+ }
493
+ };
494
+ }
495
+ function path2(slug) {
496
+ if (!slug.trim()) throw new Error("forms: slug is required.");
497
+ return encodeURIComponent(slug.trim());
498
+ }
499
+ function toPublicForm(wire) {
500
+ const fields = Array.isArray(wire.field_configuration) ? wire.field_configuration : [];
501
+ return {
502
+ id: wire.id,
503
+ slug: wire.slug,
504
+ name: wire.name,
505
+ description: text(wire.description),
506
+ successMessage: text(wire.success_message),
507
+ fields: fields.map(toFormField)
508
+ };
509
+ }
510
+ function toFormSummary(wire) {
511
+ return { id: wire.id, name: wire.name, slug: wire.slug };
512
+ }
513
+ function toFormField(wire) {
514
+ return {
515
+ name: wire.name,
516
+ // The backend only saves the ten known types (`validation.py:4-7`).
517
+ type: wire.type,
518
+ required: wire.required,
519
+ label: text(wire.label) ?? wire.name.replace(/_/g, " "),
520
+ options: wire.options?.map((o) => ({ label: o.label, value: o.value })) ?? []
521
+ };
522
+ }
523
+
524
+ // src/http/helpdesk.ts
525
+ var CREATE = { business: "required" };
526
+ function createHelpdeskApi(request) {
527
+ return {
528
+ async createInquiry(input) {
529
+ const title = input.subject.trim();
530
+ const description = input.message.trim();
531
+ if (!title || !description) {
532
+ throw new Error("helpdesk.createInquiry: subject and message are required.");
533
+ }
534
+ const json = { title, description };
535
+ const name = input.name?.trim();
536
+ const email = input.email?.trim();
537
+ const phone = input.phone?.trim();
538
+ if (name) json.name = name;
539
+ if (email) json.email = email;
540
+ if (phone) json.phone_number = phone;
541
+ await request("POST", "/helpdesk/inquiry/web/create", { ...CREATE, json });
542
+ }
543
+ };
544
+ }
545
+
546
+ // src/http/newsletter.ts
547
+ var PUBLIC6 = { anonymous: true, business: "required" };
548
+ function createNewsletterApi(request) {
549
+ return {
550
+ async subscribe(email) {
551
+ const json = { email: required(email, "email") };
552
+ await request("POST", "/subscribers/create/email", {
553
+ ...PUBLIC6,
554
+ json
555
+ });
556
+ },
557
+ async verify(email, otp) {
558
+ const json = { email: required(email, "email"), otp: required(otp, "otp") };
559
+ await request("PATCH", "/subscribers/email/verify", {
560
+ ...PUBLIC6,
561
+ json
562
+ });
563
+ }
564
+ };
565
+ }
566
+ function required(value, name) {
567
+ const trimmed = value.trim();
568
+ if (!trimmed) throw new Error(`newsletter: ${name} is required.`);
569
+ return trimmed;
570
+ }
571
+
572
+ // src/http/live-support.ts
573
+ var PUBLIC7 = { anonymous: true, business: "required" };
574
+ function createLiveSupportApi(request) {
575
+ return {
576
+ async widget() {
577
+ return toLiveSupportWidget(
578
+ await request(
579
+ "GET",
580
+ "/website/live-support-widget/config/get",
581
+ PUBLIC7
582
+ )
583
+ );
584
+ }
585
+ };
586
+ }
587
+ function toLiveSupportWidget(wire) {
588
+ const number = text(wire.whatsapp_no)?.trim();
589
+ if (!wire.is_active || !number) return null;
590
+ return { channel: "whatsapp", whatsappNumber: number };
591
+ }
592
+
593
+ // src/http/policies.ts
594
+ var TYPES = {
595
+ privacy_policy: "privacy",
596
+ terms_of_use: "terms-of-use",
597
+ cancellations_refunds_policy: "cancellation-refund",
598
+ shipping_policy: "shipping",
599
+ warranty_policy: "warranty"
600
+ };
601
+ var WIRE_TYPES = Object.fromEntries(
602
+ Object.entries(TYPES).map(([wire, type]) => [type, wire])
603
+ );
604
+ var PUBLIC8 = { business: "required", anonymous: true };
605
+ var MERCHANT5 = { merchant: true, anonymous: true };
606
+ function createPoliciesApi(request) {
607
+ return {
608
+ async list(options = {}) {
609
+ const wire = await request("GET", "/policy_and_terms/list", {
610
+ ...PUBLIC8,
611
+ query: { type: options.type && WIRE_TYPES[options.type] }
612
+ });
613
+ return wire.map(toPolicy).filter((p) => p !== null);
614
+ },
615
+ async create(input) {
616
+ const wire = await request("POST", "/policy_and_terms/create", {
617
+ ...MERCHANT5,
618
+ business: "required",
619
+ json: toWrite(input)
620
+ });
621
+ return required2(wire);
622
+ },
623
+ async createMany(inputs) {
624
+ if (inputs.length === 0) return [];
625
+ const wire = await request("POST", "/policy_and_terms/bulk/create", {
626
+ ...MERCHANT5,
627
+ json: inputs.map(toWrite)
628
+ });
629
+ return wire.map(required2);
630
+ },
631
+ async update(id, changes) {
632
+ const wire = await request("PUT", `/policy_and_terms/update/${id}`, {
633
+ ...MERCHANT5,
634
+ json: toWrite(changes)
635
+ });
636
+ return required2(wire);
637
+ },
638
+ async move(id, direction) {
639
+ const body = { direction };
640
+ await request("PUT", `/policy_and_terms/update/${id}`, {
641
+ ...MERCHANT5,
642
+ json: body
643
+ });
644
+ },
645
+ async remove(id) {
646
+ await request("DELETE", `/policy_and_terms/delete/${id}`, MERCHANT5);
647
+ }
648
+ };
649
+ }
650
+ function toWrite(input) {
651
+ const body = {};
652
+ if (input.type !== void 0) body.type = WIRE_TYPES[input.type];
653
+ if (input.heading !== void 0) body.heading = input.heading;
654
+ if (input.description !== void 0) body.description = input.description;
655
+ return body;
656
+ }
657
+ var required2 = (wire) => toPolicy(wire);
658
+ function toPolicy(wire) {
659
+ const type = TYPES[wire.type];
660
+ if (!type) return null;
661
+ return {
662
+ id: wire.id,
663
+ type,
664
+ heading: wire.heading,
665
+ description: text(wire.description),
666
+ displayOrder: wire.display_order
667
+ };
668
+ }
669
+
670
+ // src/http/productPage.ts
671
+ var PUBLIC9 = { business: "required", anonymous: true };
672
+ var MERCHANT6 = { merchant: true, business: "required", anonymous: true };
673
+ function targetFields(target) {
674
+ if (target === void 0) return {};
675
+ if (target === "all") return { selected_variants: [], variant: null };
676
+ if ("variantId" in target) return { variant: target.variantId };
677
+ return { selected_variants: target.variantIds, variant: null };
678
+ }
679
+ function createProductPageApi(request) {
680
+ const sentItem = (wire, target) => {
681
+ const item = toProductSectionItem(wire);
682
+ if (wire.sku != null && typeof target === "object" && "variantIds" in target) {
683
+ item.target = { skuId: wire.sku, variantIds: [...target.variantIds] };
684
+ }
685
+ return item;
686
+ };
687
+ return {
688
+ async sections(productId) {
689
+ const wire = await request(
690
+ "GET",
691
+ `/product/detail/list/${productId}`,
692
+ PUBLIC9
693
+ );
694
+ return wire.map(toProductSection);
695
+ },
696
+ async blocks(productId, options = {}) {
697
+ const wire = await request("GET", `/product/content/list/${productId}`, {
698
+ ...PUBLIC9,
699
+ query: { slug: options.slug }
700
+ });
701
+ return wire.map(toProductBlock);
702
+ },
703
+ async createSection({ productId, title }) {
704
+ return toProductSection(
705
+ await request("POST", "/product/detail/create", {
706
+ ...MERCHANT6,
707
+ json: { product: productId, title }
708
+ })
709
+ );
710
+ },
711
+ async deleteSection(id) {
712
+ await request("DELETE", `/product/detail/delete/${id}`, MERCHANT6);
713
+ },
714
+ async addSectionItem({ sectionId, data, target }) {
715
+ const wire = await request("POST", "/product/detail/content/create", {
716
+ ...MERCHANT6,
717
+ json: { product_detail: sectionId, data, ...targetFields(target) }
718
+ });
719
+ return sentItem(wire, target);
720
+ },
721
+ async updateSectionItem(id, { sectionId, data, target }) {
722
+ const wire = await request(
723
+ "PATCH",
724
+ `/product/detail/content/update/${id}`,
725
+ {
726
+ ...MERCHANT6,
727
+ json: {
728
+ product_detail: sectionId,
729
+ ...data === void 0 ? {} : { data },
730
+ ...targetFields(target)
731
+ }
732
+ }
733
+ );
734
+ return sentItem(wire, target);
735
+ },
736
+ async moveSectionItem(id, direction) {
737
+ await request("PATCH", `/product/detail/content/update/${id}`, {
738
+ ...MERCHANT6,
739
+ json: { direction }
740
+ });
741
+ },
742
+ async deleteSectionItem(id) {
743
+ await request("DELETE", `/product/detail/content/delete/${id}`, MERCHANT6);
744
+ },
745
+ async createBlock({ productId, slug, data }) {
746
+ return toProductBlock(
747
+ await request("POST", "/product/content/create", {
748
+ ...MERCHANT6,
749
+ json: { product: productId, slug, data }
750
+ })
751
+ );
752
+ },
753
+ async updateBlock(id, { slug, data }) {
754
+ return toProductBlock(
755
+ await request("PATCH", `/product/content/update/${id}`, {
756
+ ...MERCHANT6,
757
+ json: { slug, data }
758
+ })
759
+ );
760
+ },
761
+ async deleteBlock(id) {
762
+ await request("DELETE", `/product/content/delete/${id}`, MERCHANT6);
763
+ }
764
+ };
765
+ }
766
+ function toTarget(wire) {
767
+ if (wire.sku != null) {
768
+ const variants = wire.selected_variants ?? [];
769
+ return { skuId: wire.sku, variantIds: variants.length ? variants.map((v) => v.id) : null };
770
+ }
771
+ if (wire.variant != null) return { variantId: wire.variant };
772
+ return "all";
773
+ }
774
+ function toProductSectionItem(wire) {
775
+ return {
776
+ id: wire.id,
777
+ sectionId: wire.product_detail,
778
+ data: wire.data ?? null,
779
+ order: wire.display_order,
780
+ target: toTarget(wire)
781
+ };
782
+ }
783
+ function toProductSection(wire) {
784
+ return {
785
+ id: wire.id,
786
+ productId: wire.product,
787
+ title: wire.title,
788
+ // Already ordered by the backend; sorted again so the order never depends on it.
789
+ items: [...wire.content].sort((a, b) => a.display_order - b.display_order).map(toProductSectionItem)
790
+ };
791
+ }
792
+ function toProductBlock(wire) {
793
+ return {
794
+ id: wire.id,
795
+ productId: wire.product,
796
+ slug: wire.slug,
797
+ data: wire.data ?? null,
798
+ order: wire.display_order
799
+ };
800
+ }
801
+
802
+ // src/http/products.ts
803
+ var DEFAULT_LIMIT = 20;
804
+ var SORT = {
805
+ "price-low-to-high": "low_to_high",
806
+ "price-high-to-low": "high_to_low",
807
+ newest: "newest_first",
808
+ popularity: "popularity",
809
+ category: "category"
810
+ };
811
+ var PUBLIC10 = { business: "required", anonymous: true };
812
+ function createProductsApi(request) {
813
+ return {
814
+ async search(options = {}) {
815
+ const limit = options.limit ?? DEFAULT_LIMIT;
816
+ const offset = options.offset ?? 0;
817
+ const page = await request(
818
+ "GET",
819
+ "/product/sku/list/search",
820
+ {
821
+ ...PUBLIC10,
822
+ session: "required",
823
+ query: {
824
+ status: "available",
825
+ search: options.search?.trim(),
826
+ category_ids: options.categoryIds,
827
+ brand_ids: options.brandIds,
828
+ variants: options.variantLabels,
829
+ specifications: options.specifications?.length ? JSON.stringify(options.specifications) : void 0,
830
+ min_price: options.minPrice,
831
+ max_price: options.maxPrice,
832
+ min_discount: options.minDiscountPercent,
833
+ min_rating: options.minRating,
834
+ sort_by: options.sortBy ? SORT[options.sortBy] : void 0,
835
+ limit,
836
+ offset
837
+ }
838
+ }
839
+ );
840
+ return { items: page.results.map(toProductListItem), total: page.count, offset, limit };
841
+ },
842
+ async list(options = {}) {
843
+ const limit = options.limit ?? DEFAULT_LIMIT;
844
+ const offset = options.offset ?? 0;
845
+ const page = await request("GET", "/product/base/list", {
846
+ ...PUBLIC10,
847
+ // The backend does not hide drafts here by itself; the old storefront never asked it to.
848
+ query: {
849
+ status: "available",
850
+ ids: options.ids,
851
+ category_ids: options.categoryIds,
852
+ limit,
853
+ offset
854
+ }
855
+ });
856
+ return { items: page.results.map(toProductOverview), total: page.count, offset, limit };
857
+ },
858
+ async detail(id) {
859
+ return toProductDetail(
860
+ await request("GET", `/product/details/${id}`, PUBLIC10)
861
+ );
862
+ },
863
+ async filterConfig(options = {}) {
864
+ const wire = await request("GET", "/product/filter/config", {
865
+ ...PUBLIC10,
866
+ query: { category: options.categoryId }
867
+ });
868
+ return {
869
+ price: { min: wire.price.min, max: wire.price.max },
870
+ specifications: wire.specifications.map((s) => ({
871
+ id: s.id,
872
+ name: s.name,
873
+ type: s.data_type,
874
+ options: s.options
875
+ }))
876
+ };
877
+ },
878
+ async variantFilters(categoryIds) {
879
+ if (categoryIds.length === 0) return [];
880
+ const wire = await request("GET", "/product/category/filter/variant", {
881
+ ...PUBLIC10,
882
+ query: { category_ids: categoryIds }
883
+ });
884
+ return wire.map(toVariantGroup);
885
+ },
886
+ async suggestions(query) {
887
+ const word = query.trim();
888
+ if (!word) return [];
889
+ const wire = await request(
890
+ "GET",
891
+ `/product/search/${encodeURIComponent(word)}`,
892
+ PUBLIC10
893
+ );
894
+ return wire.map(
895
+ (s) => s.type === "product" ? {
896
+ type: "product",
897
+ id: s.id,
898
+ name: s.name,
899
+ categoryName: text(s.category),
900
+ image: text(s.image)
901
+ } : { type: "category", id: s.id, name: s.name }
902
+ );
903
+ }
904
+ };
905
+ }
906
+ var mrpOf = (value) => {
907
+ const n = amount(value);
908
+ return n && n > 0 ? n : null;
909
+ };
910
+ function toVariantGroup(wire) {
911
+ return {
912
+ id: wire.variant_type.id,
913
+ name: wire.variant_type.type,
914
+ options: wire.variants.filter((v) => !v.deleted).map((v) => ({ id: v.id, label: v.label, available: v.sku_available ?? true }))
915
+ };
916
+ }
917
+ function toSku(wire) {
918
+ return {
919
+ id: wire.id,
920
+ code: wire.sku,
921
+ slug: wire.url_slug,
922
+ price: amount(wire.selling_price) ?? 0,
923
+ mrp: mrpOf(wire.mrp),
924
+ stockQty: wire.stock_qty ?? 0,
925
+ active: wire.is_active !== false,
926
+ variantIds: wire.selected_variants ?? []
927
+ };
928
+ }
929
+ function toImage(wire) {
930
+ return {
931
+ id: wire.id,
932
+ url: wire.photo,
933
+ originalUrl: text(wire.original_photo),
934
+ variantId: wire.variant ?? null,
935
+ order: wire.display_order
936
+ };
937
+ }
938
+ var liveSkus = (skus) => skus.filter((s) => !s.deleted && !s.invalid);
939
+ var liveImages = (images) => images.filter((i) => !i.deleted).sort((a, b) => a.display_order - b.display_order);
940
+ function toProductListItem(wire) {
941
+ const tax = amount(wire.tax);
942
+ return {
943
+ skuId: wire.id,
944
+ productId: wire.product,
945
+ name: wire.name,
946
+ slug: wire.url_slug,
947
+ skuCode: wire.sku,
948
+ photos: wire.photo,
949
+ price: amount(wire.selling_price) ?? 0,
950
+ mrp: mrpOf(wire.mrp),
951
+ stockQty: wire.stock_qty ?? 0,
952
+ taxPercent: tax,
953
+ unit: text(wire.unit),
954
+ variantGroups: (wire.variant ?? []).map(toVariantGroup),
955
+ selectedVariantIds: wire.selected_variants,
956
+ fulfillmentMode: wire.fulfillment_mode,
957
+ hasDigitalDelivery: wire.has_digital_delivery,
958
+ advanceAmount: amount(wire.advance_amount)
959
+ };
960
+ }
961
+ function toSpecifications(wire) {
962
+ return Object.entries(wire ?? {}).map(([name, items]) => ({
963
+ name: text(name),
964
+ items: items.filter((i) => !i.deleted && i.key).map((i) => ({
965
+ key: i.key.name,
966
+ value: i.value,
967
+ appliesTo: i.sku ? { type: "sku", skuId: i.sku.id } : i.variant ? { type: "variant", variantId: i.variant.id } : { type: "all" }
968
+ }))
969
+ })).filter((g) => g.items.length > 0);
970
+ }
971
+ function toProductDetail(wire) {
972
+ const policies = wire.product_policies;
973
+ return {
974
+ id: wire.id,
975
+ name: wire.name,
976
+ slug: wire.url_slug,
977
+ description: text(wire.description),
978
+ descriptionFormat: text(wire.description_editor_type),
979
+ categoryId: wire.category ?? null,
980
+ categoryName: text(wire.category_name),
981
+ brandId: wire.brand ?? null,
982
+ taxPercent: amount(wire.tax_percent),
983
+ rating: wire.rating ?? 0,
984
+ reviewCount: wire.total_review ?? 0,
985
+ hasVariants: wire.has_variant ?? (wire.variant ?? []).length > 0,
986
+ images: liveImages(wire.images).map(toImage),
987
+ variantGroups: (wire.variant ?? []).map(toVariantGroup),
988
+ skus: liveSkus(wire.sku).map(toSku),
989
+ specifications: toSpecifications(wire.specification),
990
+ policies: policies ? {
991
+ returnDays: policies.return_days,
992
+ replacementDays: policies.replacement_days,
993
+ exchangeDays: policies.exchange_days,
994
+ exchangeEnabled: policies.exchange_enabled,
995
+ warrantyMonths: policies.warranty_months,
996
+ guaranteeMonths: policies.guarantee_months
997
+ } : null,
998
+ availableForSubscription: wire.available_for_subscription ?? false,
999
+ hasDigitalDelivery: wire.has_digital_delivery ?? false,
1000
+ fulfillmentMode: wire.fulfillment_mode ?? "none",
1001
+ advanceAmount: amount(wire.advance_amount)
1002
+ };
1003
+ }
1004
+ function toProductOverview(wire) {
1005
+ return {
1006
+ id: wire.id,
1007
+ name: wire.name,
1008
+ slug: wire.url_slug,
1009
+ categoryName: text(wire.category_name),
1010
+ images: liveImages(wire.images).map(toImage),
1011
+ variantGroups: (wire.variant ?? []).map(toVariantGroup),
1012
+ skus: liveSkus(wire.sku).map(toSku),
1013
+ priceFrom: amount(wire.starting_from),
1014
+ unit: text(wire.unit)
1015
+ };
1016
+ }
1017
+
1018
+ // src/http/errors.ts
1019
+ var GrowupsellApiError = class extends Error {
1020
+ kind;
1021
+ /** HTTP status; undefined when no response arrived (network, timeout). */
1022
+ status;
1023
+ /** Validation messages per field, as returned by the backend (`non_field_errors` included). */
1024
+ fieldErrors;
1025
+ /** The parsed response body, for anything the fields above don't cover. */
1026
+ body;
1027
+ constructor(init) {
1028
+ super(init.message);
1029
+ this.name = "GrowupsellApiError";
1030
+ this.kind = init.kind;
1031
+ this.status = init.status;
1032
+ this.fieldErrors = init.fieldErrors ?? {};
1033
+ this.body = init.body;
1034
+ }
1035
+ };
1036
+ function messageFromBody(body) {
1037
+ if (typeof body === "string") {
1038
+ const text2 = body.trim();
1039
+ return text2 && !text2.startsWith("<") ? text2 : void 0;
1040
+ }
1041
+ if (Array.isArray(body)) return body.length > 0 ? messageFromBody(body[0]) : void 0;
1042
+ if (body && typeof body === "object") {
1043
+ const record = body;
1044
+ for (const key of ["detail", "message"]) {
1045
+ if (typeof record[key] === "string" && record[key]) return record[key];
1046
+ }
1047
+ for (const value of Object.values(record)) {
1048
+ if (typeof value === "boolean" || typeof value === "number" || value == null) continue;
1049
+ const found = messageFromBody(value);
1050
+ if (found) return found;
1051
+ }
1052
+ }
1053
+ return void 0;
1054
+ }
1055
+ function fieldErrorsFromBody(body) {
1056
+ if (!body || typeof body !== "object" || Array.isArray(body)) return {};
1057
+ const errors = {};
1058
+ for (const [field, value] of Object.entries(body)) {
1059
+ if (field === "detail" || field === "message") continue;
1060
+ if (typeof value === "string") errors[field] = [value];
1061
+ else if (Array.isArray(value) && value.length > 0 && value.every((v) => typeof v === "string")) {
1062
+ errors[field] = value;
1063
+ }
1064
+ }
1065
+ return errors;
1066
+ }
1067
+
1068
+ // src/http/promotions.ts
1069
+ var DEFAULT_LIMIT2 = 20;
1070
+ var PUBLIC_BUSINESS = { anonymous: true, business: "required" };
1071
+ var SHOPPER2 = { business: "required", customerOrSession: "required" };
1072
+ function createPromotionsApi(request) {
1073
+ return {
1074
+ async offers(options = {}) {
1075
+ const limit = options.limit ?? DEFAULT_LIMIT2;
1076
+ const offset = options.offset ?? 0;
1077
+ const page = await request("GET", "/offer/list", {
1078
+ ...PUBLIC_BUSINESS,
1079
+ query: { limit, offset }
1080
+ });
1081
+ return { items: page.results.map(toOffer), total: page.count, offset, limit };
1082
+ },
1083
+ async eligibleOffers() {
1084
+ const wire = await request("GET", "/offer/eligibility/list", SHOPPER2);
1085
+ return wire.map(toOffer);
1086
+ },
1087
+ async promoCode(code) {
1088
+ const normalized = code.trim().toUpperCase();
1089
+ if (!normalized) return null;
1090
+ try {
1091
+ const wire = await request("GET", "/promocode/get", {
1092
+ ...SHOPPER2,
1093
+ query: { code: normalized }
1094
+ });
1095
+ return toPromoCode(wire);
1096
+ } catch (error) {
1097
+ if (isNotFoundJson(error)) return null;
1098
+ throw error;
1099
+ }
1100
+ },
1101
+ async validateGiftCard({ code, pin }) {
1102
+ const wire = await request("POST", "/giftcard/validate", {
1103
+ business: "required",
1104
+ json: { code: code.trim(), pin: pin.trim() }
1105
+ });
1106
+ return toGiftCard(wire);
1107
+ },
1108
+ async rewardPointSettings() {
1109
+ return toRewardPointSettings(
1110
+ await request(
1111
+ "GET",
1112
+ "/reward_points/business/setting/get",
1113
+ PUBLIC_BUSINESS
1114
+ )
1115
+ );
1116
+ }
1117
+ };
1118
+ }
1119
+ function isNotFoundJson(error) {
1120
+ return error instanceof GrowupsellApiError && error.status === 404 && typeof error.body === "object" && error.body !== null;
1121
+ }
1122
+ function toOffer(wire) {
1123
+ return {
1124
+ id: wire.id,
1125
+ name: wire.name,
1126
+ description: text(wire.description),
1127
+ image: text(wire.image),
1128
+ type: wire.type ?? "percentage",
1129
+ percentOff: amount(wire.percentage_of) ?? 0,
1130
+ amountOff: amount(wire.discount_upto_price) ?? 0,
1131
+ minOrderAmount: amount(wire.min_order_amount) ?? 0,
1132
+ startsAt: wire.start_date ?? "",
1133
+ endsAt: wire.end_date ?? "",
1134
+ couponCode: text(wire.coupon_code),
1135
+ scope: wire.only_for ?? "order",
1136
+ isActive: wire.is_active ?? false
1137
+ };
1138
+ }
1139
+ function toPromoCode(wire) {
1140
+ return {
1141
+ id: wire.id,
1142
+ code: wire.code ?? "",
1143
+ description: text(wire.description),
1144
+ type: wire.type ?? "percentage",
1145
+ value: amount(wire.value) ?? 0,
1146
+ expiresAt: wire.expire_date,
1147
+ status: wire.status
1148
+ };
1149
+ }
1150
+ function toGiftCard(wire) {
1151
+ return {
1152
+ id: wire.id,
1153
+ code: wire.code,
1154
+ amount: amount(wire.amount) ?? 0,
1155
+ usedAmount: amount(wire.used_amount) ?? 0,
1156
+ issuedAt: wire.created_at
1157
+ };
1158
+ }
1159
+ function toRewardPointSettings(wire) {
1160
+ return {
1161
+ // Points work on orders only when both switches are on.
1162
+ enabled: !!wire.is_active && !!wire.is_active_order_reward,
1163
+ pointsPerCurrencyUnit: amount(wire.order_credit_multiplier) ?? 0,
1164
+ currencyPerPoint: amount(wire.order_debit_multiplier) ?? 0,
1165
+ signupPoints: amount(wire.signup_points) ?? 0
1166
+ };
1167
+ }
1168
+
1169
+ // src/http/multipart.ts
1170
+ var runtime = globalThis;
1171
+ function multipart(fields, files, caller = "This upload") {
1172
+ if (!runtime.FormData) {
1173
+ throw new Error(`${caller}: sending a file needs FormData, which this runtime lacks.`);
1174
+ }
1175
+ const form = new runtime.FormData();
1176
+ for (const [name, value] of Object.entries(fields)) {
1177
+ if (value !== void 0) form.append(name, String(value));
1178
+ }
1179
+ for (const [name, entry] of Object.entries(files)) {
1180
+ if (!entry) continue;
1181
+ if (entry.fileName === void 0) form.append(name, entry.file);
1182
+ else form.append(name, entry.file, entry.fileName);
1183
+ }
1184
+ return form;
1185
+ }
1186
+
1187
+ // src/http/reviews.ts
1188
+ var DEFAULT_LIMIT3 = 20;
1189
+ var PUBLIC11 = { anonymous: true };
1190
+ var PUBLIC_BUSINESS2 = { anonymous: true, business: "required" };
1191
+ var CUSTOMER = { customer: "required" };
1192
+ function createReviewsApi(request) {
1193
+ function body(fields, photo) {
1194
+ if (photo === void 0) {
1195
+ return {
1196
+ json: Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0))
1197
+ };
1198
+ }
1199
+ return { formData: multipart(fields, { photo: { file: photo } }, "Review photo") };
1200
+ }
1201
+ return {
1202
+ async forProduct(productId) {
1203
+ const wire = await request(
1204
+ "GET",
1205
+ `/product/review/list/${productId}`,
1206
+ PUBLIC11
1207
+ );
1208
+ return wire.map(toReviewListItem);
1209
+ },
1210
+ async latest(options = {}) {
1211
+ const limit = options.limit ?? DEFAULT_LIMIT3;
1212
+ const offset = options.offset ?? 0;
1213
+ const page = await request("GET", "/product/review/list", {
1214
+ ...PUBLIC_BUSINESS2,
1215
+ query: {
1216
+ rating_gt: options.minRating,
1217
+ search: options.search?.trim() || void 0,
1218
+ limit,
1219
+ offset
1220
+ }
1221
+ });
1222
+ return { items: page.results.map(toReviewListItem), total: page.count, offset, limit };
1223
+ },
1224
+ async summary(productId) {
1225
+ return toReviewSummary(
1226
+ await request(
1227
+ "GET",
1228
+ `/product/review/conclusion/${productId}`,
1229
+ PUBLIC_BUSINESS2
1230
+ )
1231
+ );
1232
+ },
1233
+ async eligibility(productId) {
1234
+ const wire = await request(
1235
+ "GET",
1236
+ `/product/can/i/review/${productId}`,
1237
+ CUSTOMER
1238
+ );
1239
+ return wire.can_i_review ? { canReview: true, orderProductId: wire.order_product } : { canReview: false, orderProductId: null };
1240
+ },
1241
+ async create(input) {
1242
+ const wire = await request("POST", "/product/review/create", {
1243
+ ...CUSTOMER,
1244
+ ...body(
1245
+ {
1246
+ customer: input.customerId,
1247
+ product: input.productId,
1248
+ order_product: input.orderProductId,
1249
+ title: input.title,
1250
+ message: input.message,
1251
+ ratting: input.rating
1252
+ },
1253
+ input.photo
1254
+ )
1255
+ });
1256
+ return toReview(wire);
1257
+ },
1258
+ async update(reviewId, changes) {
1259
+ const wire = await request("PATCH", `/product/review/update/${reviewId}`, {
1260
+ ...CUSTOMER,
1261
+ ...body(
1262
+ { title: changes.title, message: changes.message, ratting: changes.rating },
1263
+ changes.photo
1264
+ )
1265
+ });
1266
+ return toReview(wire);
1267
+ },
1268
+ async remove(reviewId) {
1269
+ await request("DELETE", `/product/review/customer/delete/${reviewId}`, {
1270
+ ...CUSTOMER,
1271
+ business: "required"
1272
+ });
1273
+ }
1274
+ };
1275
+ }
1276
+ function toReview(wire) {
1277
+ return {
1278
+ id: wire.id,
1279
+ productId: wire.product,
1280
+ customerId: wire.customer,
1281
+ title: wire.title,
1282
+ message: text(wire.message),
1283
+ photo: text(wire.photo),
1284
+ rating: amount(wire.ratting),
1285
+ createdAt: wire.created_at
1286
+ };
1287
+ }
1288
+ function toReviewListItem(wire) {
1289
+ const reply = wire.reply;
1290
+ return {
1291
+ ...toReview(wire),
1292
+ productName: wire.product_name,
1293
+ customerName: text(wire.customer_name),
1294
+ customerPhoto: text(wire.customer_photo),
1295
+ // The backend also sends the replying staff member's email and phone; not passed on.
1296
+ reply: reply ? {
1297
+ id: reply.id,
1298
+ message: reply.message,
1299
+ authorName: text(reply.user_name),
1300
+ authorPhoto: text(reply.user_photo)
1301
+ } : null
1302
+ };
1303
+ }
1304
+ function toReviewSummary(wire) {
1305
+ const average = amount(wire.average_rating) ?? 0;
1306
+ return {
1307
+ reviewCount: wire.total_reviews,
1308
+ averageRating: Math.round(average * 10) / 10,
1309
+ starCounts: {
1310
+ 1: wire.total_one_star_reviews,
1311
+ 2: wire.total_two_star_reviews,
1312
+ 3: wire.total_three_star_reviews,
1313
+ 4: wire.total_four_star_reviews,
1314
+ 5: wire.total_five_star_reviews
1315
+ }
1316
+ };
1317
+ }
1318
+
1319
+ // src/http/wishlist.ts
1320
+ var CUSTOMER2 = { customer: "required" };
1321
+ function createWishlistApi(request) {
1322
+ return {
1323
+ async list() {
1324
+ const wire = await request("GET", "/product/wishlist/list/", CUSTOMER2);
1325
+ return wire.map(toWishlistItem);
1326
+ },
1327
+ async add(skuId) {
1328
+ const wire = await request("POST", "/product/wishlist/create", {
1329
+ ...CUSTOMER2,
1330
+ json: { sku: skuId }
1331
+ });
1332
+ return { id: wire.id, productId: wire.product };
1333
+ },
1334
+ async remove(wishlistId) {
1335
+ await request("DELETE", `/product/wishlist/delete/${wishlistId}`, CUSTOMER2);
1336
+ }
1337
+ };
1338
+ }
1339
+ function toWishlistItem(wire) {
1340
+ const { product, sku } = wire;
1341
+ const mrp = sku ? amount(sku.mrp) : null;
1342
+ return {
1343
+ id: wire.id,
1344
+ name: wire.label,
1345
+ photo: text(wire.photo),
1346
+ productId: product.id,
1347
+ productSlug: product.url_slug,
1348
+ sku: sku ? {
1349
+ id: sku.id,
1350
+ code: sku.sku,
1351
+ slug: sku.url_slug,
1352
+ price: amount(sku.selling_price) ?? 0,
1353
+ // An MRP of 0 means "not set".
1354
+ mrp: mrp && mrp > 0 ? mrp : null,
1355
+ stockQty: sku.stock_qty,
1356
+ // As in products: only an explicit `false` deactivates a SKU.
1357
+ active: sku.is_active !== false,
1358
+ variantIds: sku.selected_variants
1359
+ } : null,
1360
+ // The backend lists entries whose product or SKU is gone or hidden. Same rule as the
1361
+ // storefront listing (ProductSKUSearchView with a guest session).
1362
+ available: !!sku && !sku.deleted && !sku.invalid && product.is_active && !product.is_draft && !product.deleted && product.available_on.includes("website"),
1363
+ fulfillmentMode: text(wire.fulfillment_mode) ?? "none",
1364
+ advanceAmount: amount(wire.advance_amount),
1365
+ hasDigitalDelivery: wire.has_digital_delivery
1366
+ };
1367
+ }
1368
+
1369
+ // src/http/theme-content.ts
1370
+ var USAGES = {
1371
+ banner: "banner",
1372
+ slider: "slider",
1373
+ header_logo: "header-logo",
1374
+ footer_logo: "footer-logo",
1375
+ other: "other"
1376
+ };
1377
+ var WIRE_USAGES = Object.fromEntries(
1378
+ Object.entries(USAGES).map(([wire, usage]) => [usage, wire])
1379
+ );
1380
+ var PUBLIC12 = { business: "required", anonymous: true };
1381
+ var MERCHANT7 = { business: "required", merchant: true, anonymous: true };
1382
+ function createThemeContentApi(request) {
1383
+ async function getMany(themeId2, slugs) {
1384
+ const keys = slugs.map(listSlug);
1385
+ if (keys.length === 0) return [];
1386
+ const wire = await request("GET", `/content/get/${themeId2}/v2`, {
1387
+ ...PUBLIC12,
1388
+ query: { slug: [...new Set(keys)] }
1389
+ });
1390
+ if (!Array.isArray(wire)) {
1391
+ throw new GrowupsellApiError({
1392
+ kind: "parse",
1393
+ message: "Expected a list of theme content sections.",
1394
+ body: wire
1395
+ });
1396
+ }
1397
+ const bySlug = new Map(wire.flatMap((row) => row ? [[row.slug, toThemeContent(row)]] : []));
1398
+ return keys.map((key) => bySlug.get(key) ?? null);
1399
+ }
1400
+ return {
1401
+ // The return types are proven by the tests; the implementation works on plain lists.
1402
+ getMany,
1403
+ async get(themeId2, slug) {
1404
+ if (slug === "v2") return (await getMany(themeId2, [slug]))[0];
1405
+ const wire = await request(
1406
+ "GET",
1407
+ `/content/get/${themeId2}/${segment(slug)}`,
1408
+ PUBLIC12
1409
+ );
1410
+ return toThemeContent(wire);
1411
+ },
1412
+ async update(themeId2, slug, content) {
1413
+ const json = { slug, platform: content.platform, data: content.data };
1414
+ const wire = await request(
1415
+ "PUT",
1416
+ `/content/update/${themeId2}/${segment(slug)}`,
1417
+ { ...MERCHANT7, json }
1418
+ );
1419
+ return toThemeContent(wire);
1420
+ },
1421
+ async uploadImage(upload) {
1422
+ if (upload.file == null) throw new Error("themeContent.uploadImage needs a file.");
1423
+ upload.form.append("image", upload.file, upload.fileName);
1424
+ if (upload.usage) upload.form.append("related", WIRE_USAGES[upload.usage]);
1425
+ const wire = await request("POST", "/content/images/create", {
1426
+ ...MERCHANT7,
1427
+ formData: upload.form
1428
+ });
1429
+ return toContentImage(wire);
1430
+ }
1431
+ };
1432
+ }
1433
+ function segment(slug) {
1434
+ if (slug.trim() === "") throw new Error("themeContent: slug is required.");
1435
+ return encodeURIComponent(slug);
1436
+ }
1437
+ function listSlug(slug) {
1438
+ const trimmed = slug.trim();
1439
+ if (trimmed === "") throw new Error("themeContent: slug is required.");
1440
+ if (trimmed.includes(",")) throw new Error(`themeContent: slug "${slug}" contains a comma.`);
1441
+ return trimmed;
1442
+ }
1443
+ function toThemeContent(wire) {
1444
+ if (!("id" in wire)) return null;
1445
+ return {
1446
+ id: wire.id,
1447
+ themeId: wire.theme,
1448
+ slug: wire.slug,
1449
+ platform: wire.platform,
1450
+ data: wire.data
1451
+ };
1452
+ }
1453
+ function toContentImage(wire) {
1454
+ if (!wire.image) {
1455
+ throw new GrowupsellApiError({
1456
+ kind: "parse",
1457
+ message: "The upload was accepted but no file was stored.",
1458
+ body: wire
1459
+ });
1460
+ }
1461
+ return {
1462
+ id: wire.id,
1463
+ url: wire.image,
1464
+ usage: wire.related ? USAGES[wire.related] ?? null : null
1465
+ };
1466
+ }
1467
+
1468
+ // src/http/customPages.ts
1469
+ var PUBLIC13 = { business: "required", anonymous: true };
1470
+ var MERCHANT8 = { merchant: true, anonymous: true };
1471
+ var HERO_BLOCK_SLUG = "__hero_slider";
1472
+ function createCustomPagesApi(request) {
1473
+ return {
1474
+ async byPath(path3) {
1475
+ const urlPath = normalizePath(path3);
1476
+ try {
1477
+ const wire = await request("GET", "/website/custom-page/public", {
1478
+ ...PUBLIC13,
1479
+ query: { url_path: urlPath }
1480
+ });
1481
+ return toCustomPage(wire);
1482
+ } catch (error) {
1483
+ if (error instanceof GrowupsellApiError && error.status === 404) return null;
1484
+ throw error;
1485
+ }
1486
+ },
1487
+ async content(pageId) {
1488
+ const wire = await request(
1489
+ "GET",
1490
+ contentPath(pageId, "content"),
1491
+ MERCHANT8
1492
+ );
1493
+ return toCustomPageContent(wire);
1494
+ },
1495
+ async updateContent(pageId, input) {
1496
+ const path3 = contentPath(pageId, "updateContent");
1497
+ const json = toContentWrite(input);
1498
+ const wire = await request("PATCH", path3, { ...MERCHANT8, json });
1499
+ return toCustomPageContent(wire);
1500
+ }
1501
+ };
1502
+ }
1503
+ function normalizePath(path3) {
1504
+ const segments = path3.trim().replace(/\\/g, "/").split("/");
1505
+ const normalized = segments.filter((s) => s.trim() !== "").join("/");
1506
+ if (!normalized) throw new Error("customPages.byPath: path is required.");
1507
+ return normalized;
1508
+ }
1509
+ function contentPath(pageId, method) {
1510
+ if (!Number.isInteger(pageId) || pageId <= 0) {
1511
+ throw new Error(`customPages.${method}: page id must be a positive integer.`);
1512
+ }
1513
+ return `/website/custom-page/${pageId}/content`;
1514
+ }
1515
+ function toHero(raw) {
1516
+ if (Array.isArray(raw)) return raw.length > 0 ? { type: "slider", data: raw } : null;
1517
+ if (!isObject(raw)) return null;
1518
+ const { type, data } = raw;
1519
+ return typeof type === "string" && type !== "" ? { type, data } : null;
1520
+ }
1521
+ function toDocument(heroContent, blocks) {
1522
+ const objects = (blocks ?? []).filter(isObject);
1523
+ const hero = heroContent != null ? toHero(heroContent) : toHero(objects.find((b) => b.slug === HERO_BLOCK_SLUG)?.content);
1524
+ return { hero, sections: objects.filter((b) => b.slug !== HERO_BLOCK_SLUG) };
1525
+ }
1526
+ function toCustomPage(wire) {
1527
+ return {
1528
+ id: wire.id,
1529
+ title: wire.title,
1530
+ path: wire.url_path,
1531
+ seoTitle: text(wire.seo_title),
1532
+ seoDescription: text(wire.seo_description),
1533
+ seoKeywords: (wire.seo_keywords ?? []).flatMap(
1534
+ (k) => typeof k === "string" && k.trim() !== "" ? [k.trim()] : []
1535
+ ),
1536
+ ...toDocument(wire.hero_content, wire.contents)
1537
+ };
1538
+ }
1539
+ function toCustomPageContent(wire) {
1540
+ return {
1541
+ id: wire.id,
1542
+ pageId: wire.page,
1543
+ ...toDocument(wire.hero_content, wire.content),
1544
+ createdAt: wire.created_at,
1545
+ updatedAt: wire.updated_at
1546
+ };
1547
+ }
1548
+ function toContentWrite(input) {
1549
+ const { hero } = input;
1550
+ if (hero && hero.type.trim() === "") {
1551
+ throw new Error("customPages.updateContent: the hero type is required.");
1552
+ }
1553
+ return {
1554
+ content: input.sections.filter((s) => isObject(s) && s.slug !== HERO_BLOCK_SLUG),
1555
+ hero_content: hero ? { type: hero.type, data: hero.data } : null
1556
+ };
1557
+ }
1558
+ function isObject(value) {
1559
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1560
+ }
1561
+
1562
+ // src/http/session.ts
1563
+ function createSessionApi(request) {
1564
+ return {
1565
+ async create() {
1566
+ const wire = await request("POST", "/session/create", {
1567
+ anonymous: true,
1568
+ session: "omit",
1569
+ json: {}
1570
+ });
1571
+ return toGuestSession(wire);
1572
+ }
1573
+ };
1574
+ }
1575
+ function toGuestSession(wire) {
1576
+ return { id: wire.id, token: wire.session_token };
1577
+ }
1578
+
1579
+ // src/http/lookup.ts
1580
+ var PUBLIC14 = { anonymous: true };
1581
+ function createLookupApi(request) {
1582
+ return {
1583
+ async countries(search) {
1584
+ const wire = await request("GET", "/lookup/country/list", {
1585
+ ...PUBLIC14,
1586
+ query: { search }
1587
+ });
1588
+ return wire.data.map(toCountry);
1589
+ },
1590
+ async states(countryCode) {
1591
+ const wire = await request("GET", "/lookup/country/state/list", {
1592
+ ...PUBLIC14,
1593
+ query: { country: countryCode }
1594
+ });
1595
+ return wire.map(toCountryState);
1596
+ }
1597
+ };
1598
+ }
1599
+ function toCountry(wire) {
1600
+ return { code: wire.code, name: wire.name };
1601
+ }
1602
+ function toCountryState(wire) {
1603
+ return {
1604
+ id: wire.id,
1605
+ name: wire.name,
1606
+ shortName: wire.short_name,
1607
+ code: wire.code,
1608
+ country: wire.country
1609
+ };
1610
+ }
1611
+
1612
+ // src/http/auth.ts
1613
+ var SIGNED_OUT = { anonymous: true };
1614
+ var BUSINESS = { ...SIGNED_OUT, business: "required" };
1615
+ function createAuthApi(request) {
1616
+ return {
1617
+ async settings() {
1618
+ const wire = await request("GET", "/customer/setting/get", BUSINESS);
1619
+ return toSettings(wire);
1620
+ },
1621
+ async login({ username, password }) {
1622
+ const wire = await request("POST", "/customer/login", {
1623
+ ...BUSINESS,
1624
+ session: "required",
1625
+ json: { username, password }
1626
+ });
1627
+ return toAuthResult(wire, username);
1628
+ },
1629
+ async verifyOtp({ customerId, otp }) {
1630
+ const wire = await request("POST", "/customer/otp/verify", {
1631
+ ...SIGNED_OUT,
1632
+ json: { otp, customer: customerId }
1633
+ });
1634
+ return toSignedIn(wire, wire.token);
1635
+ },
1636
+ async register(input) {
1637
+ const wire = await request(
1638
+ "POST",
1639
+ "/customer/register",
1640
+ { ...BUSINESS, session: "required", json: fromRegisterInput(input) }
1641
+ );
1642
+ return toAuthResult(wire);
1643
+ },
1644
+ async sendOtp(username) {
1645
+ const { message } = await request("POST", "/customer/otp/resend", {
1646
+ ...BUSINESS,
1647
+ json: { username }
1648
+ });
1649
+ return { message };
1650
+ },
1651
+ async resetPassword({ username, otp, newPassword, confirmPassword }) {
1652
+ const { message } = await request("POST", "/customer/password/reset", {
1653
+ ...BUSINESS,
1654
+ json: { username, otp, new_password: newPassword, confirm_password: confirmPassword }
1655
+ });
1656
+ return { message };
1657
+ }
1658
+ };
1659
+ }
1660
+ function fromRegisterInput(input) {
1661
+ return {
1662
+ ...input.email !== void 0 && { email: input.email },
1663
+ ...input.phone !== void 0 && { phone: input.phone },
1664
+ password: input.password,
1665
+ type: input.type,
1666
+ first_name: input.firstName ?? null,
1667
+ last_name: input.lastName ?? null,
1668
+ business_name: input.businessName ?? null,
1669
+ tax_number: input.taxNumber ?? null,
1670
+ ...input.referralCode && { referral_code: input.referralCode }
1671
+ };
1672
+ }
1673
+ function toSettings(wire) {
1674
+ return {
1675
+ authType: wire.authentication_type ?? "email",
1676
+ allowBusinessSignup: wire.allow_business_signup ?? false,
1677
+ requireVerification: wire.require_customer_verification ?? true,
1678
+ lastNameRequired: wire.is_last_name_required ?? false
1679
+ };
1680
+ }
1681
+ function toCustomerProfile(wire) {
1682
+ const photo = text(wire.photo);
1683
+ return {
1684
+ id: wire.id,
1685
+ type: wire.type ?? "individual",
1686
+ firstName: text(wire.first_name),
1687
+ lastName: text(wire.last_name),
1688
+ email: text(wire.email),
1689
+ // Sign-up without verification sends `str(None)` for a missing phone.
1690
+ phone: wire.phone === "None" ? null : text(wire.phone),
1691
+ businessName: text(wire.business_name),
1692
+ taxNumber: text(wire.tax_number),
1693
+ // The login dump sends the storage path, not a URL; only a URL is usable.
1694
+ photo: photo && /^https?:\/\//.test(photo) ? photo : null,
1695
+ isActive: wire.is_active ?? false,
1696
+ emailVerified: wire.email_verified ?? false,
1697
+ phoneVerified: wire.phone_verified ?? false
1698
+ };
1699
+ }
1700
+ function toAuthResult(wire, username) {
1701
+ if (wire.is_active && typeof wire.token === "string") {
1702
+ return { status: "signed-in", session: toSignedIn(wire, wire.token) };
1703
+ }
1704
+ const profile = toCustomerProfile(wire);
1705
+ return {
1706
+ status: "verification-required",
1707
+ customerId: wire.id,
1708
+ contact: username ?? profile.phone ?? profile.email
1709
+ };
1710
+ }
1711
+ function toSignedIn(wire, token) {
1712
+ return {
1713
+ customer: toCustomerProfile(wire),
1714
+ token,
1715
+ rewardPoints: wire.reward_points ?? null
1716
+ };
1717
+ }
1718
+
1719
+ // src/http/customer.ts
1720
+ var CUSTOMER3 = { customer: "required" };
1721
+ var CUSTOMER_BUSINESS = { ...CUSTOMER3, business: "required" };
1722
+ function createCustomerApi(request) {
1723
+ return {
1724
+ async updateProfile(id, input) {
1725
+ const wire = await request("PATCH", `/customer/update/${id}`, {
1726
+ ...CUSTOMER_BUSINESS,
1727
+ json: fromProfileInput(input)
1728
+ });
1729
+ return toCustomerProfile(wire);
1730
+ },
1731
+ async changePassword({ oldPassword, newPassword, confirmPassword }) {
1732
+ const { message } = await request("PUT", "/customer/password/update", {
1733
+ ...CUSTOMER3,
1734
+ json: {
1735
+ old_password: oldPassword,
1736
+ new_password: newPassword,
1737
+ confirm_password: confirmPassword
1738
+ }
1739
+ });
1740
+ return { message };
1741
+ },
1742
+ async requestEmailChange(email) {
1743
+ await request("POST", "/customer/email/change/request", {
1744
+ ...CUSTOMER_BUSINESS,
1745
+ json: { email }
1746
+ });
1747
+ },
1748
+ async confirmEmailChange({ email, otp }) {
1749
+ const wire = await request("PUT", "/customer/email/change/verify", {
1750
+ ...CUSTOMER3,
1751
+ json: { email, otp }
1752
+ });
1753
+ return { message: wire.message, newEmail: wire.new_email };
1754
+ },
1755
+ async requestPhoneChange(phone) {
1756
+ await request("POST", "/customer/phone/change/request", {
1757
+ ...CUSTOMER_BUSINESS,
1758
+ json: { phone }
1759
+ });
1760
+ },
1761
+ async confirmPhoneChange({ phone, otp }) {
1762
+ const wire = await request("PUT", "/customer/phone/change/verify", {
1763
+ ...CUSTOMER3,
1764
+ json: { phone, otp }
1765
+ });
1766
+ return { message: wire.message, newPhone: wire.new_phone };
1767
+ }
1768
+ };
1769
+ }
1770
+ function fromProfileInput(input) {
1771
+ const body = {};
1772
+ if (input.firstName !== void 0) body.first_name = input.firstName;
1773
+ if (input.lastName !== void 0) body.last_name = input.lastName;
1774
+ if (input.businessName !== void 0) body.business_name = input.businessName;
1775
+ if (input.taxNumber !== void 0) body.tax_number = input.taxNumber;
1776
+ return body;
1777
+ }
1778
+
1779
+ // src/http/addresses.ts
1780
+ var CUSTOMER4 = { customer: "required" };
1781
+ function createAddressesApi(request) {
1782
+ return {
1783
+ async list() {
1784
+ const wire = await request("GET", "/customer/address/list", CUSTOMER4);
1785
+ return wire.map(toAddress);
1786
+ },
1787
+ async create(input) {
1788
+ const wire = await request("POST", "/customer/address/create", {
1789
+ ...CUSTOMER4,
1790
+ json: fromAddressInput(input)
1791
+ });
1792
+ return toAddress(wire);
1793
+ },
1794
+ async update(id, input) {
1795
+ const wire = await request("PUT", `/customer/address/update/${id}`, {
1796
+ ...CUSTOMER4,
1797
+ json: fromAddressInput(input)
1798
+ });
1799
+ return toAddress(wire);
1800
+ },
1801
+ async remove(id) {
1802
+ await request("DELETE", `/customer/address/delete/${id}`, {
1803
+ ...CUSTOMER4,
1804
+ business: "required"
1805
+ });
1806
+ }
1807
+ };
1808
+ }
1809
+ function toAddress(wire) {
1810
+ return {
1811
+ id: wire.id,
1812
+ addressLine1: wire.address_line_1,
1813
+ addressLine2: wire.address_line_2,
1814
+ postalCode: text(wire.postal_code),
1815
+ city: wire.city,
1816
+ type: wire.type,
1817
+ country: text(wire.country),
1818
+ state: wire.state ?? null,
1819
+ stateName: text(wire.state_name),
1820
+ landmark: wire.landmark,
1821
+ contactNo: text(wire.contact_no),
1822
+ latitude: amount(wire.latitude),
1823
+ longitude: amount(wire.longitude),
1824
+ isDefault: wire.default ?? false,
1825
+ isUsed: wire.is_used
1826
+ };
1827
+ }
1828
+ function fromAddressInput(input) {
1829
+ return {
1830
+ address_line_1: input.addressLine1,
1831
+ address_line_2: input.addressLine2,
1832
+ postal_code: input.postalCode ?? "",
1833
+ city: input.city,
1834
+ type: input.type,
1835
+ country: input.country ?? null,
1836
+ state: input.state ?? null,
1837
+ landmark: input.landmark,
1838
+ contact_no: input.contactNo ?? null,
1839
+ latitude: input.latitude ?? null,
1840
+ longitude: input.longitude ?? null,
1841
+ default: input.isDefault ?? false
1842
+ };
1843
+ }
1844
+
1845
+ // src/http/checkout.ts
1846
+ var PUBLIC_BUSINESS3 = { anonymous: true, business: "required" };
1847
+ var CUSTOMER_BUSINESS2 = { business: "required", customer: "required" };
1848
+ var PLACE_ORDER_TIMEOUT_MS = 6e4;
1849
+ function createCheckoutApi(request) {
1850
+ const placing = /* @__PURE__ */ new Map();
1851
+ async function charge(cartId, options) {
1852
+ const body = {
1853
+ giftcard: [...options.giftCardIds ?? []],
1854
+ use_reward_points: options.useRewardPoints ?? false
1855
+ };
1856
+ const wire = await request("POST", `/order/charge/delivery/${cartId}`, {
1857
+ ...CUSTOMER_BUSINESS2,
1858
+ timeoutMs: PLACE_ORDER_TIMEOUT_MS,
1859
+ json: body
1860
+ });
1861
+ if (wire.action === "done" || wire.payment.type === "cod") {
1862
+ return { status: "placed", orderId: cartId };
1863
+ }
1864
+ return { status: "payment-required", orderId: cartId, payment: toPendingPayment(wire.payment) };
1865
+ }
1866
+ return {
1867
+ async settings() {
1868
+ return toCheckoutSettings(
1869
+ await request(
1870
+ "GET",
1871
+ "/order/business/setting/get",
1872
+ PUBLIC_BUSINESS3
1873
+ )
1874
+ );
1875
+ },
1876
+ placeOrder(cartId, options = {}) {
1877
+ const inFlight = placing.get(cartId);
1878
+ if (inFlight) return inFlight;
1879
+ if ((options.giftCardIds?.length ?? 0) > 0 && options.useRewardPoints) {
1880
+ return Promise.reject(
1881
+ new Error(
1882
+ "placeOrder: gift cards and reward points cannot be combined; choose one (backend issue W36)."
1883
+ )
1884
+ );
1885
+ }
1886
+ const promise = charge(cartId, options).finally(() => placing.delete(cartId));
1887
+ placing.set(cartId, promise);
1888
+ return promise;
1889
+ },
1890
+ async createAdvanceOrder(input) {
1891
+ const body = {
1892
+ product: input.productId,
1893
+ sku: input.skuId,
1894
+ custom_data: input.customData,
1895
+ qty: 1,
1896
+ type: input.type,
1897
+ shipping_address: input.shippingAddressId,
1898
+ billing_address: input.billingAddressId
1899
+ };
1900
+ const wire = await request(
1901
+ "POST",
1902
+ "/order/custom-advance/create",
1903
+ { ...CUSTOMER_BUSINESS2, timeoutMs: PLACE_ORDER_TIMEOUT_MS, json: body }
1904
+ );
1905
+ return { orderId: wire.order_id, payment: toPendingPayment(wire.payment) };
1906
+ }
1907
+ };
1908
+ }
1909
+ function toCheckoutSettings(wire) {
1910
+ return {
1911
+ allowCashOnDelivery: wire.allow_cash_on_delivery ?? false,
1912
+ allowCancellation: wire.allow_cancellation_from_customer ?? false,
1913
+ autoAccept: wire.auto_accept ?? false,
1914
+ onlineOrdersEnabled: wire.enable_online_order ?? false,
1915
+ deliveryChargeEnabled: wire.delivery_charge ?? false,
1916
+ maxQuantityIndividual: wire.max_allowed_qty_for_individual ?? 0,
1917
+ maxQuantityBusiness: wire.max_allowed_qty_for_business ?? 0
1918
+ };
1919
+ }
1920
+ function toPendingPayment(wire) {
1921
+ const method = wire.payment_method?.type ?? null;
1922
+ return {
1923
+ id: wire.id,
1924
+ amount: amount(wire.amount) ?? 0,
1925
+ method,
1926
+ publicKey: text(wire.payment_method?.payment_key),
1927
+ providerOrderId: text(wire.payment_order_key),
1928
+ // `payment_key` means something else for every provider; only Stripe's is a client secret.
1929
+ clientSecret: method === "stripe" ? text(wire.payment_key) : null,
1930
+ // Razorpay and Stripe rows carry links too, to pages the SDK flows do not use.
1931
+ redirectUrl: method === "phonepe" ? text(wire.payment_url) : null
1932
+ };
1933
+ }
1934
+
1935
+ // src/http/payments.ts
1936
+ var PUBLIC_BUSINESS4 = { anonymous: true, business: "required" };
1937
+ var CUSTOMER_BUSINESS3 = { customer: "required", business: "required" };
1938
+ var STRIPE_VERIFICATION_FAILED = "Payment verification failed";
1939
+ function createPaymentsApi(request) {
1940
+ const inFlight = /* @__PURE__ */ new Map();
1941
+ function once(key, run) {
1942
+ const existing = inFlight.get(key);
1943
+ if (existing) return existing;
1944
+ const promise = run().finally(() => inFlight.delete(key));
1945
+ inFlight.set(key, promise);
1946
+ return promise;
1947
+ }
1948
+ return {
1949
+ confirmPhonePe(paymentId) {
1950
+ return once(`phonepe:${paymentId}`, async () => {
1951
+ const wire = await request(
1952
+ "GET",
1953
+ `/payment/phonepe/order/web/success/${paymentId}`,
1954
+ PUBLIC_BUSINESS4
1955
+ );
1956
+ return { status: wire.payment_status === "paid" ? "paid" : "unpaid" };
1957
+ });
1958
+ },
1959
+ confirmStripe(payment) {
1960
+ const intentId = payment.providerOrderId;
1961
+ if (!intentId) {
1962
+ return Promise.reject(
1963
+ new TypeError("confirmStripe: the payment has no providerOrderId (PaymentIntent id).")
1964
+ );
1965
+ }
1966
+ return once(`stripe:${payment.id}`, async () => {
1967
+ const json = { payment_intent_id: intentId };
1968
+ try {
1969
+ await request(
1970
+ "POST",
1971
+ `/payment/stripe/order/web/success/${payment.id}`,
1972
+ { ...PUBLIC_BUSINESS4, json }
1973
+ );
1974
+ return { status: "paid" };
1975
+ } catch (error) {
1976
+ if (isStripeVerificationFailure(error)) return { status: "unpaid" };
1977
+ throw error;
1978
+ }
1979
+ });
1980
+ },
1981
+ confirmRazorpay(payment, result) {
1982
+ return once(`razorpay:${payment.id}`, async () => {
1983
+ const json = {
1984
+ razorpay_payment_id: result.razorpayPaymentId,
1985
+ razorpay_order_id: result.razorpayOrderId,
1986
+ razorpay_signature: result.razorpaySignature
1987
+ };
1988
+ await request(
1989
+ "POST",
1990
+ `/payment/razorpay/order/web/success/${payment.id}`,
1991
+ { ...PUBLIC_BUSINESS4, json }
1992
+ );
1993
+ });
1994
+ },
1995
+ submitTransferProof(paymentId, screenshot) {
1996
+ return once(`transfer:${paymentId}`, async () => {
1997
+ const formData = multipart(
1998
+ {},
1999
+ { verification_screenshot: { file: screenshot } },
2000
+ "Transfer proof"
2001
+ );
2002
+ await request("PATCH", `/payment/status/update/${paymentId}`, {
2003
+ ...CUSTOMER_BUSINESS3,
2004
+ formData
2005
+ });
2006
+ });
2007
+ }
2008
+ };
2009
+ }
2010
+ function isStripeVerificationFailure(error) {
2011
+ return error instanceof GrowupsellApiError && error.status === 400 && error.body?.message === STRIPE_VERIFICATION_FAILED;
2012
+ }
2013
+
2014
+ // src/http/custom-manufacturing.ts
2015
+ var CUSTOMER_BUSINESS4 = { customer: "required", business: "required" };
2016
+ function createCustomManufacturingApi(request) {
2017
+ return {
2018
+ async uploadFile(file, fileName) {
2019
+ if (file == null) throw new Error("customManufacturing.uploadFile needs a file.");
2020
+ const wire = await request(
2021
+ "POST",
2022
+ "/manufacture/custom-request/customer/upload",
2023
+ {
2024
+ ...CUSTOMER_BUSINESS4,
2025
+ formData: multipart({}, { file: { file, fileName } }, "customManufacturing.uploadFile")
2026
+ }
2027
+ );
2028
+ if (!wire.url)
2029
+ throw new Error("customManufacturing.uploadFile: the backend returned no URL.");
2030
+ return { id: wire.id, url: wire.url };
2031
+ }
2032
+ };
2033
+ }
2034
+
2035
+ // src/http/subscriptions.ts
2036
+ var DEFAULT_LIMIT4 = 20;
2037
+ var CUSTOMER5 = { customer: "required", business: "required" };
2038
+ function createSubscriptionsApi(request) {
2039
+ return {
2040
+ async list(options = {}) {
2041
+ const limit = options.limit ?? DEFAULT_LIMIT4;
2042
+ const offset = options.offset ?? 0;
2043
+ const page = await request("GET", "/subscription/list", {
2044
+ ...CUSTOMER5,
2045
+ query: { limit, offset }
2046
+ });
2047
+ return { items: page.results.map(toSubscription), total: page.count, offset, limit };
2048
+ },
2049
+ async detail(id) {
2050
+ return toSubscription(
2051
+ await request("GET", `/subscription/detail/${id}`, CUSTOMER5)
2052
+ );
2053
+ },
2054
+ async deliveries(id) {
2055
+ const wire = await request(
2056
+ "GET",
2057
+ `/subscription/delivery/list/${id}`,
2058
+ CUSTOMER5
2059
+ );
2060
+ return wire.map(toSubscriptionDelivery);
2061
+ },
2062
+ async holds(id) {
2063
+ const wire = await request(
2064
+ "GET",
2065
+ `/subscription/hold/history/list/${id}`,
2066
+ CUSTOMER5
2067
+ );
2068
+ return wire.map(toSubscriptionHold);
2069
+ },
2070
+ async invoices(id) {
2071
+ const wire = await request(
2072
+ "GET",
2073
+ `/subscription/invoice/list/${id}`,
2074
+ CUSTOMER5
2075
+ );
2076
+ return wire.map(toSubscriptionInvoice);
2077
+ },
2078
+ async payInvoice(invoiceId) {
2079
+ return toInvoicePayment(
2080
+ await request(
2081
+ "GET",
2082
+ `/subscription/invoice/get/${invoiceId}`,
2083
+ CUSTOMER5
2084
+ )
2085
+ );
2086
+ },
2087
+ async create(input) {
2088
+ const body = toCreateBody(input);
2089
+ return toSubscription(
2090
+ await request("POST", "/subscription/create", {
2091
+ ...CUSTOMER5,
2092
+ json: body
2093
+ })
2094
+ );
2095
+ },
2096
+ hold: (id) => updateStatus(id, { action: "hold" }),
2097
+ resume: (id) => updateStatus(id, { action: "resume" }),
2098
+ async cancel(id, reason) {
2099
+ const trimmed = reason?.trim();
2100
+ if (!trimmed) throw new Error("subscriptions.cancel needs a reason.");
2101
+ return updateStatus(id, { action: "cancel", decline_message: trimmed });
2102
+ }
2103
+ };
2104
+ async function updateStatus(id, body) {
2105
+ return toSubscription(
2106
+ await request("POST", `/subscription/status/update/${id}`, {
2107
+ ...CUSTOMER5,
2108
+ json: body
2109
+ })
2110
+ );
2111
+ }
2112
+ }
2113
+ var WIRE_INTERVAL = {
2114
+ daily: "daily",
2115
+ "alternate-day": "alternate_day",
2116
+ weekly: "weekly",
2117
+ monthly: "monthly"
2118
+ };
2119
+ function toCreateBody(input) {
2120
+ const fail = (message) => {
2121
+ throw new Error(`subscriptions.create: ${message}`);
2122
+ };
2123
+ if (!Number.isInteger(input.quantity) || input.quantity < 1) {
2124
+ fail("quantity must be a whole number of at least 1.");
2125
+ }
2126
+ if (input.endDate < input.startDate) fail("endDate is before startDate.");
2127
+ const body = {
2128
+ customer: input.customerId,
2129
+ address: input.addressId,
2130
+ start_date: input.startDate,
2131
+ end_date: input.endDate,
2132
+ interval: WIRE_INTERVAL[input.interval],
2133
+ products: [{ id: input.skuId, quantity: input.quantity, price: input.price }]
2134
+ };
2135
+ if (input.interval === "weekly") {
2136
+ if (!input.weekdays?.length) fail("weekly needs at least one weekday.");
2137
+ body.selected_days = input.weekdays;
2138
+ }
2139
+ if (input.interval === "monthly") {
2140
+ if (!input.monthDay)
2141
+ fail("monthly needs monthDay (an ISO date whose day is the delivery day).");
2142
+ body.month_day = input.monthDay;
2143
+ }
2144
+ return body;
2145
+ }
2146
+ var STATUS = {
2147
+ created: "created",
2148
+ accepted: "accepted",
2149
+ paid: "paid",
2150
+ "in-progress": "in-progress",
2151
+ hold: "hold",
2152
+ completed: "completed",
2153
+ cancelled: "cancelled",
2154
+ decline: "declined"
2155
+ };
2156
+ var INTERVAL = {
2157
+ daily: "daily",
2158
+ alternate_day: "alternate-day",
2159
+ weekly: "weekly",
2160
+ monthly: "monthly"
2161
+ };
2162
+ function weekdays(value) {
2163
+ if (value == null) return [];
2164
+ const names = Array.isArray(value) ? value : value.split(",");
2165
+ return names.map((name) => name.trim()).filter((name) => name !== "");
2166
+ }
2167
+ function toSubscription(wire) {
2168
+ const address = wire.address;
2169
+ return {
2170
+ id: wire.id,
2171
+ number: text(wire.subscription_no),
2172
+ // The model defaults to `created`; null only for rows written outside the API.
2173
+ status: wire.status ? STATUS[wire.status] : "created",
2174
+ interval: wire.interval ? INTERVAL[wire.interval] : null,
2175
+ startDate: wire.start_date,
2176
+ endDate: wire.end_date,
2177
+ weekdays: weekdays(wire.weekly_days),
2178
+ monthDay: wire.month_day,
2179
+ // The list sends the address id, the detail the address row.
2180
+ addressId: typeof address === "object" ? address?.id ?? null : address ?? null,
2181
+ reason: text(wire.decline_message),
2182
+ createdAt: wire.created_at
2183
+ };
2184
+ }
2185
+ function toSubscriptionDelivery(wire) {
2186
+ return {
2187
+ id: wire.id,
2188
+ date: wire.date,
2189
+ status: wire.status,
2190
+ items: wire.products.map((p) => ({
2191
+ id: p.id,
2192
+ skuId: p.sku,
2193
+ name: p.product_name,
2194
+ quantity: p.qty,
2195
+ unit: text(p.unit),
2196
+ price: amount(p.price),
2197
+ status: p.status,
2198
+ removed: p.deleted
2199
+ }))
2200
+ };
2201
+ }
2202
+ function toSubscriptionHold(wire) {
2203
+ return { id: wire.id, startDate: wire.start_date ?? null, endDate: wire.end_date ?? null };
2204
+ }
2205
+ function toInvoicePayment(wire) {
2206
+ return wire.action === "done" ? { status: "paid" } : { status: "payment-required", payment: toPendingPayment(wire.payment) };
2207
+ }
2208
+ function toSubscriptionInvoice(wire) {
2209
+ return {
2210
+ id: wire.id,
2211
+ orderId: wire.order,
2212
+ invoiceNumber: text(wire.invoice_number),
2213
+ invoiceDate: wire.invoice_date,
2214
+ dueDate: wire.due_date ?? null,
2215
+ billingPeriod: wire.billing_period,
2216
+ amount: amount(wire.amount),
2217
+ paymentStatus: wire.payment?.status ?? "unpaid"
2218
+ };
2219
+ }
2220
+
2221
+ // src/http/orders.ts
2222
+ var CUSTOMER6 = { business: "required", customer: "required" };
2223
+ var DEFAULT_LIMIT5 = 10;
2224
+ var TRACKING_TIMEOUT_MS = 3e4;
2225
+ var DOCUMENT_TIMEOUT_MS = 6e4;
2226
+ function createOrdersApi(request) {
2227
+ function pdf(path3, options = {}) {
2228
+ return request("POST", path3, {
2229
+ ...CUSTOMER6,
2230
+ query: { send_to_customer: false, invoice_type: options.format ?? "a4" },
2231
+ responseType: "binary",
2232
+ timeoutMs: DOCUMENT_TIMEOUT_MS
2233
+ });
2234
+ }
2235
+ async function email(path3, options = {}) {
2236
+ await request("POST", path3, {
2237
+ ...CUSTOMER6,
2238
+ query: { send_to_customer: true, invoice_type: options.format ?? "a4" },
2239
+ timeoutMs: DOCUMENT_TIMEOUT_MS
2240
+ });
2241
+ }
2242
+ return {
2243
+ async list(options = {}) {
2244
+ const limit = options.limit ?? DEFAULT_LIMIT5;
2245
+ const offset = options.offset ?? 0;
2246
+ const wire = await request("GET", "/order/list", {
2247
+ ...CUSTOMER6,
2248
+ query: { limit, offset, status: options.status, search: options.search }
2249
+ });
2250
+ return { items: wire.results.map(toOrderListItem), total: wire.count, offset, limit };
2251
+ },
2252
+ async detail(orderId) {
2253
+ return toOrderDetail(
2254
+ await request("GET", `/order/detail/${orderId}`, CUSTOMER6)
2255
+ );
2256
+ },
2257
+ async summary(orderId) {
2258
+ return toOrderSummary(
2259
+ await request("GET", `/order/ecommerce/detail/${orderId}`, CUSTOMER6)
2260
+ );
2261
+ },
2262
+ async shipments(orderId) {
2263
+ const wire = await request(
2264
+ "GET",
2265
+ `/order/product/delivery/info/list/${orderId}`,
2266
+ CUSTOMER6
2267
+ );
2268
+ return wire.map(toShipment);
2269
+ },
2270
+ async tracking(trackingNumber) {
2271
+ const wire = await request(
2272
+ "GET",
2273
+ `/delivery/order/tracking/info/${encodeURIComponent(trackingNumber)}`,
2274
+ { ...CUSTOMER6, timeoutMs: TRACKING_TIMEOUT_MS }
2275
+ );
2276
+ return { trackingUrl: text(wire.tracking_url), events: wire.data };
2277
+ },
2278
+ invoice: (orderId, options) => pdf(`/order/invoice/pdf/send/${orderId}`, options),
2279
+ emailInvoice: (orderId, options) => email(`/order/invoice/pdf/send/${orderId}`, options),
2280
+ creditNote: (returnId, options) => pdf(`/order/credit/note/pdf/${returnId}`, options),
2281
+ emailCreditNote: (returnId, options) => email(`/order/credit/note/pdf/${returnId}`, options),
2282
+ async cancelCodItems({ orderId, items, reason }) {
2283
+ if (items.length === 0) throw new TypeError("cancelCodItems: items must not be empty.");
2284
+ if (items.some((item) => !(item.quantity >= 1))) {
2285
+ throw new RangeError("cancelCodItems: every quantity must be at least 1.");
2286
+ }
2287
+ if (reason.trim() === "") throw new TypeError("cancelCodItems: reason must not be blank.");
2288
+ await request("POST", "/order/cancel/cod", {
2289
+ ...CUSTOMER6,
2290
+ json: {
2291
+ order: orderId,
2292
+ order_products: items.map((item) => ({ id: item.orderLineId, qty: item.quantity })),
2293
+ status_message: reason
2294
+ }
2295
+ });
2296
+ }
2297
+ };
2298
+ }
2299
+ var status = (value) => value ?? "created";
2300
+ function toOrderListItem(wire) {
2301
+ return {
2302
+ id: wire.id,
2303
+ orderNumber: text(wire.order_no),
2304
+ type: wire.type,
2305
+ status: status(wire.status),
2306
+ payable: amount(wire.final_payable_amount) ?? 0,
2307
+ placedAt: wire.created_at
2308
+ };
2309
+ }
2310
+ function toOrderLine(wire) {
2311
+ return {
2312
+ id: wire.id,
2313
+ skuId: wire.sku?.id ?? null,
2314
+ productId: wire.sku?.product?.id ?? null,
2315
+ name: wire.label ?? wire.name ?? "",
2316
+ photo: text(wire.photo),
2317
+ unitPrice: amount(wire.price) ?? 0,
2318
+ quantity: wire.qty,
2319
+ lineTotal: amount(wire.total) ?? 0,
2320
+ offerDiscount: amount(wire.offer_redeem_amount) ?? 0,
2321
+ taxPercent: amount(wire.tax),
2322
+ isDigital: wire.is_digital_product,
2323
+ customData: wire.custom_data,
2324
+ canReplace: wire.can_replace,
2325
+ canExchange: wire.can_exchange,
2326
+ maxReplaceOrExchangeQuantity: wire.max_qty_for_replace_or_exchange
2327
+ };
2328
+ }
2329
+ function toOrderAddress(wire) {
2330
+ return {
2331
+ id: wire.id,
2332
+ line1: text(wire.address_line_1),
2333
+ line2: text(wire.address_line_2),
2334
+ landmark: text(wire.landmark),
2335
+ city: text(wire.city),
2336
+ state: text(wire.state_name),
2337
+ postalCode: text(wire.postal_code),
2338
+ country: text(wire.country),
2339
+ type: text(wire.type)
2340
+ };
2341
+ }
2342
+ function toOrderPaymentEntry(wire) {
2343
+ return {
2344
+ id: wire.id,
2345
+ type: text(wire.type),
2346
+ amount: amount(wire.amount) ?? 0,
2347
+ status: wire.status,
2348
+ method: wire.payment_method?.type ?? null,
2349
+ label: wire.payment_method_label
2350
+ };
2351
+ }
2352
+ function toOrderDetail(wire) {
2353
+ const lines = (wire.products ?? []).map(toOrderLine);
2354
+ const orderOffer = amount(wire.offer_redeem_amount) ?? 0;
2355
+ const offerDiscount = orderOffer > 0 ? orderOffer : lines.reduce((sum2, line) => sum2 + line.offerDiscount, 0);
2356
+ return {
2357
+ id: wire.id,
2358
+ orderNumber: text(wire.order_no),
2359
+ invoiceNumber: text(wire.invoice_number),
2360
+ type: wire.type,
2361
+ status: status(wire.status),
2362
+ statusMessage: text(wire.status_message),
2363
+ placedAt: wire.created_at,
2364
+ lines,
2365
+ subtotal: amount(wire.total_amount) ?? 0,
2366
+ offerDiscount,
2367
+ promoDiscount: amount(wire.promo_redeem_amount) ?? 0,
2368
+ deliveryCharge: amount(wire.delivery_charge) ?? 0,
2369
+ payable: amount(wire.final_payable_amount) ?? 0,
2370
+ shippingAddress: wire.shipping_address ? toOrderAddress(wire.shipping_address) : null,
2371
+ billingAddress: wire.billing_address ? toOrderAddress(wire.billing_address) : null,
2372
+ payments: (wire.payment_history ?? []).map(toOrderPaymentEntry),
2373
+ hasExchangeOrReplacement: wire.has_exchange_or_replacement
2374
+ };
2375
+ }
2376
+ function toOrderSummary(wire) {
2377
+ return {
2378
+ id: wire.id,
2379
+ orderNumber: text(wire.order_no),
2380
+ invoiceNumber: text(wire.invoice_number),
2381
+ status: status(wire.status),
2382
+ subtotal: amount(wire.total_amount) ?? 0,
2383
+ payable: amount(wire.order_total) ?? 0,
2384
+ placedAt: wire.placed_at,
2385
+ items: wire.order_products.map((item) => ({
2386
+ name: item.product_name ?? "",
2387
+ variant: text(item.variant_name),
2388
+ photo: text(item.photo),
2389
+ quantity: item.qty,
2390
+ unitPrice: amount(item.price) ?? 0
2391
+ })),
2392
+ deliveryAddress: wire.delivery_address ? toOrderAddress(wire.delivery_address) : null,
2393
+ payment: wire.payment ? {
2394
+ id: wire.payment.id,
2395
+ status: wire.payment.status ?? "unpaid",
2396
+ amount: wire.payment.order_amount ?? 0,
2397
+ mode: text(wire.payment.mode)
2398
+ } : null
2399
+ };
2400
+ }
2401
+ function toShipment(wire) {
2402
+ const info = wire.delivery_info;
2403
+ return {
2404
+ id: info.id,
2405
+ partner: text(info.label) ?? "Self",
2406
+ trackingNumber: text(info.tracking_number),
2407
+ status: info.status,
2408
+ createdAt: info.created_at,
2409
+ items: wire.products.map((item) => ({
2410
+ orderLineId: item.order_product,
2411
+ name: item.product_name ?? "",
2412
+ photo: text(item.photo),
2413
+ quantity: item.qty
2414
+ }))
2415
+ };
2416
+ }
2417
+
2418
+ // src/http/returns.ts
2419
+ var CUSTOMER7 = { business: "required", customer: "required" };
2420
+ function createReturnsApi(request) {
2421
+ return {
2422
+ async eligibleItems(orderId) {
2423
+ const wire = await request(
2424
+ "GET",
2425
+ `/order/return/eligible/products/${orderId}/`,
2426
+ CUSTOMER7
2427
+ );
2428
+ return wire.map(toReturnableItem);
2429
+ },
2430
+ async list(orderId) {
2431
+ const wire = await request(
2432
+ "GET",
2433
+ `/order/return/order/list/${orderId}`,
2434
+ CUSTOMER7
2435
+ );
2436
+ return wire.map(toReturnRequest);
2437
+ },
2438
+ async create({ orderId, reason, items }) {
2439
+ if (items.length === 0) throw new TypeError("returns.create: items must not be empty.");
2440
+ if (items.some((item) => !(item.quantity >= 1))) {
2441
+ throw new RangeError("returns.create: every quantity must be at least 1.");
2442
+ }
2443
+ if (reason.trim() === "") throw new TypeError("returns.create: reason must not be blank.");
2444
+ const wire = await request("POST", "/order/return/customer/create", {
2445
+ ...CUSTOMER7,
2446
+ json: {
2447
+ order: orderId,
2448
+ requested_message: reason,
2449
+ order_products: items.map((item) => ({
2450
+ id: item.orderLineId,
2451
+ qty: item.quantity,
2452
+ ...item.unitTrackingIds ? { tracking_info_ids: item.unitTrackingIds } : {}
2453
+ }))
2454
+ }
2455
+ });
2456
+ return { id: wire.id };
2457
+ }
2458
+ };
2459
+ }
2460
+ function toReturnableItem(wire) {
2461
+ return {
2462
+ orderLineId: wire.id,
2463
+ name: wire.product_name ?? "",
2464
+ photo: text(wire.product_photo),
2465
+ unitPrice: amount(wire.price) ?? 0,
2466
+ maxQuantity: wire.max_returnable_qty,
2467
+ unitTrackingIds: wire.unit_tracking_infos.map((unit) => unit.id)
2468
+ };
2469
+ }
2470
+ function toReturnRequest(wire) {
2471
+ return {
2472
+ id: wire.id,
2473
+ status: wire.status,
2474
+ createdAt: wire.created_at,
2475
+ amount: amount(wire.final_amount) ?? 0,
2476
+ items: (wire.products ?? []).map((item) => ({
2477
+ id: item.id,
2478
+ name: item.product_name ?? "",
2479
+ quantity: item.qty,
2480
+ status: item.status
2481
+ }))
2482
+ };
2483
+ }
2484
+
2485
+ // src/http/exchanges.ts
2486
+ var CUSTOMER8 = { business: "required", customer: "required" };
2487
+ function createExchangesApi(request) {
2488
+ return {
2489
+ async create(input) {
2490
+ const wire = await request(
2491
+ "POST",
2492
+ "/order/exchange-replacement/customer/create",
2493
+ { ...CUSTOMER8, ...exchangeBody(input) }
2494
+ );
2495
+ return toExchangeResult(wire);
2496
+ },
2497
+ async list(orderId) {
2498
+ const wire = await request(
2499
+ "GET",
2500
+ `/order/exchange-replacement/list/by/order/${orderId}`,
2501
+ CUSTOMER8
2502
+ );
2503
+ return wire.map(toExchangeListItem);
2504
+ },
2505
+ async detail(exchangeId) {
2506
+ return toExchangeDetail(
2507
+ await request(
2508
+ "GET",
2509
+ `/order/exchange-replacement/detail/${exchangeId}`,
2510
+ CUSTOMER8
2511
+ )
2512
+ );
2513
+ }
2514
+ };
2515
+ }
2516
+ function exchangeBody({ orderId, type, reason, items }) {
2517
+ if (items.length === 0) throw new TypeError("exchanges.create: items must not be empty.");
2518
+ if (items.some((item) => !(item.quantity >= 1))) {
2519
+ throw new RangeError("exchanges.create: every quantity must be at least 1.");
2520
+ }
2521
+ if (type === "exchange" && items.some((item) => item.targetSkuId === void 0)) {
2522
+ throw new TypeError("exchanges.create: every exchange item needs a targetSkuId.");
2523
+ }
2524
+ const products = items.map((item) => ({
2525
+ order_product_id: item.orderLineId,
2526
+ qty: item.quantity,
2527
+ ...type === "exchange" ? { target_sku_id: item.targetSkuId } : {},
2528
+ ...item.reason !== void 0 ? { requested_message: item.reason } : {}
2529
+ }));
2530
+ if (items.every((item) => item.photo === void 0)) {
2531
+ return { json: { order_id: orderId, type, requested_message: reason, products } };
2532
+ }
2533
+ const files = Object.fromEntries(
2534
+ items.map((item, index) => [
2535
+ `request_media_${index}`,
2536
+ item.photo === void 0 ? void 0 : { file: item.photo }
2537
+ ])
2538
+ );
2539
+ return {
2540
+ formData: multipart(
2541
+ { order_id: orderId, type, requested_message: reason, products: JSON.stringify(products) },
2542
+ files,
2543
+ "Exchange photo"
2544
+ )
2545
+ };
2546
+ }
2547
+ function toExchangeResult(wire) {
2548
+ if (wire.action === "pay" && wire.payment.type !== "cod") {
2549
+ return { status: "payment-required", id: wire.id, payment: toPendingPayment(wire.payment) };
2550
+ }
2551
+ return { status: "submitted", id: wire.id, message: text(wire.message) };
2552
+ }
2553
+ function toExchangeListItem(wire) {
2554
+ return {
2555
+ id: wire.id,
2556
+ type: wire.type,
2557
+ status: wire.status,
2558
+ createdAt: wire.created_at,
2559
+ paymentId: wire.payment_id,
2560
+ itemCount: wire.products_count
2561
+ };
2562
+ }
2563
+ function toExchangeDetail(wire) {
2564
+ return {
2565
+ ...toExchangeListItem({ ...wire, products_count: wire.products.length }),
2566
+ items: wire.products.map((row) => ({
2567
+ orderLineId: row.order_product,
2568
+ name: text(row.original_sku?.product_name),
2569
+ targetSkuId: row.target_sku,
2570
+ quantity: row.qty,
2571
+ priceDifference: amount(row.price_difference) ?? 0,
2572
+ status: row.status
2573
+ }))
2574
+ };
2575
+ }
2576
+
2577
+ // src/http/query.ts
2578
+ function buildQuery(query) {
2579
+ if (!query) return "";
2580
+ const parts = [];
2581
+ for (const [key, raw] of Object.entries(query)) {
2582
+ if (raw === void 0 || raw === null || raw === "") continue;
2583
+ let value;
2584
+ if (Array.isArray(raw)) {
2585
+ if (raw.length === 0) continue;
2586
+ value = raw.join(",");
2587
+ } else {
2588
+ value = String(raw);
2589
+ }
2590
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
2591
+ }
2592
+ return parts.length > 0 ? `?${parts.join("&")}` : "";
2593
+ }
2594
+
2595
+ // src/http/client.ts
2596
+ var DEFAULT_TIMEOUT_MS = 15e3;
2597
+ var runtime2 = globalThis;
2598
+ function createClient(options) {
2599
+ if (!options.baseURL) throw new Error("createClient: baseURL is required.");
2600
+ if (!options.fetch && typeof runtime2.fetch !== "function") {
2601
+ throw new Error("createClient: no global fetch in this runtime; pass the `fetch` option.");
2602
+ }
2603
+ const baseURL = options.baseURL.replace(/\/+$/, "");
2604
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2605
+ const doFetch = options.fetch ? (url, init) => options.fetch(url, init) : (url, init) => runtime2.fetch(url, init);
2606
+ const reported = {};
2607
+ async function request(method, path3, opts = {}) {
2608
+ const credentials = await options.credentials?.() ?? {};
2609
+ if (opts.business === "required" && (credentials.businessId == null || credentials.businessId === "")) {
2610
+ throw new Error(
2611
+ `${method} ${path3} needs the business: return businessId from credentials().`
2612
+ );
2613
+ }
2614
+ if (opts.session === "required" && !credentials.sessionToken) {
2615
+ throw new Error(
2616
+ `${method} ${path3} needs a guest session: return sessionToken from credentials().`
2617
+ );
2618
+ }
2619
+ if (opts.customer === "required" && !credentials.customerToken) {
2620
+ throw new Error(
2621
+ `${method} ${path3} needs a logged-in customer: return customerToken from credentials().`
2622
+ );
2623
+ }
2624
+ if (opts.merchant && !credentials.merchantToken) {
2625
+ throw new Error(
2626
+ `${method} ${path3} is a merchant request: return merchantToken from credentials().`
2627
+ );
2628
+ }
2629
+ const headers = buildHeaders(credentials, opts);
2630
+ const binary = opts.responseType === "binary";
2631
+ if (binary) headers.accept = "*/*";
2632
+ if (opts.customerOrSession === "required" && !headers["x-customer-token"] && !headers["x-session-token"]) {
2633
+ throw new Error(
2634
+ `${method} ${path3} needs a customer or a guest session: return customerToken or sessionToken from credentials().`
2635
+ );
2636
+ }
2637
+ const init = { method, headers };
2638
+ if (opts.formData) {
2639
+ init.body = opts.formData;
2640
+ } else if (opts.json !== void 0) {
2641
+ headers["content-type"] = "application/json";
2642
+ init.body = JSON.stringify(opts.json);
2643
+ }
2644
+ const url = `${baseURL}/${path3.replace(/^\/+/, "")}${buildQuery(opts.query)}`;
2645
+ const { status: status2, ok, text: text2, data } = await send(url, init, opts.timeoutMs ?? timeoutMs, binary);
2646
+ if (data) return data;
2647
+ const body = parseBody(text2, ok, status2);
2648
+ if (ok) return body;
2649
+ if (status2 === 401) reportUnauthorized(headers);
2650
+ if (isDeadSession(status2, body, headers)) report("session", headers["x-session-token"]);
2651
+ throw new GrowupsellApiError({
2652
+ kind: "http",
2653
+ status: status2,
2654
+ message: messageFromBody(body) ?? `Request failed with status ${status2}`,
2655
+ fieldErrors: fieldErrorsFromBody(body),
2656
+ body
2657
+ });
2658
+ }
2659
+ async function send(url, init, ms, binary) {
2660
+ const controller = runtime2.AbortController ? new runtime2.AbortController() : void 0;
2661
+ if (controller) init.signal = controller.signal;
2662
+ let timedOut = false;
2663
+ let timer;
2664
+ const timeout = new Promise((_, reject) => {
2665
+ timer = runtime2.setTimeout(() => {
2666
+ timedOut = true;
2667
+ controller?.abort();
2668
+ reject(timeoutError(ms));
2669
+ }, ms);
2670
+ });
2671
+ try {
2672
+ return await Promise.race([
2673
+ doFetch(url, init).then(async (res) => ({
2674
+ status: res.status,
2675
+ ok: res.ok,
2676
+ // Error responses are read as text even for binary requests: they carry the message.
2677
+ text: binary && res.ok ? "" : await res.text(),
2678
+ data: binary && res.ok ? await readBinary(res) : void 0
2679
+ })),
2680
+ timeout
2681
+ ]);
2682
+ } catch (error) {
2683
+ if (timedOut) throw timeoutError(ms);
2684
+ if (error instanceof GrowupsellApiError) throw error;
2685
+ throw new GrowupsellApiError({
2686
+ kind: "network",
2687
+ message: error instanceof Error ? error.message : "Network request failed"
2688
+ });
2689
+ } finally {
2690
+ runtime2.clearTimeout(timer);
2691
+ }
2692
+ }
2693
+ function reportUnauthorized(headers) {
2694
+ const merchant = headers.authorization?.replace(/^Token /, "");
2695
+ const customer = headers["x-customer-token"];
2696
+ const kind = merchant ? "merchant" : customer ? "customer" : void 0;
2697
+ if (!kind) return;
2698
+ report(kind, kind === "merchant" ? merchant : customer);
2699
+ }
2700
+ function report(kind, token) {
2701
+ if (reported[kind] === token) return;
2702
+ reported[kind] = token;
2703
+ options.onUnauthorized?.({ kind });
2704
+ }
2705
+ return {
2706
+ request,
2707
+ business: createBusinessApi(request),
2708
+ catalog: createCatalogApi(request),
2709
+ products: createProductsApi(request),
2710
+ reviews: createReviewsApi(request),
2711
+ wishlist: createWishlistApi(request),
2712
+ policies: createPoliciesApi(request),
2713
+ faq: createFaqApi(request),
2714
+ themeContent: createThemeContentApi(request),
2715
+ productPage: createProductPageApi(request),
2716
+ customPages: createCustomPagesApi(request),
2717
+ session: createSessionApi(request),
2718
+ lookup: createLookupApi(request),
2719
+ auth: createAuthApi(request),
2720
+ customer: createCustomerApi(request),
2721
+ addresses: createAddressesApi(request),
2722
+ checkout: createCheckoutApi(request),
2723
+ payments: createPaymentsApi(request),
2724
+ blog: createBlogApi(request),
2725
+ forms: createFormsApi(request),
2726
+ helpdesk: createHelpdeskApi(request),
2727
+ newsletter: createNewsletterApi(request),
2728
+ liveSupport: createLiveSupportApi(request),
2729
+ customManufacturing: createCustomManufacturingApi(request),
2730
+ subscriptions: createSubscriptionsApi(request),
2731
+ cart: createCartApi(request),
2732
+ promotions: createPromotionsApi(request),
2733
+ orders: createOrdersApi(request),
2734
+ returns: createReturnsApi(request),
2735
+ exchanges: createExchangesApi(request)
2736
+ };
2737
+ }
2738
+ function buildHeaders(credentials, opts) {
2739
+ const headers = { accept: "application/json" };
2740
+ if (credentials.businessId != null && credentials.businessId !== "") {
2741
+ headers.business = String(credentials.businessId);
2742
+ }
2743
+ if (credentials.customerToken && !opts.anonymous) {
2744
+ headers["x-customer-token"] = credentials.customerToken;
2745
+ }
2746
+ if (credentials.sessionToken && opts.session !== "omit") {
2747
+ headers["x-session-token"] = credentials.sessionToken;
2748
+ }
2749
+ if (opts.merchant && credentials.merchantToken) {
2750
+ headers.authorization = `Token ${credentials.merchantToken}`;
2751
+ }
2752
+ return headers;
2753
+ }
2754
+ function isDeadSession(status2, body, headers) {
2755
+ return status2 === 404 && typeof body === "string" && !!headers["x-session-token"];
2756
+ }
2757
+ function parseBody(text2, ok, status2) {
2758
+ if (text2 === "") return void 0;
2759
+ try {
2760
+ return JSON.parse(text2);
2761
+ } catch {
2762
+ if (!ok) return text2;
2763
+ throw new GrowupsellApiError({
2764
+ kind: "parse",
2765
+ status: status2,
2766
+ message: "The server returned a response that is not valid JSON.",
2767
+ body: text2
2768
+ });
2769
+ }
2770
+ }
2771
+ async function readBinary(res) {
2772
+ if (!res.arrayBuffer) {
2773
+ throw new GrowupsellApiError({
2774
+ kind: "parse",
2775
+ status: res.status,
2776
+ message: "This fetch cannot read binary responses."
2777
+ });
2778
+ }
2779
+ return { contentType: res.headers?.get("content-type") ?? null, data: await res.arrayBuffer() };
2780
+ }
2781
+ function timeoutError(ms) {
2782
+ return new GrowupsellApiError({ kind: "timeout", message: `Request timed out after ${ms} ms` });
2783
+ }
2784
+
2785
+ export {
2786
+ GrowupsellApiError,
2787
+ createClient
2788
+ };
2789
+ //# sourceMappingURL=chunk-YZJTPFCH.js.map