@yoryoboy/bi-mcp 1.16.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.js +179 -6
- package/dist/index.js.map +2 -2
- package/dist/mcp-use.json +3 -6
- package/dist/src/config/__tests__/google-store.test.js +30 -0
- package/dist/src/config/__tests__/google-store.test.js.map +7 -0
- package/dist/src/config/__tests__/mcp-tool-deadline.test.js +64 -0
- package/dist/src/config/__tests__/mcp-tool-deadline.test.js.map +7 -0
- package/dist/src/config/google-store.js +18 -0
- package/dist/src/config/google-store.js.map +2 -2
- package/dist/src/config/mcp-tool-deadline-context.js +34 -0
- package/dist/src/config/mcp-tool-deadline-context.js.map +7 -0
- package/dist/src/config/mcp-tool-deadline.js +67 -0
- package/dist/src/config/mcp-tool-deadline.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js +38 -3
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js.map +2 -2
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-buyer-reconciliation.test.js +226 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-buyer-reconciliation.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-claim-reconciliation.test.js +127 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-claim-reconciliation.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-kits.test.js +103 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-kits.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-order-billing-info.test.js +582 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-order-billing-info.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js +10 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js.map +2 -2
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-packs.test.js +36 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-packs.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-payments.test.js +137 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-payments.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-ad-group-ads.test.js +105 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-ad-group-ads.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-ad-groups.test.js +143 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-ad-groups.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-campaigns.test.js +100 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-campaigns.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads.test.js +350 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-return-reconciliation.test.js +228 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-return-reconciliation.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-shipping.test.js +58 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-shipping.test.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-api.js +15 -3
- package/dist/src/services/mercadolibre/mercadolibre-api.js.map +2 -2
- package/dist/src/services/mercadolibre/mercadolibre-billing.js +110 -3
- package/dist/src/services/mercadolibre/mercadolibre-billing.js.map +2 -2
- package/dist/src/services/mercadolibre/mercadolibre-buyer-reconciliation.js +368 -0
- package/dist/src/services/mercadolibre/mercadolibre-buyer-reconciliation.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-claim-reconciliation.js +399 -0
- package/dist/src/services/mercadolibre/mercadolibre-claim-reconciliation.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-kits.js +55 -0
- package/dist/src/services/mercadolibre/mercadolibre-kits.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-order-billing-info.js +392 -0
- package/dist/src/services/mercadolibre/mercadolibre-order-billing-info.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-orders.js +8 -8
- package/dist/src/services/mercadolibre/mercadolibre-orders.js.map +2 -2
- package/dist/src/services/mercadolibre/mercadolibre-packs.js +21 -0
- package/dist/src/services/mercadolibre/mercadolibre-packs.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-payments.js +60 -0
- package/dist/src/services/mercadolibre/mercadolibre-payments.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-product-ads.js +429 -0
- package/dist/src/services/mercadolibre/mercadolibre-product-ads.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-return-reconciliation.js +436 -0
- package/dist/src/services/mercadolibre/mercadolibre-return-reconciliation.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-shipping.js +35 -0
- package/dist/src/services/mercadolibre/mercadolibre-shipping.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-api.test.js +67 -0
- package/dist/src/services/vtex/__tests__/vtex-api.test.js.map +7 -0
- package/dist/src/services/vtex/vtex-api.js +6 -0
- package/dist/src/services/vtex/vtex-api.js.map +2 -2
- package/dist/src/tools/config/__tests__/list-profiles.test.js +154 -0
- package/dist/src/tools/config/__tests__/list-profiles.test.js.map +7 -0
- package/dist/src/tools/config/list-profiles.js +39 -3
- package/dist/src/tools/config/list-profiles.js.map +2 -2
- package/dist/src/tools/mercadolibre/__tests__/get-order-billing-info.test.js +339 -0
- package/dist/src/tools/mercadolibre/__tests__/get-order-billing-info.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-ad-group-ads.test.js +225 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-ad-group-ads.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-ad-groups.test.js +148 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-ad-groups.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-campaigns.test.js +60 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-campaigns.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-helpers.test.js +128 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-helpers.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-tools.test.js +435 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-tools.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/reconcile-buyer-data.test.js +31 -0
- package/dist/src/tools/mercadolibre/__tests__/reconcile-buyer-data.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/reconcile-claim-data.test.js +36 -0
- package/dist/src/tools/mercadolibre/__tests__/reconcile-claim-data.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/validate-connection.test.js +205 -0
- package/dist/src/tools/mercadolibre/__tests__/validate-connection.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-billing-provisions-by-order.js +313 -0
- package/dist/src/tools/mercadolibre/get-billing-provisions-by-order.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-bundle-context-by-item.js +176 -0
- package/dist/src/tools/mercadolibre/get-bundle-context-by-item.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-full-storage-charges.js +183 -0
- package/dist/src/tools/mercadolibre/get-full-storage-charges.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-order-billing-info.js +189 -0
- package/dist/src/tools/mercadolibre/get-order-billing-info.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-pack-context-by-order.js +150 -0
- package/dist/src/tools/mercadolibre/get-pack-context-by-order.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-payment-details.js +322 -0
- package/dist/src/tools/mercadolibre/get-payment-details.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-product-ads-ad-group-ads.js +144 -0
- package/dist/src/tools/mercadolibre/get-product-ads-ad-group-ads.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-product-ads-advertisers.js +59 -0
- package/dist/src/tools/mercadolibre/get-product-ads-advertisers.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-shipping-quote-by-declared-dimensions.js +104 -0
- package/dist/src/tools/mercadolibre/get-shipping-quote-by-declared-dimensions.js.map +7 -0
- package/dist/src/tools/mercadolibre/index.js +32 -0
- package/dist/src/tools/mercadolibre/index.js.map +2 -2
- package/dist/src/tools/mercadolibre/product-ads-helpers.js +130 -0
- package/dist/src/tools/mercadolibre/product-ads-helpers.js.map +7 -0
- package/dist/src/tools/mercadolibre/reconcile-buyer-data.js +27 -0
- package/dist/src/tools/mercadolibre/reconcile-buyer-data.js.map +7 -0
- package/dist/src/tools/mercadolibre/reconcile-claim-data.js +46 -0
- package/dist/src/tools/mercadolibre/reconcile-claim-data.js.map +7 -0
- package/dist/src/tools/mercadolibre/reconcile-return-data.js +25 -0
- package/dist/src/tools/mercadolibre/reconcile-return-data.js.map +7 -0
- package/dist/src/tools/mercadolibre/search-product-ads-ad-groups.js +162 -0
- package/dist/src/tools/mercadolibre/search-product-ads-ad-groups.js.map +7 -0
- package/dist/src/tools/mercadolibre/search-product-ads-ads.js +216 -0
- package/dist/src/tools/mercadolibre/search-product-ads-ads.js.map +7 -0
- package/dist/src/tools/mercadolibre/search-product-ads-campaigns.js +116 -0
- package/dist/src/tools/mercadolibre/search-product-ads-campaigns.js.map +7 -0
- package/dist/src/tools/mercadolibre/validate-connection.js +40 -12
- package/dist/src/tools/mercadolibre/validate-connection.js.map +2 -2
- package/dist/tests/meli/mercadolibre-billing-provisions.test.js +378 -0
- package/dist/tests/meli/mercadolibre-billing-provisions.test.js.map +2 -2
- package/dist/tests/meli/mercadolibre-order-billing-info-surface.test.js +123 -0
- package/dist/tests/meli/mercadolibre-order-billing-info-surface.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-payment-details.test.js +356 -0
- package/dist/tests/meli/mercadolibre-payment-details.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-payments.test.js +62 -0
- package/dist/tests/meli/mercadolibre-payments.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-product-ads-ad-groups-surface.test.js +90 -0
- package/dist/tests/meli/mercadolibre-product-ads-ad-groups-surface.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-product-ads-campaigns-surface.test.js +72 -0
- package/dist/tests/meli/mercadolibre-product-ads-campaigns-surface.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-pack-kit-shipping.test.js +400 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-pack-kit-shipping.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js +31 -12
- package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js.map +2 -2
- package/dist/tests/vtex/vtex-commercial-conditions-docs.test.js +1 -1
- package/dist/tests/vtex/vtex-commercial-conditions-docs.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/src/google-ads/report-utils.js +0 -78
- package/dist/src/google-ads/report-utils.js.map +0 -7
- package/dist/src/search-console/search-console-utils.js +0 -275
- package/dist/src/search-console/search-console-utils.js.map +0 -7
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js +0 -165
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js.map +0 -7
- package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js +0 -233
- package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js.map +0 -7
- package/dist/src/tools/vtex/catalog-admin-batch.js +0 -298
- package/dist/src/tools/vtex/catalog-admin-batch.js.map +0 -7
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/services/mercadolibre/__tests__/mercadolibre-product-ads.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { MercadoLibreApiError } from \"../mercadolibre-api.js\";\nimport {\n listMercadoLibreProductAdsAdvertisers,\n searchMercadoLibreProductAdsAds,\n searchMercadoLibreProductAdsAdsBatch,\n} from \"../mercadolibre-product-ads.js\";\nimport { getMercadoLibreApiContext, runMercadoLibreBatch } from \"../mercadolibre-api.js\";\n\nvi.mock(\"../mercadolibre-api.js\", async () => {\n const actual = await vi.importActual<typeof import(\"../mercadolibre-api.js\")>(\"../mercadolibre-api.js\");\n\n return {\n ...actual,\n getMercadoLibreApiContext: vi.fn(),\n runMercadoLibreBatch: vi.fn(),\n };\n});\n\ndescribe(\"mercadolibre-product-ads service\", () => {\n const getMock = vi.fn();\n const searchRequest = {\n dateFrom: \"2026-06-01\",\n dateTo: \"2026-06-30\",\n metrics: [\"direct_amount\", \"indirect_amount\", \"total_amount\", \"clicks\", \"impressions\", \"cost\"],\n metricsSummary: true,\n campaignId: \"campaign-1\",\n adGroupId: \"ad-group-1\",\n itemId: \"item-1\",\n channel: \"mshops\",\n status: \"active\",\n brandValueName: \"Brand 1\",\n };\n\n beforeEach(() => {\n vi.clearAllMocks();\n vi.useRealTimers();\n vi.mocked(getMercadoLibreApiContext).mockResolvedValue({\n api: { get: getMock },\n sellerId: \"seller-1\",\n accessToken: \"token-1\",\n } as never);\n });\n\n it(\"retries transient Product Ads advertiser discovery failures before succeeding\", async () => {\n const setTimeoutSpy = vi.spyOn(globalThis, \"setTimeout\").mockImplementation(((handler: any) => {\n if (typeof handler === \"function\") {\n handler();\n }\n\n return 0 as never;\n }) as unknown as typeof setTimeout);\n\n getMock\n .mockRejectedValueOnce(new MercadoLibreApiError(\"rate limited\", 429))\n .mockRejectedValueOnce(new MercadoLibreApiError(\"rate limited\", 429))\n .mockResolvedValueOnce({\n data: {\n advertisers: [\n {\n advertiser_id: \"adv-1\",\n advertiser_site_id: \"MLA\",\n },\n ],\n },\n });\n\n const promise = listMercadoLibreProductAdsAdvertisers(\"profile-1\");\n\n await expect(promise).resolves.toEqual([\n {\n advertiser_id: \"adv-1\",\n advertiser_site_id: \"MLA\",\n account_name: \"\",\n business_name: \"\",\n status: undefined,\n },\n ]);\n\n expect(getMock).toHaveBeenCalledTimes(3);\n setTimeoutSpy.mockRestore();\n });\n\n it(\"lists Product Ads advertisers with the expected discovery endpoint\", async () => {\n getMock.mockResolvedValueOnce({\n data: {\n advertisers: [\n {\n advertiser_id: \"adv-1\",\n advertiser_site_id: \"MLA\",\n account_name: \"Main account\",\n business_name: \"Main business\",\n status: \"active\",\n },\n ],\n },\n });\n\n await expect(listMercadoLibreProductAdsAdvertisers(\"profile-1\")).resolves.toEqual([\n {\n advertiser_id: \"adv-1\",\n advertiser_site_id: \"MLA\",\n account_name: \"Main account\",\n business_name: \"Main business\",\n status: \"active\",\n },\n ]);\n\n expect(getMock).toHaveBeenCalledWith(\"/advertising/advertisers\", {\n params: { product_id: \"PADS\" },\n headers: { \"Api-Version\": \"v1\" },\n });\n });\n\n it(\"searches Product Ads ads with the expected site and advertiser path\", async () => {\n getMock.mockResolvedValueOnce({\n data: {\n paging: { total: 1, limit: 50, offset: 0 },\n results: [\n {\n item_id: \"item-1\",\n title: \"Item one\",\n status: \"active\",\n campaign_id: \"campaign-1\",\n campaign_name: \"Campaign one\",\n ad_group_id: \"ad-group-1\",\n ad_group_name: \"Ad group one\",\n channel: \"mshops\",\n brand_value_name: \"Brand one\",\n metrics: {\n direct_amount: 12.5,\n indirect_amount: 2.5,\n total_amount: 15,\n clicks: 7,\n impressions: 120,\n cost: 9.75,\n advertising_items_quantity: 1,\n currency_id: \"ARS\",\n },\n },\n ],\n },\n });\n\n await expect(\n searchMercadoLibreProductAdsAds(\"profile-1\", {\n siteId: \"MLA\",\n advertiserId: \"89015\",\n dateFrom: searchRequest.dateFrom,\n dateTo: searchRequest.dateTo,\n metrics: searchRequest.metrics,\n metricsSummary: searchRequest.metricsSummary,\n campaignId: searchRequest.campaignId,\n adGroupId: searchRequest.adGroupId,\n itemId: searchRequest.itemId,\n channel: searchRequest.channel,\n status: searchRequest.status,\n brandValueName: searchRequest.brandValueName,\n limit: 25,\n offset: 10,\n })\n ).resolves.toEqual({\n paging: { total: 1, limit: 50, offset: 0 },\n results: [\n {\n item_id: \"item-1\",\n title: \"Item one\",\n status: \"active\",\n campaign_id: \"campaign-1\",\n campaign_name: \"Campaign one\",\n ad_group_id: \"ad-group-1\",\n ad_group_name: \"Ad group one\",\n channel: \"mshops\",\n brand_value_name: \"Brand one\",\n metrics: {\n direct_amount: 12.5,\n indirect_amount: 2.5,\n total_amount: 15,\n clicks: 7,\n impressions: 120,\n cost: 9.75,\n advertising_items_quantity: 1,\n currency_id: \"ARS\",\n },\n },\n ],\n });\n\n expect(getMock).toHaveBeenCalledWith(\n \"/advertising/MLA/advertisers/89015/product_ads/ads/search\",\n {\n params: {\n date_from: searchRequest.dateFrom,\n date_to: searchRequest.dateTo,\n metrics: searchRequest.metrics.join(\",\"),\n metrics_summary: searchRequest.metricsSummary,\n campaign_id: searchRequest.campaignId,\n ad_group_id: searchRequest.adGroupId,\n item_id: searchRequest.itemId,\n channel: searchRequest.channel,\n status: searchRequest.status,\n brand_value_name: searchRequest.brandValueName,\n limit: 25,\n offset: 10,\n },\n headers: { \"Api-Version\": \"v2\" },\n }\n );\n });\n\n it(\"returns no fabricated ads rows when the search envelope has only paging metadata\", async () => {\n getMock.mockResolvedValueOnce({\n data: {\n paging: { total: 0, limit: 50, offset: 0 },\n },\n });\n\n await expect(\n searchMercadoLibreProductAdsAds(\"profile-1\", {\n siteId: \"MLA\",\n advertiserId: \"89015\",\n dateFrom: searchRequest.dateFrom,\n dateTo: searchRequest.dateTo,\n metrics: searchRequest.metrics,\n limit: 25,\n offset: 10,\n })\n ).resolves.toEqual({\n paging: { total: 0, limit: 50, offset: 0 },\n results: [],\n });\n });\n\n it(\"exhausts retry attempts with a clear failure when Product Ads search keeps returning transient errors\", async () => {\n const setTimeoutSpy = vi.spyOn(globalThis, \"setTimeout\").mockImplementation(((handler: any) => {\n if (typeof handler === \"function\") {\n handler();\n }\n\n return 0 as never;\n }) as unknown as typeof setTimeout);\n\n getMock.mockRejectedValue(new MercadoLibreApiError(\"temporarily unavailable\", 503));\n\n const promise = searchMercadoLibreProductAdsAds(\"profile-1\", {\n siteId: \"MLA\",\n advertiserId: \"89015\",\n dateFrom: searchRequest.dateFrom,\n dateTo: searchRequest.dateTo,\n metrics: searchRequest.metrics,\n limit: 25,\n offset: 10,\n });\n\n await expect(promise).rejects.toMatchObject({\n name: \"MercadoLibreApiError\",\n status: 503,\n message: \"temporarily unavailable\",\n });\n\n expect(getMock).toHaveBeenCalledTimes(3);\n setTimeoutSpy.mockRestore();\n });\n\n it(\"delegates Product Ads page batches to the shared batch runner\", async () => {\n vi.mocked(runMercadoLibreBatch).mockImplementation(async (ids, fetcher) => ({\n successful: [{ id: ids[0], document: await fetcher(ids[0]) }],\n failed: [],\n }));\n getMock.mockResolvedValueOnce({\n data: {\n paging: { total: 2, limit: 50, offset: 50 },\n results: [\n {\n item_id: \"item-2\",\n title: \"Item two\",\n status: \"paused\",\n campaign_id: \"campaign-2\",\n campaign_name: \"Campaign two\",\n ad_group_id: \"ad-group-2\",\n ad_group_name: \"Ad group two\",\n channel: \"marketplace\",\n brand_value_name: \"Brand two\",\n metrics: {\n direct_amount: 25,\n indirect_amount: 5,\n total_amount: 30,\n clicks: 14,\n impressions: 240,\n cost: 19.5,\n advertising_items_quantity: 1,\n currency_id: \"ARS\",\n },\n },\n ],\n },\n });\n\n await expect(\n searchMercadoLibreProductAdsAdsBatch(\n \"profile-1\",\n {\n siteId: \"MLA\",\n advertiserId: \"89015\",\n dateFrom: searchRequest.dateFrom,\n dateTo: searchRequest.dateTo,\n metrics: searchRequest.metrics,\n metricsSummary: searchRequest.metricsSummary,\n campaignId: searchRequest.campaignId,\n adGroupId: searchRequest.adGroupId,\n itemId: searchRequest.itemId,\n channel: searchRequest.channel,\n status: searchRequest.status,\n brandValueName: searchRequest.brandValueName,\n limit: 50,\n },\n [50],\n { maxConcurrency: 3 }\n )\n ).resolves.toEqual({\n successful: [\n {\n id: \"50\",\n document: {\n paging: { total: 2, limit: 50, offset: 50 },\n results: [\n {\n item_id: \"item-2\",\n title: \"Item two\",\n status: \"paused\",\n campaign_id: \"campaign-2\",\n campaign_name: \"Campaign two\",\n ad_group_id: \"ad-group-2\",\n ad_group_name: \"Ad group two\",\n channel: \"marketplace\",\n brand_value_name: \"Brand two\",\n metrics: {\n direct_amount: 25,\n indirect_amount: 5,\n total_amount: 30,\n clicks: 14,\n impressions: 240,\n cost: 19.5,\n advertising_items_quantity: 1,\n currency_id: \"ARS\",\n },\n },\n ],\n },\n },\n ],\n failed: [],\n });\n\n expect(runMercadoLibreBatch).toHaveBeenCalledWith([\"50\"], expect.any(Function), {\n maxConcurrency: 3,\n });\n expect(getMock).toHaveBeenCalledWith(\n \"/advertising/MLA/advertisers/89015/product_ads/ads/search\",\n {\n params: {\n date_from: searchRequest.dateFrom,\n date_to: searchRequest.dateTo,\n metrics: searchRequest.metrics.join(\",\"),\n metrics_summary: searchRequest.metricsSummary,\n campaign_id: searchRequest.campaignId,\n ad_group_id: searchRequest.adGroupId,\n item_id: searchRequest.itemId,\n channel: searchRequest.channel,\n status: searchRequest.status,\n brand_value_name: searchRequest.brandValueName,\n limit: 50,\n offset: 50,\n },\n headers: { \"Api-Version\": \"v2\" },\n }\n );\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { MercadoLibreApiError, getMercadoLibreApiContext } from "../mercadolibre-api.js";
|
|
3
|
+
import { reconcileMercadoLibreReturnData } from "../mercadolibre-return-reconciliation.js";
|
|
4
|
+
import { meliReconcileReturnDataSchema } from "../../../tools/mercadolibre/reconcile-return-data.js";
|
|
5
|
+
const { getMock } = vi.hoisted(() => ({ getMock: vi.fn() }));
|
|
6
|
+
vi.mock("../mercadolibre-api.js", async () => {
|
|
7
|
+
const actual = await vi.importActual("../mercadolibre-api.js");
|
|
8
|
+
return { ...actual, getMercadoLibreApiContext: vi.fn() };
|
|
9
|
+
});
|
|
10
|
+
describe("reconcileMercadoLibreReturnData", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vi.clearAllMocks();
|
|
13
|
+
vi.mocked(getMercadoLibreApiContext).mockResolvedValue({ api: { get: getMock }, sellerId: "seller-1", accessToken: "token" });
|
|
14
|
+
});
|
|
15
|
+
it("keeps an order without a return concise and does not expose internal identifiers", async () => {
|
|
16
|
+
getMock.mockImplementation(async (path) => {
|
|
17
|
+
if (path === "/post-purchase/v1/claims/search") return { data: { paging: { total: 0, limit: 50, offset: 0 }, data: [] } };
|
|
18
|
+
if (path === "/orders/order-1") return { data: { id: "order-1", seller: { id: "seller-1" } } };
|
|
19
|
+
throw new Error(`unexpected ${path}`);
|
|
20
|
+
});
|
|
21
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["order-1"]);
|
|
22
|
+
expect(result.results[0]).toMatchObject({
|
|
23
|
+
sale_reference: "order-1",
|
|
24
|
+
resolution: { kind: "order", status: "resolved" },
|
|
25
|
+
return_context: { return_status: "without_return" }
|
|
26
|
+
});
|
|
27
|
+
expect(result.summary).toMatchObject({ requested: 1, without_return: 1, with_return: 0, orders: 1 });
|
|
28
|
+
expect(result.results[0]).not.toHaveProperty("internal_order_id");
|
|
29
|
+
});
|
|
30
|
+
it("projects direct return evidence without localized Excel meanings or claim IDs", async () => {
|
|
31
|
+
const shipmentRequests = [];
|
|
32
|
+
getMock.mockImplementation(async (path, config) => {
|
|
33
|
+
if (path === "/post-purchase/v1/claims/search") return { data: { paging: { total: config?.params?.type === "returns" ? 1 : 0, limit: 50, offset: 0 }, data: config?.params?.type === "returns" ? [{ id: "claim-secret", resource_id: "order-1", type: "returns" }] : [] } };
|
|
34
|
+
if (path === "/orders/order-1") return { data: { id: "order-1", seller: { id: "seller-1" }, shipping: { id: "shipment-1" } } };
|
|
35
|
+
if (path === "/post-purchase/v1/claims/claim-secret") return { data: { id: "claim-secret", type: "returns", status: "closed", stage: "closed", reason_id: "accepted", benefited: "buyer" } };
|
|
36
|
+
if (path === "/post-purchase/v1/claims/reasons/accepted") return { data: { id: "accepted", description: "The return was accepted." } };
|
|
37
|
+
if (path === "/post-purchase/v1/claims/claim-secret/actions-history") return { data: { actions: [{ action: "close_claim", occurred_at: "2026-06-20T13:05:00Z", id: "action-secret" }] } };
|
|
38
|
+
if (path === "/post-purchase/v2/claims/claim-secret/returns") return { data: { id: "return-secret", related_entities: [{ type: "order" }], shipments: [{ shipment_id: "shipment-1" }, { shipment_id: "shipment-2" }, { shipment_id: "shipment-1" }], status: "delivered", money_status: "refunded", quantities: { returned: 1 } } };
|
|
39
|
+
if (path === "/post-purchase/v1/returns/return-secret/reviews") return { data: { reviews: [{ resource_reviews: { method: "triage", stage: "closed", status: "success", product_condition: "saleable", product_destination: "mystery_destination", benefited: "buyer" } }] } };
|
|
40
|
+
if (path === "/post-purchase/v1/claims/claim-secret/charges/return-cost") return { data: { amount: 1250.5, currency_id: "ARS" } };
|
|
41
|
+
if (path.startsWith("/shipments/")) {
|
|
42
|
+
shipmentRequests.push({ path, headers: config?.headers });
|
|
43
|
+
return { data: { status: "delivered", substatus: "delivered", tracking_number: `TRACKING-${path.endsWith("1") ? "1" : "2"}` } };
|
|
44
|
+
}
|
|
45
|
+
throw new Error(`unexpected ${path}`);
|
|
46
|
+
});
|
|
47
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["order-1"]);
|
|
48
|
+
const serialized = JSON.stringify(result);
|
|
49
|
+
expect(result.results[0].return_context).toMatchObject({
|
|
50
|
+
return_status: "with_return",
|
|
51
|
+
claim: { type: "returns", reason: { code: "accepted", description: "The return was accepted." } },
|
|
52
|
+
return: { status: "delivered", money_status: "refunded" },
|
|
53
|
+
review: { method: "triage", product_condition: { code: "saleable" }, product_destination: { code: "mystery_destination", meaning: "Unknown Mercado Libre code: mystery_destination", meaning_unavailable: true } },
|
|
54
|
+
shipments: [
|
|
55
|
+
{ status: "delivered", tracking_number: "TRACKING-1" },
|
|
56
|
+
{ status: "delivered", tracking_number: "TRACKING-2" }
|
|
57
|
+
],
|
|
58
|
+
return_cost: { amount: 1250.5, currency: "ARS" }
|
|
59
|
+
});
|
|
60
|
+
expect(result.summary).toMatchObject({ with_return: 1, without_return: 0 });
|
|
61
|
+
expect(shipmentRequests).toEqual([
|
|
62
|
+
{ path: "/shipments/shipment-1", headers: { "x-format-new": "true" } },
|
|
63
|
+
{ path: "/shipments/shipment-2", headers: { "x-format-new": "true" } }
|
|
64
|
+
]);
|
|
65
|
+
expect(serialized).not.toContain("claim-secret");
|
|
66
|
+
expect(serialized).not.toContain("return-secret");
|
|
67
|
+
expect(serialized).not.toContain("action-secret");
|
|
68
|
+
});
|
|
69
|
+
it("keeps pack member records opaque and preserves duplicate input ordering", async () => {
|
|
70
|
+
getMock.mockImplementation(async (path) => {
|
|
71
|
+
if (path === "/post-purchase/v1/claims/search") return { data: { paging: { total: 0, limit: 50, offset: 0 }, data: [] } };
|
|
72
|
+
if (path === "/orders/pack-1") throw new MercadoLibreApiError("not found", 404);
|
|
73
|
+
if (path === "/packs/pack-1") return { data: { orders: [{ id: "member-1" }, { id: "member-2" }] } };
|
|
74
|
+
if (path === "/orders/member-1") return { data: { id: "member-1", seller: { id: "seller-1" } } };
|
|
75
|
+
if (path === "/orders/member-2") return { data: { id: "member-2", seller: { id: "foreign-seller" } } };
|
|
76
|
+
throw new Error(`unexpected ${path}`);
|
|
77
|
+
});
|
|
78
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["pack-1", "pack-1"]);
|
|
79
|
+
expect(result.results.map((item) => item.sale_reference)).toEqual(["pack-1", "pack-1"]);
|
|
80
|
+
expect(result.results[0].members).toEqual(expect.arrayContaining([
|
|
81
|
+
expect.objectContaining({ member_index: 0, return_context: { return_status: "without_return" } }),
|
|
82
|
+
expect.objectContaining({ member_index: 1, issues: [expect.objectContaining({ code: "seller_ownership_mismatch" })] })
|
|
83
|
+
]));
|
|
84
|
+
expect(JSON.stringify(result)).not.toContain("member-1");
|
|
85
|
+
expect(JSON.stringify(result)).not.toContain("foreign-seller");
|
|
86
|
+
expect(result.summary).toMatchObject({ requested: 2, packs: 2, partial: 2 });
|
|
87
|
+
});
|
|
88
|
+
it("preserves original indexes when a pack starts with a foreign member and repeats an owned order", async () => {
|
|
89
|
+
getMock.mockImplementation(async (path) => {
|
|
90
|
+
if (path === "/post-purchase/v1/claims/search") return { data: { paging: { total: 0, limit: 50, offset: 0 }, data: [] } };
|
|
91
|
+
if (path === "/orders/pack-indexes") throw new MercadoLibreApiError("not found", 404);
|
|
92
|
+
if (path === "/packs/pack-indexes") return { data: { orders: [{ id: "foreign-order" }, { id: "owned-order" }, { id: "owned-order" }] } };
|
|
93
|
+
if (path === "/orders/foreign-order") return { data: { seller: { id: "foreign-seller" } } };
|
|
94
|
+
if (path === "/orders/owned-order") return { data: { seller: { id: "seller-1" } } };
|
|
95
|
+
throw new Error(`unexpected ${path}`);
|
|
96
|
+
});
|
|
97
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["pack-indexes"]);
|
|
98
|
+
expect(result.results[0].members).toEqual([
|
|
99
|
+
expect.objectContaining({ member_index: 0, issues: [expect.objectContaining({ code: "seller_ownership_mismatch" })] }),
|
|
100
|
+
expect.objectContaining({ member_index: 1, return_context: { return_status: "without_return" } }),
|
|
101
|
+
expect.objectContaining({ member_index: 2, return_context: { return_status: "without_return" } })
|
|
102
|
+
]);
|
|
103
|
+
});
|
|
104
|
+
it("does not report with_return when claim detail enrichment fails", async () => {
|
|
105
|
+
getMock.mockImplementation(async (path, config) => {
|
|
106
|
+
if (path === "/post-purchase/v1/claims/search") return { data: { paging: { total: config?.params?.type === "returns" ? 1 : 0, limit: 50, offset: 0 }, data: config?.params?.type === "returns" ? [{ id: "claim-detail-failure", resource_id: "order-detail-failure", type: "returns" }] : [] } };
|
|
107
|
+
if (path === "/orders/order-detail-failure") return { data: { seller: { id: "seller-1" } } };
|
|
108
|
+
if (path === "/post-purchase/v1/claims/claim-detail-failure") throw new MercadoLibreApiError("upstream", 503);
|
|
109
|
+
throw new Error(`unexpected ${path}`);
|
|
110
|
+
});
|
|
111
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["order-detail-failure"]);
|
|
112
|
+
expect(result.results[0]).toMatchObject({ return_context: { return_status: "claim_found" }, resolution: { status: "partial" } });
|
|
113
|
+
expect(result.summary.with_return).toBe(0);
|
|
114
|
+
expect(result.results[0].issues).toContainEqual(expect.objectContaining({ code: "claim_data_unavailable" }));
|
|
115
|
+
});
|
|
116
|
+
it("does not report with_return when return detail enrichment fails", async () => {
|
|
117
|
+
getMock.mockImplementation(async (path, config) => {
|
|
118
|
+
if (path === "/post-purchase/v1/claims/search") return { data: { paging: { total: config?.params?.type === "returns" ? 1 : 0, limit: 50, offset: 0 }, data: config?.params?.type === "returns" ? [{ id: "claim-return-failure", resource_id: "order-return-failure", type: "returns" }] : [] } };
|
|
119
|
+
if (path === "/orders/order-return-failure") return { data: { seller: { id: "seller-1" } } };
|
|
120
|
+
if (path === "/post-purchase/v1/claims/claim-return-failure") return { data: { id: "claim-return-failure", type: "returns", status: "closed" } };
|
|
121
|
+
if (path === "/post-purchase/v2/claims/claim-return-failure/returns") throw new MercadoLibreApiError("upstream", 503);
|
|
122
|
+
if (path.includes("/actions-history") || path.includes("/reviews") || path.includes("/return-cost")) throw new MercadoLibreApiError("not found", 404);
|
|
123
|
+
throw new Error(`unexpected ${path}`);
|
|
124
|
+
});
|
|
125
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["order-return-failure"], { sleep: async () => void 0 });
|
|
126
|
+
expect(result.results[0]).toMatchObject({ return_context: { return_status: "return_unverified" }, resolution: { status: "partial" } });
|
|
127
|
+
expect(result.summary.with_return).toBe(0);
|
|
128
|
+
expect(result.results[0].issues).toContainEqual(expect.objectContaining({ code: "return_data_unavailable" }));
|
|
129
|
+
});
|
|
130
|
+
it("keeps verified returns but exposes review, shipment and cost enrichment failures separately", async () => {
|
|
131
|
+
getMock.mockImplementation(async (path, config) => {
|
|
132
|
+
if (path === "/post-purchase/v1/claims/search") return { data: { paging: { total: config?.params?.type === "returns" ? 3 : 0, limit: 50, offset: 0 }, data: config?.params?.type === "returns" ? [1, 2, 3].map((n) => ({ id: `claim-${n}`, resource_id: `order-${n}`, type: "returns" })) : [] } };
|
|
133
|
+
const orderMatch = path.match(/^\/orders\/(order-[1-3])$/);
|
|
134
|
+
if (orderMatch) return { data: { seller: { id: "seller-1" } } };
|
|
135
|
+
const claimMatch = path.match(/\/claims\/(claim-[1-3])$/);
|
|
136
|
+
if (claimMatch) return { data: { id: claimMatch[1], type: "returns", status: "closed" } };
|
|
137
|
+
const returnMatch = path.match(/\/claims\/(claim-[1-3])\/returns$/);
|
|
138
|
+
if (returnMatch) return { data: { id: `return-${returnMatch[1]}`, related_entities: [], shipments: [{ shipment_id: `shipment-${returnMatch[1]}` }], status: "delivered" } };
|
|
139
|
+
if (path.endsWith("/reviews") || path.endsWith("/return-cost") || path.startsWith("/shipments/")) throw new MercadoLibreApiError("upstream", 503);
|
|
140
|
+
throw new Error(`unexpected ${path}`);
|
|
141
|
+
});
|
|
142
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["order-1", "order-2", "order-3"], { sleep: async () => void 0 });
|
|
143
|
+
expect(result.summary).toMatchObject({ with_return: 3, partial: 3 });
|
|
144
|
+
expect(result.results.flatMap((entry) => entry.issues ?? [])).toEqual(expect.arrayContaining([
|
|
145
|
+
expect.objectContaining({ code: "review_data_unavailable" }),
|
|
146
|
+
expect.objectContaining({ code: "shipment_data_unavailable" }),
|
|
147
|
+
expect.objectContaining({ code: "return_cost_unavailable" })
|
|
148
|
+
]));
|
|
149
|
+
expect(result.results.every((entry) => entry.return_context?.return_status === "with_return")).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
it("reports claim page failures and deadlines without upstream details", async () => {
|
|
152
|
+
getMock.mockRejectedValue(new MercadoLibreApiError("upstream secret payload", 503));
|
|
153
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["order-1"], { operationDeadlineMs: 1, now: () => 100 });
|
|
154
|
+
expect(result.results[0].issues).toEqual(expect.arrayContaining([expect.objectContaining({ code: "reference_unavailable" })]));
|
|
155
|
+
expect(JSON.stringify(result)).not.toContain("upstream secret payload");
|
|
156
|
+
expect(JSON.stringify(result)).not.toContain("503");
|
|
157
|
+
});
|
|
158
|
+
it("marks a failed claims page as partial while retaining the resolved order", async () => {
|
|
159
|
+
getMock.mockImplementation(async (path, config) => {
|
|
160
|
+
if (path === "/post-purchase/v1/claims/search" && config?.params?.type === "returns" && config.params.offset === 0) {
|
|
161
|
+
return { data: { paging: { total: 51, limit: 50, offset: 0 }, data: [] } };
|
|
162
|
+
}
|
|
163
|
+
if (path === "/post-purchase/v1/claims/search" && config?.params?.type === "mediations") {
|
|
164
|
+
return { data: { paging: { total: 0, limit: 50, offset: 0 }, data: [] } };
|
|
165
|
+
}
|
|
166
|
+
if (path === "/post-purchase/v1/claims/search") throw new MercadoLibreApiError("upstream response", 503);
|
|
167
|
+
if (path === "/orders/order-1") return { data: { seller: { id: "seller-1" } } };
|
|
168
|
+
throw new Error(`unexpected ${path}`);
|
|
169
|
+
});
|
|
170
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["order-1"], { sleep: async () => void 0 });
|
|
171
|
+
expect(result.results[0]).toMatchObject({ resolution: { kind: "order", status: "partial" } });
|
|
172
|
+
expect(result.results[0].issues).toContainEqual(expect.objectContaining({ code: "claims_page_partial" }));
|
|
173
|
+
});
|
|
174
|
+
it("reads the documented data shape for both claim types across pages and maps pack members", async () => {
|
|
175
|
+
const claimSearches = [];
|
|
176
|
+
getMock.mockImplementation(async (path, config) => {
|
|
177
|
+
if (path === "/post-purchase/v1/claims/search") {
|
|
178
|
+
claimSearches.push(config?.params ?? {});
|
|
179
|
+
const type = config?.params?.type;
|
|
180
|
+
const offset = config?.params?.offset ?? 0;
|
|
181
|
+
if (type === "returns" && offset === 0) {
|
|
182
|
+
return { data: { paging: { total: 101, limit: 50, offset: 0 }, data: [{ id: "return-claim", resource_id: "member-1", type: "returns" }] } };
|
|
183
|
+
}
|
|
184
|
+
if (type === "returns" && offset === 50) {
|
|
185
|
+
return { data: { paging: { total: 101, limit: 50, offset: 50 }, data: [{ id: "return-claim-2", resource: "other-member", type: "returns" }] } };
|
|
186
|
+
}
|
|
187
|
+
if (type === "returns" && offset === 100) {
|
|
188
|
+
return { data: { paging: { total: 101, limit: 50, offset: 100 }, data: [] } };
|
|
189
|
+
}
|
|
190
|
+
if (type === "mediations" && offset === 0) {
|
|
191
|
+
return { data: { paging: { total: 51, limit: 50, offset: 0 }, data: [{ id: "mediation-claim", resource_id: "member-2", type: "mediations" }] } };
|
|
192
|
+
}
|
|
193
|
+
if (type === "mediations" && offset === 50) {
|
|
194
|
+
return { data: { paging: { total: 51, limit: 50, offset: 50 }, data: [] } };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (path === "/orders/pack-1") throw new MercadoLibreApiError("not found", 404);
|
|
198
|
+
if (path === "/packs/pack-1") return { data: { orders: [{ id: "member-1" }, { id: "member-2" }] } };
|
|
199
|
+
if (path === "/orders/member-1" || path === "/orders/member-2") return { data: { seller: { id: "seller-1" } } };
|
|
200
|
+
if (path === "/post-purchase/v1/claims/return-claim") return { data: { id: "return-claim", type: "returns", status: "closed" } };
|
|
201
|
+
if (path === "/post-purchase/v1/claims/mediation-claim") return { data: { id: "mediation-claim", type: "mediations", status: "open" } };
|
|
202
|
+
if (path === "/post-purchase/v2/claims/return-claim/returns") return { data: { id: "return-secret", related_entities: [], shipments: [], status: "delivered" } };
|
|
203
|
+
if (path.endsWith("/returns")) throw new MercadoLibreApiError("not found", 404);
|
|
204
|
+
if (path.includes("/actions-history") || path.includes("/charges/return-cost") || path.includes("/reviews")) throw new MercadoLibreApiError("not found", 404);
|
|
205
|
+
throw new Error(`unexpected ${path}`);
|
|
206
|
+
});
|
|
207
|
+
const result = await reconcileMercadoLibreReturnData("triplim", ["pack-1"], { sleep: async () => void 0 });
|
|
208
|
+
expect(claimSearches).toEqual(expect.arrayContaining([
|
|
209
|
+
expect.objectContaining({ type: "returns", offset: 0 }),
|
|
210
|
+
expect.objectContaining({ type: "returns", offset: 50 }),
|
|
211
|
+
expect.objectContaining({ type: "returns", offset: 100 }),
|
|
212
|
+
expect.objectContaining({ type: "mediations", offset: 0 }),
|
|
213
|
+
expect.objectContaining({ type: "mediations", offset: 50 })
|
|
214
|
+
]));
|
|
215
|
+
expect(result.results[0].members).toEqual(expect.arrayContaining([
|
|
216
|
+
expect.objectContaining({ member_index: 0, return_context: expect.objectContaining({ return_status: "with_return" }) }),
|
|
217
|
+
expect.objectContaining({ member_index: 1, return_context: expect.objectContaining({ return_status: "mediation" }) })
|
|
218
|
+
]));
|
|
219
|
+
expect(result.summary).toMatchObject({ requested: 1, packs: 1, with_return: 1, mediations: 1, without_return: 0 });
|
|
220
|
+
});
|
|
221
|
+
it("limits the public schema to five sale references", () => {
|
|
222
|
+
const valid = meliReconcileReturnDataSchema.safeParse({ profileId: "triplim", saleReferences: ["1", "2", "3", "4", "5"] });
|
|
223
|
+
const invalid = meliReconcileReturnDataSchema.safeParse({ profileId: "triplim", saleReferences: ["1", "2", "3", "4", "5", "6"] });
|
|
224
|
+
expect(valid.success).toBe(true);
|
|
225
|
+
expect(invalid.success).toBe(false);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
//# sourceMappingURL=mercadolibre-return-reconciliation.test.js.map
|
package/dist/src/services/mercadolibre/__tests__/mercadolibre-return-reconciliation.test.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/services/mercadolibre/__tests__/mercadolibre-return-reconciliation.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { MercadoLibreApiError, getMercadoLibreApiContext } from \"../mercadolibre-api.js\";\nimport { reconcileMercadoLibreReturnData } from \"../mercadolibre-return-reconciliation.js\";\nimport { meliReconcileReturnDataSchema } from \"../../../tools/mercadolibre/reconcile-return-data.js\";\n\nconst { getMock } = vi.hoisted(() => ({ getMock: vi.fn() }));\n\nvi.mock(\"../mercadolibre-api.js\", async () => {\n const actual = await vi.importActual<typeof import(\"../mercadolibre-api.js\")>(\"../mercadolibre-api.js\");\n return { ...actual, getMercadoLibreApiContext: vi.fn() };\n});\n\ndescribe(\"reconcileMercadoLibreReturnData\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n vi.mocked(getMercadoLibreApiContext).mockResolvedValue({ api: { get: getMock }, sellerId: \"seller-1\", accessToken: \"token\" } as never);\n });\n\n it(\"keeps an order without a return concise and does not expose internal identifiers\", async () => {\n getMock.mockImplementation(async (path: string) => {\n if (path === \"/post-purchase/v1/claims/search\") return { data: { paging: { total: 0, limit: 50, offset: 0 }, data: [] } };\n if (path === \"/orders/order-1\") return { data: { id: \"order-1\", seller: { id: \"seller-1\" } } };\n throw new Error(`unexpected ${path}`);\n });\n\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"order-1\"]);\n\n expect(result.results[0]).toMatchObject({\n sale_reference: \"order-1\",\n resolution: { kind: \"order\", status: \"resolved\" },\n return_context: { return_status: \"without_return\" },\n });\n expect(result.summary).toMatchObject({ requested: 1, without_return: 1, with_return: 0, orders: 1 });\n expect(result.results[0]).not.toHaveProperty(\"internal_order_id\");\n });\n\n it(\"projects direct return evidence without localized Excel meanings or claim IDs\", async () => {\n const shipmentRequests: Array<{ path: string; headers?: Record<string, string> }> = [];\n getMock.mockImplementation(async (path: string, config?: { headers?: Record<string, string>; params?: { type?: string } }) => {\n if (path === \"/post-purchase/v1/claims/search\") return { data: { paging: { total: config?.params?.type === \"returns\" ? 1 : 0, limit: 50, offset: 0 }, data: config?.params?.type === \"returns\" ? [{ id: \"claim-secret\", resource_id: \"order-1\", type: \"returns\" }] : [] } };\n if (path === \"/orders/order-1\") return { data: { id: \"order-1\", seller: { id: \"seller-1\" }, shipping: { id: \"shipment-1\" } } };\n if (path === \"/post-purchase/v1/claims/claim-secret\") return { data: { id: \"claim-secret\", type: \"returns\", status: \"closed\", stage: \"closed\", reason_id: \"accepted\", benefited: \"buyer\" } };\n if (path === \"/post-purchase/v1/claims/reasons/accepted\") return { data: { id: \"accepted\", description: \"The return was accepted.\" } };\n if (path === \"/post-purchase/v1/claims/claim-secret/actions-history\") return { data: { actions: [{ action: \"close_claim\", occurred_at: \"2026-06-20T13:05:00Z\", id: \"action-secret\" }] } };\n if (path === \"/post-purchase/v2/claims/claim-secret/returns\") return { data: { id: \"return-secret\", related_entities: [{ type: \"order\" }], shipments: [{ shipment_id: \"shipment-1\" }, { shipment_id: \"shipment-2\" }, { shipment_id: \"shipment-1\" }], status: \"delivered\", money_status: \"refunded\", quantities: { returned: 1 } } };\n if (path === \"/post-purchase/v1/returns/return-secret/reviews\") return { data: { reviews: [{ resource_reviews: { method: \"triage\", stage: \"closed\", status: \"success\", product_condition: \"saleable\", product_destination: \"mystery_destination\", benefited: \"buyer\" } }] } };\n if (path === \"/post-purchase/v1/claims/claim-secret/charges/return-cost\") return { data: { amount: 1250.5, currency_id: \"ARS\" } };\n if (path.startsWith(\"/shipments/\")) {\n shipmentRequests.push({ path, headers: config?.headers });\n return { data: { status: \"delivered\", substatus: \"delivered\", tracking_number: `TRACKING-${path.endsWith(\"1\") ? \"1\" : \"2\"}` } };\n }\n throw new Error(`unexpected ${path}`);\n });\n\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"order-1\"]);\n const serialized = JSON.stringify(result);\n\n expect(result.results[0].return_context).toMatchObject({\n return_status: \"with_return\",\n claim: { type: \"returns\", reason: { code: \"accepted\", description: \"The return was accepted.\" } },\n return: { status: \"delivered\", money_status: \"refunded\" },\n review: { method: \"triage\", product_condition: { code: \"saleable\" }, product_destination: { code: \"mystery_destination\", meaning: \"Unknown Mercado Libre code: mystery_destination\", meaning_unavailable: true } },\n shipments: [\n { status: \"delivered\", tracking_number: \"TRACKING-1\" },\n { status: \"delivered\", tracking_number: \"TRACKING-2\" },\n ],\n return_cost: { amount: 1250.5, currency: \"ARS\" },\n });\n expect(result.summary).toMatchObject({ with_return: 1, without_return: 0 });\n expect(shipmentRequests).toEqual([\n { path: \"/shipments/shipment-1\", headers: { \"x-format-new\": \"true\" } },\n { path: \"/shipments/shipment-2\", headers: { \"x-format-new\": \"true\" } },\n ]);\n expect(serialized).not.toContain(\"claim-secret\");\n expect(serialized).not.toContain(\"return-secret\");\n expect(serialized).not.toContain(\"action-secret\");\n });\n\n it(\"keeps pack member records opaque and preserves duplicate input ordering\", async () => {\n getMock.mockImplementation(async (path: string) => {\n if (path === \"/post-purchase/v1/claims/search\") return { data: { paging: { total: 0, limit: 50, offset: 0 }, data: [] } };\n if (path === \"/orders/pack-1\") throw new MercadoLibreApiError(\"not found\", 404);\n if (path === \"/packs/pack-1\") return { data: { orders: [{ id: \"member-1\" }, { id: \"member-2\" }] } };\n if (path === \"/orders/member-1\") return { data: { id: \"member-1\", seller: { id: \"seller-1\" } } };\n if (path === \"/orders/member-2\") return { data: { id: \"member-2\", seller: { id: \"foreign-seller\" } } };\n throw new Error(`unexpected ${path}`);\n });\n\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"pack-1\", \"pack-1\"]);\n\n expect(result.results.map((item) => item.sale_reference)).toEqual([\"pack-1\", \"pack-1\"]);\n expect(result.results[0].members).toEqual(expect.arrayContaining([\n expect.objectContaining({ member_index: 0, return_context: { return_status: \"without_return\" } }),\n expect.objectContaining({ member_index: 1, issues: [expect.objectContaining({ code: \"seller_ownership_mismatch\" })] }),\n ]));\n expect(JSON.stringify(result)).not.toContain(\"member-1\");\n expect(JSON.stringify(result)).not.toContain(\"foreign-seller\");\n expect(result.summary).toMatchObject({ requested: 2, packs: 2, partial: 2 });\n });\n\n it(\"preserves original indexes when a pack starts with a foreign member and repeats an owned order\", async () => {\n getMock.mockImplementation(async (path: string) => {\n if (path === \"/post-purchase/v1/claims/search\") return { data: { paging: { total: 0, limit: 50, offset: 0 }, data: [] } };\n if (path === \"/orders/pack-indexes\") throw new MercadoLibreApiError(\"not found\", 404);\n if (path === \"/packs/pack-indexes\") return { data: { orders: [{ id: \"foreign-order\" }, { id: \"owned-order\" }, { id: \"owned-order\" }] } };\n if (path === \"/orders/foreign-order\") return { data: { seller: { id: \"foreign-seller\" } } };\n if (path === \"/orders/owned-order\") return { data: { seller: { id: \"seller-1\" } } };\n throw new Error(`unexpected ${path}`);\n });\n\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"pack-indexes\"]);\n expect(result.results[0].members).toEqual([\n expect.objectContaining({ member_index: 0, issues: [expect.objectContaining({ code: \"seller_ownership_mismatch\" })] }),\n expect.objectContaining({ member_index: 1, return_context: { return_status: \"without_return\" } }),\n expect.objectContaining({ member_index: 2, return_context: { return_status: \"without_return\" } }),\n ]);\n });\n\n it(\"does not report with_return when claim detail enrichment fails\", async () => {\n getMock.mockImplementation(async (path: string, config?: { params?: { type?: string } }) => {\n if (path === \"/post-purchase/v1/claims/search\") return { data: { paging: { total: config?.params?.type === \"returns\" ? 1 : 0, limit: 50, offset: 0 }, data: config?.params?.type === \"returns\" ? [{ id: \"claim-detail-failure\", resource_id: \"order-detail-failure\", type: \"returns\" }] : [] } };\n if (path === \"/orders/order-detail-failure\") return { data: { seller: { id: \"seller-1\" } } };\n if (path === \"/post-purchase/v1/claims/claim-detail-failure\") throw new MercadoLibreApiError(\"upstream\", 503);\n throw new Error(`unexpected ${path}`);\n });\n\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"order-detail-failure\"]);\n expect(result.results[0]).toMatchObject({ return_context: { return_status: \"claim_found\" }, resolution: { status: \"partial\" } });\n expect(result.summary.with_return).toBe(0);\n expect(result.results[0].issues).toContainEqual(expect.objectContaining({ code: \"claim_data_unavailable\" }));\n });\n\n it(\"does not report with_return when return detail enrichment fails\", async () => {\n getMock.mockImplementation(async (path: string, config?: { params?: { type?: string } }) => {\n if (path === \"/post-purchase/v1/claims/search\") return { data: { paging: { total: config?.params?.type === \"returns\" ? 1 : 0, limit: 50, offset: 0 }, data: config?.params?.type === \"returns\" ? [{ id: \"claim-return-failure\", resource_id: \"order-return-failure\", type: \"returns\" }] : [] } };\n if (path === \"/orders/order-return-failure\") return { data: { seller: { id: \"seller-1\" } } };\n if (path === \"/post-purchase/v1/claims/claim-return-failure\") return { data: { id: \"claim-return-failure\", type: \"returns\", status: \"closed\" } };\n if (path === \"/post-purchase/v2/claims/claim-return-failure/returns\") throw new MercadoLibreApiError(\"upstream\", 503);\n if (path.includes(\"/actions-history\") || path.includes(\"/reviews\") || path.includes(\"/return-cost\")) throw new MercadoLibreApiError(\"not found\", 404);\n throw new Error(`unexpected ${path}`);\n });\n\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"order-return-failure\"], { sleep: async () => undefined });\n expect(result.results[0]).toMatchObject({ return_context: { return_status: \"return_unverified\" }, resolution: { status: \"partial\" } });\n expect(result.summary.with_return).toBe(0);\n expect(result.results[0].issues).toContainEqual(expect.objectContaining({ code: \"return_data_unavailable\" }));\n });\n\n it(\"keeps verified returns but exposes review, shipment and cost enrichment failures separately\", async () => {\n getMock.mockImplementation(async (path: string, config?: { params?: { type?: string } }) => {\n if (path === \"/post-purchase/v1/claims/search\") return { data: { paging: { total: config?.params?.type === \"returns\" ? 3 : 0, limit: 50, offset: 0 }, data: config?.params?.type === \"returns\" ? [1, 2, 3].map((n) => ({ id: `claim-${n}`, resource_id: `order-${n}`, type: \"returns\" })) : [] } };\n const orderMatch = path.match(/^\\/orders\\/(order-[1-3])$/);\n if (orderMatch) return { data: { seller: { id: \"seller-1\" } } };\n const claimMatch = path.match(/\\/claims\\/(claim-[1-3])$/);\n if (claimMatch) return { data: { id: claimMatch[1], type: \"returns\", status: \"closed\" } };\n const returnMatch = path.match(/\\/claims\\/(claim-[1-3])\\/returns$/);\n if (returnMatch) return { data: { id: `return-${returnMatch[1]}`, related_entities: [], shipments: [{ shipment_id: `shipment-${returnMatch[1]}` }], status: \"delivered\" } };\n if (path.endsWith(\"/reviews\") || path.endsWith(\"/return-cost\") || path.startsWith(\"/shipments/\")) throw new MercadoLibreApiError(\"upstream\", 503);\n throw new Error(`unexpected ${path}`);\n });\n\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"order-1\", \"order-2\", \"order-3\"], { sleep: async () => undefined });\n expect(result.summary).toMatchObject({ with_return: 3, partial: 3 });\n expect(result.results.flatMap((entry) => entry.issues ?? [])).toEqual(expect.arrayContaining([\n expect.objectContaining({ code: \"review_data_unavailable\" }),\n expect.objectContaining({ code: \"shipment_data_unavailable\" }),\n expect.objectContaining({ code: \"return_cost_unavailable\" }),\n ]));\n expect(result.results.every((entry) => entry.return_context?.return_status === \"with_return\")).toBe(true);\n });\n\n it(\"reports claim page failures and deadlines without upstream details\", async () => {\n getMock.mockRejectedValue(new MercadoLibreApiError(\"upstream secret payload\", 503));\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"order-1\"], { operationDeadlineMs: 1, now: () => 100 });\n\n expect(result.results[0].issues).toEqual(expect.arrayContaining([expect.objectContaining({ code: \"reference_unavailable\" })]));\n expect(JSON.stringify(result)).not.toContain(\"upstream secret payload\");\n expect(JSON.stringify(result)).not.toContain(\"503\");\n });\n\n it(\"marks a failed claims page as partial while retaining the resolved order\", async () => {\n getMock.mockImplementation(async (path: string, config?: { params?: { type?: string; offset?: number } }) => {\n if (path === \"/post-purchase/v1/claims/search\" && config?.params?.type === \"returns\" && config.params.offset === 0) {\n return { data: { paging: { total: 51, limit: 50, offset: 0 }, data: [] } };\n }\n if (path === \"/post-purchase/v1/claims/search\" && config?.params?.type === \"mediations\") {\n return { data: { paging: { total: 0, limit: 50, offset: 0 }, data: [] } };\n }\n if (path === \"/post-purchase/v1/claims/search\") throw new MercadoLibreApiError(\"upstream response\", 503);\n if (path === \"/orders/order-1\") return { data: { seller: { id: \"seller-1\" } } };\n throw new Error(`unexpected ${path}`);\n });\n\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"order-1\"], { sleep: async () => undefined });\n\n expect(result.results[0]).toMatchObject({ resolution: { kind: \"order\", status: \"partial\" } });\n expect(result.results[0].issues).toContainEqual(expect.objectContaining({ code: \"claims_page_partial\" }));\n });\n\n it(\"reads the documented data shape for both claim types across pages and maps pack members\", async () => {\n const claimSearches: Array<{ type?: string; offset?: number }> = [];\n getMock.mockImplementation(async (path: string, config?: { params?: { type?: string; offset?: number } }) => {\n if (path === \"/post-purchase/v1/claims/search\") {\n claimSearches.push(config?.params ?? {});\n const type = config?.params?.type;\n const offset = config?.params?.offset ?? 0;\n if (type === \"returns\" && offset === 0) {\n return { data: { paging: { total: 101, limit: 50, offset: 0 }, data: [{ id: \"return-claim\", resource_id: \"member-1\", type: \"returns\" }] } };\n }\n if (type === \"returns\" && offset === 50) {\n return { data: { paging: { total: 101, limit: 50, offset: 50 }, data: [{ id: \"return-claim-2\", resource: \"other-member\", type: \"returns\" }] } };\n }\n if (type === \"returns\" && offset === 100) {\n return { data: { paging: { total: 101, limit: 50, offset: 100 }, data: [] } };\n }\n if (type === \"mediations\" && offset === 0) {\n return { data: { paging: { total: 51, limit: 50, offset: 0 }, data: [{ id: \"mediation-claim\", resource_id: \"member-2\", type: \"mediations\" }] } };\n }\n if (type === \"mediations\" && offset === 50) {\n return { data: { paging: { total: 51, limit: 50, offset: 50 }, data: [] } };\n }\n }\n if (path === \"/orders/pack-1\") throw new MercadoLibreApiError(\"not found\", 404);\n if (path === \"/packs/pack-1\") return { data: { orders: [{ id: \"member-1\" }, { id: \"member-2\" }] } };\n if (path === \"/orders/member-1\" || path === \"/orders/member-2\") return { data: { seller: { id: \"seller-1\" } } };\n if (path === \"/post-purchase/v1/claims/return-claim\") return { data: { id: \"return-claim\", type: \"returns\", status: \"closed\" } };\n if (path === \"/post-purchase/v1/claims/mediation-claim\") return { data: { id: \"mediation-claim\", type: \"mediations\", status: \"open\" } };\n if (path === \"/post-purchase/v2/claims/return-claim/returns\") return { data: { id: \"return-secret\", related_entities: [], shipments: [], status: \"delivered\" } };\n if (path.endsWith(\"/returns\")) throw new MercadoLibreApiError(\"not found\", 404);\n if (path.includes(\"/actions-history\") || path.includes(\"/charges/return-cost\") || path.includes(\"/reviews\")) throw new MercadoLibreApiError(\"not found\", 404);\n throw new Error(`unexpected ${path}`);\n });\n\n const result = await reconcileMercadoLibreReturnData(\"triplim\", [\"pack-1\"], { sleep: async () => undefined });\n\n expect(claimSearches).toEqual(expect.arrayContaining([\n expect.objectContaining({ type: \"returns\", offset: 0 }),\n expect.objectContaining({ type: \"returns\", offset: 50 }),\n expect.objectContaining({ type: \"returns\", offset: 100 }),\n expect.objectContaining({ type: \"mediations\", offset: 0 }),\n expect.objectContaining({ type: \"mediations\", offset: 50 }),\n ]));\n expect(result.results[0].members).toEqual(expect.arrayContaining([\n expect.objectContaining({ member_index: 0, return_context: expect.objectContaining({ return_status: \"with_return\" }) }),\n expect.objectContaining({ member_index: 1, return_context: expect.objectContaining({ return_status: \"mediation\" }) }),\n ]));\n expect(result.summary).toMatchObject({ requested: 1, packs: 1, with_return: 1, mediations: 1, without_return: 0 });\n });\n\n it(\"limits the public schema to five sale references\", () => {\n const valid = meliReconcileReturnDataSchema.safeParse({ profileId: \"triplim\", saleReferences: [\"1\", \"2\", \"3\", \"4\", \"5\"] });\n const invalid = meliReconcileReturnDataSchema.safeParse({ profileId: \"triplim\", saleReferences: [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"] });\n expect(valid.success).toBe(true);\n expect(invalid.success).toBe(false);\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { getMercadoLibreApiContext } from "../mercadolibre-api.js";
|
|
3
|
+
import { getMercadoLibreDeclaredDimensionsShippingQuote } from "../mercadolibre-shipping.js";
|
|
4
|
+
vi.mock("../mercadolibre-api.js", () => ({
|
|
5
|
+
getMercadoLibreApiContext: vi.fn()
|
|
6
|
+
}));
|
|
7
|
+
describe("mercadolibre-shipping service", () => {
|
|
8
|
+
const getMock = vi.fn();
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
vi.clearAllMocks();
|
|
11
|
+
vi.mocked(getMercadoLibreApiContext).mockResolvedValue({
|
|
12
|
+
api: { get: getMock },
|
|
13
|
+
sellerId: "seller-123",
|
|
14
|
+
accessToken: "token"
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
it("queries the official seller shipping quote endpoint with declared dimensions", async () => {
|
|
18
|
+
getMock.mockResolvedValueOnce({ data: { coverage: { all_country: { list_cost: 8106.49 } } } });
|
|
19
|
+
const result = await getMercadoLibreDeclaredDimensionsShippingQuote("profile-1", {
|
|
20
|
+
dimensions: "9x17x22,462",
|
|
21
|
+
verbose: true,
|
|
22
|
+
itemPrice: 300,
|
|
23
|
+
listingTypeId: "gold_pro",
|
|
24
|
+
mode: "me2",
|
|
25
|
+
condition: "new",
|
|
26
|
+
logisticType: "drop_off",
|
|
27
|
+
freeShipping: true,
|
|
28
|
+
currencyId: "ARS",
|
|
29
|
+
categoryId: "MLA123",
|
|
30
|
+
tags: ["foo", "bar"]
|
|
31
|
+
});
|
|
32
|
+
expect(getMock).toHaveBeenCalledWith("/users/seller-123/shipping_options/free", {
|
|
33
|
+
params: {
|
|
34
|
+
dimensions: "9x17x22,462",
|
|
35
|
+
verbose: true,
|
|
36
|
+
item_id: void 0,
|
|
37
|
+
item_price: 300,
|
|
38
|
+
listing_type_id: "gold_pro",
|
|
39
|
+
mode: "me2",
|
|
40
|
+
condition: "new",
|
|
41
|
+
logistic_type: "drop_off",
|
|
42
|
+
free_shipping: true,
|
|
43
|
+
currency_id: "ARS",
|
|
44
|
+
category_id: "MLA123",
|
|
45
|
+
variation_id: void 0,
|
|
46
|
+
seller_status: void 0,
|
|
47
|
+
seller_type: void 0,
|
|
48
|
+
reputation: void 0,
|
|
49
|
+
tags: "foo,bar",
|
|
50
|
+
zip_code: void 0,
|
|
51
|
+
city_to: void 0,
|
|
52
|
+
state_id: void 0
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
expect(result).toEqual({ coverage: { all_country: { list_cost: 8106.49 } } });
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=mercadolibre-shipping.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/services/mercadolibre/__tests__/mercadolibre-shipping.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { getMercadoLibreApiContext } from \"../mercadolibre-api.js\";\nimport { getMercadoLibreDeclaredDimensionsShippingQuote } from \"../mercadolibre-shipping.js\";\n\nvi.mock(\"../mercadolibre-api.js\", () => ({\n getMercadoLibreApiContext: vi.fn(),\n}));\n\ndescribe(\"mercadolibre-shipping service\", () => {\n const getMock = vi.fn();\n\n beforeEach(() => {\n vi.clearAllMocks();\n vi.mocked(getMercadoLibreApiContext).mockResolvedValue({\n api: { get: getMock },\n sellerId: \"seller-123\",\n accessToken: \"token\",\n } as never);\n });\n\n it(\"queries the official seller shipping quote endpoint with declared dimensions\", async () => {\n getMock.mockResolvedValueOnce({ data: { coverage: { all_country: { list_cost: 8106.49 } } } });\n\n const result = await getMercadoLibreDeclaredDimensionsShippingQuote(\"profile-1\", {\n dimensions: \"9x17x22,462\",\n verbose: true,\n itemPrice: 300,\n listingTypeId: \"gold_pro\",\n mode: \"me2\",\n condition: \"new\",\n logisticType: \"drop_off\",\n freeShipping: true,\n currencyId: \"ARS\",\n categoryId: \"MLA123\",\n tags: [\"foo\", \"bar\"],\n });\n\n expect(getMock).toHaveBeenCalledWith(\"/users/seller-123/shipping_options/free\", {\n params: {\n dimensions: \"9x17x22,462\",\n verbose: true,\n item_id: undefined,\n item_price: 300,\n listing_type_id: \"gold_pro\",\n mode: \"me2\",\n condition: \"new\",\n logistic_type: \"drop_off\",\n free_shipping: true,\n currency_id: \"ARS\",\n category_id: \"MLA123\",\n variation_id: undefined,\n seller_status: undefined,\n seller_type: undefined,\n reputation: undefined,\n tags: \"foo,bar\",\n zip_code: undefined,\n city_to: undefined,\n state_id: undefined,\n },\n });\n expect(result).toEqual({ coverage: { all_country: { list_cost: 8106.49 } } });\n });\n});\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY,UAAU,QAAQ,IAAI,UAAU;AAErD,SAAS,iCAAiC;AAC1C,SAAS,sDAAsD;AAE/D,GAAG,KAAK,0BAA0B,OAAO;AAAA,EACvC,2BAA2B,GAAG,GAAG;AACnC,EAAE;AAEF,SAAS,iCAAiC,MAAM;AAC9C,QAAM,UAAU,GAAG,GAAG;AAEtB,aAAW,MAAM;AACf,OAAG,cAAc;AACjB,OAAG,OAAO,yBAAyB,EAAE,kBAAkB;AAAA,MACrD,KAAK,EAAE,KAAK,QAAQ;AAAA,MACpB,UAAU;AAAA,MACV,aAAa;AAAA,IACf,CAAU;AAAA,EACZ,CAAC;AAED,KAAG,gFAAgF,YAAY;AAC7F,YAAQ,sBAAsB,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,QAAQ,EAAE,EAAE,EAAE,CAAC;AAE7F,UAAM,SAAS,MAAM,+CAA+C,aAAa;AAAA,MAC/E,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,WAAW;AAAA,MACX,eAAe;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,cAAc;AAAA,MACd,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,MAAM,CAAC,OAAO,KAAK;AAAA,IACrB,CAAC;AAED,WAAO,OAAO,EAAE,qBAAqB,2CAA2C;AAAA,MAC9E,QAAQ;AAAA,QACN,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,iBAAiB;AAAA,QACjB,MAAM;AAAA,QACN,WAAW;AAAA,QACX,eAAe;AAAA,QACf,eAAe;AAAA,QACf,aAAa;AAAA,QACb,aAAa;AAAA,QACb,cAAc;AAAA,QACd,eAAe;AAAA,QACf,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS;AAAA,QACT,UAAU;AAAA,MACZ;AAAA,IACF,CAAC;AACD,WAAO,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,QAAQ,EAAE,EAAE,CAAC;AAAA,EAC9E,CAAC;AACH,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import { getMercadoLibreAccessForProfile } from "../../config/mercadolibre.js";
|
|
3
|
+
import { composeMcpToolDeadlineSignal } from "../../config/mcp-tool-deadline-context.js";
|
|
3
4
|
const BASE_URL = "https://api.mercadolibre.com";
|
|
4
5
|
const RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
5
6
|
class MercadoLibreApiError extends Error {
|
|
@@ -63,16 +64,21 @@ function formatMercadoLibreError(error, fallback = "Unexpected MercadoLibre API
|
|
|
63
64
|
}
|
|
64
65
|
return sanitizeMercadoLibreText(fallback);
|
|
65
66
|
}
|
|
66
|
-
function createMercadoLibreClient(accessToken) {
|
|
67
|
+
function createMercadoLibreClient(accessToken, baseURL = BASE_URL) {
|
|
67
68
|
const client = axios.create({
|
|
68
|
-
baseURL
|
|
69
|
-
timeout:
|
|
69
|
+
baseURL,
|
|
70
|
+
timeout: 9e4,
|
|
70
71
|
headers: {
|
|
71
72
|
Accept: "application/json",
|
|
72
73
|
"Content-Type": "application/json",
|
|
73
74
|
Authorization: `Bearer ${accessToken}`
|
|
74
75
|
}
|
|
75
76
|
});
|
|
77
|
+
client.interceptors.request.use((config) => {
|
|
78
|
+
const signal = composeMcpToolDeadlineSignal(config.signal);
|
|
79
|
+
if (signal) config.signal = signal;
|
|
80
|
+
return config;
|
|
81
|
+
});
|
|
76
82
|
client.interceptors.response.use(
|
|
77
83
|
(response) => response,
|
|
78
84
|
(error) => {
|
|
@@ -97,6 +103,11 @@ async function getMercadoLibreUsersMe(profileId) {
|
|
|
97
103
|
const response = await api.get("/users/me");
|
|
98
104
|
return response.data;
|
|
99
105
|
}
|
|
106
|
+
async function getMercadoLibreUsersMeWithAccessToken(accessToken) {
|
|
107
|
+
const api = createMercadoLibreClient(accessToken);
|
|
108
|
+
const response = await api.get("/users/me");
|
|
109
|
+
return response.data;
|
|
110
|
+
}
|
|
100
111
|
function isRetryableMercadoLibreError(error) {
|
|
101
112
|
if (error instanceof MercadoLibreApiError) {
|
|
102
113
|
return error.status ? RETRYABLE_STATUS_CODES.has(error.status) : true;
|
|
@@ -195,6 +206,7 @@ export {
|
|
|
195
206
|
formatMercadoLibreError,
|
|
196
207
|
getMercadoLibreApiContext,
|
|
197
208
|
getMercadoLibreUsersMe,
|
|
209
|
+
getMercadoLibreUsersMeWithAccessToken,
|
|
198
210
|
isRetryableMercadoLibreError,
|
|
199
211
|
normalizeMercadoLibrePaging,
|
|
200
212
|
parseMercadoLibreMultiget,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/services/mercadolibre/mercadolibre-api.ts"],
|
|
4
|
-
"sourcesContent": ["import axios from \"axios\";\nimport type { AxiosError, AxiosInstance, AxiosResponse } from \"axios\";\n\nimport { getMercadoLibreAccessForProfile } from \"../../config/mercadolibre.js\";\n\nconst BASE_URL = \"https://api.mercadolibre.com\";\nconst RETRYABLE_STATUS_CODES = new Set([408, 429, 500, 502, 503, 504]);\n\nexport class MercadoLibreApiError extends Error {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly code?: string,\n public readonly retryAfterMs?: number\n ) {\n super(message);\n this.name = \"MercadoLibreApiError\";\n }\n}\n\nexport interface MercadoLibreBatchFetchOptions {\n maxConcurrency?: number;\n maxRetries?: number;\n baseRetryDelayMs?: number;\n}\n\nexport interface MercadoLibreDocumentSuccess<T> {\n id: string;\n document: T;\n}\n\nexport interface MercadoLibreDocumentFailure {\n id: string;\n message: string;\n statusCode?: number;\n attempts: number;\n retryable: boolean;\n}\n\nexport interface MercadoLibreDocumentsBatchResult<T> {\n successful: Array<MercadoLibreDocumentSuccess<T>>;\n failed: MercadoLibreDocumentFailure[];\n}\n\nexport function sanitizeMercadoLibreText(value: unknown): string {\n const raw = typeof value === \"string\" ? value : String(value ?? \"MercadoLibre request failed\");\n return raw\n .replace(/access_token[=:][^\\s&]+/gi, \"access_token=[redacted]\")\n .replace(/refresh_token[=:][^\\s&]+/gi, \"refresh_token=[redacted]\")\n .replace(/client_secret[=:][^\\s&]+/gi, \"client_secret=[redacted]\")\n .replace(/code[=:][^\\s&]+/gi, \"code=[redacted]\")\n .replace(/Bearer\\s+[A-Za-z0-9._-]+/gi, \"Bearer [redacted]\")\n .slice(0, 220);\n}\n\nfunction extractRetryAfterMs(error: AxiosError): number | undefined {\n const rawHeader = error.response?.headers?.[\"retry-after\"];\n const candidate = Array.isArray(rawHeader) ? rawHeader[0] : rawHeader;\n if (!candidate) {\n return undefined;\n }\n\n const seconds = Number(candidate);\n if (Number.isFinite(seconds) && seconds > 0) {\n return seconds * 1000;\n }\n\n const dateMs = new Date(candidate).getTime();\n if (Number.isFinite(dateMs)) {\n return Math.max(0, dateMs - Date.now());\n }\n\n return undefined;\n}\n\nfunction buildApiError(error: AxiosError): MercadoLibreApiError {\n const status = error.response?.status;\n const responseData = error.response?.data;\n let code: string | undefined;\n let message: string | undefined;\n\n if (responseData && typeof responseData === \"object\") {\n const candidate = responseData as {\n error?: unknown;\n message?: unknown;\n error_description?: unknown;\n cause?: Array<{ code?: unknown; message?: unknown }>;\n };\n code = typeof candidate.error === \"string\" ? candidate.error : undefined;\n message =\n (typeof candidate.message === \"string\" ? candidate.message : undefined) ??\n (typeof candidate.error_description === \"string\" ? candidate.error_description : undefined) ??\n (Array.isArray(candidate.cause)\n ? candidate.cause\n .map((entry) => (typeof entry.message === \"string\" ? entry.message : null))\n .filter(Boolean)\n .join(\"; \")\n : undefined);\n } else if (typeof responseData === \"string\") {\n message = responseData;\n }\n\n const safeMessage = sanitizeMercadoLibreText(message ?? error.message ?? \"MercadoLibre request failed\");\n return new MercadoLibreApiError(\n `MercadoLibre API request failed${status ? ` (${status})` : \"\"}: ${safeMessage}`,\n status,\n code,\n extractRetryAfterMs(error)\n );\n}\n\nexport function formatMercadoLibreError(\n error: unknown,\n fallback = \"Unexpected MercadoLibre API error\"\n): string {\n if (error instanceof MercadoLibreApiError) {\n return error.message;\n }\n\n if (axios.isAxiosError(error)) {\n return buildApiError(error).message;\n }\n\n if (error instanceof Error) {\n return sanitizeMercadoLibreText(error.message);\n }\n\n return sanitizeMercadoLibreText(fallback);\n}\n\nexport function createMercadoLibreClient(accessToken: string): AxiosInstance {\n const client = axios.create({\n baseURL: BASE_URL,\n timeout: 30000,\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n client.interceptors.response.use(\n (response) => response,\n (error: AxiosError) => {\n const method = error.config?.method?.toUpperCase() ?? \"UNKNOWN\";\n const url = error.config?.url ?? \"UNKNOWN_URL\";\n const status = error.response?.status ?? \"NO_STATUS\";\n console.error(`[MERCADOLIBRE API ERROR] ${method} ${url} -> ${status}`);\n return Promise.reject(buildApiError(error));\n }\n );\n\n return client;\n}\n\nexport async function getMercadoLibreApiContext(profileId: string) {\n const access = await getMercadoLibreAccessForProfile(profileId);\n\n return {\n ...access,\n api: createMercadoLibreClient(access.accessToken),\n };\n}\n\nexport async function getMercadoLibreUsersMe(profileId: string): Promise<Record<string, unknown>> {\n const { api } = await getMercadoLibreApiContext(profileId);\n const response = await api.get<Record<string, unknown>>(\"/users/me\");\n return response.data;\n}\n\nexport function isRetryableMercadoLibreError(error: unknown): boolean {\n if (error instanceof MercadoLibreApiError) {\n return error.status ? RETRYABLE_STATUS_CODES.has(error.status) : true;\n }\n\n return false;\n}\n\nfunction classifyMercadoLibreBatchFailure(\n id: string,\n error: unknown,\n attempts: number\n): MercadoLibreDocumentFailure {\n const statusCode = error instanceof MercadoLibreApiError ? error.status : undefined;\n return {\n id,\n message: formatMercadoLibreError(error, `Failed to fetch MercadoLibre resource ${id}`),\n statusCode,\n attempts,\n retryable: isRetryableMercadoLibreError(error),\n };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function withMercadoLibreRetry<T>(\n id: string,\n fetcher: (id: string) => Promise<T>,\n options: MercadoLibreBatchFetchOptions = {}\n): Promise<MercadoLibreDocumentSuccess<T>> {\n const maxRetries = Math.max(0, Math.floor(options.maxRetries ?? 2));\n const baseRetryDelayMs = Math.max(50, Math.floor(options.baseRetryDelayMs ?? 250));\n let attempts = 0;\n\n while (true) {\n attempts += 1;\n\n try {\n const document = await fetcher(id);\n return { id, document };\n } catch (error) {\n const retryable = isRetryableMercadoLibreError(error);\n if (!retryable || attempts > maxRetries + 1) {\n throw classifyMercadoLibreBatchFailure(id, error, attempts);\n }\n\n const retryAfterMs = error instanceof MercadoLibreApiError ? error.retryAfterMs : undefined;\n const jitterMs = Math.floor(Math.random() * 100);\n const delayMs = retryAfterMs ?? baseRetryDelayMs * 2 ** (attempts - 1) + jitterMs;\n await sleep(delayMs);\n }\n }\n}\n\nexport async function runMercadoLibreBatch<T>(\n ids: string[],\n fetcher: (id: string) => Promise<T>,\n options: MercadoLibreBatchFetchOptions = {}\n): Promise<MercadoLibreDocumentsBatchResult<T>> {\n const maxConcurrency = Math.max(1, Math.floor(options.maxConcurrency ?? 10));\n const successful: Array<MercadoLibreDocumentSuccess<T>> = [];\n const failed: MercadoLibreDocumentFailure[] = [];\n\n for (let start = 0; start < ids.length; start += maxConcurrency) {\n const chunk = ids.slice(start, start + maxConcurrency);\n const settled = await Promise.allSettled(\n chunk.map((id) => withMercadoLibreRetry(id, fetcher, options))\n );\n\n settled.forEach((result, index) => {\n const id = chunk[index];\n if (result.status === \"fulfilled\") {\n successful.push(result.value);\n return;\n }\n\n const reason = result.reason;\n if (\n reason &&\n typeof reason === \"object\" &&\n \"id\" in reason &&\n \"attempts\" in reason &&\n \"message\" in reason\n ) {\n failed.push(reason as MercadoLibreDocumentFailure);\n return;\n }\n\n failed.push(classifyMercadoLibreBatchFailure(id, reason, 1));\n });\n }\n\n return { successful, failed };\n}\n\nexport function normalizeMercadoLibrePaging(input: Record<string, unknown> | null | undefined) {\n const paging = input ?? {};\n const total = Number((paging.total as number | undefined) ?? 0);\n const limit = Number((paging.limit as number | undefined) ?? 0);\n const offset = Number((paging.offset as number | undefined) ?? 0);\n\n return {\n total: Number.isFinite(total) ? Math.max(0, total) : 0,\n limit: Number.isFinite(limit) ? Math.max(0, limit) : 0,\n offset: Number.isFinite(offset) ? Math.max(0, offset) : 0,\n };\n}\n\nexport function buildMercadoLibrePaginationMetadata(params: {\n total: number;\n limit: number;\n offset: number;\n returned: number;\n nextField: string;\n}) {\n const effectiveLimit = params.limit > 0 ? params.limit : params.returned;\n const hasMore = params.offset + params.returned < params.total;\n\n return {\n total: params.total,\n limit: effectiveLimit,\n offset: params.offset,\n returned: params.returned,\n has_more: hasMore,\n continuation:\n hasMore && effectiveLimit > 0\n ? `Reenviar la misma tool con ${params.nextField}=${params.offset + effectiveLimit} para continuar.`\n : \"No more pages for this query.\",\n };\n}\n\nexport async function parseMercadoLibreMultiget<T>(\n response: AxiosResponse<Array<{ code?: number; body?: T }>>\n): Promise<T[]> {\n return response.data\n .filter((entry) => Number(entry.code ?? 0) >= 200 && Number(entry.code ?? 0) < 300 && entry.body)\n .map((entry) => entry.body as T);\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,WAAW;AAGlB,SAAS,uCAAuC;
|
|
4
|
+
"sourcesContent": ["import axios from \"axios\";\nimport type { AxiosError, AxiosInstance, AxiosResponse } from \"axios\";\n\nimport { getMercadoLibreAccessForProfile } from \"../../config/mercadolibre.js\";\nimport { composeMcpToolDeadlineSignal } from \"../../config/mcp-tool-deadline-context.js\";\n\nconst BASE_URL = \"https://api.mercadolibre.com\";\nconst RETRYABLE_STATUS_CODES = new Set([408, 429, 500, 502, 503, 504]);\n\nexport class MercadoLibreApiError extends Error {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly code?: string,\n public readonly retryAfterMs?: number\n ) {\n super(message);\n this.name = \"MercadoLibreApiError\";\n }\n}\n\nexport interface MercadoLibreBatchFetchOptions {\n maxConcurrency?: number;\n maxRetries?: number;\n baseRetryDelayMs?: number;\n}\n\nexport interface MercadoLibreDocumentSuccess<T> {\n id: string;\n document: T;\n}\n\nexport interface MercadoLibreDocumentFailure {\n id: string;\n message: string;\n statusCode?: number;\n attempts: number;\n retryable: boolean;\n}\n\nexport interface MercadoLibreDocumentsBatchResult<T> {\n successful: Array<MercadoLibreDocumentSuccess<T>>;\n failed: MercadoLibreDocumentFailure[];\n}\n\nexport function sanitizeMercadoLibreText(value: unknown): string {\n const raw = typeof value === \"string\" ? value : String(value ?? \"MercadoLibre request failed\");\n return raw\n .replace(/access_token[=:][^\\s&]+/gi, \"access_token=[redacted]\")\n .replace(/refresh_token[=:][^\\s&]+/gi, \"refresh_token=[redacted]\")\n .replace(/client_secret[=:][^\\s&]+/gi, \"client_secret=[redacted]\")\n .replace(/code[=:][^\\s&]+/gi, \"code=[redacted]\")\n .replace(/Bearer\\s+[A-Za-z0-9._-]+/gi, \"Bearer [redacted]\")\n .slice(0, 220);\n}\n\nfunction extractRetryAfterMs(error: AxiosError): number | undefined {\n const rawHeader = error.response?.headers?.[\"retry-after\"];\n const candidate = Array.isArray(rawHeader) ? rawHeader[0] : rawHeader;\n if (!candidate) {\n return undefined;\n }\n\n const seconds = Number(candidate);\n if (Number.isFinite(seconds) && seconds > 0) {\n return seconds * 1000;\n }\n\n const dateMs = new Date(candidate).getTime();\n if (Number.isFinite(dateMs)) {\n return Math.max(0, dateMs - Date.now());\n }\n\n return undefined;\n}\n\nfunction buildApiError(error: AxiosError): MercadoLibreApiError {\n const status = error.response?.status;\n const responseData = error.response?.data;\n let code: string | undefined;\n let message: string | undefined;\n\n if (responseData && typeof responseData === \"object\") {\n const candidate = responseData as {\n error?: unknown;\n message?: unknown;\n error_description?: unknown;\n cause?: Array<{ code?: unknown; message?: unknown }>;\n };\n code = typeof candidate.error === \"string\" ? candidate.error : undefined;\n message =\n (typeof candidate.message === \"string\" ? candidate.message : undefined) ??\n (typeof candidate.error_description === \"string\" ? candidate.error_description : undefined) ??\n (Array.isArray(candidate.cause)\n ? candidate.cause\n .map((entry) => (typeof entry.message === \"string\" ? entry.message : null))\n .filter(Boolean)\n .join(\"; \")\n : undefined);\n } else if (typeof responseData === \"string\") {\n message = responseData;\n }\n\n const safeMessage = sanitizeMercadoLibreText(message ?? error.message ?? \"MercadoLibre request failed\");\n return new MercadoLibreApiError(\n `MercadoLibre API request failed${status ? ` (${status})` : \"\"}: ${safeMessage}`,\n status,\n code,\n extractRetryAfterMs(error)\n );\n}\n\nexport function formatMercadoLibreError(\n error: unknown,\n fallback = \"Unexpected MercadoLibre API error\"\n): string {\n if (error instanceof MercadoLibreApiError) {\n return error.message;\n }\n\n if (axios.isAxiosError(error)) {\n return buildApiError(error).message;\n }\n\n if (error instanceof Error) {\n return sanitizeMercadoLibreText(error.message);\n }\n\n return sanitizeMercadoLibreText(fallback);\n}\n\nexport function createMercadoLibreClient(accessToken: string, baseURL = BASE_URL): AxiosInstance {\n const client = axios.create({\n baseURL,\n timeout: 90000,\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n client.interceptors.request.use((config) => {\n const signal = composeMcpToolDeadlineSignal(config.signal);\n if (signal) config.signal = signal;\n return config;\n });\n\n client.interceptors.response.use(\n (response) => response,\n (error: AxiosError) => {\n const method = error.config?.method?.toUpperCase() ?? \"UNKNOWN\";\n const url = error.config?.url ?? \"UNKNOWN_URL\";\n const status = error.response?.status ?? \"NO_STATUS\";\n console.error(`[MERCADOLIBRE API ERROR] ${method} ${url} -> ${status}`);\n return Promise.reject(buildApiError(error));\n }\n );\n\n return client;\n}\n\nexport async function getMercadoLibreApiContext(profileId: string) {\n const access = await getMercadoLibreAccessForProfile(profileId);\n\n return {\n ...access,\n api: createMercadoLibreClient(access.accessToken),\n };\n}\n\nexport async function getMercadoLibreUsersMe(profileId: string): Promise<Record<string, unknown>> {\n const { api } = await getMercadoLibreApiContext(profileId);\n const response = await api.get<Record<string, unknown>>(\"/users/me\");\n return response.data;\n}\n\nexport async function getMercadoLibreUsersMeWithAccessToken(\n accessToken: string\n): Promise<Record<string, unknown>> {\n const api = createMercadoLibreClient(accessToken);\n const response = await api.get<Record<string, unknown>>(\"/users/me\");\n return response.data;\n}\n\nexport function isRetryableMercadoLibreError(error: unknown): boolean {\n if (error instanceof MercadoLibreApiError) {\n return error.status ? RETRYABLE_STATUS_CODES.has(error.status) : true;\n }\n\n return false;\n}\n\nfunction classifyMercadoLibreBatchFailure(\n id: string,\n error: unknown,\n attempts: number\n): MercadoLibreDocumentFailure {\n const statusCode = error instanceof MercadoLibreApiError ? error.status : undefined;\n return {\n id,\n message: formatMercadoLibreError(error, `Failed to fetch MercadoLibre resource ${id}`),\n statusCode,\n attempts,\n retryable: isRetryableMercadoLibreError(error),\n };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function withMercadoLibreRetry<T>(\n id: string,\n fetcher: (id: string) => Promise<T>,\n options: MercadoLibreBatchFetchOptions = {}\n): Promise<MercadoLibreDocumentSuccess<T>> {\n const maxRetries = Math.max(0, Math.floor(options.maxRetries ?? 2));\n const baseRetryDelayMs = Math.max(50, Math.floor(options.baseRetryDelayMs ?? 250));\n let attempts = 0;\n\n while (true) {\n attempts += 1;\n\n try {\n const document = await fetcher(id);\n return { id, document };\n } catch (error) {\n const retryable = isRetryableMercadoLibreError(error);\n if (!retryable || attempts > maxRetries + 1) {\n throw classifyMercadoLibreBatchFailure(id, error, attempts);\n }\n\n const retryAfterMs = error instanceof MercadoLibreApiError ? error.retryAfterMs : undefined;\n const jitterMs = Math.floor(Math.random() * 100);\n const delayMs = retryAfterMs ?? baseRetryDelayMs * 2 ** (attempts - 1) + jitterMs;\n await sleep(delayMs);\n }\n }\n}\n\nexport async function runMercadoLibreBatch<T>(\n ids: string[],\n fetcher: (id: string) => Promise<T>,\n options: MercadoLibreBatchFetchOptions = {}\n): Promise<MercadoLibreDocumentsBatchResult<T>> {\n const maxConcurrency = Math.max(1, Math.floor(options.maxConcurrency ?? 10));\n const successful: Array<MercadoLibreDocumentSuccess<T>> = [];\n const failed: MercadoLibreDocumentFailure[] = [];\n\n for (let start = 0; start < ids.length; start += maxConcurrency) {\n const chunk = ids.slice(start, start + maxConcurrency);\n const settled = await Promise.allSettled(\n chunk.map((id) => withMercadoLibreRetry(id, fetcher, options))\n );\n\n settled.forEach((result, index) => {\n const id = chunk[index];\n if (result.status === \"fulfilled\") {\n successful.push(result.value);\n return;\n }\n\n const reason = result.reason;\n if (\n reason &&\n typeof reason === \"object\" &&\n \"id\" in reason &&\n \"attempts\" in reason &&\n \"message\" in reason\n ) {\n failed.push(reason as MercadoLibreDocumentFailure);\n return;\n }\n\n failed.push(classifyMercadoLibreBatchFailure(id, reason, 1));\n });\n }\n\n return { successful, failed };\n}\n\nexport function normalizeMercadoLibrePaging(input: Record<string, unknown> | null | undefined) {\n const paging = input ?? {};\n const total = Number((paging.total as number | undefined) ?? 0);\n const limit = Number((paging.limit as number | undefined) ?? 0);\n const offset = Number((paging.offset as number | undefined) ?? 0);\n\n return {\n total: Number.isFinite(total) ? Math.max(0, total) : 0,\n limit: Number.isFinite(limit) ? Math.max(0, limit) : 0,\n offset: Number.isFinite(offset) ? Math.max(0, offset) : 0,\n };\n}\n\nexport function buildMercadoLibrePaginationMetadata(params: {\n total: number;\n limit: number;\n offset: number;\n returned: number;\n nextField: string;\n}) {\n const effectiveLimit = params.limit > 0 ? params.limit : params.returned;\n const hasMore = params.offset + params.returned < params.total;\n\n return {\n total: params.total,\n limit: effectiveLimit,\n offset: params.offset,\n returned: params.returned,\n has_more: hasMore,\n continuation:\n hasMore && effectiveLimit > 0\n ? `Reenviar la misma tool con ${params.nextField}=${params.offset + effectiveLimit} para continuar.`\n : \"No more pages for this query.\",\n };\n}\n\nexport async function parseMercadoLibreMultiget<T>(\n response: AxiosResponse<Array<{ code?: number; body?: T }>>\n): Promise<T[]> {\n return response.data\n .filter((entry) => Number(entry.code ?? 0) >= 200 && Number(entry.code ?? 0) < 300 && entry.body)\n .map((entry) => entry.body as T);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|