@yoryoboy/bi-mcp 1.5.2 → 1.6.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 (182) hide show
  1. package/README.md +87 -87
  2. package/bin/bi-mcp.js +9 -9
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/mcp-use.json +2 -2
  7. package/dist/public/icon.svg +6 -6
  8. package/dist/scripts/_helpers.js.map +1 -1
  9. package/dist/scripts/admin-profile-delete.js.map +1 -1
  10. package/dist/scripts/admin-profile-list.js.map +1 -1
  11. package/dist/scripts/admin-profile-upsert.js.map +1 -1
  12. package/dist/scripts/admin-vtex-list.js.map +1 -1
  13. package/dist/scripts/admin-vtex-upsert.js.map +1 -1
  14. package/dist/scripts/admin-vtex-validate.js.map +1 -1
  15. package/dist/scripts/decrypt-secret.js +36 -0
  16. package/dist/scripts/decrypt-secret.js.map +7 -0
  17. package/dist/scripts/run-migrations.js.map +1 -1
  18. package/dist/scripts/test-db-connection.js.map +1 -1
  19. package/dist/src/analytics/ga4-channel-groups.js.map +1 -1
  20. package/dist/src/analytics/ga4-report-utils.js.map +1 -1
  21. package/dist/src/config/benchmarks.js.map +1 -1
  22. package/dist/src/config/google-store.js.map +1 -1
  23. package/dist/src/config/google.js.map +1 -1
  24. package/dist/src/config/mercadolibre-profile-store.js.map +1 -1
  25. package/dist/src/config/mercadolibre.js.map +1 -1
  26. package/dist/src/config/meta.js.map +1 -1
  27. package/dist/src/config/profile-store.js.map +1 -1
  28. package/dist/src/config/vtex-crypto.js.map +1 -1
  29. package/dist/src/config/vtex-profile-store.js.map +1 -1
  30. package/dist/src/config/vtex.js.map +1 -1
  31. package/dist/src/db/client.js.map +1 -1
  32. package/dist/src/meta/meta-utils.js.map +1 -1
  33. package/dist/src/prompts/reporte-ventas.js.map +1 -1
  34. package/dist/src/services/analytics/ga4-client.js.map +1 -1
  35. package/dist/src/services/analytics/oauth.js.map +1 -1
  36. package/dist/src/services/google-ads/google-ads-client.js.map +1 -1
  37. package/dist/src/services/mercadolibre/mercadolibre-api.js.map +1 -1
  38. package/dist/src/services/mercadolibre/mercadolibre-items.js.map +1 -1
  39. package/dist/src/services/mercadolibre/mercadolibre-orders.js +19 -5
  40. package/dist/src/services/mercadolibre/mercadolibre-orders.js.map +2 -2
  41. package/dist/src/services/mercadolibre/mercadolibre-questions.js.map +1 -1
  42. package/dist/src/services/meta/meta-ads.js.map +1 -1
  43. package/dist/src/services/meta/meta-api.js.map +1 -1
  44. package/dist/src/services/search-console/search-console-client.js.map +1 -1
  45. package/dist/src/services/search-console/search-console-utils.js.map +1 -1
  46. package/dist/src/services/vtex/vtex-api.js.map +1 -1
  47. package/dist/src/services/vtex/vtex-catalog-write.js.map +1 -1
  48. package/dist/src/services/vtex/vtex-catalog.js.map +1 -1
  49. package/dist/src/services/vtex/vtex-logistics.js.map +1 -1
  50. package/dist/src/services/vtex/vtex-orders-write.js.map +1 -1
  51. package/dist/src/services/vtex/vtex-orders.js.map +1 -1
  52. package/dist/src/services/vtex/vtex-pricing-write.js.map +1 -1
  53. package/dist/src/services/vtex/vtex-pricing.js.map +1 -1
  54. package/dist/src/services/vtex/vtex-write.js.map +1 -1
  55. package/dist/src/tools/analytics/attribution-gaps.js.map +1 -1
  56. package/dist/src/tools/analytics/channel-mix.js.map +1 -1
  57. package/dist/src/tools/analytics/ecommerce-tracking-health.js.map +1 -1
  58. package/dist/src/tools/analytics/engagement-overview.js.map +1 -1
  59. package/dist/src/tools/analytics/index.js.map +1 -1
  60. package/dist/src/tools/analytics/list-accessible-properties.js.map +1 -1
  61. package/dist/src/tools/analytics/property-info.js.map +1 -1
  62. package/dist/src/tools/analytics/revenue-by-channel.js.map +1 -1
  63. package/dist/src/tools/analytics/revenue-overview.js.map +1 -1
  64. package/dist/src/tools/analytics/revenue-trend.js.map +1 -1
  65. package/dist/src/tools/analytics/source-medium-breakdown.js.map +1 -1
  66. package/dist/src/tools/analytics/top-landing-pages.js.map +1 -1
  67. package/dist/src/tools/config/check-database-connection.js.map +1 -1
  68. package/dist/src/tools/config/index.js.map +1 -1
  69. package/dist/src/tools/config/list-profiles.js.map +1 -1
  70. package/dist/src/tools/google-ads/account-overview.js.map +1 -1
  71. package/dist/src/tools/google-ads/account-risks.js.map +1 -1
  72. package/dist/src/tools/google-ads/break-even-analysis.js.map +1 -1
  73. package/dist/src/tools/google-ads/campaign-performance.js.map +1 -1
  74. package/dist/src/tools/google-ads/channel-mix.js.map +1 -1
  75. package/dist/src/tools/google-ads/compare-accounts.js.map +1 -1
  76. package/dist/src/tools/google-ads/customer-clients.js.map +1 -1
  77. package/dist/src/tools/google-ads/customer-info.js.map +1 -1
  78. package/dist/src/tools/google-ads/index.js.map +1 -1
  79. package/dist/src/tools/google-ads/scaling-health.js.map +1 -1
  80. package/dist/src/tools/google-ads/search-terms-summary.js.map +1 -1
  81. package/dist/src/tools/google-ads/time-series.js.map +1 -1
  82. package/dist/src/tools/index.js.map +1 -1
  83. package/dist/src/tools/mercadolibre/answer-question.js.map +1 -1
  84. package/dist/src/tools/mercadolibre/create-item.js.map +1 -1
  85. package/dist/src/tools/mercadolibre/estimate-listing-fee.js.map +1 -1
  86. package/dist/src/tools/mercadolibre/get-account-context.js.map +1 -1
  87. package/dist/src/tools/mercadolibre/get-category-requirements.js.map +1 -1
  88. package/dist/src/tools/mercadolibre/get-item-details.js.map +1 -1
  89. package/dist/src/tools/mercadolibre/get-item-visits.js.map +1 -1
  90. package/dist/src/tools/mercadolibre/get-listing-quality.js.map +1 -1
  91. package/dist/src/tools/mercadolibre/get-order-details.js.map +1 -1
  92. package/dist/src/tools/mercadolibre/get-orders-summary.js +670 -38
  93. package/dist/src/tools/mercadolibre/get-orders-summary.js.map +2 -2
  94. package/dist/src/tools/mercadolibre/get-sales-by-item.js.map +1 -1
  95. package/dist/src/tools/mercadolibre/get-sales-trend.js.map +1 -1
  96. package/dist/src/tools/mercadolibre/get-shipping-summary.js.map +1 -1
  97. package/dist/src/tools/mercadolibre/get-store-performance.js.map +1 -1
  98. package/dist/src/tools/mercadolibre/helpers.js +13 -0
  99. package/dist/src/tools/mercadolibre/helpers.js.map +2 -2
  100. package/dist/src/tools/mercadolibre/index.js.map +1 -1
  101. package/dist/src/tools/mercadolibre/pause-or-reactivate-item.js.map +1 -1
  102. package/dist/src/tools/mercadolibre/predict-category.js.map +1 -1
  103. package/dist/src/tools/mercadolibre/profile-resolution.js.map +1 -1
  104. package/dist/src/tools/mercadolibre/search-items.js.map +1 -1
  105. package/dist/src/tools/mercadolibre/search-questions.js.map +1 -1
  106. package/dist/src/tools/mercadolibre/update-item-basic-fields.js.map +1 -1
  107. package/dist/src/tools/mercadolibre/update-item-description.js.map +1 -1
  108. package/dist/src/tools/mercadolibre/update-item-pictures.js.map +1 -1
  109. package/dist/src/tools/mercadolibre/validate-connection.js.map +1 -1
  110. package/dist/src/tools/mercadolibre/write-helpers.js.map +1 -1
  111. package/dist/src/tools/meta/account-overview.js.map +1 -1
  112. package/dist/src/tools/meta/ad-account-info.js.map +1 -1
  113. package/dist/src/tools/meta/ads-performance.js.map +1 -1
  114. package/dist/src/tools/meta/campaign-performance.js.map +1 -1
  115. package/dist/src/tools/meta/index.js.map +1 -1
  116. package/dist/src/tools/meta/list-accessible-ad-accounts.js.map +1 -1
  117. package/dist/src/tools/meta/list-accessible-businesses.js.map +1 -1
  118. package/dist/src/tools/meta/placement-mix.js.map +1 -1
  119. package/dist/src/tools/meta/time-series.js.map +1 -1
  120. package/dist/src/tools/search-console/country-breakdown.js.map +1 -1
  121. package/dist/src/tools/search-console/device-breakdown.js.map +1 -1
  122. package/dist/src/tools/search-console/high-impression-low-click-queries.js.map +1 -1
  123. package/dist/src/tools/search-console/index.js.map +1 -1
  124. package/dist/src/tools/search-console/list-accessible-sites.js.map +1 -1
  125. package/dist/src/tools/search-console/low-ctr-opportunities.js.map +1 -1
  126. package/dist/src/tools/search-console/page-performance.js.map +1 -1
  127. package/dist/src/tools/search-console/product-demand-low-capture-queries.js.map +1 -1
  128. package/dist/src/tools/search-console/query-page-matrix.js.map +1 -1
  129. package/dist/src/tools/search-console/query-performance.js.map +1 -1
  130. package/dist/src/tools/search-console/quick-win-opportunities.js.map +1 -1
  131. package/dist/src/tools/search-console/rising-non-brand-queries.js.map +1 -1
  132. package/dist/src/tools/search-console/search-performance.js.map +1 -1
  133. package/dist/src/tools/search-console/site-context.js.map +1 -1
  134. package/dist/src/tools/search-console/visibility-declines.js.map +1 -1
  135. package/dist/src/tools/vtex/activate-sku.js.map +1 -1
  136. package/dist/src/tools/vtex/add-order-tracking.js.map +1 -1
  137. package/dist/src/tools/vtex/associate-specification.js.map +1 -1
  138. package/dist/src/tools/vtex/attach-catalog-image.js.map +1 -1
  139. package/dist/src/tools/vtex/cancel-order.js.map +1 -1
  140. package/dist/src/tools/vtex/computed-price.js.map +1 -1
  141. package/dist/src/tools/vtex/create-brand.js.map +1 -1
  142. package/dist/src/tools/vtex/create-category.js.map +1 -1
  143. package/dist/src/tools/vtex/create-product-with-sku.js.map +1 -1
  144. package/dist/src/tools/vtex/create-product.js.map +1 -1
  145. package/dist/src/tools/vtex/create-sku.js.map +1 -1
  146. package/dist/src/tools/vtex/create-specification-value.js.map +1 -1
  147. package/dist/src/tools/vtex/create-specification.js.map +1 -1
  148. package/dist/src/tools/vtex/deactivate-sku.js.map +1 -1
  149. package/dist/src/tools/vtex/delete-fixed-price.js.map +1 -1
  150. package/dist/src/tools/vtex/index.js.map +1 -1
  151. package/dist/src/tools/vtex/inventory-check.js.map +1 -1
  152. package/dist/src/tools/vtex/invoice-order.js.map +1 -1
  153. package/dist/src/tools/vtex/order-details.js.map +1 -1
  154. package/dist/src/tools/vtex/orders-summary.js.map +1 -1
  155. package/dist/src/tools/vtex/product-offers.js.map +1 -1
  156. package/dist/src/tools/vtex/profile-resolution.js.map +1 -1
  157. package/dist/src/tools/vtex/sku-offers.js.map +1 -1
  158. package/dist/src/tools/vtex/sku-price.js.map +1 -1
  159. package/dist/src/tools/vtex/toggle-unlimited-quantity.js.map +1 -1
  160. package/dist/src/tools/vtex/update-inventory.js.map +1 -1
  161. package/dist/src/tools/vtex/update-lead-time.js.map +1 -1
  162. package/dist/src/tools/vtex/update-product-basic-fields.js.map +1 -1
  163. package/dist/src/tools/vtex/update-sku-basic-fields.js.map +1 -1
  164. package/dist/src/tools/vtex/update-sku-price.js.map +1 -1
  165. package/dist/src/tools/vtex/upsert-fixed-price.js.map +1 -1
  166. package/dist/src/tools/vtex/warehouse-inventory.js.map +1 -1
  167. package/dist/src/tools/vtex/write-helpers.js.map +1 -1
  168. package/dist/src/utils/case-conversion.js.map +1 -1
  169. package/dist/src/utils/currency.js.map +1 -1
  170. package/dist/src/utils/format-order-details.js.map +1 -1
  171. package/dist/src/utils/google-ads.js.map +1 -1
  172. package/dist/src/utils/money.js.map +1 -1
  173. package/dist/src/utils/order-status.js.map +1 -1
  174. package/dist/src/utils/pagination.js.map +1 -1
  175. package/dist/src/utils/strip-payload.js.map +1 -1
  176. package/dist/src/utils/type-guards.js.map +1 -1
  177. package/package.json +4 -3
  178. package/public/icon.svg +6 -6
  179. package/dist/src/google-ads/report-utils.js +0 -78
  180. package/dist/src/google-ads/report-utils.js.map +0 -7
  181. package/dist/src/search-console/search-console-utils.js +0 -275
  182. package/dist/src/search-console/search-console-utils.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/vtex/write-helpers.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport { formatVtexError } from \"../../services/vtex/vtex-api.js\";\nimport {\n VtexOperationError,\n buildConfirmationRequiredResult,\n buildWriteSuccessResult,\n type VtexWriteRiskLevel,\n} from \"../../services/vtex/vtex-write.js\";\nimport { toSnakeCaseKeys } from \"../../utils/case-conversion.js\";\nimport { resolveVtexProfileOrSelection } from \"./profile-resolution.js\";\n\nexport const vtexProfileIdSchemaField = z\n .string()\n .trim()\n .min(1)\n .optional()\n .describe(\n \"Explicit VTEX profile id to use. If omitted, the tool returns a guided selection payload with available active profiles.\"\n );\n\nexport const confirmationSchemaField = z\n .boolean()\n .optional()\n .describe(\n \"Set to true only after the assistant has explained the impact of this operation and the user has explicitly confirmed they want to proceed.\"\n );\n\nexport const confirmationNoteSchemaField = z\n .string()\n .trim()\n .min(1)\n .optional()\n .describe(\"Optional note summarizing the user's explicit confirmation in natural language.\");\n\nexport async function resolveVtexWriteProfile(profileId?: string) {\n const profileResolution = await resolveVtexProfileOrSelection(profileId);\n if (!profileResolution.ok) {\n return profileResolution;\n }\n\n return {\n ok: true as const,\n value: {\n profileId: profileResolution.value.profileId,\n },\n };\n}\n\nexport function requireExplicitConfirmation(\n confirmed: boolean | undefined,\n operation: string,\n details?: Record<string, unknown>\n) {\n if (confirmed === true) {\n return null;\n }\n\n return object(buildConfirmationRequiredResult(operation, details));\n}\n\nexport function toWriteSummary(\n before?: Record<string, unknown> | null,\n after?: Record<string, unknown> | null\n) {\n return {\n before: before ? (toSnakeCaseKeys(before) as Record<string, unknown>) : before,\n after: after ? (toSnakeCaseKeys(after) as Record<string, unknown>) : after,\n };\n}\n\nexport function buildWriteSuccessResponse(params: {\n profileId: string;\n operation: string;\n resourceId?: string;\n riskLevel: VtexWriteRiskLevel;\n confirmed?: boolean;\n confirmationNote?: string;\n message: string;\n before?: Record<string, unknown> | null;\n after?: Record<string, unknown> | null;\n details?: Record<string, unknown>;\n}) {\n return object(\n buildWriteSuccessResult({\n profileId: params.profileId,\n operation: params.operation,\n resourceId: params.resourceId,\n riskLevel: params.riskLevel,\n confirmed: params.confirmed,\n confirmationNote: params.confirmationNote,\n message: params.message,\n summary: toWriteSummary(params.before, params.after),\n details: params.details ? (toSnakeCaseKeys(params.details) as Record<string, unknown>) : undefined,\n })\n );\n}\n\nexport function handleVtexWriteError(err: unknown, fallback: string) {\n if (err instanceof VtexOperationError) {\n return error(err.message);\n }\n\n return error(formatVtexError(err, fallback));\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport { formatVtexError } from \"../../services/vtex/vtex-api.js\";\r\nimport {\r\n VtexOperationError,\r\n buildConfirmationRequiredResult,\r\n buildWriteSuccessResult,\r\n type VtexWriteRiskLevel,\r\n} from \"../../services/vtex/vtex-write.js\";\r\nimport { toSnakeCaseKeys } from \"../../utils/case-conversion.js\";\r\nimport { resolveVtexProfileOrSelection } from \"./profile-resolution.js\";\r\n\r\nexport const vtexProfileIdSchemaField = z\r\n .string()\r\n .trim()\r\n .min(1)\r\n .optional()\r\n .describe(\r\n \"Explicit VTEX profile id to use. If omitted, the tool returns a guided selection payload with available active profiles.\"\r\n );\r\n\r\nexport const confirmationSchemaField = z\r\n .boolean()\r\n .optional()\r\n .describe(\r\n \"Set to true only after the assistant has explained the impact of this operation and the user has explicitly confirmed they want to proceed.\"\r\n );\r\n\r\nexport const confirmationNoteSchemaField = z\r\n .string()\r\n .trim()\r\n .min(1)\r\n .optional()\r\n .describe(\"Optional note summarizing the user's explicit confirmation in natural language.\");\r\n\r\nexport async function resolveVtexWriteProfile(profileId?: string) {\r\n const profileResolution = await resolveVtexProfileOrSelection(profileId);\r\n if (!profileResolution.ok) {\r\n return profileResolution;\r\n }\r\n\r\n return {\r\n ok: true as const,\r\n value: {\r\n profileId: profileResolution.value.profileId,\r\n },\r\n };\r\n}\r\n\r\nexport function requireExplicitConfirmation(\r\n confirmed: boolean | undefined,\r\n operation: string,\r\n details?: Record<string, unknown>\r\n) {\r\n if (confirmed === true) {\r\n return null;\r\n }\r\n\r\n return object(buildConfirmationRequiredResult(operation, details));\r\n}\r\n\r\nexport function toWriteSummary(\r\n before?: Record<string, unknown> | null,\r\n after?: Record<string, unknown> | null\r\n) {\r\n return {\r\n before: before ? (toSnakeCaseKeys(before) as Record<string, unknown>) : before,\r\n after: after ? (toSnakeCaseKeys(after) as Record<string, unknown>) : after,\r\n };\r\n}\r\n\r\nexport function buildWriteSuccessResponse(params: {\r\n profileId: string;\r\n operation: string;\r\n resourceId?: string;\r\n riskLevel: VtexWriteRiskLevel;\r\n confirmed?: boolean;\r\n confirmationNote?: string;\r\n message: string;\r\n before?: Record<string, unknown> | null;\r\n after?: Record<string, unknown> | null;\r\n details?: Record<string, unknown>;\r\n}) {\r\n return object(\r\n buildWriteSuccessResult({\r\n profileId: params.profileId,\r\n operation: params.operation,\r\n resourceId: params.resourceId,\r\n riskLevel: params.riskLevel,\r\n confirmed: params.confirmed,\r\n confirmationNote: params.confirmationNote,\r\n message: params.message,\r\n summary: toWriteSummary(params.before, params.after),\r\n details: params.details ? (toSnakeCaseKeys(params.details) as Record<string, unknown>) : undefined,\r\n })\r\n );\r\n}\r\n\r\nexport function handleVtexWriteError(err: unknown, fallback: string) {\r\n if (err instanceof VtexOperationError) {\r\n return error(err.message);\r\n }\r\n\r\n return error(formatVtexError(err, fallback));\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,uBAAuB;AAChC,SAAS,qCAAqC;AAEvC,MAAM,2BAA2B,EACrC,OAAO,EACP,KAAK,EACL,IAAI,CAAC,EACL,SAAS,EACT;AAAA,EACC;AACF;AAEK,MAAM,0BAA0B,EACpC,QAAQ,EACR,SAAS,EACT;AAAA,EACC;AACF;AAEK,MAAM,8BAA8B,EACxC,OAAO,EACP,KAAK,EACL,IAAI,CAAC,EACL,SAAS,EACT,SAAS,iFAAiF;AAE7F,eAAsB,wBAAwB,WAAoB;AAChE,QAAM,oBAAoB,MAAM,8BAA8B,SAAS;AACvE,MAAI,CAAC,kBAAkB,IAAI;AACzB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,MACL,WAAW,kBAAkB,MAAM;AAAA,IACrC;AAAA,EACF;AACF;AAEO,SAAS,4BACd,WACA,WACA,SACA;AACA,MAAI,cAAc,MAAM;AACtB,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,gCAAgC,WAAW,OAAO,CAAC;AACnE;AAEO,SAAS,eACd,QACA,OACA;AACA,SAAO;AAAA,IACL,QAAQ,SAAU,gBAAgB,MAAM,IAAgC;AAAA,IACxE,OAAO,QAAS,gBAAgB,KAAK,IAAgC;AAAA,EACvE;AACF;AAEO,SAAS,0BAA0B,QAWvC;AACD,SAAO;AAAA,IACL,wBAAwB;AAAA,MACtB,WAAW,OAAO;AAAA,MAClB,WAAW,OAAO;AAAA,MAClB,YAAY,OAAO;AAAA,MACnB,WAAW,OAAO;AAAA,MAClB,WAAW,OAAO;AAAA,MAClB,kBAAkB,OAAO;AAAA,MACzB,SAAS,OAAO;AAAA,MAChB,SAAS,eAAe,OAAO,QAAQ,OAAO,KAAK;AAAA,MACnD,SAAS,OAAO,UAAW,gBAAgB,OAAO,OAAO,IAAgC;AAAA,IAC3F,CAAC;AAAA,EACH;AACF;AAEO,SAAS,qBAAqB,KAAc,UAAkB;AACnE,MAAI,eAAe,oBAAoB;AACrC,WAAO,MAAM,IAAI,OAAO;AAAA,EAC1B;AAEA,SAAO,MAAM,gBAAgB,KAAK,QAAQ,CAAC;AAC7C;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/case-conversion.ts"],
4
- "sourcesContent": ["import { isObject } from \"./type-guards.js\";\n\nfunction toSnakeCase(input: string): string {\n return input\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\n .replace(/[\\s-]+/g, \"_\")\n .toLowerCase();\n}\n\nexport function toSnakeCaseKeys(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => toSnakeCaseKeys(item));\n }\n\n if (isObject(value)) {\n const convertedEntries: [string, unknown][] = [];\n\n for (const [key, nestedValue] of Object.entries(value)) {\n convertedEntries.push([toSnakeCase(key), toSnakeCaseKeys(nestedValue)]);\n }\n\n return Object.fromEntries(convertedEntries);\n }\n\n return value;\n}\n"],
4
+ "sourcesContent": ["import { isObject } from \"./type-guards.js\";\r\n\r\nfunction toSnakeCase(input: string): string {\r\n return input\r\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\r\n .replace(/[\\s-]+/g, \"_\")\r\n .toLowerCase();\r\n}\r\n\r\nexport function toSnakeCaseKeys(value: unknown): unknown {\r\n if (Array.isArray(value)) {\r\n return value.map((item) => toSnakeCaseKeys(item));\r\n }\r\n\r\n if (isObject(value)) {\r\n const convertedEntries: [string, unknown][] = [];\r\n\r\n for (const [key, nestedValue] of Object.entries(value)) {\r\n convertedEntries.push([toSnakeCase(key), toSnakeCaseKeys(nestedValue)]);\r\n }\r\n\r\n return Object.fromEntries(convertedEntries);\r\n }\r\n\r\n return value;\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,gBAAgB;AAEzB,SAAS,YAAY,OAAuB;AAC1C,SAAO,MACJ,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,WAAW,GAAG,EACtB,YAAY;AACjB;AAEO,SAAS,gBAAgB,OAAyB;AACvD,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,SAAS,gBAAgB,IAAI,CAAC;AAAA,EAClD;AAEA,MAAI,SAAS,KAAK,GAAG;AACnB,UAAM,mBAAwC,CAAC;AAE/C,eAAW,CAAC,KAAK,WAAW,KAAK,OAAO,QAAQ,KAAK,GAAG;AACtD,uBAAiB,KAAK,CAAC,YAAY,GAAG,GAAG,gBAAgB,WAAW,CAAC,CAAC;AAAA,IACxE;AAEA,WAAO,OAAO,YAAY,gBAAgB;AAAA,EAC5C;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/currency.ts"],
4
- "sourcesContent": ["import type { VtexOrderListItem } from \"../services/vtex/vtex-orders.js\";\nimport { moneyFromCentsPrecise, roundMoney } from \"./money.js\";\nimport { normalizeOrderStatus } from \"./order-status.js\";\n\nexport type Breakdown = [number, number];\n\nexport interface CurrencyMetrics {\n count: number;\n avg: number;\n total: number;\n by_status: Record<string, Breakdown>;\n by_payment: Record<string, Breakdown>;\n by_channel: Record<string, Breakdown>;\n}\n\nfunction addMetric(group: Record<string, Breakdown>, key: string, orderValue: number): void {\n const safeKey = key.trim() || \"unknown\";\n const existing = group[safeKey] ?? [0, 0];\n\n existing[0] += 1;\n existing[1] += orderValue;\n\n group[safeKey] = existing;\n}\n\nexport function buildCurrencyMetrics(orders: VtexOrderListItem[]): Record<string, CurrencyMetrics> {\n const metricsByCurrency: Record<string, CurrencyMetrics> = {};\n\n for (const order of orders) {\n const currencyCode = (order.currencyCode ?? \"UNKNOWN\").trim() || \"UNKNOWN\";\n const orderValue = moneyFromCentsPrecise(order.totalValue);\n const status = normalizeOrderStatus(String(order.status ?? \"unknown\"));\n const paymentName = String(order.paymentNames ?? \"unknown\");\n const salesChannel = String(order.salesChannel ?? \"unknown\");\n\n const currencyMetrics = metricsByCurrency[currencyCode] ?? {\n count: 0,\n avg: 0,\n total: 0,\n by_status: {},\n by_payment: {},\n by_channel: {},\n };\n\n currencyMetrics.count += 1;\n currencyMetrics.total += orderValue;\n\n addMetric(currencyMetrics.by_status, status, orderValue);\n addMetric(currencyMetrics.by_payment, paymentName, orderValue);\n addMetric(currencyMetrics.by_channel, salesChannel, orderValue);\n\n currencyMetrics.avg =\n currencyMetrics.count > 0\n ? currencyMetrics.total / currencyMetrics.count\n : 0;\n\n metricsByCurrency[currencyCode] = currencyMetrics;\n }\n\n for (const currencyMetrics of Object.values(metricsByCurrency)) {\n for (const statusMetrics of Object.values(currencyMetrics.by_status)) {\n statusMetrics[1] = roundMoney(statusMetrics[1]);\n }\n\n for (const paymentMetrics of Object.values(currencyMetrics.by_payment)) {\n paymentMetrics[1] = roundMoney(paymentMetrics[1]);\n }\n\n for (const channelMetrics of Object.values(currencyMetrics.by_channel)) {\n channelMetrics[1] = roundMoney(channelMetrics[1]);\n }\n\n currencyMetrics.total = roundMoney(currencyMetrics.total);\n currencyMetrics.avg = roundMoney(currencyMetrics.avg);\n }\n\n return metricsByCurrency;\n}\n"],
4
+ "sourcesContent": ["import type { VtexOrderListItem } from \"../services/vtex/vtex-orders.js\";\r\nimport { moneyFromCentsPrecise, roundMoney } from \"./money.js\";\r\nimport { normalizeOrderStatus } from \"./order-status.js\";\r\n\r\nexport type Breakdown = [number, number];\r\n\r\nexport interface CurrencyMetrics {\r\n count: number;\r\n avg: number;\r\n total: number;\r\n by_status: Record<string, Breakdown>;\r\n by_payment: Record<string, Breakdown>;\r\n by_channel: Record<string, Breakdown>;\r\n}\r\n\r\nfunction addMetric(group: Record<string, Breakdown>, key: string, orderValue: number): void {\r\n const safeKey = key.trim() || \"unknown\";\r\n const existing = group[safeKey] ?? [0, 0];\r\n\r\n existing[0] += 1;\r\n existing[1] += orderValue;\r\n\r\n group[safeKey] = existing;\r\n}\r\n\r\nexport function buildCurrencyMetrics(orders: VtexOrderListItem[]): Record<string, CurrencyMetrics> {\r\n const metricsByCurrency: Record<string, CurrencyMetrics> = {};\r\n\r\n for (const order of orders) {\r\n const currencyCode = (order.currencyCode ?? \"UNKNOWN\").trim() || \"UNKNOWN\";\r\n const orderValue = moneyFromCentsPrecise(order.totalValue);\r\n const status = normalizeOrderStatus(String(order.status ?? \"unknown\"));\r\n const paymentName = String(order.paymentNames ?? \"unknown\");\r\n const salesChannel = String(order.salesChannel ?? \"unknown\");\r\n\r\n const currencyMetrics = metricsByCurrency[currencyCode] ?? {\r\n count: 0,\r\n avg: 0,\r\n total: 0,\r\n by_status: {},\r\n by_payment: {},\r\n by_channel: {},\r\n };\r\n\r\n currencyMetrics.count += 1;\r\n currencyMetrics.total += orderValue;\r\n\r\n addMetric(currencyMetrics.by_status, status, orderValue);\r\n addMetric(currencyMetrics.by_payment, paymentName, orderValue);\r\n addMetric(currencyMetrics.by_channel, salesChannel, orderValue);\r\n\r\n currencyMetrics.avg =\r\n currencyMetrics.count > 0\r\n ? currencyMetrics.total / currencyMetrics.count\r\n : 0;\r\n\r\n metricsByCurrency[currencyCode] = currencyMetrics;\r\n }\r\n\r\n for (const currencyMetrics of Object.values(metricsByCurrency)) {\r\n for (const statusMetrics of Object.values(currencyMetrics.by_status)) {\r\n statusMetrics[1] = roundMoney(statusMetrics[1]);\r\n }\r\n\r\n for (const paymentMetrics of Object.values(currencyMetrics.by_payment)) {\r\n paymentMetrics[1] = roundMoney(paymentMetrics[1]);\r\n }\r\n\r\n for (const channelMetrics of Object.values(currencyMetrics.by_channel)) {\r\n channelMetrics[1] = roundMoney(channelMetrics[1]);\r\n }\r\n\r\n currencyMetrics.total = roundMoney(currencyMetrics.total);\r\n currencyMetrics.avg = roundMoney(currencyMetrics.avg);\r\n }\r\n\r\n return metricsByCurrency;\r\n}\r\n"],
5
5
  "mappings": "AACA,SAAS,uBAAuB,kBAAkB;AAClD,SAAS,4BAA4B;AAarC,SAAS,UAAU,OAAkC,KAAa,YAA0B;AAC1F,QAAM,UAAU,IAAI,KAAK,KAAK;AAC9B,QAAM,WAAW,MAAM,OAAO,KAAK,CAAC,GAAG,CAAC;AAExC,WAAS,CAAC,KAAK;AACf,WAAS,CAAC,KAAK;AAEf,QAAM,OAAO,IAAI;AACnB;AAEO,SAAS,qBAAqB,QAA8D;AACjG,QAAM,oBAAqD,CAAC;AAE5D,aAAW,SAAS,QAAQ;AAC1B,UAAM,gBAAgB,MAAM,gBAAgB,WAAW,KAAK,KAAK;AACjE,UAAM,aAAa,sBAAsB,MAAM,UAAU;AACzD,UAAM,SAAS,qBAAqB,OAAO,MAAM,UAAU,SAAS,CAAC;AACrE,UAAM,cAAc,OAAO,MAAM,gBAAgB,SAAS;AAC1D,UAAM,eAAe,OAAO,MAAM,gBAAgB,SAAS;AAE3D,UAAM,kBAAkB,kBAAkB,YAAY,KAAK;AAAA,MACzD,OAAO;AAAA,MACP,KAAK;AAAA,MACL,OAAO;AAAA,MACP,WAAW,CAAC;AAAA,MACZ,YAAY,CAAC;AAAA,MACb,YAAY,CAAC;AAAA,IACf;AAEA,oBAAgB,SAAS;AACzB,oBAAgB,SAAS;AAEzB,cAAU,gBAAgB,WAAW,QAAQ,UAAU;AACvD,cAAU,gBAAgB,YAAY,aAAa,UAAU;AAC7D,cAAU,gBAAgB,YAAY,cAAc,UAAU;AAE9D,oBAAgB,MACd,gBAAgB,QAAQ,IACpB,gBAAgB,QAAQ,gBAAgB,QACxC;AAEN,sBAAkB,YAAY,IAAI;AAAA,EACpC;AAEA,aAAW,mBAAmB,OAAO,OAAO,iBAAiB,GAAG;AAC9D,eAAW,iBAAiB,OAAO,OAAO,gBAAgB,SAAS,GAAG;AACpE,oBAAc,CAAC,IAAI,WAAW,cAAc,CAAC,CAAC;AAAA,IAChD;AAEA,eAAW,kBAAkB,OAAO,OAAO,gBAAgB,UAAU,GAAG;AACtE,qBAAe,CAAC,IAAI,WAAW,eAAe,CAAC,CAAC;AAAA,IAClD;AAEA,eAAW,kBAAkB,OAAO,OAAO,gBAAgB,UAAU,GAAG;AACtE,qBAAe,CAAC,IAAI,WAAW,eAAe,CAAC,CAAC;AAAA,IAClD;AAEA,oBAAgB,QAAQ,WAAW,gBAAgB,KAAK;AACxD,oBAAgB,MAAM,WAAW,gBAAgB,GAAG;AAAA,EACtD;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/format-order-details.ts"],
4
- "sourcesContent": ["import { moneyFromCents, roundMoney } from \"./money.js\";\nimport { isObject } from \"./type-guards.js\";\n\nfunction toFiniteNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction formatDate(isoDate: unknown, includeTime: boolean): string {\n if (typeof isoDate !== \"string\") return \"\";\n \n try {\n const date = new Date(isoDate);\n const year = date.getUTCFullYear();\n const month = String(date.getUTCMonth() + 1).padStart(2, \"0\");\n const day = String(date.getUTCDate()).padStart(2, \"0\");\n\n if (!includeTime) {\n return `${year}-${month}-${day}`;\n }\n\n const hours = String(date.getUTCHours()).padStart(2, \"0\");\n const minutes = String(date.getUTCMinutes()).padStart(2, \"0\");\n return `${year}-${month}-${day} ${hours}:${minutes}`;\n } catch {\n return \"\";\n }\n}\n\nfunction computePaymentsTotalValue(paymentData: unknown): number | undefined {\n if (!isObject(paymentData)) {\n return undefined;\n }\n\n const transactions = Array.isArray(paymentData.transactions) ? paymentData.transactions : [];\n let total = 0;\n let hasAnyPaymentValue = false;\n\n for (const transaction of transactions) {\n if (!isObject(transaction)) {\n continue;\n }\n\n const payments = Array.isArray(transaction.payments) ? transaction.payments : [];\n for (const payment of payments) {\n if (!isObject(payment)) {\n continue;\n }\n\n const paymentValue = toFiniteNumber(payment.value);\n if (paymentValue === undefined) {\n continue;\n }\n\n hasAnyPaymentValue = true;\n total += moneyFromCents(paymentValue);\n }\n }\n\n return hasAnyPaymentValue ? roundMoney(total) : undefined;\n}\n\ninterface FormattedOrderDetails {\n id: string;\n status: string;\n created: string;\n invoiced?: string;\n total: number;\n cancel_reason?: string;\n client_email?: string;\n client_name?: string;\n client_phone?: string;\n client_doc_type?: string;\n client_doc?: string;\n items: Array<[string, string, string, number, number, number]>; // [id, ref, name, qty, price, list]\n shipping: Array<[string, string, string]>; // [carrier, estimate, delivery_date]\n payments: Array<[string, number, number]>; // [system, installments, value]\n payments_total?: number;\n payment_delta?: number;\n payment_mismatch?: boolean;\n utm_source?: string;\n utm_campaign?: string;\n coupon?: string;\n}\n\nexport function formatOrderDetails(order: Record<string, unknown>): FormattedOrderDetails {\n const items = Array.isArray(order.items) ? order.items : [];\n const shippingData = isObject(order.shippingData) ? order.shippingData : undefined;\n const paymentData = isObject(order.paymentData) ? order.paymentData : undefined;\n const clientData = isObject(order.clientProfileData) ? order.clientProfileData : undefined;\n const marketingData = isObject(order.marketingData) ? order.marketingData : undefined;\n\n // Format items as arrays\n const formattedItems = items\n .filter((item) => isObject(item))\n .map((item): [string, string, string, number, number, number] => [\n String(item.id ?? \"\"),\n String(item.refId ?? \"\"),\n String(item.name ?? \"\"),\n Number(item.quantity ?? 0),\n moneyFromCents(item.price),\n moneyFromCents(item.listPrice),\n ]);\n\n // Format shipping as arrays\n const logisticsInfo = Array.isArray(shippingData?.logisticsInfo) \n ? shippingData.logisticsInfo \n : [];\n \n const formattedShipping = logisticsInfo\n .filter((entry) => isObject(entry))\n .map((entry): [string, string, string] => [\n String(entry.deliveryCompany ?? \"\"),\n String(entry.shippingEstimate ?? \"\"),\n formatDate(entry.shippingEstimateDate, false),\n ]);\n\n // Format payments as arrays\n const transactions = Array.isArray(paymentData?.transactions)\n ? paymentData.transactions\n : [];\n \n const formattedPayments: Array<[string, number, number]> = [];\n \n for (const transaction of transactions) {\n if (!isObject(transaction)) continue;\n \n const payments = Array.isArray(transaction.payments) ? transaction.payments : [];\n \n for (const payment of payments) {\n if (!isObject(payment)) continue;\n \n formattedPayments.push([\n String(payment.paymentSystemName ?? \"\"),\n Number(payment.installments ?? 1),\n moneyFromCents(payment.value),\n ]);\n }\n }\n\n // Build client name\n const firstName = String(clientData?.firstName ?? \"\");\n const lastName = String(clientData?.lastName ?? \"\");\n const clientName = `${firstName} ${lastName}`.trim();\n\n // Build formatted order\n const formatted: FormattedOrderDetails = {\n id: String(order.orderId ?? order.id ?? \"\"),\n status: String(order.status ?? \"\"),\n created: formatDate(order.creationDate, true),\n total: moneyFromCents(order.value),\n items: formattedItems,\n shipping: formattedShipping,\n payments: formattedPayments,\n };\n\n // Optional fields\n if (order.invoicedDate) {\n formatted.invoiced = formatDate(order.invoicedDate, true);\n }\n \n if (order.cancelReason) {\n formatted.cancel_reason = String(order.cancelReason);\n }\n\n if (clientData?.email) {\n formatted.client_email = String(clientData.email);\n }\n\n if (clientName) {\n formatted.client_name = clientName;\n }\n\n if (clientData?.phone) {\n formatted.client_phone = String(clientData.phone);\n }\n\n if (clientData?.documentType) {\n formatted.client_doc_type = String(clientData.documentType);\n }\n\n if (clientData?.document) {\n formatted.client_doc = String(clientData.document);\n }\n\n if (marketingData?.utmSource) {\n formatted.utm_source = String(marketingData.utmSource);\n }\n\n if (marketingData?.utmCampaign) {\n formatted.utm_campaign = String(marketingData.utmCampaign);\n }\n\n if (marketingData?.coupon) {\n formatted.coupon = String(marketingData.coupon);\n }\n\n // Payment validation\n const paymentsTotalValue = computePaymentsTotalValue(paymentData);\n if (paymentsTotalValue !== undefined) {\n formatted.payments_total = paymentsTotalValue;\n \n const paymentValueDelta = roundMoney(paymentsTotalValue - formatted.total);\n formatted.payment_delta = paymentValueDelta;\n formatted.payment_mismatch = Math.abs(paymentValueDelta) > 0.5;\n }\n\n return formatted;\n}\n"],
4
+ "sourcesContent": ["import { moneyFromCents, roundMoney } from \"./money.js\";\r\nimport { isObject } from \"./type-guards.js\";\r\n\r\nfunction toFiniteNumber(value: unknown): number | undefined {\r\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\r\n}\r\n\r\nfunction formatDate(isoDate: unknown, includeTime: boolean): string {\r\n if (typeof isoDate !== \"string\") return \"\";\r\n \r\n try {\r\n const date = new Date(isoDate);\r\n const year = date.getUTCFullYear();\r\n const month = String(date.getUTCMonth() + 1).padStart(2, \"0\");\r\n const day = String(date.getUTCDate()).padStart(2, \"0\");\r\n\r\n if (!includeTime) {\r\n return `${year}-${month}-${day}`;\r\n }\r\n\r\n const hours = String(date.getUTCHours()).padStart(2, \"0\");\r\n const minutes = String(date.getUTCMinutes()).padStart(2, \"0\");\r\n return `${year}-${month}-${day} ${hours}:${minutes}`;\r\n } catch {\r\n return \"\";\r\n }\r\n}\r\n\r\nfunction computePaymentsTotalValue(paymentData: unknown): number | undefined {\r\n if (!isObject(paymentData)) {\r\n return undefined;\r\n }\r\n\r\n const transactions = Array.isArray(paymentData.transactions) ? paymentData.transactions : [];\r\n let total = 0;\r\n let hasAnyPaymentValue = false;\r\n\r\n for (const transaction of transactions) {\r\n if (!isObject(transaction)) {\r\n continue;\r\n }\r\n\r\n const payments = Array.isArray(transaction.payments) ? transaction.payments : [];\r\n for (const payment of payments) {\r\n if (!isObject(payment)) {\r\n continue;\r\n }\r\n\r\n const paymentValue = toFiniteNumber(payment.value);\r\n if (paymentValue === undefined) {\r\n continue;\r\n }\r\n\r\n hasAnyPaymentValue = true;\r\n total += moneyFromCents(paymentValue);\r\n }\r\n }\r\n\r\n return hasAnyPaymentValue ? roundMoney(total) : undefined;\r\n}\r\n\r\ninterface FormattedOrderDetails {\r\n id: string;\r\n status: string;\r\n created: string;\r\n invoiced?: string;\r\n total: number;\r\n cancel_reason?: string;\r\n client_email?: string;\r\n client_name?: string;\r\n client_phone?: string;\r\n client_doc_type?: string;\r\n client_doc?: string;\r\n items: Array<[string, string, string, number, number, number]>; // [id, ref, name, qty, price, list]\r\n shipping: Array<[string, string, string]>; // [carrier, estimate, delivery_date]\r\n payments: Array<[string, number, number]>; // [system, installments, value]\r\n payments_total?: number;\r\n payment_delta?: number;\r\n payment_mismatch?: boolean;\r\n utm_source?: string;\r\n utm_campaign?: string;\r\n coupon?: string;\r\n}\r\n\r\nexport function formatOrderDetails(order: Record<string, unknown>): FormattedOrderDetails {\r\n const items = Array.isArray(order.items) ? order.items : [];\r\n const shippingData = isObject(order.shippingData) ? order.shippingData : undefined;\r\n const paymentData = isObject(order.paymentData) ? order.paymentData : undefined;\r\n const clientData = isObject(order.clientProfileData) ? order.clientProfileData : undefined;\r\n const marketingData = isObject(order.marketingData) ? order.marketingData : undefined;\r\n\r\n // Format items as arrays\r\n const formattedItems = items\r\n .filter((item) => isObject(item))\r\n .map((item): [string, string, string, number, number, number] => [\r\n String(item.id ?? \"\"),\r\n String(item.refId ?? \"\"),\r\n String(item.name ?? \"\"),\r\n Number(item.quantity ?? 0),\r\n moneyFromCents(item.price),\r\n moneyFromCents(item.listPrice),\r\n ]);\r\n\r\n // Format shipping as arrays\r\n const logisticsInfo = Array.isArray(shippingData?.logisticsInfo) \r\n ? shippingData.logisticsInfo \r\n : [];\r\n \r\n const formattedShipping = logisticsInfo\r\n .filter((entry) => isObject(entry))\r\n .map((entry): [string, string, string] => [\r\n String(entry.deliveryCompany ?? \"\"),\r\n String(entry.shippingEstimate ?? \"\"),\r\n formatDate(entry.shippingEstimateDate, false),\r\n ]);\r\n\r\n // Format payments as arrays\r\n const transactions = Array.isArray(paymentData?.transactions)\r\n ? paymentData.transactions\r\n : [];\r\n \r\n const formattedPayments: Array<[string, number, number]> = [];\r\n \r\n for (const transaction of transactions) {\r\n if (!isObject(transaction)) continue;\r\n \r\n const payments = Array.isArray(transaction.payments) ? transaction.payments : [];\r\n \r\n for (const payment of payments) {\r\n if (!isObject(payment)) continue;\r\n \r\n formattedPayments.push([\r\n String(payment.paymentSystemName ?? \"\"),\r\n Number(payment.installments ?? 1),\r\n moneyFromCents(payment.value),\r\n ]);\r\n }\r\n }\r\n\r\n // Build client name\r\n const firstName = String(clientData?.firstName ?? \"\");\r\n const lastName = String(clientData?.lastName ?? \"\");\r\n const clientName = `${firstName} ${lastName}`.trim();\r\n\r\n // Build formatted order\r\n const formatted: FormattedOrderDetails = {\r\n id: String(order.orderId ?? order.id ?? \"\"),\r\n status: String(order.status ?? \"\"),\r\n created: formatDate(order.creationDate, true),\r\n total: moneyFromCents(order.value),\r\n items: formattedItems,\r\n shipping: formattedShipping,\r\n payments: formattedPayments,\r\n };\r\n\r\n // Optional fields\r\n if (order.invoicedDate) {\r\n formatted.invoiced = formatDate(order.invoicedDate, true);\r\n }\r\n \r\n if (order.cancelReason) {\r\n formatted.cancel_reason = String(order.cancelReason);\r\n }\r\n\r\n if (clientData?.email) {\r\n formatted.client_email = String(clientData.email);\r\n }\r\n\r\n if (clientName) {\r\n formatted.client_name = clientName;\r\n }\r\n\r\n if (clientData?.phone) {\r\n formatted.client_phone = String(clientData.phone);\r\n }\r\n\r\n if (clientData?.documentType) {\r\n formatted.client_doc_type = String(clientData.documentType);\r\n }\r\n\r\n if (clientData?.document) {\r\n formatted.client_doc = String(clientData.document);\r\n }\r\n\r\n if (marketingData?.utmSource) {\r\n formatted.utm_source = String(marketingData.utmSource);\r\n }\r\n\r\n if (marketingData?.utmCampaign) {\r\n formatted.utm_campaign = String(marketingData.utmCampaign);\r\n }\r\n\r\n if (marketingData?.coupon) {\r\n formatted.coupon = String(marketingData.coupon);\r\n }\r\n\r\n // Payment validation\r\n const paymentsTotalValue = computePaymentsTotalValue(paymentData);\r\n if (paymentsTotalValue !== undefined) {\r\n formatted.payments_total = paymentsTotalValue;\r\n \r\n const paymentValueDelta = roundMoney(paymentsTotalValue - formatted.total);\r\n formatted.payment_delta = paymentValueDelta;\r\n formatted.payment_mismatch = Math.abs(paymentValueDelta) > 0.5;\r\n }\r\n\r\n return formatted;\r\n}\r\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/google-ads.ts"],
4
- "sourcesContent": ["import { getDefaultGoogleAdsLoginCustomerId, getGoogleAdsDeveloperToken } from '../config/google.js';\nimport { getProfileGoogleServiceMapping } from '../config/google-store.js';\nimport type { GoogleAdsMetricValue, GoogleAdsSearchRow } from '../services/google-ads/google-ads-client.js';\n\nexport const dateRegex = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nexport function normalizeCustomerId(customerId: string): string { const normalized = customerId.trim().replace(/-/g, ''); if (!/^\\d+$/.test(normalized)) throw new Error('Invalid Google Ads customer ID. Use digits only, with or without hyphens.'); return normalized; }\nexport async function resolveGoogleAdsCustomerId(customerId?: string, profileId?: string): Promise<string> { if (customerId?.trim()) return normalizeCustomerId(customerId); if (profileId?.trim()) { const mapping = await getProfileGoogleServiceMapping(profileId.trim()); if (mapping?.googleAdsCustomerId) return normalizeCustomerId(mapping.googleAdsCustomerId); throw new Error(`Missing Google Ads customer ID. profileId \"${profileId.trim()}\" has no Google Ads mapping configured.`); } throw new Error('Missing Google Ads customer ID. Provide customerId explicitly or pass profileId with a configured mapping.'); }\nexport function resolveGoogleAdsLoginCustomerId(loginCustomerId?: string): string | undefined { const explicitCustomerId = loginCustomerId?.trim(); if (explicitCustomerId) return normalizeCustomerId(explicitCustomerId); return getDefaultGoogleAdsLoginCustomerId(); }\nexport function resolveGoogleAdsDeveloperToken(): string { return getGoogleAdsDeveloperToken(); }\nexport function parseGoogleAdsMetricValue(value: GoogleAdsMetricValue | undefined): number { if (typeof value === 'number' && Number.isFinite(value)) return value; if (typeof value === 'string') { const parsedValue = Number(value); return Number.isFinite(parsedValue) ? parsedValue : 0; } return 0; }\nexport function microsToCurrency(value: GoogleAdsMetricValue | undefined, decimals = 2): number { return round(parseGoogleAdsMetricValue(value) / 1_000_000, decimals); }\nexport function round(value: number, decimals = 2): number { if (!Number.isFinite(value)) return 0; const factor = 10 ** decimals; return Math.round(value * factor) / factor; }\nexport function toPercent(numerator: number, denominator: number): number { if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0) return 0; return (numerator / denominator) * 100; }\nexport function getFieldValue<T>(row: GoogleAdsSearchRow, path: string, fallback?: T): T | undefined { const segments = path.split('.'); let current: unknown = row; for (const segment of segments) { if (!current || typeof current !== 'object') return fallback; current = (current as Record<string, unknown>)[segment]; } return (current as T | undefined) ?? fallback; }\n"],
4
+ "sourcesContent": ["import { getDefaultGoogleAdsLoginCustomerId, getGoogleAdsDeveloperToken } from '../config/google.js';\r\nimport { getProfileGoogleServiceMapping } from '../config/google-store.js';\r\nimport type { GoogleAdsMetricValue, GoogleAdsSearchRow } from '../services/google-ads/google-ads-client.js';\r\n\r\nexport const dateRegex = /^\\d{4}-\\d{2}-\\d{2}$/;\r\n\r\nexport function normalizeCustomerId(customerId: string): string { const normalized = customerId.trim().replace(/-/g, ''); if (!/^\\d+$/.test(normalized)) throw new Error('Invalid Google Ads customer ID. Use digits only, with or without hyphens.'); return normalized; }\r\nexport async function resolveGoogleAdsCustomerId(customerId?: string, profileId?: string): Promise<string> { if (customerId?.trim()) return normalizeCustomerId(customerId); if (profileId?.trim()) { const mapping = await getProfileGoogleServiceMapping(profileId.trim()); if (mapping?.googleAdsCustomerId) return normalizeCustomerId(mapping.googleAdsCustomerId); throw new Error(`Missing Google Ads customer ID. profileId \"${profileId.trim()}\" has no Google Ads mapping configured.`); } throw new Error('Missing Google Ads customer ID. Provide customerId explicitly or pass profileId with a configured mapping.'); }\r\nexport function resolveGoogleAdsLoginCustomerId(loginCustomerId?: string): string | undefined { const explicitCustomerId = loginCustomerId?.trim(); if (explicitCustomerId) return normalizeCustomerId(explicitCustomerId); return getDefaultGoogleAdsLoginCustomerId(); }\r\nexport function resolveGoogleAdsDeveloperToken(): string { return getGoogleAdsDeveloperToken(); }\r\nexport function parseGoogleAdsMetricValue(value: GoogleAdsMetricValue | undefined): number { if (typeof value === 'number' && Number.isFinite(value)) return value; if (typeof value === 'string') { const parsedValue = Number(value); return Number.isFinite(parsedValue) ? parsedValue : 0; } return 0; }\r\nexport function microsToCurrency(value: GoogleAdsMetricValue | undefined, decimals = 2): number { return round(parseGoogleAdsMetricValue(value) / 1_000_000, decimals); }\r\nexport function round(value: number, decimals = 2): number { if (!Number.isFinite(value)) return 0; const factor = 10 ** decimals; return Math.round(value * factor) / factor; }\r\nexport function toPercent(numerator: number, denominator: number): number { if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0) return 0; return (numerator / denominator) * 100; }\r\nexport function getFieldValue<T>(row: GoogleAdsSearchRow, path: string, fallback?: T): T | undefined { const segments = path.split('.'); let current: unknown = row; for (const segment of segments) { if (!current || typeof current !== 'object') return fallback; current = (current as Record<string, unknown>)[segment]; } return (current as T | undefined) ?? fallback; }\r\n"],
5
5
  "mappings": "AAAA,SAAS,oCAAoC,kCAAkC;AAC/E,SAAS,sCAAsC;AAGxC,MAAM,YAAY;AAElB,SAAS,oBAAoB,YAA4B;AAAE,QAAM,aAAa,WAAW,KAAK,EAAE,QAAQ,MAAM,EAAE;AAAG,MAAI,CAAC,QAAQ,KAAK,UAAU,EAAG,OAAM,IAAI,MAAM,2EAA2E;AAAG,SAAO;AAAY;AAC1Q,eAAsB,2BAA2B,YAAqB,WAAqC;AAAE,MAAI,YAAY,KAAK,EAAG,QAAO,oBAAoB,UAAU;AAAG,MAAI,WAAW,KAAK,GAAG;AAAE,UAAM,UAAU,MAAM,+BAA+B,UAAU,KAAK,CAAC;AAAG,QAAI,SAAS,oBAAqB,QAAO,oBAAoB,QAAQ,mBAAmB;AAAG,UAAM,IAAI,MAAM,8CAA8C,UAAU,KAAK,CAAC,yCAAyC;AAAA,EAAG;AAAE,QAAM,IAAI,MAAM,4GAA4G;AAAG;AAC7lB,SAAS,gCAAgC,iBAA8C;AAAE,QAAM,qBAAqB,iBAAiB,KAAK;AAAG,MAAI,mBAAoB,QAAO,oBAAoB,kBAAkB;AAAG,SAAO,mCAAmC;AAAG;AAClQ,SAAS,iCAAyC;AAAE,SAAO,2BAA2B;AAAG;AACzF,SAAS,0BAA0B,OAAiD;AAAE,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAAO,MAAI,OAAO,UAAU,UAAU;AAAE,UAAM,cAAc,OAAO,KAAK;AAAG,WAAO,OAAO,SAAS,WAAW,IAAI,cAAc;AAAA,EAAG;AAAE,SAAO;AAAG;AACpS,SAAS,iBAAiB,OAAyC,WAAW,GAAW;AAAE,SAAO,MAAM,0BAA0B,KAAK,IAAI,KAAW,QAAQ;AAAG;AACjK,SAAS,MAAM,OAAe,WAAW,GAAW;AAAE,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AAAG,QAAM,SAAS,MAAM;AAAU,SAAO,KAAK,MAAM,QAAQ,MAAM,IAAI;AAAQ;AACxK,SAAS,UAAU,WAAmB,aAA6B;AAAE,MAAI,CAAC,OAAO,SAAS,SAAS,KAAK,CAAC,OAAO,SAAS,WAAW,KAAK,eAAe,EAAG,QAAO;AAAG,SAAQ,YAAY,cAAe;AAAK;AAC7M,SAAS,cAAiB,KAAyB,MAAc,UAA6B;AAAE,QAAM,WAAW,KAAK,MAAM,GAAG;AAAG,MAAI,UAAmB;AAAK,aAAW,WAAW,UAAU;AAAE,QAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AAAU,cAAW,QAAoC,OAAO;AAAA,EAAG;AAAE,SAAQ,WAA6B;AAAU;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/money.ts"],
4
- "sourcesContent": ["import { isObject } from \"./type-guards.js\";\n\nconst MONETARY_KEYS = new Set([\n \"value\",\n \"totalvalue\",\n \"total_value\",\n \"price\",\n \"sellingprice\",\n \"selling_price\",\n \"listprice\",\n \"list_price\",\n \"costprice\",\n \"cost_price\",\n \"baseprice\",\n \"base_price\",\n \"referencevalue\",\n \"reference_value\",\n]);\n\nfunction toNumber(value: unknown): number | undefined {\n const numeric = typeof value === \"number\" ? value : Number(value);\n if (!Number.isFinite(numeric)) {\n return undefined;\n }\n\n return numeric;\n}\n\nfunction normalizeKey(key: string): string {\n return key.trim().toLowerCase();\n}\n\nexport function roundMoney(value: number): number {\n return Math.round(value * 100) / 100;\n}\n\nexport function moneyFromCents(value: unknown): number {\n const numeric = toNumber(value);\n if (numeric === undefined) {\n return 0;\n }\n\n return roundMoney(numeric / 100);\n}\n\nexport function moneyFromCentsPrecise(value: unknown): number {\n const numeric = toNumber(value);\n if (numeric === undefined) {\n return 0;\n }\n\n return numeric / 100;\n}\n\nexport function moneyFromCentsOptional(value: unknown): number | undefined {\n const numeric = toNumber(value);\n if (numeric === undefined) {\n return undefined;\n }\n\n return roundMoney(numeric / 100);\n}\n\nexport function isMonetaryKey(keyName: string): boolean {\n const normalized = normalizeKey(keyName);\n if (!normalized) {\n return false;\n }\n\n return (\n MONETARY_KEYS.has(normalized) ||\n normalized.endsWith(\"value\") ||\n normalized.endsWith(\"_value\") ||\n normalized.endsWith(\"price\") ||\n normalized.endsWith(\"_price\")\n );\n}\n\nexport function normalizeMonetaryValues(value: unknown, keyName?: string): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => normalizeMonetaryValues(item));\n }\n\n if (isObject(value)) {\n const normalizedEntries: [string, unknown][] = [];\n\n for (const [key, nestedValue] of Object.entries(value)) {\n normalizedEntries.push([key, normalizeMonetaryValues(nestedValue, key)]);\n }\n\n return Object.fromEntries(normalizedEntries);\n }\n\n if (typeof value === \"number\" && keyName && isMonetaryKey(keyName)) {\n return moneyFromCents(value);\n }\n\n return value;\n}\n"],
4
+ "sourcesContent": ["import { isObject } from \"./type-guards.js\";\r\n\r\nconst MONETARY_KEYS = new Set([\r\n \"value\",\r\n \"totalvalue\",\r\n \"total_value\",\r\n \"price\",\r\n \"sellingprice\",\r\n \"selling_price\",\r\n \"listprice\",\r\n \"list_price\",\r\n \"costprice\",\r\n \"cost_price\",\r\n \"baseprice\",\r\n \"base_price\",\r\n \"referencevalue\",\r\n \"reference_value\",\r\n]);\r\n\r\nfunction toNumber(value: unknown): number | undefined {\r\n const numeric = typeof value === \"number\" ? value : Number(value);\r\n if (!Number.isFinite(numeric)) {\r\n return undefined;\r\n }\r\n\r\n return numeric;\r\n}\r\n\r\nfunction normalizeKey(key: string): string {\r\n return key.trim().toLowerCase();\r\n}\r\n\r\nexport function roundMoney(value: number): number {\r\n return Math.round(value * 100) / 100;\r\n}\r\n\r\nexport function moneyFromCents(value: unknown): number {\r\n const numeric = toNumber(value);\r\n if (numeric === undefined) {\r\n return 0;\r\n }\r\n\r\n return roundMoney(numeric / 100);\r\n}\r\n\r\nexport function moneyFromCentsPrecise(value: unknown): number {\r\n const numeric = toNumber(value);\r\n if (numeric === undefined) {\r\n return 0;\r\n }\r\n\r\n return numeric / 100;\r\n}\r\n\r\nexport function moneyFromCentsOptional(value: unknown): number | undefined {\r\n const numeric = toNumber(value);\r\n if (numeric === undefined) {\r\n return undefined;\r\n }\r\n\r\n return roundMoney(numeric / 100);\r\n}\r\n\r\nexport function isMonetaryKey(keyName: string): boolean {\r\n const normalized = normalizeKey(keyName);\r\n if (!normalized) {\r\n return false;\r\n }\r\n\r\n return (\r\n MONETARY_KEYS.has(normalized) ||\r\n normalized.endsWith(\"value\") ||\r\n normalized.endsWith(\"_value\") ||\r\n normalized.endsWith(\"price\") ||\r\n normalized.endsWith(\"_price\")\r\n );\r\n}\r\n\r\nexport function normalizeMonetaryValues(value: unknown, keyName?: string): unknown {\r\n if (Array.isArray(value)) {\r\n return value.map((item) => normalizeMonetaryValues(item));\r\n }\r\n\r\n if (isObject(value)) {\r\n const normalizedEntries: [string, unknown][] = [];\r\n\r\n for (const [key, nestedValue] of Object.entries(value)) {\r\n normalizedEntries.push([key, normalizeMonetaryValues(nestedValue, key)]);\r\n }\r\n\r\n return Object.fromEntries(normalizedEntries);\r\n }\r\n\r\n if (typeof value === \"number\" && keyName && isMonetaryKey(keyName)) {\r\n return moneyFromCents(value);\r\n }\r\n\r\n return value;\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,gBAAgB;AAEzB,MAAM,gBAAgB,oBAAI,IAAI;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,SAAS,OAAoC;AACpD,QAAM,UAAU,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAChE,MAAI,CAAC,OAAO,SAAS,OAAO,GAAG;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,aAAa,KAAqB;AACzC,SAAO,IAAI,KAAK,EAAE,YAAY;AAChC;AAEO,SAAS,WAAW,OAAuB;AAChD,SAAO,KAAK,MAAM,QAAQ,GAAG,IAAI;AACnC;AAEO,SAAS,eAAe,OAAwB;AACrD,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,WAAW,UAAU,GAAG;AACjC;AAEO,SAAS,sBAAsB,OAAwB;AAC5D,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,UAAU;AACnB;AAEO,SAAS,uBAAuB,OAAoC;AACzE,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,WAAW,UAAU,GAAG;AACjC;AAEO,SAAS,cAAc,SAA0B;AACtD,QAAM,aAAa,aAAa,OAAO;AACvC,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,SACE,cAAc,IAAI,UAAU,KAC5B,WAAW,SAAS,OAAO,KAC3B,WAAW,SAAS,QAAQ,KAC5B,WAAW,SAAS,OAAO,KAC3B,WAAW,SAAS,QAAQ;AAEhC;AAEO,SAAS,wBAAwB,OAAgB,SAA2B;AACjF,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,SAAS,wBAAwB,IAAI,CAAC;AAAA,EAC1D;AAEA,MAAI,SAAS,KAAK,GAAG;AACnB,UAAM,oBAAyC,CAAC;AAEhD,eAAW,CAAC,KAAK,WAAW,KAAK,OAAO,QAAQ,KAAK,GAAG;AACtD,wBAAkB,KAAK,CAAC,KAAK,wBAAwB,aAAa,GAAG,CAAC,CAAC;AAAA,IACzE;AAEA,WAAO,OAAO,YAAY,iBAAiB;AAAA,EAC7C;AAEA,MAAI,OAAO,UAAU,YAAY,WAAW,cAAc,OAAO,GAAG;AAClE,WAAO,eAAe,KAAK;AAAA,EAC7B;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/order-status.ts"],
4
- "sourcesContent": ["export function normalizeOrderStatus(status: unknown): string {\n const normalizedStatus = typeof status === \"string\" ? status : String(status ?? \"unknown\");\n\n if (normalizedStatus === \"ready-for-handling\") {\n return \"ready\";\n }\n\n return normalizedStatus;\n}\n"],
4
+ "sourcesContent": ["export function normalizeOrderStatus(status: unknown): string {\r\n const normalizedStatus = typeof status === \"string\" ? status : String(status ?? \"unknown\");\r\n\r\n if (normalizedStatus === \"ready-for-handling\") {\r\n return \"ready\";\r\n }\r\n\r\n return normalizedStatus;\r\n}\r\n"],
5
5
  "mappings": "AAAO,SAAS,qBAAqB,QAAyB;AAC5D,QAAM,mBAAmB,OAAO,WAAW,WAAW,SAAS,OAAO,UAAU,SAAS;AAEzF,MAAI,qBAAqB,sBAAsB;AAC7C,WAAO;AAAA,EACT;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/pagination.ts"],
4
- "sourcesContent": ["const DISPLAY_INSTRUCTION =\n \"Present this data directly in the chat as a well-formatted markdown report. Use tables for structured data. Do NOT create artifacts, code, or dynamic visualizations unless the user explicitly requests them.\";\n\nexport interface PagingInput {\n total?: number;\n pages?: number;\n currentPage?: number;\n}\n\nexport interface SummaryMetadata {\n total_orders: number;\n orders_in_page: number;\n page: number;\n pages: number;\n has_more: boolean;\n note: string;\n display: string;\n}\n\nexport function createPaginationMetadata(\n paging: PagingInput,\n perPage: number,\n ordersInPage: number\n): SummaryMetadata {\n const totalMatchingOrders = Math.max(0, Number(paging.total ?? 0));\n const currentPage = Math.max(1, Number(paging.currentPage ?? 1));\n const totalPagesAvailable = Math.max(\n 1,\n Number(paging.pages ?? Math.ceil(totalMatchingOrders / Math.max(perPage, 1)))\n );\n const hasMoreData = currentPage < totalPagesAvailable;\n\n if (totalMatchingOrders <= perPage) {\n return {\n total_orders: totalMatchingOrders,\n orders_in_page: ordersInPage,\n page: currentPage,\n pages: totalPagesAvailable,\n has_more: hasMoreData,\n note: `COMPLETE DATA: Page ${currentPage}/${totalPagesAvailable}. Totals are complete for this query.`,\n display: DISPLAY_INSTRUCTION,\n };\n }\n\n if (totalMatchingOrders <= 3000) {\n return {\n total_orders: totalMatchingOrders,\n orders_in_page: ordersInPage,\n page: currentPage,\n pages: totalPagesAvailable,\n has_more: hasMoreData,\n note: `PARTIAL DATA: Page ${currentPage}/${totalPagesAvailable}. Fetch more for complete totals.`,\n display: DISPLAY_INSTRUCTION,\n };\n }\n\n return {\n total_orders: totalMatchingOrders,\n orders_in_page: ordersInPage,\n page: currentPage,\n pages: totalPagesAvailable,\n has_more: hasMoreData,\n note: `CRITICAL LIMIT: ${totalMatchingOrders} matching orders exceed VTEX hard pagination limit (3,000). Narrow date range for accurate totals.`,\n display: DISPLAY_INSTRUCTION,\n };\n}\n"],
4
+ "sourcesContent": ["const DISPLAY_INSTRUCTION =\r\n \"Present this data directly in the chat as a well-formatted markdown report. Use tables for structured data. Do NOT create artifacts, code, or dynamic visualizations unless the user explicitly requests them.\";\r\n\r\nexport interface PagingInput {\r\n total?: number;\r\n pages?: number;\r\n currentPage?: number;\r\n}\r\n\r\nexport interface SummaryMetadata {\r\n total_orders: number;\r\n orders_in_page: number;\r\n page: number;\r\n pages: number;\r\n has_more: boolean;\r\n note: string;\r\n display: string;\r\n}\r\n\r\nexport function createPaginationMetadata(\r\n paging: PagingInput,\r\n perPage: number,\r\n ordersInPage: number\r\n): SummaryMetadata {\r\n const totalMatchingOrders = Math.max(0, Number(paging.total ?? 0));\r\n const currentPage = Math.max(1, Number(paging.currentPage ?? 1));\r\n const totalPagesAvailable = Math.max(\r\n 1,\r\n Number(paging.pages ?? Math.ceil(totalMatchingOrders / Math.max(perPage, 1)))\r\n );\r\n const hasMoreData = currentPage < totalPagesAvailable;\r\n\r\n if (totalMatchingOrders <= perPage) {\r\n return {\r\n total_orders: totalMatchingOrders,\r\n orders_in_page: ordersInPage,\r\n page: currentPage,\r\n pages: totalPagesAvailable,\r\n has_more: hasMoreData,\r\n note: `COMPLETE DATA: Page ${currentPage}/${totalPagesAvailable}. Totals are complete for this query.`,\r\n display: DISPLAY_INSTRUCTION,\r\n };\r\n }\r\n\r\n if (totalMatchingOrders <= 3000) {\r\n return {\r\n total_orders: totalMatchingOrders,\r\n orders_in_page: ordersInPage,\r\n page: currentPage,\r\n pages: totalPagesAvailable,\r\n has_more: hasMoreData,\r\n note: `PARTIAL DATA: Page ${currentPage}/${totalPagesAvailable}. Fetch more for complete totals.`,\r\n display: DISPLAY_INSTRUCTION,\r\n };\r\n }\r\n\r\n return {\r\n total_orders: totalMatchingOrders,\r\n orders_in_page: ordersInPage,\r\n page: currentPage,\r\n pages: totalPagesAvailable,\r\n has_more: hasMoreData,\r\n note: `CRITICAL LIMIT: ${totalMatchingOrders} matching orders exceed VTEX hard pagination limit (3,000). Narrow date range for accurate totals.`,\r\n display: DISPLAY_INSTRUCTION,\r\n };\r\n}\r\n"],
5
5
  "mappings": "AAAA,MAAM,sBACJ;AAkBK,SAAS,yBACd,QACA,SACA,cACiB;AACjB,QAAM,sBAAsB,KAAK,IAAI,GAAG,OAAO,OAAO,SAAS,CAAC,CAAC;AACjE,QAAM,cAAc,KAAK,IAAI,GAAG,OAAO,OAAO,eAAe,CAAC,CAAC;AAC/D,QAAM,sBAAsB,KAAK;AAAA,IAC/B;AAAA,IACA,OAAO,OAAO,SAAS,KAAK,KAAK,sBAAsB,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC;AAAA,EAC9E;AACA,QAAM,cAAc,cAAc;AAElC,MAAI,uBAAuB,SAAS;AAClC,WAAO;AAAA,MACL,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,MAAM;AAAA,MACN,OAAO;AAAA,MACP,UAAU;AAAA,MACV,MAAM,uBAAuB,WAAW,IAAI,mBAAmB;AAAA,MAC/D,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,uBAAuB,KAAM;AAC/B,WAAO;AAAA,MACL,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,MAAM;AAAA,MACN,OAAO;AAAA,MACP,UAAU;AAAA,MACV,MAAM,sBAAsB,WAAW,IAAI,mBAAmB;AAAA,MAC9D,SAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AAAA,IACL,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV,MAAM,mBAAmB,mBAAmB;AAAA,IAC5C,SAAS;AAAA,EACX;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/strip-payload.ts"],
4
- "sourcesContent": ["import { isObject } from \"./type-guards.js\";\n\nexport function stripNulls<T>(input: T): T {\n if (Array.isArray(input)) {\n const cleanedArray = input\n .map((item) => stripNulls(item))\n .filter((item) => {\n if (item === null || item === undefined) {\n return false;\n }\n\n if (Array.isArray(item)) {\n return item.length > 0;\n }\n\n if (isObject(item)) {\n return Object.keys(item).length > 0;\n }\n\n return true;\n });\n\n return cleanedArray as T;\n }\n\n if (isObject(input)) {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(input)) {\n if (value === null || value === undefined) {\n continue;\n }\n\n const cleanedValue = stripNulls(value);\n\n if (Array.isArray(cleanedValue) && cleanedValue.length === 0) {\n continue;\n }\n\n if (isObject(cleanedValue) && Object.keys(cleanedValue).length === 0) {\n continue;\n }\n\n result[key] = cleanedValue;\n }\n\n return result as T;\n }\n\n return input;\n}\n"],
4
+ "sourcesContent": ["import { isObject } from \"./type-guards.js\";\r\n\r\nexport function stripNulls<T>(input: T): T {\r\n if (Array.isArray(input)) {\r\n const cleanedArray = input\r\n .map((item) => stripNulls(item))\r\n .filter((item) => {\r\n if (item === null || item === undefined) {\r\n return false;\r\n }\r\n\r\n if (Array.isArray(item)) {\r\n return item.length > 0;\r\n }\r\n\r\n if (isObject(item)) {\r\n return Object.keys(item).length > 0;\r\n }\r\n\r\n return true;\r\n });\r\n\r\n return cleanedArray as T;\r\n }\r\n\r\n if (isObject(input)) {\r\n const result: Record<string, unknown> = {};\r\n\r\n for (const [key, value] of Object.entries(input)) {\r\n if (value === null || value === undefined) {\r\n continue;\r\n }\r\n\r\n const cleanedValue = stripNulls(value);\r\n\r\n if (Array.isArray(cleanedValue) && cleanedValue.length === 0) {\r\n continue;\r\n }\r\n\r\n if (isObject(cleanedValue) && Object.keys(cleanedValue).length === 0) {\r\n continue;\r\n }\r\n\r\n result[key] = cleanedValue;\r\n }\r\n\r\n return result as T;\r\n }\r\n\r\n return input;\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,gBAAgB;AAElB,SAAS,WAAc,OAAa;AACzC,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,eAAe,MAClB,IAAI,CAAC,SAAS,WAAW,IAAI,CAAC,EAC9B,OAAO,CAAC,SAAS;AAChB,UAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,eAAO;AAAA,MACT;AAEA,UAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,eAAO,KAAK,SAAS;AAAA,MACvB;AAEA,UAAI,SAAS,IAAI,GAAG;AAClB,eAAO,OAAO,KAAK,IAAI,EAAE,SAAS;AAAA,MACpC;AAEA,aAAO;AAAA,IACT,CAAC;AAEH,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,KAAK,GAAG;AACnB,UAAM,SAAkC,CAAC;AAEzC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAI,UAAU,QAAQ,UAAU,QAAW;AACzC;AAAA,MACF;AAEA,YAAM,eAAe,WAAW,KAAK;AAErC,UAAI,MAAM,QAAQ,YAAY,KAAK,aAAa,WAAW,GAAG;AAC5D;AAAA,MACF;AAEA,UAAI,SAAS,YAAY,KAAK,OAAO,KAAK,YAAY,EAAE,WAAW,GAAG;AACpE;AAAA,MACF;AAEA,aAAO,GAAG,IAAI;AAAA,IAChB;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/type-guards.ts"],
4
- "sourcesContent": ["export function isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],
4
+ "sourcesContent": ["export function isObject(value: unknown): value is Record<string, unknown> {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n"],
5
5
  "mappings": "AAAO,SAAS,SAAS,OAAkD;AACzE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yoryoboy/bi-mcp",
3
3
  "type": "module",
4
- "version": "1.5.2",
4
+ "version": "1.6.0",
5
5
  "description": "MCP server: bi-mcp",
6
6
  "author": "",
7
7
  "license": "MIT",
@@ -60,8 +60,8 @@
60
60
  },
61
61
  "scripts": {
62
62
  "generate:types": "mcp-use generate-types",
63
- "build": "mcp-use build",
64
- "dev": "mcp-use dev",
63
+ "build": "node scripts/run-mcp-use-build.mjs",
64
+ "dev": "node scripts/run-mcp-use-dev.mjs",
65
65
  "start": "mcp-use start",
66
66
  "deploy": "mcp-use deploy",
67
67
  "db:test": "pnpm exec tsx scripts/test-db-connection.ts",
@@ -69,6 +69,7 @@
69
69
  "admin:profile-upsert": "pnpm exec tsx scripts/admin-profile-upsert.ts",
70
70
  "admin:profile-list": "pnpm exec tsx scripts/admin-profile-list.ts",
71
71
  "admin:profile-delete": "pnpm exec tsx scripts/admin-profile-delete.ts",
72
+ "admin:decrypt-secret": "pnpm exec tsx scripts/decrypt-secret.ts",
72
73
  "admin:vtex-upsert": "pnpm exec tsx scripts/admin-vtex-upsert.ts",
73
74
  "admin:vtex-validate": "pnpm exec tsx scripts/admin-vtex-validate.ts",
74
75
  "admin:vtex-list": "pnpm exec tsx scripts/admin-vtex-list.ts"
package/public/icon.svg CHANGED
@@ -1,6 +1,6 @@
1
- <svg width="1000" height="1000" viewBox="0 0 1000 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <rect width="1000" height="1000" rx="500" fill="black"/>
3
- <path d="M355.933 250C414.437 250 461.865 297.607 461.865 356.333C461.865 381.829 460.493 408.403 471.068 431.602L478.975 448.947C493.584 480.997 519.265 506.7 551.303 521.336L566.155 528.121C590.142 539.079 617.694 537.335 644.066 537.335C702.571 537.335 749.999 584.942 749.999 643.668C749.999 702.394 702.571 750.001 644.066 750.001C585.562 750.001 538.134 702.394 538.134 643.668C538.134 618.974 539.24 593.275 528.992 570.807L520.586 552.38C505.948 520.29 480.214 494.565 448.118 479.939L430.164 471.758C407.282 461.331 381.078 462.666 355.933 462.666C297.428 462.666 250 415.059 250 356.333C250 297.607 297.428 250 355.933 250Z" fill="white"/>
4
- <circle cx="350.426" cy="649.575" r="100.426" fill="white"/>
5
- <path d="M750 350.426C750 405.889 705.037 450.851 649.574 450.851C594.11 450.851 549.148 405.889 549.148 350.426C549.148 294.962 594.11 250 649.574 250C705.037 250 750 294.962 750 350.426Z" fill="white"/>
6
- </svg>
1
+ <svg width="1000" height="1000" viewBox="0 0 1000 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="1000" height="1000" rx="500" fill="black"/>
3
+ <path d="M355.933 250C414.437 250 461.865 297.607 461.865 356.333C461.865 381.829 460.493 408.403 471.068 431.602L478.975 448.947C493.584 480.997 519.265 506.7 551.303 521.336L566.155 528.121C590.142 539.079 617.694 537.335 644.066 537.335C702.571 537.335 749.999 584.942 749.999 643.668C749.999 702.394 702.571 750.001 644.066 750.001C585.562 750.001 538.134 702.394 538.134 643.668C538.134 618.974 539.24 593.275 528.992 570.807L520.586 552.38C505.948 520.29 480.214 494.565 448.118 479.939L430.164 471.758C407.282 461.331 381.078 462.666 355.933 462.666C297.428 462.666 250 415.059 250 356.333C250 297.607 297.428 250 355.933 250Z" fill="white"/>
4
+ <circle cx="350.426" cy="649.575" r="100.426" fill="white"/>
5
+ <path d="M750 350.426C750 405.889 705.037 450.851 649.574 450.851C594.11 450.851 549.148 405.889 549.148 350.426C549.148 294.962 594.11 250 649.574 250C705.037 250 750 294.962 750 350.426Z" fill="white"/>
6
+ </svg>
@@ -1,78 +0,0 @@
1
- import {
2
- getDefaultGoogleAdsLoginCustomerId,
3
- getGoogleAdsDeveloperToken
4
- } from "../config/google.js";
5
- const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
6
- function normalizeCustomerId(customerId) {
7
- const normalized = customerId.trim().replace(/-/g, "");
8
- if (!/^\d+$/.test(normalized)) {
9
- throw new Error("Invalid Google Ads customer ID. Use digits only, with or without hyphens.");
10
- }
11
- return normalized;
12
- }
13
- function resolveGoogleAdsCustomerId(customerId) {
14
- if (!customerId?.trim()) {
15
- throw new Error("Missing Google Ads customer ID. Provide customerId.");
16
- }
17
- return normalizeCustomerId(customerId);
18
- }
19
- function resolveGoogleAdsLoginCustomerId(loginCustomerId) {
20
- const explicitCustomerId = loginCustomerId?.trim();
21
- if (explicitCustomerId) {
22
- return normalizeCustomerId(explicitCustomerId);
23
- }
24
- return getDefaultGoogleAdsLoginCustomerId();
25
- }
26
- function resolveGoogleAdsDeveloperToken() {
27
- return getGoogleAdsDeveloperToken();
28
- }
29
- function parseGoogleAdsMetricValue(value) {
30
- if (typeof value === "number" && Number.isFinite(value)) {
31
- return value;
32
- }
33
- if (typeof value === "string") {
34
- const parsedValue = Number(value);
35
- return Number.isFinite(parsedValue) ? parsedValue : 0;
36
- }
37
- return 0;
38
- }
39
- function microsToCurrency(value, decimals = 2) {
40
- return round(parseGoogleAdsMetricValue(value) / 1e6, decimals);
41
- }
42
- function round(value, decimals = 2) {
43
- if (!Number.isFinite(value)) {
44
- return 0;
45
- }
46
- const factor = 10 ** decimals;
47
- return Math.round(value * factor) / factor;
48
- }
49
- function toPercent(numerator, denominator) {
50
- if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0) {
51
- return 0;
52
- }
53
- return numerator / denominator * 100;
54
- }
55
- function getFieldValue(row, path, fallback) {
56
- const segments = path.split(".");
57
- let current = row;
58
- for (const segment of segments) {
59
- if (!current || typeof current !== "object") {
60
- return fallback;
61
- }
62
- current = current[segment];
63
- }
64
- return current ?? fallback;
65
- }
66
- export {
67
- dateRegex,
68
- getFieldValue,
69
- microsToCurrency,
70
- normalizeCustomerId,
71
- parseGoogleAdsMetricValue,
72
- resolveGoogleAdsCustomerId,
73
- resolveGoogleAdsDeveloperToken,
74
- resolveGoogleAdsLoginCustomerId,
75
- round,
76
- toPercent
77
- };
78
- //# sourceMappingURL=report-utils.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/google-ads/report-utils.ts"],
4
- "sourcesContent": ["import {\n getDefaultGoogleAdsLoginCustomerId,\n getGoogleAdsDeveloperToken,\n} from \"../config/google.js\";\nimport type { GoogleAdsMetricValue, GoogleAdsSearchRow } from \"../services/google-ads/google-ads-client.js\";\n\nexport const dateRegex = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nexport function normalizeCustomerId(customerId: string): string {\n const normalized = customerId.trim().replace(/-/g, \"\");\n if (!/^\\d+$/.test(normalized)) {\n throw new Error(\"Invalid Google Ads customer ID. Use digits only, with or without hyphens.\");\n }\n\n return normalized;\n}\n\nexport function resolveGoogleAdsCustomerId(customerId?: string): string {\n if (!customerId?.trim()) {\n throw new Error(\"Missing Google Ads customer ID. Provide customerId.\");\n }\n\n return normalizeCustomerId(customerId);\n}\n\nexport function resolveGoogleAdsLoginCustomerId(loginCustomerId?: string): string | undefined {\n const explicitCustomerId = loginCustomerId?.trim();\n if (explicitCustomerId) {\n return normalizeCustomerId(explicitCustomerId);\n }\n\n return getDefaultGoogleAdsLoginCustomerId();\n}\n\nexport function resolveGoogleAdsDeveloperToken(): string {\n return getGoogleAdsDeveloperToken();\n}\n\nexport function parseGoogleAdsMetricValue(value: GoogleAdsMetricValue | undefined): number {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n\n if (typeof value === \"string\") {\n const parsedValue = Number(value);\n return Number.isFinite(parsedValue) ? parsedValue : 0;\n }\n\n return 0;\n}\n\nexport function microsToCurrency(value: GoogleAdsMetricValue | undefined, decimals = 2): number {\n return round(parseGoogleAdsMetricValue(value) / 1_000_000, decimals);\n}\n\nexport function round(value: number, decimals = 2): number {\n if (!Number.isFinite(value)) {\n return 0;\n }\n\n const factor = 10 ** decimals;\n return Math.round(value * factor) / factor;\n}\n\nexport function toPercent(numerator: number, denominator: number): number {\n if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0) {\n return 0;\n }\n\n return (numerator / denominator) * 100;\n}\n\nexport function getFieldValue<T>(row: GoogleAdsSearchRow, path: string, fallback?: T): T | undefined {\n const segments = path.split(\".\");\n let current: unknown = row;\n\n for (const segment of segments) {\n if (!current || typeof current !== \"object\") {\n return fallback;\n }\n\n current = (current as Record<string, unknown>)[segment];\n }\n\n return (current as T | undefined) ?? fallback;\n}\n"],
5
- "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAGA,MAAM,YAAY;AAElB,SAAS,oBAAoB,YAA4B;AAC9D,QAAM,aAAa,WAAW,KAAK,EAAE,QAAQ,MAAM,EAAE;AACrD,MAAI,CAAC,QAAQ,KAAK,UAAU,GAAG;AAC7B,UAAM,IAAI,MAAM,2EAA2E;AAAA,EAC7F;AAEA,SAAO;AACT;AAEO,SAAS,2BAA2B,YAA6B;AACtE,MAAI,CAAC,YAAY,KAAK,GAAG;AACvB,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AAEA,SAAO,oBAAoB,UAAU;AACvC;AAEO,SAAS,gCAAgC,iBAA8C;AAC5F,QAAM,qBAAqB,iBAAiB,KAAK;AACjD,MAAI,oBAAoB;AACtB,WAAO,oBAAoB,kBAAkB;AAAA,EAC/C;AAEA,SAAO,mCAAmC;AAC5C;AAEO,SAAS,iCAAyC;AACvD,SAAO,2BAA2B;AACpC;AAEO,SAAS,0BAA0B,OAAiD;AACzF,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,GAAG;AACvD,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,cAAc,OAAO,KAAK;AAChC,WAAO,OAAO,SAAS,WAAW,IAAI,cAAc;AAAA,EACtD;AAEA,SAAO;AACT;AAEO,SAAS,iBAAiB,OAAyC,WAAW,GAAW;AAC9F,SAAO,MAAM,0BAA0B,KAAK,IAAI,KAAW,QAAQ;AACrE;AAEO,SAAS,MAAM,OAAe,WAAW,GAAW;AACzD,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,MAAM;AACrB,SAAO,KAAK,MAAM,QAAQ,MAAM,IAAI;AACtC;AAEO,SAAS,UAAU,WAAmB,aAA6B;AACxE,MAAI,CAAC,OAAO,SAAS,SAAS,KAAK,CAAC,OAAO,SAAS,WAAW,KAAK,eAAe,GAAG;AACpF,WAAO;AAAA,EACT;AAEA,SAAQ,YAAY,cAAe;AACrC;AAEO,SAAS,cAAiB,KAAyB,MAAc,UAA6B;AACnG,QAAM,WAAW,KAAK,MAAM,GAAG;AAC/B,MAAI,UAAmB;AAEvB,aAAW,WAAW,UAAU;AAC9B,QAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,aAAO;AAAA,IACT;AAEA,cAAW,QAAoC,OAAO;AAAA,EACxD;AAEA,SAAQ,WAA6B;AACvC;",
6
- "names": []
7
- }