import { createApi } from "@vmall/shared"; import type { ApiClient } from "@vmall/shared"; import { createMockApi } from "~/mock/api"; /** * Domains the live backend serves. Every other domain stays on the fixed-data * adapter, so a domain can be migrated - or rolled back - by editing this list * alone. See openspec/changes/replace-mock-api-wave-1/design.md. */ type LiveDomain = | "auth" | "account" | "catalog" | "currency" | "content" | "shops" | "brands" | "cart" | "orders" | "shipments" | "invoices" | "addresses" | "coupons" | "points" | "flashSales" | "groupBuying"; /** * Explicit per-domain method picks rather than a string allowlist: indexing * `ApiClient` by a union of method names would need an unsafe cast, and this * keeps the compiler checking that every picked key exists. */ const LIVE_PICKS = { auth: (a: ApiClient) => ({ register: a.register, login: a.login, me: a.me }), account: (a: ApiClient) => ({ getAccountSummary: a.getAccountSummary }), catalog: (a: ApiClient) => ({ listProducts: a.listProducts, getProduct: a.getProduct, listCategories: a.listCategories, }), currency: (a: ApiClient) => ({ listCurrencies: a.listCurrencies, convert: a.convert }), content: (a: ApiClient) => ({ getHomeContent: a.getHomeContent }), shops: (a: ApiClient) => ({ listShops: a.listShops, getShop: a.getShop }), brands: (a: ApiClient) => ({ listBrands: a.listBrands }), cart: (a: ApiClient) => ({ getCart: a.getCart, addCartItem: a.addCartItem, updateCartItem: a.updateCartItem, removeCartItem: a.removeCartItem, }), orders: (a: ApiClient) => ({ checkout: a.checkout, listMyOrders: a.listMyOrders, getOrder: a.getOrder, cancelOrder: a.cancelOrder, payOrder: a.payOrder, }), shipments: (a: ApiClient) => ({ confirmDelivered: a.confirmDelivered, listMyShipments: a.listMyShipments, }), invoices: (a: ApiClient) => ({ requestInvoice: a.requestInvoice, listMyInvoices: a.listMyInvoices, }), addresses: (a: ApiClient) => ({ listMyAddresses: a.listMyAddresses, createAddress: a.createAddress, updateAddress: a.updateAddress, deleteAddress: a.deleteAddress, setDefaultAddress: a.setDefaultAddress, }), coupons: (a: ApiClient) => ({ listShopCouponTemplates: a.listShopCouponTemplates, listMyCoupons: a.listMyCoupons, claimCoupon: a.claimCoupon, }), points: (a: ApiClient) => ({ listPointsProducts: a.listPointsProducts, listMyRedemptions: a.listMyRedemptions, redeemPoints: a.redeemPoints, }), flashSales: (a: ApiClient) => ({ listFlashSales: a.listFlashSales }), groupBuying: (a: ApiClient) => ({ listGroupBuyingActivities: a.listGroupBuyingActivities, }), } satisfies Record Partial>; const KNOWN_DOMAINS = Object.keys(LIVE_PICKS) as LiveDomain[]; /** Fallback when runtimeConfig supplies no list; keep in step with nuxt.config.ts. */ const DEFAULT_LIVE_DOMAINS: LiveDomain[] = [ "catalog", "currency", "content", "shops", "brands", "auth", "account", "cart", "orders", "shipments", "invoices", "addresses", "coupons", "points", "flashSales", "groupBuying", ]; export default defineNuxtPlugin(() => { const config = useRuntimeConfig(); const mock = createMockApi(); const live = createApi({ baseUrl: config.public.apiBase as string, getToken: () => (import.meta.client ? localStorage.getItem("vmall.token") : null), }); const configured = config.public.liveDomains as string[] | undefined; const liveDomains = (configured ?? DEFAULT_LIVE_DOMAINS).filter((domain): domain is LiveDomain => KNOWN_DOMAINS.includes(domain as LiveDomain), ); // The fixed-data adapter is the base object, so an unmigrated domain cannot // regress and a live domain can be rolled back by removing one entry. let api: ApiClient = mock; for (const domain of liveDomains) { api = { ...api, ...LIVE_PICKS[domain](live) }; } return { provide: { api } }; });