@yoryoboy/bi-mcp 1.11.0 → 1.13.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.
Files changed (237) hide show
  1. package/README.md +4 -5
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/index.js +398 -8
  4. package/dist/index.js.map +2 -2
  5. package/dist/mcp-use.json +2 -2
  6. package/dist/src/config/__tests__/meta.test.js +35 -0
  7. package/dist/src/config/__tests__/meta.test.js.map +7 -0
  8. package/dist/src/config/meta-store.js +109 -0
  9. package/dist/src/config/meta-store.js.map +7 -0
  10. package/dist/src/config/meta.js +0 -2
  11. package/dist/src/config/meta.js.map +2 -2
  12. package/dist/src/meta/__tests__/meta-utils.test.js +155 -0
  13. package/dist/src/meta/__tests__/meta-utils.test.js.map +7 -0
  14. package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js +265 -0
  15. package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js.map +7 -0
  16. package/dist/src/services/mercadolibre/__tests__/mercadolibre-items.test.js +311 -0
  17. package/dist/src/services/mercadolibre/__tests__/mercadolibre-items.test.js.map +7 -0
  18. package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js +220 -0
  19. package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js.map +7 -0
  20. package/dist/src/services/meta/__tests__/meta-ads.test.js +126 -0
  21. package/dist/src/services/meta/__tests__/meta-ads.test.js.map +7 -0
  22. package/dist/src/services/meta/__tests__/meta-api.test.js +70 -0
  23. package/dist/src/services/meta/__tests__/meta-api.test.js.map +7 -0
  24. package/dist/src/services/meta/meta-ads.js +94 -40
  25. package/dist/src/services/meta/meta-ads.js.map +2 -2
  26. package/dist/src/services/meta/meta-api.js +8 -6
  27. package/dist/src/services/meta/meta-api.js.map +2 -2
  28. package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js +165 -0
  29. package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js.map +7 -0
  30. package/dist/src/services/vtex/__tests__/vtex-catalog-write-eans.test.js +92 -0
  31. package/dist/src/services/vtex/__tests__/vtex-catalog-write-eans.test.js.map +7 -0
  32. package/dist/src/services/vtex/__tests__/vtex-catalog-write-products.test.js +41 -0
  33. package/dist/src/services/vtex/__tests__/vtex-catalog-write-products.test.js.map +7 -0
  34. package/dist/src/services/vtex/__tests__/vtex-catalog.test.js +80 -0
  35. package/dist/src/services/vtex/__tests__/vtex-catalog.test.js.map +7 -0
  36. package/dist/src/services/vtex/__tests__/vtex-orders-write-http.test.js +85 -0
  37. package/dist/src/services/vtex/__tests__/vtex-orders-write-http.test.js.map +7 -0
  38. package/dist/src/services/vtex/__tests__/vtex-orders-write-state-validation.test.js +251 -0
  39. package/dist/src/services/vtex/__tests__/vtex-orders-write-state-validation.test.js.map +7 -0
  40. package/dist/src/services/vtex/__tests__/vtex-write.test.js +111 -0
  41. package/dist/src/services/vtex/__tests__/vtex-write.test.js.map +7 -0
  42. package/dist/src/services/vtex/vtex-catalog-write.js +194 -1
  43. package/dist/src/services/vtex/vtex-catalog-write.js.map +2 -2
  44. package/dist/src/services/vtex/vtex-catalog.js +118 -1
  45. package/dist/src/services/vtex/vtex-catalog.js.map +2 -2
  46. package/dist/src/services/vtex/vtex-pricing-write.js +5 -0
  47. package/dist/src/services/vtex/vtex-pricing-write.js.map +2 -2
  48. package/dist/src/services/vtex/vtex-write.js +67 -1
  49. package/dist/src/services/vtex/vtex-write.js.map +2 -2
  50. package/dist/src/tools/config/list-profiles.js +37 -6
  51. package/dist/src/tools/config/list-profiles.js.map +2 -2
  52. package/dist/src/tools/index.js.map +2 -2
  53. package/dist/src/tools/mercadolibre/__tests__/helpers.test.js +37 -0
  54. package/dist/src/tools/mercadolibre/__tests__/helpers.test.js.map +7 -0
  55. package/dist/src/tools/mercadolibre/__tests__/profile-resolution.test.js +30 -0
  56. package/dist/src/tools/mercadolibre/__tests__/profile-resolution.test.js.map +7 -0
  57. package/dist/src/tools/mercadolibre/profile-resolution.js +4 -50
  58. package/dist/src/tools/mercadolibre/profile-resolution.js.map +2 -2
  59. package/dist/src/tools/meta/__tests__/pagination.test.js +133 -0
  60. package/dist/src/tools/meta/__tests__/pagination.test.js.map +7 -0
  61. package/dist/src/tools/meta/__tests__/profile-access.test.js +262 -0
  62. package/dist/src/tools/meta/__tests__/profile-access.test.js.map +7 -0
  63. package/dist/src/tools/meta/__tests__/read-tools.test.js +722 -0
  64. package/dist/src/tools/meta/__tests__/read-tools.test.js.map +7 -0
  65. package/dist/src/tools/meta/__tests__/schemas.test.js +103 -0
  66. package/dist/src/tools/meta/__tests__/schemas.test.js.map +7 -0
  67. package/dist/src/tools/meta/account-overview.js +37 -19
  68. package/dist/src/tools/meta/account-overview.js.map +2 -2
  69. package/dist/src/tools/meta/ad-account-info.js +31 -6
  70. package/dist/src/tools/meta/ad-account-info.js.map +2 -2
  71. package/dist/src/tools/meta/ads-performance.js +35 -21
  72. package/dist/src/tools/meta/ads-performance.js.map +2 -2
  73. package/dist/src/tools/meta/campaign-performance.js +35 -18
  74. package/dist/src/tools/meta/campaign-performance.js.map +2 -2
  75. package/dist/src/tools/meta/list-accessible-ad-accounts.js +39 -10
  76. package/dist/src/tools/meta/list-accessible-ad-accounts.js.map +2 -2
  77. package/dist/src/tools/meta/list-accessible-businesses.js +37 -13
  78. package/dist/src/tools/meta/list-accessible-businesses.js.map +2 -2
  79. package/dist/src/tools/meta/placement-mix.js +37 -22
  80. package/dist/src/tools/meta/placement-mix.js.map +2 -2
  81. package/dist/src/tools/meta/profile-access.js +215 -0
  82. package/dist/src/tools/meta/profile-access.js.map +7 -0
  83. package/dist/src/tools/meta/schema-helpers.js +19 -0
  84. package/dist/src/tools/meta/schema-helpers.js.map +7 -0
  85. package/dist/src/tools/meta/time-series.js +40 -23
  86. package/dist/src/tools/meta/time-series.js.map +2 -2
  87. package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js +233 -0
  88. package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js.map +7 -0
  89. package/dist/src/tools/vtex/__tests__/catalog-admin-categories.test.js +649 -0
  90. package/dist/src/tools/vtex/__tests__/catalog-admin-categories.test.js.map +7 -0
  91. package/dist/src/tools/vtex/__tests__/catalog-admin-product-specifications.test.js +339 -0
  92. package/dist/src/tools/vtex/__tests__/catalog-admin-product-specifications.test.js.map +7 -0
  93. package/dist/src/tools/vtex/__tests__/catalog-admin-products-skus.test.js +235 -0
  94. package/dist/src/tools/vtex/__tests__/catalog-admin-products-skus.test.js.map +7 -0
  95. package/dist/src/tools/vtex/__tests__/catalog-navigation-reads.test.js +372 -0
  96. package/dist/src/tools/vtex/__tests__/catalog-navigation-reads.test.js.map +7 -0
  97. package/dist/src/tools/vtex/__tests__/catalog-navigation-surface.test.js +57 -0
  98. package/dist/src/tools/vtex/__tests__/catalog-navigation-surface.test.js.map +7 -0
  99. package/dist/src/tools/vtex/__tests__/write-helpers.test.js +97 -0
  100. package/dist/src/tools/vtex/__tests__/write-helpers.test.js.map +7 -0
  101. package/dist/src/tools/vtex/activate-sku.js +1 -48
  102. package/dist/src/tools/vtex/activate-sku.js.map +2 -2
  103. package/dist/src/tools/vtex/associate-specification.js +3 -54
  104. package/dist/src/tools/vtex/associate-specification.js.map +2 -2
  105. package/dist/src/tools/vtex/attach-catalog-image.js +3 -57
  106. package/dist/src/tools/vtex/attach-catalog-image.js.map +2 -2
  107. package/dist/src/tools/vtex/catalog-admin-batch.js +298 -0
  108. package/dist/src/tools/vtex/catalog-admin-batch.js.map +7 -0
  109. package/dist/src/tools/vtex/catalog-admin-categories.js +542 -0
  110. package/dist/src/tools/vtex/catalog-admin-categories.js.map +7 -0
  111. package/dist/src/tools/vtex/catalog-admin-product-specifications.js +275 -0
  112. package/dist/src/tools/vtex/catalog-admin-product-specifications.js.map +7 -0
  113. package/dist/src/tools/vtex/catalog-admin-products-skus.js +475 -0
  114. package/dist/src/tools/vtex/catalog-admin-products-skus.js.map +7 -0
  115. package/dist/src/tools/vtex/catalog-navigation-reads.js +430 -0
  116. package/dist/src/tools/vtex/catalog-navigation-reads.js.map +7 -0
  117. package/dist/src/tools/vtex/create-brand.js +1 -64
  118. package/dist/src/tools/vtex/create-brand.js.map +2 -2
  119. package/dist/src/tools/vtex/create-category.js +1 -76
  120. package/dist/src/tools/vtex/create-category.js.map +2 -2
  121. package/dist/src/tools/vtex/create-product-with-sku.js +3 -114
  122. package/dist/src/tools/vtex/create-product-with-sku.js.map +2 -2
  123. package/dist/src/tools/vtex/create-specification-value.js +3 -47
  124. package/dist/src/tools/vtex/create-specification-value.js.map +2 -2
  125. package/dist/src/tools/vtex/create-specification.js +1 -80
  126. package/dist/src/tools/vtex/create-specification.js.map +2 -2
  127. package/dist/src/tools/vtex/deactivate-sku.js +1 -48
  128. package/dist/src/tools/vtex/deactivate-sku.js.map +2 -2
  129. package/dist/src/tools/vtex/delete-all-product-specifications.js +9 -0
  130. package/dist/src/tools/vtex/delete-all-product-specifications.js.map +7 -0
  131. package/dist/src/tools/vtex/delete-all-sku-specifications.js +9 -0
  132. package/dist/src/tools/vtex/delete-all-sku-specifications.js.map +7 -0
  133. package/dist/src/tools/vtex/delete-brand.js +9 -0
  134. package/dist/src/tools/vtex/delete-brand.js.map +7 -0
  135. package/dist/src/tools/vtex/delete-catalog-image.js +9 -0
  136. package/dist/src/tools/vtex/delete-catalog-image.js.map +7 -0
  137. package/dist/src/tools/vtex/delete-product-specification.js +9 -0
  138. package/dist/src/tools/vtex/delete-product-specification.js.map +7 -0
  139. package/dist/src/tools/vtex/delete-sku-price.js +55 -0
  140. package/dist/src/tools/vtex/delete-sku-price.js.map +7 -0
  141. package/dist/src/tools/vtex/delete-sku-specification.js +9 -0
  142. package/dist/src/tools/vtex/delete-sku-specification.js.map +7 -0
  143. package/dist/src/tools/vtex/get-brand.js +6 -0
  144. package/dist/src/tools/vtex/get-brand.js.map +7 -0
  145. package/dist/src/tools/vtex/get-category-tree.js +6 -0
  146. package/dist/src/tools/vtex/get-category-tree.js.map +7 -0
  147. package/dist/src/tools/vtex/get-category.js +6 -0
  148. package/dist/src/tools/vtex/get-category.js.map +7 -0
  149. package/dist/src/tools/vtex/get-product-specifications.js +9 -0
  150. package/dist/src/tools/vtex/get-product-specifications.js.map +7 -0
  151. package/dist/src/tools/vtex/get-product.js +6 -0
  152. package/dist/src/tools/vtex/get-product.js.map +7 -0
  153. package/dist/src/tools/vtex/get-sku.js +6 -0
  154. package/dist/src/tools/vtex/get-sku.js.map +7 -0
  155. package/dist/src/tools/vtex/index.js +23 -1
  156. package/dist/src/tools/vtex/index.js.map +2 -2
  157. package/dist/src/tools/vtex/list-brands.js +6 -0
  158. package/dist/src/tools/vtex/list-brands.js.map +7 -0
  159. package/dist/src/tools/vtex/list-categories.js +6 -0
  160. package/dist/src/tools/vtex/list-categories.js.map +7 -0
  161. package/dist/src/tools/vtex/list-products-by-category.js +6 -0
  162. package/dist/src/tools/vtex/list-products-by-category.js.map +7 -0
  163. package/dist/src/tools/vtex/list-products.js +6 -0
  164. package/dist/src/tools/vtex/list-products.js.map +7 -0
  165. package/dist/src/tools/vtex/list-skus-by-product.js +6 -0
  166. package/dist/src/tools/vtex/list-skus-by-product.js.map +7 -0
  167. package/dist/src/tools/vtex/list-specification-groups.js +9 -0
  168. package/dist/src/tools/vtex/list-specification-groups.js.map +7 -0
  169. package/dist/src/tools/vtex/move-category.js +6 -0
  170. package/dist/src/tools/vtex/move-category.js.map +7 -0
  171. package/dist/src/tools/vtex/profile-resolution.js +4 -51
  172. package/dist/src/tools/vtex/profile-resolution.js.map +2 -2
  173. package/dist/src/tools/vtex/update-brand.js +6 -0
  174. package/dist/src/tools/vtex/update-brand.js.map +7 -0
  175. package/dist/src/tools/vtex/update-category.js +6 -0
  176. package/dist/src/tools/vtex/update-category.js.map +7 -0
  177. package/dist/src/tools/vtex/update-product-basic-fields.js +3 -65
  178. package/dist/src/tools/vtex/update-product-basic-fields.js.map +2 -2
  179. package/dist/src/tools/vtex/update-sku-basic-fields.js +1 -87
  180. package/dist/src/tools/vtex/update-sku-basic-fields.js.map +2 -2
  181. package/dist/src/tools/vtex/update-sku-price.js +21 -1
  182. package/dist/src/tools/vtex/update-sku-price.js.map +2 -2
  183. package/dist/src/tools/vtex/write-helpers.js +104 -14
  184. package/dist/src/tools/vtex/write-helpers.js.map +2 -2
  185. package/dist/src/utils/provider-profile-selection.js +117 -0
  186. package/dist/src/utils/provider-profile-selection.js.map +7 -0
  187. package/dist/tests/meli/mercadolibre-tool-handlers-medium-batch4.test.js +678 -0
  188. package/dist/tests/meli/mercadolibre-tool-handlers-medium-batch4.test.js.map +7 -0
  189. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch5.test.js +564 -0
  190. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch5.test.js.map +7 -0
  191. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch6.test.js +387 -0
  192. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch6.test.js.map +7 -0
  193. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch7.test.js +368 -0
  194. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch7.test.js.map +7 -0
  195. package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js +626 -0
  196. package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js.map +7 -0
  197. package/dist/tests/meli/mercadolibre-tool-handlers-write-batch8.test.js +480 -0
  198. package/dist/tests/meli/mercadolibre-tool-handlers-write-batch8.test.js.map +7 -0
  199. package/dist/tests/setup.js +2 -0
  200. package/dist/tests/setup.js.map +7 -0
  201. package/dist/tests/smoke/test-harness.test.js +7 -0
  202. package/dist/tests/smoke/test-harness.test.js.map +7 -0
  203. package/dist/tests/vtex/read-only-utils.test.js +161 -0
  204. package/dist/tests/vtex/read-only-utils.test.js.map +7 -0
  205. package/dist/tests/vtex/vtex-catalog-admin-docs.test.js +24 -0
  206. package/dist/tests/vtex/vtex-catalog-admin-docs.test.js.map +7 -0
  207. package/dist/tests/vtex/vtex-catalog-admin-surface.test.js +30 -0
  208. package/dist/tests/vtex/vtex-catalog-admin-surface.test.js.map +7 -0
  209. package/dist/tests/vtex/vtex-catalog-write-tools.test.js +712 -0
  210. package/dist/tests/vtex/vtex-catalog-write-tools.test.js.map +7 -0
  211. package/dist/tests/vtex/vtex-catalog.service.test.js +51 -0
  212. package/dist/tests/vtex/vtex-catalog.service.test.js.map +7 -0
  213. package/dist/tests/vtex/vtex-inventory-tools.test.js +201 -0
  214. package/dist/tests/vtex/vtex-inventory-tools.test.js.map +7 -0
  215. package/dist/tests/vtex/vtex-logistics.service.test.js +134 -0
  216. package/dist/tests/vtex/vtex-logistics.service.test.js.map +7 -0
  217. package/dist/tests/vtex/vtex-order-details.tool.test.js +141 -0
  218. package/dist/tests/vtex/vtex-order-details.tool.test.js.map +7 -0
  219. package/dist/tests/vtex/vtex-order-write-tools.test.js +483 -0
  220. package/dist/tests/vtex/vtex-order-write-tools.test.js.map +7 -0
  221. package/dist/tests/vtex/vtex-orders-summary.tool.test.js +185 -0
  222. package/dist/tests/vtex/vtex-orders-summary.tool.test.js.map +7 -0
  223. package/dist/tests/vtex/vtex-orders.service.test.js +120 -0
  224. package/dist/tests/vtex/vtex-orders.service.test.js.map +7 -0
  225. package/dist/tests/vtex/vtex-pricing-write-tools.test.js +202 -0
  226. package/dist/tests/vtex/vtex-pricing-write-tools.test.js.map +7 -0
  227. package/dist/tests/vtex/vtex-pricing-write.service.test.js +106 -0
  228. package/dist/tests/vtex/vtex-pricing-write.service.test.js.map +7 -0
  229. package/dist/tests/vtex/vtex-pricing.service.test.js +88 -0
  230. package/dist/tests/vtex/vtex-pricing.service.test.js.map +7 -0
  231. package/dist/tests/vtex/vtex-small-tools.test.js +190 -0
  232. package/dist/tests/vtex/vtex-small-tools.test.js.map +7 -0
  233. package/dist/tests/vtex/vtex-write-simple-tools.test.js +647 -0
  234. package/dist/tests/vtex/vtex-write-simple-tools.test.js.map +7 -0
  235. package/dist/vitest.config.js +15 -0
  236. package/dist/vitest.config.js.map +7 -0
  237. package/package.json +6 -2
@@ -0,0 +1,126 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ const metaFetchMock = vi.fn();
3
+ vi.mock("../meta-api.js", () => ({
4
+ metaFetch: metaFetchMock
5
+ }));
6
+ const metaAdsModule = await import("../meta-ads.js");
7
+ describe("meta ads service", () => {
8
+ beforeEach(() => {
9
+ vi.clearAllMocks();
10
+ });
11
+ it("lists businesses and ad accounts with normalized paging and account ids", async () => {
12
+ metaFetchMock.mockResolvedValueOnce({
13
+ data: [{ id: "biz-1", name: "Main Business", verification_status: "verified" }],
14
+ paging: {
15
+ next: "https://graph.facebook.com/v25.0/me/businesses?after=cursor-2"
16
+ }
17
+ }).mockResolvedValueOnce({
18
+ data: [
19
+ {
20
+ id: "act_123",
21
+ name: "Main Account",
22
+ currency: "ARS",
23
+ account_status: 1,
24
+ timezone_name: "America/Argentina/Buenos_Aires",
25
+ business: { id: "biz-1", name: "Main Business" }
26
+ }
27
+ ],
28
+ paging: {
29
+ cursors: { before: "before-1", after: "after-1" },
30
+ next: "https://graph.facebook.com/v25.0/me/adaccounts?after=after-1"
31
+ }
32
+ });
33
+ await expect(
34
+ metaAdsModule.listAccessibleMetaBusinesses({ accessToken: "token" })
35
+ ).resolves.toEqual({
36
+ businesses: [{ id: "biz-1", name: "Main Business", verification_status: "verified" }],
37
+ paging: { after: "cursor-2", before: void 0, has_more: true }
38
+ });
39
+ await expect(
40
+ metaAdsModule.listAccessibleMetaAdAccounts({ accessToken: "token" }, "after-0")
41
+ ).resolves.toEqual({
42
+ adAccounts: [
43
+ {
44
+ id: "act_123",
45
+ account_id: "123",
46
+ name: "Main Account",
47
+ currency: "ARS",
48
+ account_status: 1,
49
+ timezone_name: "America/Argentina/Buenos_Aires",
50
+ business: { id: "biz-1", name: "Main Business" }
51
+ }
52
+ ],
53
+ paging: { after: "after-1", before: "before-1", has_more: true }
54
+ });
55
+ expect(metaFetchMock).toHaveBeenNthCalledWith(
56
+ 1,
57
+ { accessToken: "token" },
58
+ "/me/businesses",
59
+ { fields: "id,name,verification_status", limit: 100, after: void 0 }
60
+ );
61
+ expect(metaFetchMock).toHaveBeenNthCalledWith(
62
+ 2,
63
+ { accessToken: "token" },
64
+ "/me/adaccounts",
65
+ {
66
+ fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}",
67
+ limit: 100,
68
+ after: "after-0"
69
+ }
70
+ );
71
+ });
72
+ it("fetches individual objects and uses act_ account ids for account-scoped reads", async () => {
73
+ metaFetchMock.mockResolvedValueOnce({ id: "biz-1", name: "Business" }).mockResolvedValueOnce({ id: "act_321", name: "Scoped", currency: "USD" }).mockResolvedValueOnce({ data: [{ id: "cmp-1", name: "Campaign" }] }).mockResolvedValueOnce({ data: [{ id: "ad-1", name: "Ad", creative: { id: "crt-1" } }] }).mockResolvedValueOnce({ data: [{ spend: "10" }] }).mockResolvedValueOnce({ data: [{ publisher_platform: "instagram", spend: "4" }] });
74
+ await expect(metaAdsModule.getMetaBusinessInfo({ accessToken: "token" }, "biz-1")).resolves.toEqual({
75
+ id: "biz-1",
76
+ name: "Business"
77
+ });
78
+ await expect(metaAdsModule.getMetaAdAccountInfo({ accessToken: "token" }, "321")).resolves.toEqual({
79
+ id: "act_321",
80
+ account_id: "321",
81
+ name: "Scoped",
82
+ currency: "USD"
83
+ });
84
+ await expect(metaAdsModule.getMetaCampaigns({ accessToken: "token" }, "321", 10)).resolves.toEqual([
85
+ { id: "cmp-1", name: "Campaign" }
86
+ ]);
87
+ await expect(metaAdsModule.getMetaAds({ accessToken: "token" }, "321", 10)).resolves.toEqual([
88
+ { id: "ad-1", name: "Ad", creative: { id: "crt-1" } }
89
+ ]);
90
+ await expect(
91
+ metaAdsModule.getMetaInsights({ accessToken: "token" }, "act_321", { level: "account" })
92
+ ).resolves.toEqual([{ spend: "10" }]);
93
+ await expect(
94
+ metaAdsModule.getMetaPlacementInsights({ accessToken: "token" }, "321", { level: "account" })
95
+ ).resolves.toEqual([{ publisher_platform: "instagram", spend: "4" }]);
96
+ expect(metaFetchMock).toHaveBeenCalledWith(
97
+ { accessToken: "token" },
98
+ "/act_321",
99
+ {
100
+ fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap"
101
+ }
102
+ );
103
+ expect(metaFetchMock).toHaveBeenCalledWith(
104
+ { accessToken: "token" },
105
+ "/act_321/campaigns",
106
+ { fields: "id,name,status,effective_status,objective", limit: 10 }
107
+ );
108
+ });
109
+ it("deduplicates account ids and ignores rejected account lookups", async () => {
110
+ metaFetchMock.mockResolvedValueOnce({ id: "act_111", name: "One", currency: "ARS" }).mockRejectedValueOnce(new Error("not found"));
111
+ await expect(
112
+ metaAdsModule.getMetaAdAccountsByIds({ accessToken: "token" }, ["act_111", "111", "222"])
113
+ ).resolves.toEqual([{ id: "act_111", account_id: "111", name: "One", currency: "ARS" }]);
114
+ expect(metaFetchMock).toHaveBeenCalledTimes(2);
115
+ });
116
+ it("returns empty arrays when list responses omit data", async () => {
117
+ metaFetchMock.mockResolvedValue({});
118
+ await expect(metaAdsModule.getMetaInsights({ accessToken: "token" }, "obj-1", {})).resolves.toEqual([]);
119
+ await expect(metaAdsModule.getMetaCampaigns({ accessToken: "token" }, "123")).resolves.toEqual([]);
120
+ await expect(metaAdsModule.getMetaAds({ accessToken: "token" }, "123")).resolves.toEqual([]);
121
+ await expect(metaAdsModule.getMetaPlacementInsights({ accessToken: "token" }, "123", {})).resolves.toEqual(
122
+ []
123
+ );
124
+ });
125
+ });
126
+ //# sourceMappingURL=meta-ads.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/services/meta/__tests__/meta-ads.test.ts"],
4
+ "sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nconst metaFetchMock = vi.fn();\n\nvi.mock(\"../meta-api.js\", () => ({\n metaFetch: metaFetchMock,\n}));\n\nconst metaAdsModule = await import(\"../meta-ads.js\");\n\ndescribe(\"meta ads service\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"lists businesses and ad accounts with normalized paging and account ids\", async () => {\n metaFetchMock\n .mockResolvedValueOnce({\n data: [{ id: \"biz-1\", name: \"Main Business\", verification_status: \"verified\" }],\n paging: {\n next: \"https://graph.facebook.com/v25.0/me/businesses?after=cursor-2\",\n },\n })\n .mockResolvedValueOnce({\n data: [\n {\n id: \"act_123\",\n name: \"Main Account\",\n currency: \"ARS\",\n account_status: 1,\n timezone_name: \"America/Argentina/Buenos_Aires\",\n business: { id: \"biz-1\", name: \"Main Business\" },\n },\n ],\n paging: {\n cursors: { before: \"before-1\", after: \"after-1\" },\n next: \"https://graph.facebook.com/v25.0/me/adaccounts?after=after-1\",\n },\n });\n\n await expect(\n metaAdsModule.listAccessibleMetaBusinesses({ accessToken: \"token\" })\n ).resolves.toEqual({\n businesses: [{ id: \"biz-1\", name: \"Main Business\", verification_status: \"verified\" }],\n paging: { after: \"cursor-2\", before: undefined, has_more: true },\n });\n\n await expect(\n metaAdsModule.listAccessibleMetaAdAccounts({ accessToken: \"token\" }, \"after-0\")\n ).resolves.toEqual({\n adAccounts: [\n {\n id: \"act_123\",\n account_id: \"123\",\n name: \"Main Account\",\n currency: \"ARS\",\n account_status: 1,\n timezone_name: \"America/Argentina/Buenos_Aires\",\n business: { id: \"biz-1\", name: \"Main Business\" },\n },\n ],\n paging: { after: \"after-1\", before: \"before-1\", has_more: true },\n });\n\n expect(metaFetchMock).toHaveBeenNthCalledWith(\n 1,\n { accessToken: \"token\" },\n \"/me/businesses\",\n { fields: \"id,name,verification_status\", limit: 100, after: undefined }\n );\n expect(metaFetchMock).toHaveBeenNthCalledWith(\n 2,\n { accessToken: \"token\" },\n \"/me/adaccounts\",\n {\n fields:\n \"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}\",\n limit: 100,\n after: \"after-0\",\n }\n );\n });\n\n it(\"fetches individual objects and uses act_ account ids for account-scoped reads\", async () => {\n metaFetchMock\n .mockResolvedValueOnce({ id: \"biz-1\", name: \"Business\" })\n .mockResolvedValueOnce({ id: \"act_321\", name: \"Scoped\", currency: \"USD\" })\n .mockResolvedValueOnce({ data: [{ id: \"cmp-1\", name: \"Campaign\" }] })\n .mockResolvedValueOnce({ data: [{ id: \"ad-1\", name: \"Ad\", creative: { id: \"crt-1\" } }] })\n .mockResolvedValueOnce({ data: [{ spend: \"10\" }] })\n .mockResolvedValueOnce({ data: [{ publisher_platform: \"instagram\", spend: \"4\" }] });\n\n await expect(metaAdsModule.getMetaBusinessInfo({ accessToken: \"token\" }, \"biz-1\")).resolves.toEqual({\n id: \"biz-1\",\n name: \"Business\",\n });\n await expect(metaAdsModule.getMetaAdAccountInfo({ accessToken: \"token\" }, \"321\")).resolves.toEqual({\n id: \"act_321\",\n account_id: \"321\",\n name: \"Scoped\",\n currency: \"USD\",\n });\n await expect(metaAdsModule.getMetaCampaigns({ accessToken: \"token\" }, \"321\", 10)).resolves.toEqual([\n { id: \"cmp-1\", name: \"Campaign\" },\n ]);\n await expect(metaAdsModule.getMetaAds({ accessToken: \"token\" }, \"321\", 10)).resolves.toEqual([\n { id: \"ad-1\", name: \"Ad\", creative: { id: \"crt-1\" } },\n ]);\n await expect(\n metaAdsModule.getMetaInsights({ accessToken: \"token\" }, \"act_321\", { level: \"account\" })\n ).resolves.toEqual([{ spend: \"10\" }]);\n await expect(\n metaAdsModule.getMetaPlacementInsights({ accessToken: \"token\" }, \"321\", { level: \"account\" })\n ).resolves.toEqual([{ publisher_platform: \"instagram\", spend: \"4\" }]);\n\n expect(metaFetchMock).toHaveBeenCalledWith(\n { accessToken: \"token\" },\n \"/act_321\",\n {\n fields:\n \"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap\",\n }\n );\n expect(metaFetchMock).toHaveBeenCalledWith(\n { accessToken: \"token\" },\n \"/act_321/campaigns\",\n { fields: \"id,name,status,effective_status,objective\", limit: 10 }\n );\n });\n\n it(\"deduplicates account ids and ignores rejected account lookups\", async () => {\n metaFetchMock\n .mockResolvedValueOnce({ id: \"act_111\", name: \"One\", currency: \"ARS\" })\n .mockRejectedValueOnce(new Error(\"not found\"));\n\n await expect(\n metaAdsModule.getMetaAdAccountsByIds({ accessToken: \"token\" }, [\"act_111\", \"111\", \"222\"])\n ).resolves.toEqual([{ id: \"act_111\", account_id: \"111\", name: \"One\", currency: \"ARS\" }]);\n\n expect(metaFetchMock).toHaveBeenCalledTimes(2);\n });\n\n it(\"returns empty arrays when list responses omit data\", async () => {\n metaFetchMock.mockResolvedValue({});\n\n await expect(metaAdsModule.getMetaInsights({ accessToken: \"token\" }, \"obj-1\", {})).resolves.toEqual([]);\n await expect(metaAdsModule.getMetaCampaigns({ accessToken: \"token\" }, \"123\")).resolves.toEqual([]);\n await expect(metaAdsModule.getMetaAds({ accessToken: \"token\" }, \"123\")).resolves.toEqual([]);\n await expect(metaAdsModule.getMetaPlacementInsights({ accessToken: \"token\" }, \"123\", {})).resolves.toEqual(\n []\n );\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,70 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ const getMetaConfigMock = vi.fn();
3
+ const createMetaAppSecretProofMock = vi.fn();
4
+ vi.mock("../../../config/meta.js", () => ({
5
+ getMetaConfig: getMetaConfigMock,
6
+ createMetaAppSecretProof: createMetaAppSecretProofMock
7
+ }));
8
+ const { metaFetch } = await import("../meta-api.js");
9
+ describe("meta api", () => {
10
+ beforeEach(() => {
11
+ vi.clearAllMocks();
12
+ getMetaConfigMock.mockReturnValue({
13
+ baseUrl: "https://graph.facebook.com/v25.0",
14
+ appSecret: "app-secret"
15
+ });
16
+ createMetaAppSecretProofMock.mockReturnValue("proof-123");
17
+ });
18
+ it("builds Graph API URLs with token, proof and non-empty query params", async () => {
19
+ const fetchMock = vi.fn().mockResolvedValue({
20
+ ok: true,
21
+ json: vi.fn().mockResolvedValue({ data: [{ id: "biz-1" }] })
22
+ });
23
+ vi.stubGlobal("fetch", fetchMock);
24
+ const result = await metaFetch(
25
+ { accessToken: "access-token" },
26
+ "me/businesses",
27
+ {
28
+ fields: "id,name",
29
+ limit: 100,
30
+ after: void 0,
31
+ empty: ""
32
+ }
33
+ );
34
+ expect(result).toEqual({ data: [{ id: "biz-1" }] });
35
+ expect(createMetaAppSecretProofMock).toHaveBeenCalledWith("access-token", "app-secret");
36
+ expect(fetchMock).toHaveBeenCalledWith(
37
+ "https://graph.facebook.com/v25.0/me/businesses?access_token=access-token&appsecret_proof=proof-123&fields=id%2Cname&limit=100",
38
+ {
39
+ method: "GET",
40
+ headers: { Accept: "application/json" }
41
+ }
42
+ );
43
+ });
44
+ it("prefers user-facing Meta errors and falls back to status-based messages", async () => {
45
+ const userFacingResponse = {
46
+ ok: false,
47
+ status: 400,
48
+ json: vi.fn().mockResolvedValue({
49
+ error: {
50
+ error_user_msg: "Choose a different account.",
51
+ message: "Low-level error"
52
+ }
53
+ })
54
+ };
55
+ const statusFallbackResponse = {
56
+ ok: false,
57
+ status: 503,
58
+ json: vi.fn().mockRejectedValue(new Error("invalid json"))
59
+ };
60
+ const fetchMock = vi.fn().mockResolvedValueOnce(userFacingResponse).mockResolvedValueOnce(statusFallbackResponse);
61
+ vi.stubGlobal("fetch", fetchMock);
62
+ await expect(metaFetch({ accessToken: "token" }, "/broken")).rejects.toThrow(
63
+ "Choose a different account."
64
+ );
65
+ await expect(metaFetch({ accessToken: "token" }, "/broken-again")).rejects.toThrow(
66
+ "Meta API request failed with status 503"
67
+ );
68
+ });
69
+ });
70
+ //# sourceMappingURL=meta-api.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/services/meta/__tests__/meta-api.test.ts"],
4
+ "sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nconst getMetaConfigMock = vi.fn();\nconst createMetaAppSecretProofMock = vi.fn();\n\nvi.mock(\"../../../config/meta.js\", () => ({\n getMetaConfig: getMetaConfigMock,\n createMetaAppSecretProof: createMetaAppSecretProofMock,\n}));\n\nconst { metaFetch } = await import(\"../meta-api.js\");\n\ndescribe(\"meta api\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n getMetaConfigMock.mockReturnValue({\n baseUrl: \"https://graph.facebook.com/v25.0\",\n appSecret: \"app-secret\",\n });\n createMetaAppSecretProofMock.mockReturnValue(\"proof-123\");\n });\n\n it(\"builds Graph API URLs with token, proof and non-empty query params\", async () => {\n const fetchMock = vi.fn().mockResolvedValue({\n ok: true,\n json: vi.fn().mockResolvedValue({ data: [{ id: \"biz-1\" }] }),\n });\n vi.stubGlobal(\"fetch\", fetchMock);\n\n const result = await metaFetch(\n { accessToken: \"access-token\" },\n \"me/businesses\",\n {\n fields: \"id,name\",\n limit: 100,\n after: undefined,\n empty: \"\",\n }\n );\n\n expect(result).toEqual({ data: [{ id: \"biz-1\" }] });\n expect(createMetaAppSecretProofMock).toHaveBeenCalledWith(\"access-token\", \"app-secret\");\n expect(fetchMock).toHaveBeenCalledWith(\n \"https://graph.facebook.com/v25.0/me/businesses?access_token=access-token&appsecret_proof=proof-123&fields=id%2Cname&limit=100\",\n {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n }\n );\n });\n\n it(\"prefers user-facing Meta errors and falls back to status-based messages\", async () => {\n const userFacingResponse = {\n ok: false,\n status: 400,\n json: vi.fn().mockResolvedValue({\n error: {\n error_user_msg: \"Choose a different account.\",\n message: \"Low-level error\",\n },\n }),\n };\n const statusFallbackResponse = {\n ok: false,\n status: 503,\n json: vi.fn().mockRejectedValue(new Error(\"invalid json\")),\n };\n const fetchMock = vi\n .fn()\n .mockResolvedValueOnce(userFacingResponse)\n .mockResolvedValueOnce(statusFallbackResponse);\n vi.stubGlobal(\"fetch\", fetchMock);\n\n await expect(metaFetch({ accessToken: \"token\" }, \"/broken\")).rejects.toThrow(\n \"Choose a different account.\"\n );\n await expect(metaFetch({ accessToken: \"token\" }, \"/broken-again\")).rejects.toThrow(\n \"Meta API request failed with status 503\"\n );\n });\n});\n"],
5
+ "mappings": "AAAA,SAAS,YAAY,UAAU,QAAQ,IAAI,UAAU;AAErD,MAAM,oBAAoB,GAAG,GAAG;AAChC,MAAM,+BAA+B,GAAG,GAAG;AAE3C,GAAG,KAAK,2BAA2B,OAAO;AAAA,EACxC,eAAe;AAAA,EACf,0BAA0B;AAC5B,EAAE;AAEF,MAAM,EAAE,UAAU,IAAI,MAAM,OAAO,gBAAgB;AAEnD,SAAS,YAAY,MAAM;AACzB,aAAW,MAAM;AACf,OAAG,cAAc;AACjB,sBAAkB,gBAAgB;AAAA,MAChC,SAAS;AAAA,MACT,WAAW;AAAA,IACb,CAAC;AACD,iCAA6B,gBAAgB,WAAW;AAAA,EAC1D,CAAC;AAED,KAAG,sEAAsE,YAAY;AACnF,UAAM,YAAY,GAAG,GAAG,EAAE,kBAAkB;AAAA,MAC1C,IAAI;AAAA,MACJ,MAAM,GAAG,GAAG,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,IAAI,QAAQ,CAAC,EAAE,CAAC;AAAA,IAC7D,CAAC;AACD,OAAG,WAAW,SAAS,SAAS;AAEhC,UAAM,SAAS,MAAM;AAAA,MACnB,EAAE,aAAa,eAAe;AAAA,MAC9B;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,IAAI,QAAQ,CAAC,EAAE,CAAC;AAClD,WAAO,4BAA4B,EAAE,qBAAqB,gBAAgB,YAAY;AACtF,WAAO,SAAS,EAAE;AAAA,MAChB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,QAAQ,mBAAmB;AAAA,MACxC;AAAA,IACF;AAAA,EACF,CAAC;AAED,KAAG,2EAA2E,YAAY;AACxF,UAAM,qBAAqB;AAAA,MACzB,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,MAAM,GAAG,GAAG,EAAE,kBAAkB;AAAA,QAC9B,OAAO;AAAA,UACL,gBAAgB;AAAA,UAChB,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,yBAAyB;AAAA,MAC7B,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,MAAM,GAAG,GAAG,EAAE,kBAAkB,IAAI,MAAM,cAAc,CAAC;AAAA,IAC3D;AACA,UAAM,YAAY,GACf,GAAG,EACH,sBAAsB,kBAAkB,EACxC,sBAAsB,sBAAsB;AAC/C,OAAG,WAAW,SAAS,SAAS;AAEhC,UAAM,OAAO,UAAU,EAAE,aAAa,QAAQ,GAAG,SAAS,CAAC,EAAE,QAAQ;AAAA,MACnE;AAAA,IACF;AACA,UAAM,OAAO,UAAU,EAAE,aAAa,QAAQ,GAAG,eAAe,CAAC,EAAE,QAAQ;AAAA,MACzE;AAAA,IACF;AAAA,EACF,CAAC;AACH,CAAC;",
6
+ "names": []
7
+ }
@@ -1,4 +1,6 @@
1
- import { metaFetch } from "./meta-api.js";
1
+ import {
2
+ metaFetch
3
+ } from "./meta-api.js";
2
4
  import { normalizeMetaAdAccountId, toActId } from "../../meta/meta-utils.js";
3
5
  function decodePagingCursor(url) {
4
6
  if (!url) {
@@ -10,12 +12,22 @@ function decodePagingCursor(url) {
10
12
  return void 0;
11
13
  }
12
14
  }
13
- async function listAccessibleMetaBusinesses(after) {
14
- const response = await metaFetch("/me/businesses", {
15
- fields: "id,name,verification_status",
16
- limit: 100,
17
- after
18
- });
15
+ function normalizeMetaAdAccount(account) {
16
+ return {
17
+ ...account,
18
+ account_id: account.account_id || normalizeMetaAdAccountId(account.id || "")
19
+ };
20
+ }
21
+ async function listAccessibleMetaBusinesses(context, after) {
22
+ const response = await metaFetch(
23
+ context,
24
+ "/me/businesses",
25
+ {
26
+ fields: "id,name,verification_status",
27
+ limit: 100,
28
+ after
29
+ }
30
+ );
19
31
  return {
20
32
  businesses: response.data ?? [],
21
33
  paging: {
@@ -25,16 +37,22 @@ async function listAccessibleMetaBusinesses(after) {
25
37
  }
26
38
  };
27
39
  }
28
- async function listAccessibleMetaAdAccounts(after) {
29
- const response = await metaFetch("/me/adaccounts", {
30
- fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}",
31
- limit: 100,
32
- after
40
+ async function getMetaBusinessInfo(context, businessId) {
41
+ return metaFetch(context, `/${businessId}`, {
42
+ fields: "id,name,verification_status"
33
43
  });
34
- const accounts = (response.data ?? []).map((account) => ({
35
- ...account,
36
- account_id: account.account_id || normalizeMetaAdAccountId(account.id || "")
37
- }));
44
+ }
45
+ async function listAccessibleMetaAdAccounts(context, after) {
46
+ const response = await metaFetch(
47
+ context,
48
+ "/me/adaccounts",
49
+ {
50
+ fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}",
51
+ limit: 100,
52
+ after
53
+ }
54
+ );
55
+ const accounts = (response.data ?? []).map(normalizeMetaAdAccount);
38
56
  return {
39
57
  adAccounts: accounts,
40
58
  paging: {
@@ -44,42 +62,78 @@ async function listAccessibleMetaAdAccounts(after) {
44
62
  }
45
63
  };
46
64
  }
47
- async function getMetaAdAccountInfo(adAccountId) {
48
- return metaFetch(`/${toActId(adAccountId)}`, {
49
- fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap"
50
- });
65
+ async function getMetaAdAccountInfo(context, adAccountId) {
66
+ const account = await metaFetch(
67
+ context,
68
+ `/${toActId(adAccountId)}`,
69
+ {
70
+ fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap"
71
+ }
72
+ );
73
+ return normalizeMetaAdAccount(account);
51
74
  }
52
- async function getMetaInsights(objectId, params) {
53
- const response = await metaFetch(`/${objectId}/insights`, {
54
- fields: "impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas,date_start,date_stop",
55
- ...params
56
- });
75
+ async function getMetaAdAccountsByIds(context, adAccountIds) {
76
+ const uniqueIds = Array.from(
77
+ new Set(
78
+ adAccountIds.map((adAccountId) => normalizeMetaAdAccountId(adAccountId))
79
+ )
80
+ );
81
+ const results = await Promise.allSettled(
82
+ uniqueIds.map((adAccountId) => getMetaAdAccountInfo(context, adAccountId))
83
+ );
84
+ return results.flatMap(
85
+ (result) => result.status === "fulfilled" ? [result.value] : []
86
+ );
87
+ }
88
+ async function getMetaInsights(context, objectId, params) {
89
+ const response = await metaFetch(
90
+ context,
91
+ `/${objectId}/insights`,
92
+ {
93
+ fields: "impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas,date_start,date_stop",
94
+ ...params
95
+ }
96
+ );
57
97
  return response.data ?? [];
58
98
  }
59
- async function getMetaCampaigns(adAccountId, limit = 50) {
60
- const response = await metaFetch(`/${toActId(adAccountId)}/campaigns`, {
61
- fields: "id,name,status,effective_status,objective",
62
- limit
63
- });
99
+ async function getMetaCampaigns(context, adAccountId, limit = 50) {
100
+ const response = await metaFetch(
101
+ context,
102
+ `/${toActId(adAccountId)}/campaigns`,
103
+ {
104
+ fields: "id,name,status,effective_status,objective",
105
+ limit
106
+ }
107
+ );
64
108
  return response.data ?? [];
65
109
  }
66
- async function getMetaAds(adAccountId, limit = 50) {
67
- const response = await metaFetch(`/${toActId(adAccountId)}/ads`, {
68
- fields: "id,name,status,effective_status,creative{id}",
69
- limit
70
- });
110
+ async function getMetaAds(context, adAccountId, limit = 50) {
111
+ const response = await metaFetch(
112
+ context,
113
+ `/${toActId(adAccountId)}/ads`,
114
+ {
115
+ fields: "id,name,status,effective_status,creative{id}",
116
+ limit
117
+ }
118
+ );
71
119
  return response.data ?? [];
72
120
  }
73
- async function getMetaPlacementInsights(adAccountId, params) {
74
- const response = await metaFetch(`/${toActId(adAccountId)}/insights`, {
75
- fields: "impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas",
76
- ...params
77
- });
121
+ async function getMetaPlacementInsights(context, adAccountId, params) {
122
+ const response = await metaFetch(
123
+ context,
124
+ `/${toActId(adAccountId)}/insights`,
125
+ {
126
+ fields: "impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas",
127
+ ...params
128
+ }
129
+ );
78
130
  return response.data ?? [];
79
131
  }
80
132
  export {
81
133
  getMetaAdAccountInfo,
134
+ getMetaAdAccountsByIds,
82
135
  getMetaAds,
136
+ getMetaBusinessInfo,
83
137
  getMetaCampaigns,
84
138
  getMetaInsights,
85
139
  getMetaPlacementInsights,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/services/meta/meta-ads.ts"],
4
- "sourcesContent": ["import { metaFetch, type MetaListResponse } from \"./meta-api.js\";\nimport { normalizeMetaAdAccountId, toActId } from \"../../meta/meta-utils.js\";\n\nexport interface MetaBusiness {\n id?: string;\n name?: string;\n verification_status?: string;\n}\n\nexport interface MetaBusinessRef {\n id?: string;\n name?: string;\n}\n\nexport interface MetaAdAccount {\n id?: string;\n account_id?: string;\n name?: string;\n currency?: string;\n account_status?: number;\n timezone_name?: string;\n timezone_offset_hours_utc?: number;\n business?: MetaBusinessRef;\n}\n\nexport interface MetaActionMetric {\n action_type?: string;\n value?: string;\n}\n\nexport interface MetaRoasMetric {\n action_type?: string;\n value?: string;\n}\n\nexport interface MetaAdInsight {\n date_start?: string;\n date_stop?: string;\n impressions?: string;\n reach?: string;\n clicks?: string;\n spend?: string;\n ctr?: string;\n cpc?: string;\n cpm?: string;\n actions?: MetaActionMetric[];\n action_values?: MetaActionMetric[];\n purchase_roas?: MetaRoasMetric[];\n}\n\nexport interface MetaCampaign {\n id?: string;\n name?: string;\n status?: string;\n effective_status?: string;\n objective?: string;\n}\n\nexport interface MetaAd {\n id?: string;\n name?: string;\n status?: string;\n effective_status?: string;\n creative?: {\n id?: string;\n };\n}\n\nexport interface MetaPlacementRow extends MetaAdInsight {\n publisher_platform?: string;\n platform_position?: string;\n impression_device?: string;\n}\n\nfunction decodePagingCursor(url: string | undefined): string | undefined {\n if (!url) {\n return undefined;\n }\n\n try {\n return new URL(url).searchParams.get(\"after\") || undefined;\n } catch {\n return undefined;\n }\n}\n\nexport async function listAccessibleMetaBusinesses(after?: string) {\n const response = await metaFetch<MetaListResponse<MetaBusiness>>(\"/me/businesses\", {\n fields: \"id,name,verification_status\",\n limit: 100,\n after,\n });\n\n return {\n businesses: response.data ?? [],\n paging: {\n after: response.paging?.cursors?.after || decodePagingCursor(response.paging?.next),\n before: response.paging?.cursors?.before,\n has_more: Boolean(response.paging?.next),\n },\n };\n}\n\nexport async function listAccessibleMetaAdAccounts(after?: string) {\n const response = await metaFetch<MetaListResponse<MetaAdAccount>>(\"/me/adaccounts\", {\n fields:\n \"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}\",\n limit: 100,\n after,\n });\n\n const accounts = (response.data ?? []).map((account) => ({\n ...account,\n account_id: account.account_id || normalizeMetaAdAccountId(account.id || \"\"),\n }));\n\n return {\n adAccounts: accounts,\n paging: {\n after: response.paging?.cursors?.after || decodePagingCursor(response.paging?.next),\n before: response.paging?.cursors?.before,\n has_more: Boolean(response.paging?.next),\n },\n };\n}\n\nexport async function getMetaAdAccountInfo(adAccountId: string): Promise<MetaAdAccount> {\n return metaFetch<MetaAdAccount>(`/${toActId(adAccountId)}`, {\n fields:\n \"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap\",\n });\n}\n\nexport async function getMetaInsights(\n objectId: string,\n params: Record<string, string | number | undefined>\n): Promise<MetaAdInsight[]> {\n const response = await metaFetch<MetaListResponse<MetaAdInsight>>(`/${objectId}/insights`, {\n fields:\n \"impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas,date_start,date_stop\",\n ...params,\n });\n\n return response.data ?? [];\n}\n\nexport async function getMetaCampaigns(adAccountId: string, limit = 50): Promise<MetaCampaign[]> {\n const response = await metaFetch<MetaListResponse<MetaCampaign>>(`/${toActId(adAccountId)}/campaigns`, {\n fields: \"id,name,status,effective_status,objective\",\n limit,\n });\n\n return response.data ?? [];\n}\n\nexport async function getMetaAds(adAccountId: string, limit = 50): Promise<MetaAd[]> {\n const response = await metaFetch<MetaListResponse<MetaAd>>(`/${toActId(adAccountId)}/ads`, {\n fields: \"id,name,status,effective_status,creative{id}\",\n limit,\n });\n\n return response.data ?? [];\n}\n\nexport async function getMetaPlacementInsights(\n adAccountId: string,\n params: Record<string, string | number | undefined>\n): Promise<MetaPlacementRow[]> {\n const response = await metaFetch<MetaListResponse<MetaPlacementRow>>(`/${toActId(adAccountId)}/insights`, {\n fields:\n \"impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas\",\n ...params,\n });\n\n return response.data ?? [];\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAwC;AACjD,SAAS,0BAA0B,eAAe;AAyElD,SAAS,mBAAmB,KAA6C;AACvE,MAAI,CAAC,KAAK;AACR,WAAO;AAAA,EACT;AAEA,MAAI;AACF,WAAO,IAAI,IAAI,GAAG,EAAE,aAAa,IAAI,OAAO,KAAK;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,6BAA6B,OAAgB;AACjE,QAAM,WAAW,MAAM,UAA0C,kBAAkB;AAAA,IACjF,QAAQ;AAAA,IACR,OAAO;AAAA,IACP;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,YAAY,SAAS,QAAQ,CAAC;AAAA,IAC9B,QAAQ;AAAA,MACN,OAAO,SAAS,QAAQ,SAAS,SAAS,mBAAmB,SAAS,QAAQ,IAAI;AAAA,MAClF,QAAQ,SAAS,QAAQ,SAAS;AAAA,MAClC,UAAU,QAAQ,SAAS,QAAQ,IAAI;AAAA,IACzC;AAAA,EACF;AACF;AAEA,eAAsB,6BAA6B,OAAgB;AACjE,QAAM,WAAW,MAAM,UAA2C,kBAAkB;AAAA,IAClF,QACE;AAAA,IACF,OAAO;AAAA,IACP;AAAA,EACF,CAAC;AAED,QAAM,YAAY,SAAS,QAAQ,CAAC,GAAG,IAAI,CAAC,aAAa;AAAA,IACvD,GAAG;AAAA,IACH,YAAY,QAAQ,cAAc,yBAAyB,QAAQ,MAAM,EAAE;AAAA,EAC7E,EAAE;AAEF,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,QAAQ;AAAA,MACN,OAAO,SAAS,QAAQ,SAAS,SAAS,mBAAmB,SAAS,QAAQ,IAAI;AAAA,MAClF,QAAQ,SAAS,QAAQ,SAAS;AAAA,MAClC,UAAU,QAAQ,SAAS,QAAQ,IAAI;AAAA,IACzC;AAAA,EACF;AACF;AAEA,eAAsB,qBAAqB,aAA6C;AACtF,SAAO,UAAyB,IAAI,QAAQ,WAAW,CAAC,IAAI;AAAA,IAC1D,QACE;AAAA,EACJ,CAAC;AACH;AAEA,eAAsB,gBACpB,UACA,QAC0B;AAC1B,QAAM,WAAW,MAAM,UAA2C,IAAI,QAAQ,aAAa;AAAA,IACzF,QACE;AAAA,IACF,GAAG;AAAA,EACL,CAAC;AAED,SAAO,SAAS,QAAQ,CAAC;AAC3B;AAEA,eAAsB,iBAAiB,aAAqB,QAAQ,IAA6B;AAC/F,QAAM,WAAW,MAAM,UAA0C,IAAI,QAAQ,WAAW,CAAC,cAAc;AAAA,IACrG,QAAQ;AAAA,IACR;AAAA,EACF,CAAC;AAED,SAAO,SAAS,QAAQ,CAAC;AAC3B;AAEA,eAAsB,WAAW,aAAqB,QAAQ,IAAuB;AACnF,QAAM,WAAW,MAAM,UAAoC,IAAI,QAAQ,WAAW,CAAC,QAAQ;AAAA,IACzF,QAAQ;AAAA,IACR;AAAA,EACF,CAAC;AAED,SAAO,SAAS,QAAQ,CAAC;AAC3B;AAEA,eAAsB,yBACpB,aACA,QAC6B;AAC7B,QAAM,WAAW,MAAM,UAA8C,IAAI,QAAQ,WAAW,CAAC,aAAa;AAAA,IACxG,QACE;AAAA,IACF,GAAG;AAAA,EACL,CAAC;AAED,SAAO,SAAS,QAAQ,CAAC;AAC3B;",
4
+ "sourcesContent": ["import {\n\tmetaFetch,\n\ttype MetaListResponse,\n\ttype MetaRequestContext,\n} from \"./meta-api.js\";\nimport { normalizeMetaAdAccountId, toActId } from \"../../meta/meta-utils.js\";\n\nexport interface MetaBusiness {\n\tid?: string;\n\tname?: string;\n\tverification_status?: string;\n}\n\nexport interface MetaBusinessRef {\n\tid?: string;\n\tname?: string;\n}\n\nexport interface MetaAdAccount {\n\tid?: string;\n\taccount_id?: string;\n\tname?: string;\n\tcurrency?: string;\n\taccount_status?: number;\n\ttimezone_name?: string;\n\ttimezone_offset_hours_utc?: number;\n\tbusiness?: MetaBusinessRef;\n}\n\nexport interface MetaActionMetric {\n\taction_type?: string;\n\tvalue?: string;\n}\n\nexport interface MetaRoasMetric {\n\taction_type?: string;\n\tvalue?: string;\n}\n\nexport interface MetaAdInsight {\n\tdate_start?: string;\n\tdate_stop?: string;\n\timpressions?: string;\n\treach?: string;\n\tclicks?: string;\n\tspend?: string;\n\tctr?: string;\n\tcpc?: string;\n\tcpm?: string;\n\tactions?: MetaActionMetric[];\n\taction_values?: MetaActionMetric[];\n\tpurchase_roas?: MetaRoasMetric[];\n}\n\nexport interface MetaCampaign {\n\tid?: string;\n\tname?: string;\n\tstatus?: string;\n\teffective_status?: string;\n\tobjective?: string;\n}\n\nexport interface MetaAd {\n\tid?: string;\n\tname?: string;\n\tstatus?: string;\n\teffective_status?: string;\n\tcreative?: {\n\t\tid?: string;\n\t};\n}\n\nexport interface MetaPlacementRow extends MetaAdInsight {\n\tpublisher_platform?: string;\n\tplatform_position?: string;\n\timpression_device?: string;\n}\n\nfunction decodePagingCursor(url: string | undefined): string | undefined {\n\tif (!url) {\n\t\treturn undefined;\n\t}\n\n\ttry {\n\t\treturn new URL(url).searchParams.get(\"after\") || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction normalizeMetaAdAccount(account: MetaAdAccount): MetaAdAccount {\n\treturn {\n\t\t...account,\n\t\taccount_id:\n\t\t\taccount.account_id || normalizeMetaAdAccountId(account.id || \"\"),\n\t};\n}\n\nexport async function listAccessibleMetaBusinesses(\n\tcontext: MetaRequestContext,\n\tafter?: string,\n) {\n\tconst response = await metaFetch<MetaListResponse<MetaBusiness>>(\n\t\tcontext,\n\t\t\"/me/businesses\",\n\t\t{\n\t\t\tfields: \"id,name,verification_status\",\n\t\t\tlimit: 100,\n\t\t\tafter,\n\t\t},\n\t);\n\n\treturn {\n\t\tbusinesses: response.data ?? [],\n\t\tpaging: {\n\t\t\tafter:\n\t\t\t\tresponse.paging?.cursors?.after ||\n\t\t\t\tdecodePagingCursor(response.paging?.next),\n\t\t\tbefore: response.paging?.cursors?.before,\n\t\t\thas_more: Boolean(response.paging?.next),\n\t\t},\n\t};\n}\n\nexport async function getMetaBusinessInfo(\n\tcontext: MetaRequestContext,\n\tbusinessId: string,\n): Promise<MetaBusiness> {\n\treturn metaFetch<MetaBusiness>(context, `/${businessId}`, {\n\t\tfields: \"id,name,verification_status\",\n\t});\n}\n\nexport async function listAccessibleMetaAdAccounts(\n\tcontext: MetaRequestContext,\n\tafter?: string,\n) {\n\tconst response = await metaFetch<MetaListResponse<MetaAdAccount>>(\n\t\tcontext,\n\t\t\"/me/adaccounts\",\n\t\t{\n\t\t\tfields:\n\t\t\t\t\"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}\",\n\t\t\tlimit: 100,\n\t\t\tafter,\n\t\t},\n\t);\n\n\tconst accounts = (response.data ?? []).map(normalizeMetaAdAccount);\n\n\treturn {\n\t\tadAccounts: accounts,\n\t\tpaging: {\n\t\t\tafter:\n\t\t\t\tresponse.paging?.cursors?.after ||\n\t\t\t\tdecodePagingCursor(response.paging?.next),\n\t\t\tbefore: response.paging?.cursors?.before,\n\t\t\thas_more: Boolean(response.paging?.next),\n\t\t},\n\t};\n}\n\nexport async function getMetaAdAccountInfo(\n\tcontext: MetaRequestContext,\n\tadAccountId: string,\n): Promise<MetaAdAccount> {\n\tconst account = await metaFetch<MetaAdAccount>(\n\t\tcontext,\n\t\t`/${toActId(adAccountId)}`,\n\t\t{\n\t\t\tfields:\n\t\t\t\t\"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap\",\n\t\t},\n\t);\n\n\treturn normalizeMetaAdAccount(account);\n}\n\nexport async function getMetaAdAccountsByIds(\n\tcontext: MetaRequestContext,\n\tadAccountIds: string[],\n): Promise<MetaAdAccount[]> {\n\tconst uniqueIds = Array.from(\n\t\tnew Set(\n\t\t\tadAccountIds.map((adAccountId) => normalizeMetaAdAccountId(adAccountId)),\n\t\t),\n\t);\n\tconst results = await Promise.allSettled(\n\t\tuniqueIds.map((adAccountId) => getMetaAdAccountInfo(context, adAccountId)),\n\t);\n\n\treturn results.flatMap((result) =>\n\t\tresult.status === \"fulfilled\" ? [result.value] : [],\n\t);\n}\n\nexport async function getMetaInsights(\n\tcontext: MetaRequestContext,\n\tobjectId: string,\n\tparams: Record<string, string | number | undefined>,\n): Promise<MetaAdInsight[]> {\n\tconst response = await metaFetch<MetaListResponse<MetaAdInsight>>(\n\t\tcontext,\n\t\t`/${objectId}/insights`,\n\t\t{\n\t\t\tfields:\n\t\t\t\t\"impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas,date_start,date_stop\",\n\t\t\t...params,\n\t\t},\n\t);\n\n\treturn response.data ?? [];\n}\n\nexport async function getMetaCampaigns(\n\tcontext: MetaRequestContext,\n\tadAccountId: string,\n\tlimit = 50,\n): Promise<MetaCampaign[]> {\n\tconst response = await metaFetch<MetaListResponse<MetaCampaign>>(\n\t\tcontext,\n\t\t`/${toActId(adAccountId)}/campaigns`,\n\t\t{\n\t\t\tfields: \"id,name,status,effective_status,objective\",\n\t\t\tlimit,\n\t\t},\n\t);\n\n\treturn response.data ?? [];\n}\n\nexport async function getMetaAds(\n\tcontext: MetaRequestContext,\n\tadAccountId: string,\n\tlimit = 50,\n): Promise<MetaAd[]> {\n\tconst response = await metaFetch<MetaListResponse<MetaAd>>(\n\t\tcontext,\n\t\t`/${toActId(adAccountId)}/ads`,\n\t\t{\n\t\t\tfields: \"id,name,status,effective_status,creative{id}\",\n\t\t\tlimit,\n\t\t},\n\t);\n\n\treturn response.data ?? [];\n}\n\nexport async function getMetaPlacementInsights(\n\tcontext: MetaRequestContext,\n\tadAccountId: string,\n\tparams: Record<string, string | number | undefined>,\n): Promise<MetaPlacementRow[]> {\n\tconst response = await metaFetch<MetaListResponse<MetaPlacementRow>>(\n\t\tcontext,\n\t\t`/${toActId(adAccountId)}/insights`,\n\t\t{\n\t\t\tfields:\n\t\t\t\t\"impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas\",\n\t\t\t...params,\n\t\t},\n\t);\n\n\treturn response.data ?? [];\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,11 +1,13 @@
1
1
  import { createMetaAppSecretProof, getMetaConfig } from "../../config/meta.js";
2
- function buildUrl(path, query) {
2
+ function buildUrl(context, path, query) {
3
3
  const config = getMetaConfig();
4
- const accessToken = config.accessToken;
5
- const appSecretProof = createMetaAppSecretProof(accessToken, config.appSecret);
4
+ const appSecretProof = createMetaAppSecretProof(
5
+ context.accessToken,
6
+ config.appSecret
7
+ );
6
8
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
7
9
  const searchParams = new URLSearchParams();
8
- searchParams.set("access_token", accessToken);
10
+ searchParams.set("access_token", context.accessToken);
9
11
  searchParams.set("appsecret_proof", appSecretProof);
10
12
  for (const [key, value] of Object.entries(query)) {
11
13
  if (value === void 0 || value === null || value === "") {
@@ -15,8 +17,8 @@ function buildUrl(path, query) {
15
17
  }
16
18
  return `${config.baseUrl}${normalizedPath}?${searchParams.toString()}`;
17
19
  }
18
- async function metaFetch(path, query = {}) {
19
- const response = await fetch(buildUrl(path, query), {
20
+ async function metaFetch(context, path, query = {}) {
21
+ const response = await fetch(buildUrl(context, path, query), {
20
22
  method: "GET",
21
23
  headers: {
22
24
  Accept: "application/json"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/services/meta/meta-api.ts"],
4
- "sourcesContent": ["import { createMetaAppSecretProof, getMetaConfig } from \"../../config/meta.js\";\n\ninterface MetaErrorPayload {\n error?: {\n message?: string;\n type?: string;\n code?: number;\n error_subcode?: number;\n error_user_title?: string;\n error_user_msg?: string;\n fbtrace_id?: string;\n };\n}\n\nexport interface MetaPagingCursor {\n before?: string;\n after?: string;\n}\n\nexport interface MetaPaging {\n cursors?: MetaPagingCursor;\n next?: string;\n previous?: string;\n}\n\nexport interface MetaListResponse<T> {\n data?: T[];\n paging?: MetaPaging;\n}\n\nfunction buildUrl(path: string, query: Record<string, string | number | undefined>): string {\n const config = getMetaConfig();\n const accessToken = config.accessToken;\n const appSecretProof = createMetaAppSecretProof(accessToken, config.appSecret);\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n const searchParams = new URLSearchParams();\n\n searchParams.set(\"access_token\", accessToken);\n searchParams.set(\"appsecret_proof\", appSecretProof);\n\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null || value === \"\") {\n continue;\n }\n\n searchParams.set(key, String(value));\n }\n\n return `${config.baseUrl}${normalizedPath}?${searchParams.toString()}`;\n}\n\nexport async function metaFetch<T>(\n path: string,\n query: Record<string, string | number | undefined> = {}\n): Promise<T> {\n const response = await fetch(buildUrl(path, query), {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as MetaErrorPayload;\n const message =\n payload.error?.error_user_msg ||\n payload.error?.error_user_title ||\n payload.error?.message ||\n `Meta API request failed with status ${response.status}`;\n\n throw new Error(message);\n }\n\n return (await response.json()) as T;\n}\n"],
5
- "mappings": "AAAA,SAAS,0BAA0B,qBAAqB;AA8BxD,SAAS,SAAS,MAAc,OAA4D;AAC1F,QAAM,SAAS,cAAc;AAC7B,QAAM,cAAc,OAAO;AAC3B,QAAM,iBAAiB,yBAAyB,aAAa,OAAO,SAAS;AAC7E,QAAM,iBAAiB,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AAC7D,QAAM,eAAe,IAAI,gBAAgB;AAEzC,eAAa,IAAI,gBAAgB,WAAW;AAC5C,eAAa,IAAI,mBAAmB,cAAc;AAElD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD;AAAA,IACF;AAEA,iBAAa,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,EACrC;AAEA,SAAO,GAAG,OAAO,OAAO,GAAG,cAAc,IAAI,aAAa,SAAS,CAAC;AACtE;AAEA,eAAsB,UACpB,MACA,QAAqD,CAAC,GAC1C;AACZ,QAAM,WAAW,MAAM,MAAM,SAAS,MAAM,KAAK,GAAG;AAAA,IAClD,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,UAAW,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACvD,UAAM,UACJ,QAAQ,OAAO,kBACf,QAAQ,OAAO,oBACf,QAAQ,OAAO,WACf,uCAAuC,SAAS,MAAM;AAExD,UAAM,IAAI,MAAM,OAAO;AAAA,EACzB;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;",
4
+ "sourcesContent": ["import { createMetaAppSecretProof, getMetaConfig } from \"../../config/meta.js\";\n\ninterface MetaErrorPayload {\n\terror?: {\n\t\tmessage?: string;\n\t\ttype?: string;\n\t\tcode?: number;\n\t\terror_subcode?: number;\n\t\terror_user_title?: string;\n\t\terror_user_msg?: string;\n\t\tfbtrace_id?: string;\n\t};\n}\n\nexport interface MetaRequestContext {\n\taccessToken: string;\n}\n\nexport interface MetaPagingCursor {\n\tbefore?: string;\n\tafter?: string;\n}\n\nexport interface MetaPaging {\n\tcursors?: MetaPagingCursor;\n\tnext?: string;\n\tprevious?: string;\n}\n\nexport interface MetaListResponse<T> {\n\tdata?: T[];\n\tpaging?: MetaPaging;\n}\n\nfunction buildUrl(\n\tcontext: MetaRequestContext,\n\tpath: string,\n\tquery: Record<string, string | number | undefined>,\n): string {\n\tconst config = getMetaConfig();\n\tconst appSecretProof = createMetaAppSecretProof(\n\t\tcontext.accessToken,\n\t\tconfig.appSecret,\n\t);\n\tconst normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n\tconst searchParams = new URLSearchParams();\n\n\tsearchParams.set(\"access_token\", context.accessToken);\n\tsearchParams.set(\"appsecret_proof\", appSecretProof);\n\n\tfor (const [key, value] of Object.entries(query)) {\n\t\tif (value === undefined || value === null || value === \"\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tsearchParams.set(key, String(value));\n\t}\n\n\treturn `${config.baseUrl}${normalizedPath}?${searchParams.toString()}`;\n}\n\nexport async function metaFetch<T>(\n\tcontext: MetaRequestContext,\n\tpath: string,\n\tquery: Record<string, string | number | undefined> = {},\n): Promise<T> {\n\tconst response = await fetch(buildUrl(context, path, query), {\n\t\tmethod: \"GET\",\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t});\n\n\tif (!response.ok) {\n\t\tconst payload = (await response\n\t\t\t.json()\n\t\t\t.catch(() => ({}))) as MetaErrorPayload;\n\t\tconst message =\n\t\t\tpayload.error?.error_user_msg ||\n\t\t\tpayload.error?.error_user_title ||\n\t\t\tpayload.error?.message ||\n\t\t\t`Meta API request failed with status ${response.status}`;\n\n\t\tthrow new Error(message);\n\t}\n\n\treturn (await response.json()) as T;\n}\n"],
5
+ "mappings": "AAAA,SAAS,0BAA0B,qBAAqB;AAkCxD,SAAS,SACR,SACA,MACA,OACS;AACT,QAAM,SAAS,cAAc;AAC7B,QAAM,iBAAiB;AAAA,IACtB,QAAQ;AAAA,IACR,OAAO;AAAA,EACR;AACA,QAAM,iBAAiB,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AAC7D,QAAM,eAAe,IAAI,gBAAgB;AAEzC,eAAa,IAAI,gBAAgB,QAAQ,WAAW;AACpD,eAAa,IAAI,mBAAmB,cAAc;AAElD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACjD,QAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AAC1D;AAAA,IACD;AAEA,iBAAa,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,EACpC;AAEA,SAAO,GAAG,OAAO,OAAO,GAAG,cAAc,IAAI,aAAa,SAAS,CAAC;AACrE;AAEA,eAAsB,UACrB,SACA,MACA,QAAqD,CAAC,GACzC;AACb,QAAM,WAAW,MAAM,MAAM,SAAS,SAAS,MAAM,KAAK,GAAG;AAAA,IAC5D,QAAQ;AAAA,IACR,SAAS;AAAA,MACR,QAAQ;AAAA,IACT;AAAA,EACD,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AACjB,UAAM,UAAW,MAAM,SACrB,KAAK,EACL,MAAM,OAAO,CAAC,EAAE;AAClB,UAAM,UACL,QAAQ,OAAO,kBACf,QAAQ,OAAO,oBACf,QAAQ,OAAO,WACf,uCAAuC,SAAS,MAAM;AAEvD,UAAM,IAAI,MAAM,OAAO;AAAA,EACxB;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC7B;",
6
6
  "names": []
7
7
  }