// Mock API adapter: implements the full @vmall/shared ApiClient surface with // fixed data + in-memory cart/order state. Pages call $api exactly as they // would against the live backend, so switching back is a config flip. import { ApiError } from "@vmall/shared"; import type { AccountSummary, Address, AddressBookEntry, AddressInput, ApiClient, AuthTokens, Cart, CartItem, Coupon, CouponTemplate, CouponTemplateInput, FlashSaleItem, FlashSaleItemInput, FlashSaleSession, FlashSaleSessionInput, GroupBuyIntent, GroupBuyingActivity, GroupBuyingActivityInput, GroupBuyingActivityView, HomeContent, IntegralOrder, IntegralProduct, IntegralProductInput, Invoice, InvoiceKind, Order, Product, PublicFlashSaleSession, RedeemPointsBody, Shipment, ShopProfile, User, } from "@vmall/shared"; import { BASE_CURRENCY, MOCK_BANNERS, collectiveProducts, MOCK_BRANDS, MOCK_CATEGORIES, MOCK_COUPONS, MOCK_CURRENCIES, INTEGRAL_PRODUCTS, MOCK_PROMOS, MOCK_QUICK_LINKS, MOCK_STORES, MOCK_USER, USER_STATS, MOCK_ADDRESSES, defaultAddress, lowestSku, mockConvertMinor, productById, searchMockProducts, seckillProducts, seedOrders, storeById, } from "./data"; interface MockState { token: string | null; cart: CartItem[]; orders: Order[]; shipments: Shipment[]; invoices: Invoice[]; addresses: AddressBookEntry[]; /** In-memory only: claims made during this browser session. */ coupons: Coupon[]; /** In-memory points catalog and redemptions for the fixed-data path. */ pointsProducts: IntegralProduct[]; redemptions: IntegralOrder[]; addressSeq: number; orderSeq: number; invoiceSeq: number; redemptionSeq: number; } // v3: address book joined the persisted state. const STORAGE_KEY = "vmall.mock.state.v3"; type PersistedState = Pick; // Load cart/order session state persisted by a previous page load (client only). function loadPersisted(): PersistedState | null { if (!import.meta.client) return null; try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return null; const parsed: unknown = JSON.parse(raw); if (typeof parsed !== "object" || parsed === null) return null; const p = parsed as Partial; if (!Array.isArray(p.cart) || !Array.isArray(p.orders)) return null; if (!Array.isArray(p.shipments) || !Array.isArray(p.invoices)) return null; if (typeof p.orderSeq !== "number" || typeof p.invoiceSeq !== "number") return null; if (!Array.isArray(p.addresses) || typeof p.addressSeq !== "number") return null; return p as PersistedState; } catch { return null; } } /** Mock coupons are global fixtures, so any shop id renders them as its own. */ function mockTemplate(t: (typeof MOCK_COUPONS)[number], shopId: string): CouponTemplate { return { id: t.id, shop_id: shopId, title: t.title, amount_minor: t.amountMinor, threshold_minor: t.thresholdMinor, currency: t.currency, stock: 100, enabled: true, starts_at: "2026-01-01T00:00:00.000Z", ends_at: `${t.expiresAt}T23:59:59.000Z`, created_at: "2026-01-01T00:00:00.000Z", }; } function mockOwnedCoupon(t: (typeof MOCK_COUPONS)[number]): Coupon { const template = mockTemplate(t, MOCK_STORES[0].id); return { id: `uc-${t.id}`, user_id: MOCK_USER.id, template_id: template.id, shop_id: template.shop_id, title: template.title, amount_minor: template.amount_minor, threshold_minor: template.threshold_minor, currency: template.currency, starts_at: template.starts_at, ends_at: template.ends_at, status: "claimed", order_id: null, claimed_at: "2026-01-01T00:00:00.000Z", redeemed_at: null, }; } function seedCoupons(): Coupon[] { return MOCK_COUPONS.map(mockOwnedCoupon); } function seedPointsProducts(): IntegralProduct[] { return INTEGRAL_PRODUCTS.map((p) => ({ id: p.id, name: p.name, subtitle: null, content: null, image: p.image, points_price: p.points, stock: p.stock, published: true, recommend: false, position: 0, created_at: "2026-01-01T00:00:00.000Z", updated_at: "2026-01-01T00:00:00.000Z", })); } /** One synthesized active session covering the fixed seckill fixtures. */ function mockFlashSales(): PublicFlashSaleSession[] { const now = Date.now(); const items = seckillProducts().map((entry, index) => { const sku = lowestSku(entry.product); const stock = sku?.stock ?? 100; const sold = Math.round((stock * entry.soldPct) / 100); return { id: `fsi${index + 1}`, session_id: "fs1", sku_id: sku?.id ?? "", product_id: entry.product.id, product_slug: entry.product.slug, product_name: entry.product.name, image: entry.product.images[0] ?? null, sku_code: sku?.sku_code ?? "", sale_price_minor: entry.seckillPriceMinor, currency: sku?.currency ?? BASE_CURRENCY, original_price_minor: sku?.price_minor ?? entry.seckillPriceMinor, original_currency: sku?.currency ?? BASE_CURRENCY, reserved_stock: Math.max(stock - sold, 0), sold_count: sold, per_customer_limit: 2, }; }); return [ { id: "fs1", shop_id: MOCK_STORES[0].id, label: { en: "Flash sale", zh: "限时秒杀" }, starts_at: new Date(now - 3_600_000).toISOString(), ends_at: new Date(now + 6 * 3_600_000).toISOString(), enabled: true, created_at: "2026-01-01T00:00:00.000Z", updated_at: "2026-01-01T00:00:00.000Z", items, }, ]; } /** Synthesized activities from the fixed collective fixtures. */ function mockGroupBuying(): GroupBuyingActivityView[] { const now = Date.now(); return collectiveProducts().map((entry, index) => { const sku = lowestSku(entry.product); const price = sku?.price_minor ?? 1000; return { id: `gb${index + 1}`, shop_id: entry.product.shop_id, sku_id: sku?.id ?? "", name: { en: `${entry.need}-person group`, zh: `${entry.need}人团` }, description: null, image: entry.product.images[0] ?? null, group_price_minor: Math.max(1, Math.round(price * 0.8)), currency: sku?.currency ?? BASE_CURRENCY, required_members: entry.need, starts_at: new Date(now - 3_600_000).toISOString(), ends_at: new Date(now + 7 * 24 * 3_600_000).toISOString(), group_lifetime_hours: 24, enabled: true, created_at: "2026-01-01T00:00:00.000Z", updated_at: "2026-01-01T00:00:00.000Z", sku_code: sku?.sku_code ?? "", product_id: entry.product.id, product_slug: entry.product.slug, product_name: entry.product.name, original_price_minor: price, original_currency: sku?.currency ?? BASE_CURRENCY, open_groups: [ { id: `g${index + 1}`, paid_member_count: Math.min(1, entry.need - 1), required_members: entry.need, expires_at: new Date(now + 6 * 3_600_000).toISOString(), }, ], }; }); } function initialState(): MockState { const persisted = loadPersisted(); // Coupons and points are session-only, so a restored snapshot re-seeds them. if (persisted) { return { token: null, ...persisted, coupons: seedCoupons(), pointsProducts: seedPointsProducts(), redemptions: [], redemptionSeq: 0, }; } const seed = seedOrders(MOCK_USER.id); const seededAddresses: AddressBookEntry[] = MOCK_ADDRESSES.map((a, i) => ({ id: a.id, user_id: MOCK_USER.id, recipient: a.recipient, phone: a.phone, country: "US", region: a.region, city: a.city, line1: a.line1, postal_code: a.postalCode, is_default: a.isDefault, created_at: `2026-08-0${i + 1}T09:00:00.000Z`, })); return { token: null, cart: [], orders: seed.orders, shipments: seed.shipments, invoices: seed.invoices, addresses: seededAddresses, coupons: seedCoupons(), pointsProducts: seedPointsProducts(), redemptions: [], addressSeq: 100, orderSeq: 100, invoiceSeq: 100, redemptionSeq: 0, }; } function unsupported(): never { throw new ApiError(501, "MOCK_UNSUPPORTED", "This admin/shop endpoint is not part of the mall mock."); } type MockStoreRecord = NonNullable>; /** Mock stores carry a nested `rate`; the API exposes flat score fields. */ function toShopProfile(store: MockStoreRecord): ShopProfile { return { id: store.id, slug: store.slug, name: store.name, company: store.company, region: store.region, address: store.address, logo: store.logo, banner: store.banner, notice: store.notice, after_sale: store.afterSale, score_rating: store.rate.score, score_agreement: store.rate.agree, score_service: store.rate.service, score_speed: store.rate.speed, }; } function skuIndex(): Record { const index: Record = {}; for (const p of searchMockProducts({ perPage: 1000 }).items) { for (const s of p.skus ?? []) index[s.id] = { product: p, skuId: s.id }; } return index; } export function createMockApi(): ApiClient { const state = initialState(); function persist(): void { if (!import.meta.client) return; try { const snapshot: PersistedState = { cart: state.cart, orders: state.orders, shipments: state.shipments, invoices: state.invoices, orderSeq: state.orderSeq, invoiceSeq: state.invoiceSeq, addresses: state.addresses, addressSeq: state.addressSeq, }; localStorage.setItem(STORAGE_KEY, JSON.stringify(snapshot)); } catch { /* storage unavailable: keep in-memory only */ } } function tokens(): AuthTokens { state.token = `mock-token-${Date.now()}`; return { token: state.token, user: MOCK_USER }; } function cartSnapshot(): Cart { return { items: state.cart.map((i) => ({ ...i })) }; } function nextOrderNo(): string { state.orderSeq += 1; return `VM20260917${String(state.orderSeq).padStart(3, "0")}`; } return { register: () => Promise.resolve(tokens()), login: () => Promise.resolve(tokens()), me: (): Promise => Promise.resolve(MOCK_USER), getAccountSummary: (): Promise => Promise.resolve({ balance_minor: USER_STATS.balanceMinor, frozen_minor: USER_STATS.frozenMinor, currency: BASE_CURRENCY, points: USER_STATS.points, }), listProducts: (q = {}) => Promise.resolve( searchMockProducts({ q: q.q, categoryId: q.category_id, brandId: q.brand_id, shopId: q.shop_id, sort: q.sort, order: q.order, page: q.page, perPage: q.per_page, }), ), getProduct: (idOrSlug) => { const product = productById(idOrSlug); if (!product) return Promise.reject(new ApiError(404, "NOT_FOUND", "Product not found")); return Promise.resolve(product); }, listCategories: () => Promise.resolve(MOCK_CATEGORIES), listBrands: () => Promise.resolve(MOCK_BRANDS), listCurrencies: () => Promise.resolve(MOCK_CURRENCIES), convert: (amountMinor, from, to) => Promise.resolve({ amount_minor: mockConvertMinor(amountMinor, from, to), currency: to }), getCart: () => Promise.resolve(cartSnapshot()), addCartItem: (skuId, qty) => { const entry = skuIndex()[skuId]; if (!entry) return Promise.reject(new ApiError(404, "NOT_FOUND", "SKU not found")); const sku = entry.product.skus?.find((s) => s.id === skuId); if (!sku || !sku.active) return Promise.reject(new ApiError(400, "SKU_INACTIVE", "SKU is not available")); const existing = state.cart.find((i) => i.sku_id === skuId); const nextQty = (existing?.qty ?? 0) + qty; if (nextQty > sku.stock) return Promise.reject(new ApiError(400, "OUT_OF_STOCK", "Not enough stock")); if (existing) existing.qty = nextQty; else { state.cart.push({ sku_id: skuId, product_id: entry.product.id, product_name: entry.product.name, sku_code: sku.sku_code, image: entry.product.images[0] ?? null, unit_price_minor: sku.price_minor, currency: sku.currency, qty, // The live cart view carries these too; see replace-mock-api-wave-3. shop_id: entry.product.shop_id, shop_name: storeById(entry.product.shop_id)?.name ?? MOCK_STORES[0].name, stock: sku.stock, }); } persist(); return Promise.resolve(cartSnapshot()); }, updateCartItem: (skuId, qty) => { const item = state.cart.find((i) => i.sku_id === skuId); if (!item) return Promise.reject(new ApiError(404, "NOT_FOUND", "Cart item not found")); if (qty <= 0) state.cart = state.cart.filter((i) => i.sku_id !== skuId); else item.qty = qty; persist(); return Promise.resolve(cartSnapshot()); }, removeCartItem: (skuId) => { state.cart = state.cart.filter((i) => i.sku_id !== skuId); persist(); return Promise.resolve(cartSnapshot()); }, checkout: ( shippingAddress: Address, currency: string, couponByShop: Record = {}, groupBuy?: GroupBuyIntent, ) => { if (state.cart.length === 0) { return Promise.reject(new ApiError(400, "EMPTY_CART", "Cart is empty")); } // A group-buy intent repriced exactly one SKU at the activity price. const activity = groupBuy ? mockGroupBuying().find((entry) => entry.id === groupBuy.activity_id) : undefined; if (groupBuy && !activity) { return Promise.reject(new ApiError(409, "CONFLICT", "Group activity is not available")); } const cartItems = state.cart.map((item) => activity && groupBuy && item.sku_id === groupBuy.sku_id ? { ...item, unit_price_minor: activity.group_price_minor } : item, ); const byShop: Record = {}; for (const item of cartItems) { const product = productById(item.product_id); const shopId = product?.shop_id ?? "unknown"; (byShop[shopId] ??= []).push(item); } const created: Order[] = []; for (const [shopId, items] of Object.entries(byShop)) { const subtotal = items.reduce((sum, i) => sum + i.unit_price_minor * i.qty, 0); const groupHere = activity && groupBuy && items.some((i) => i.sku_id === groupBuy.sku_id); const couponId = couponByShop[shopId]; let discount = 0; if (couponId && groupHere) { return Promise.reject( new ApiError(409, "CONFLICT", "A coupon cannot be combined with group pricing"), ); } if (couponId) { const coupon = state.coupons.find((c) => c.id === couponId); if (!coupon || coupon.status !== "claimed" || coupon.shop_id !== shopId) { return Promise.reject(new ApiError(409, "CONFLICT", "Coupon is not redeemable")); } if (subtotal < coupon.threshold_minor) { return Promise.reject( new ApiError(409, "CONFLICT", "Order does not reach the coupon threshold"), ); } discount = Math.min(coupon.amount_minor, subtotal); } const order: Order = { id: `o-${Date.now()}-${shopId}`, order_no: nextOrderNo(), shop_id: shopId, user_id: MOCK_USER.id, status: "pending_payment", currency, total_minor: subtotal - discount, discount_minor: discount, coupon_id: couponId ?? null, group_activity_id: groupHere ? activity.id : null, group_id: groupHere ? `g-${Date.now()}` : null, items: items.map((i, k) => ({ id: `oi-${Date.now()}-${k}`, sku_id: i.sku_id, product_name: i.product_name, sku_code: i.sku_code, unit_price_minor: i.unit_price_minor, qty: i.qty, image: i.image, flash_sale_item_id: null, })), shipping_address: shippingAddress, created_at: new Date().toISOString(), }; if (couponId) { const coupon = state.coupons.find((c) => c.id === couponId); if (coupon) { coupon.status = "redeemed"; coupon.order_id = order.id; coupon.redeemed_at = order.created_at; } } created.push(order); } state.orders = [...created, ...state.orders]; state.cart = []; persist(); return Promise.resolve(created); }, listMyOrders: (page = 1) => { const perPage = 10; const start = (page - 1) * perPage; return Promise.resolve({ items: state.orders.slice(start, start + perPage), total: state.orders.length, page, per_page: perPage, }); }, getOrder: (id) => { const order = state.orders.find((o) => o.id === id || o.order_no === id); if (!order) return Promise.reject(new ApiError(404, "NOT_FOUND", "Order not found")); return Promise.resolve(order); }, cancelOrder: (id) => { const order = state.orders.find((o) => o.id === id); if (!order) return Promise.reject(new ApiError(404, "NOT_FOUND", "Order not found")); if (order.status !== "pending_payment") { return Promise.reject(new ApiError(409, "INVALID_STATE", "Only unpaid orders can be cancelled")); } order.status = "cancelled"; const coupon = state.coupons.find((c) => c.order_id === order.id); if (coupon) { coupon.status = "claimed"; coupon.order_id = null; coupon.redeemed_at = null; } persist(); return Promise.resolve(order); }, payOrder: (id) => { const order = state.orders.find((o) => o.id === id); if (!order) return Promise.reject(new ApiError(404, "NOT_FOUND", "Order not found")); if (order.status !== "pending_payment") { return Promise.reject(new ApiError(409, "INVALID_STATE", "Only unpaid orders can be paid")); } order.status = "paid"; persist(); return Promise.resolve(order); }, confirmDelivered: (shipmentId) => { const shipment = state.shipments.find((s) => s.id === shipmentId); if (!shipment) return Promise.reject(new ApiError(404, "NOT_FOUND", "Shipment not found")); shipment.status = "delivered"; shipment.delivered_at = new Date().toISOString(); const order = state.orders.find((o) => o.id === shipment.order_id); if (order) order.status = "completed"; persist(); return Promise.resolve(shipment); }, listMyShipments: () => Promise.resolve(state.shipments.map((s) => ({ ...s }))), requestInvoice: (orderId: string, title: string, taxNo: string | null, kind: InvoiceKind) => { const order = state.orders.find((o) => o.id === orderId); if (!order) return Promise.reject(new ApiError(404, "NOT_FOUND", "Order not found")); state.invoiceSeq += 1; const invoice: Invoice = { id: `inv-${Date.now()}`, invoice_no: `INV20260917${String(state.invoiceSeq).padStart(3, "0")}`, order_id: order.id, order_no: order.order_no, title, tax_no: taxNo, kind, amount_minor: order.total_minor, currency: order.currency, status: "requested", issued_at: null, created_at: new Date().toISOString(), }; state.invoices = [invoice, ...state.invoices]; persist(); return Promise.resolve(invoice); }, listMyInvoices: () => Promise.resolve(state.invoices.map((i) => ({ ...i }))), // Mirror of the seeded storefront-content rows, so the home page renders // identically when every domain is configured to fixed data. getHomeContent: (): Promise => Promise.resolve({ banners: MOCK_BANNERS.map((b, i) => ({ id: `bn${i + 1}`, image: b.image, url: b.url, position: i, active: true, })), promos: MOCK_PROMOS.map((p, i) => ({ id: `pr${i + 1}`, image: p.image, url: p.url, position: i, active: true, })), quick_links: MOCK_QUICK_LINKS.map((q, i) => ({ id: `ql${i + 1}`, label: q.label, url: q.url, glyph: q.glyph, position: i, active: true, })), floor_adverts: Array.from({ length: 6 }, (_, i) => ({ id: `fa${i + 1}`, image: `/mock/floor-adv-${i + 1}.svg`, position: i, active: true, })), }), listShops: (): Promise => Promise.resolve(MOCK_STORES.map(toShopProfile)), getShop: (slug) => { const store = storeById(slug); if (!store) return Promise.reject(new ApiError(404, "NOT_FOUND", "Shop not found")); return Promise.resolve(toShopProfile(store)); }, listMyAddresses: () => Promise.resolve( [...state.addresses].sort( (a, b) => Number(b.is_default) - Number(a.is_default) || b.created_at.localeCompare(a.created_at), ), ), createAddress: (input: AddressInput) => { const makeDefault = input.is_default === true || state.addresses.length === 0; if (makeDefault) for (const a of state.addresses) a.is_default = false; state.addressSeq += 1; const entry: AddressBookEntry = { id: `a-${state.addressSeq}`, user_id: MOCK_USER.id, recipient: input.recipient, phone: input.phone, country: input.country, region: input.region, city: input.city, line1: input.line1, postal_code: input.postal_code, is_default: makeDefault, created_at: new Date().toISOString(), }; state.addresses.push(entry); persist(); return Promise.resolve(entry); }, updateAddress: (id: string, input: AddressInput) => { const entry = state.addresses.find((a) => a.id === id); if (!entry) return Promise.reject(new ApiError(404, "NOT_FOUND", "Address not found")); if (input.is_default === true && !entry.is_default) { for (const a of state.addresses) a.is_default = false; } entry.recipient = input.recipient; entry.phone = input.phone; entry.country = input.country; entry.region = input.region; entry.city = input.city; entry.line1 = input.line1; entry.postal_code = input.postal_code; entry.is_default = input.is_default === true || entry.is_default; persist(); return Promise.resolve({ ...entry }); }, deleteAddress: (id: string) => { const entry = state.addresses.find((a) => a.id === id); if (!entry) return Promise.reject(new ApiError(404, "NOT_FOUND", "Address not found")); const wasDefault = entry.is_default; state.addresses = state.addresses.filter((a) => a.id !== id); if (wasDefault && state.addresses.length > 0) { const latest = state.addresses.reduce((a, b) => (a.created_at > b.created_at ? a : b)); latest.is_default = true; } persist(); return Promise.resolve([...state.addresses]); }, setDefaultAddress: (id: string) => { const entry = state.addresses.find((a) => a.id === id); if (!entry) return Promise.reject(new ApiError(404, "NOT_FOUND", "Address not found")); for (const a of state.addresses) a.is_default = a.id === id; persist(); return Promise.resolve({ ...entry }); }, listShopCouponTemplates: (shopId: string) => Promise.resolve(MOCK_COUPONS.map((t) => mockTemplate(t, shopId))), listMyCoupons: () => Promise.resolve(state.coupons.map((c) => ({ ...c }))), claimCoupon: (templateId: string) => { const template = MOCK_COUPONS.find((t) => t.id === templateId); if (!template) return Promise.reject(new ApiError(404, "NOT_FOUND", "Coupon not found")); if (state.coupons.some((c) => c.template_id === templateId)) { return Promise.reject(new ApiError(409, "CONFLICT", "Coupon already claimed")); } const coupon = mockOwnedCoupon(template); state.coupons.push(coupon); persist(); return Promise.resolve({ ...coupon }); }, listPointsProducts: () => Promise.resolve( state.pointsProducts.filter((p) => p.published).map((p) => ({ ...p })), ), listFlashSales: () => Promise.resolve(mockFlashSales()), listGroupBuyingActivities: () => Promise.resolve(mockGroupBuying()), listMyRedemptions: () => Promise.resolve(state.redemptions.map((o) => ({ ...o }))), redeemPoints: (body: RedeemPointsBody) => { const product = state.pointsProducts.find( (p) => p.id === body.product_id && p.published, ); if (!product) { return Promise.reject(new ApiError(404, "NOT_FOUND", "Points product not found")); } if (body.qty <= 0 || body.qty > product.stock) { return Promise.reject(new ApiError(409, "CONFLICT", "Insufficient points stock")); } const total = product.points_price * body.qty; if (total > USER_STATS.points) { return Promise.reject(new ApiError(409, "CONFLICT", "Insufficient points")); } state.redemptionSeq += 1; const order: IntegralOrder = { id: `po-${state.redemptionSeq}`, order_no: `PM${String(state.redemptionSeq).padStart(8, "0")}`, user_id: MOCK_USER.id, status: "pending_fulfillment", total_points: total, shipping_address: body.shipping_address, items: [ { id: `poi-${state.redemptionSeq}`, order_id: `po-${state.redemptionSeq}`, product_id: product.id, name: product.name, image: product.image, points_price: product.points_price, qty: body.qty, }, ], created_at: new Date().toISOString(), }; product.stock -= body.qty; state.redemptions = [order, ...state.redemptions]; persist(); return Promise.resolve({ ...order }); }, shop: { getMyShop: () => unsupported(), listMyProducts: () => unsupported(), getProduct: () => unsupported(), createProduct: () => unsupported(), updateProduct: () => unsupported(), publish: () => unsupported(), unpublish: () => unsupported(), upsertSku: () => unsupported(), listOrders: () => unsupported(), getOrder: () => unsupported(), createShipment: () => unsupported(), listShipments: () => unsupported(), markShipped: () => unsupported(), listInvoices: () => unsupported(), issueInvoice: () => unsupported(), listCouponTemplates: () => unsupported(), createCouponTemplate: (_body: CouponTemplateInput) => unsupported(), updateCouponTemplate: (_id: string, _body: CouponTemplateInput) => unsupported(), deleteCouponTemplate: (_id: string) => unsupported(), listFlashSales: () => unsupported(), createFlashSale: (_body: FlashSaleSessionInput) => unsupported(), updateFlashSale: (_id: string, _body: FlashSaleSessionInput) => unsupported(), deleteFlashSale: (_id: string) => unsupported(), addFlashSaleItem: (_sessionId: string, _body: FlashSaleItemInput) => unsupported(), updateFlashSaleItem: (_id: string, _body: FlashSaleItemInput) => unsupported(), deleteFlashSaleItem: (_id: string) => unsupported(), listGroupBuyingActivities: () => unsupported(), createGroupBuyingActivity: (_body: GroupBuyingActivityInput) => unsupported(), updateGroupBuyingActivity: (_id: string, _body: GroupBuyingActivityInput) => unsupported(), deleteGroupBuyingActivity: (_id: string) => unsupported(), }, admin: { listUsers: () => unsupported(), setUserRole: () => unsupported(), listShops: () => unsupported(), createShop: () => unsupported(), setShopStatus: () => unsupported(), listOrders: () => unsupported(), listCurrencies: () => unsupported(), upsertCurrency: () => unsupported(), setRate: () => unsupported(), getContent: () => unsupported(), replaceContent: () => unsupported(), setShopProfile: () => unsupported(), getBrands: () => unsupported(), replaceBrands: () => unsupported(), listPointsProducts: () => unsupported(), createPointsProduct: (_body: IntegralProductInput) => unsupported(), updatePointsProduct: (_id: string, _body: IntegralProductInput) => unsupported(), setPointsProductPublished: (_id: string, _published: boolean) => unsupported(), listPointsRedemptions: (_page?: number) => unsupported(), fulfillRedemption: (_id: string) => unsupported(), cancelRedemption: (_id: string) => unsupported(), }, }; } export { defaultAddress };