@blotoutio/edgetag-sdk-js 1.67.0 → 1.68.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 (3) hide show
  1. package/index.cjs.js +52 -23
  2. package/index.mjs +52 -23
  3. package/package.json +1 -1
package/index.cjs.js CHANGED
@@ -1494,7 +1494,7 @@ const getStandardPayload = (destination, payload) => {
1494
1494
  referrer: getReferrer(destination),
1495
1495
  search: getSearch(destination),
1496
1496
  locale: getLocale(),
1497
- sdkVersion: "1.67.0" ,
1497
+ sdkVersion: "1.68.0" ,
1498
1498
  ...(payload || {}),
1499
1499
  };
1500
1500
  let storage = {};
@@ -1618,7 +1618,6 @@ const addStub = (destination, stub) => {
1618
1618
 
1619
1619
  const preparePayloadWithConversion = (payloadData, currencySettings) => {
1620
1620
  const currency = payloadData['currency'];
1621
- const value = parseFloat(payloadData['value']);
1622
1621
  if (!currency || !currencySettings) {
1623
1622
  return null;
1624
1623
  }
@@ -1626,15 +1625,31 @@ const preparePayloadWithConversion = (payloadData, currencySettings) => {
1626
1625
  if (!conversion) {
1627
1626
  return null;
1628
1627
  }
1628
+ // Convert the whole money breakdown, not just `value`: applyPurchaseValueMode
1629
+ // recomputes value from grossValue/taxCost/shippingCost, app-lovin reads the
1630
+ // same fields, and discount_value nets against item_price downstream.
1631
+ const convert = (obj, key) => {
1632
+ const amount = parseFloat(obj[key]);
1633
+ if (amount) {
1634
+ obj[key] = parseFloat((amount * conversion.value).toFixed(3));
1635
+ }
1636
+ };
1629
1637
  payloadData['currency'] = conversion.to;
1630
- if (value) {
1631
- payloadData['value'] = parseFloat((value * conversion.value).toFixed(3));
1638
+ convert(payloadData, 'value');
1639
+ convert(payloadData, 'grossValue');
1640
+ convert(payloadData, 'taxCost');
1641
+ convert(payloadData, 'shippingCost');
1642
+ // predicted_ltv is a monetary string (Facebook custom_data); keep it a string.
1643
+ if (payloadData['predicted_ltv']) {
1644
+ const ltv = parseFloat(payloadData['predicted_ltv']);
1645
+ if (ltv) {
1646
+ payloadData['predicted_ltv'] = parseFloat((ltv * conversion.value).toFixed(3)).toString();
1647
+ }
1632
1648
  }
1633
1649
  if ('contents' in payloadData && Array.isArray(payloadData['contents'])) {
1634
1650
  for (const content of payloadData['contents']) {
1635
- if ('item_price' in content) {
1636
- content.item_price = parseFloat((content.item_price * conversion.value).toFixed(3));
1637
- }
1651
+ convert(content, 'item_price');
1652
+ convert(content, 'discount_value');
1638
1653
  }
1639
1654
  }
1640
1655
  return {
@@ -1769,14 +1784,28 @@ const getPlugins = (destination) => {
1769
1784
  }
1770
1785
  };
1771
1786
  // Dispatches a plugin's additional events. Mirrors the edge: each event is
1772
- // stamped with the emitting plugin (so it cannot reprocess itself) and limited
1773
- // to that plugin's scope.
1774
- const dispatchAdditionalEvents = (plugin, pluginScopes, events) => {
1787
+ // stamped with the emitting plugin and stage (so it cannot reprocess itself at
1788
+ // that stage or an earlier one) and limited to that plugin's scope.
1789
+ const dispatchAdditionalEvents = (plugin, pluginScopes, stage, events) => {
1775
1790
  const providerLimits = getPluginProviderLimits(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name]);
1776
1791
  for (const evt of events) {
1777
- handleTag(evt.eventName, evt.data, providerLimits, undefined, plugin.name);
1792
+ handleTag(evt.eventName, evt.data, providerLimits, undefined, plugin.name, stage);
1778
1793
  }
1779
1794
  };
1795
+ const stageOrder = {
1796
+ root: 0,
1797
+ channel: 1,
1798
+ instance: 2,
1799
+ };
1800
+ // The plugin that emitted an additional
1801
+ // event is suppressed at the emitting stage and every earlier one, but still
1802
+ // runs at later stages so it can post-process its own events.
1803
+ const isPluginSuppressed = (pluginName, pluginSource, pluginStage, stage) => {
1804
+ if (pluginName !== pluginSource) {
1805
+ return false;
1806
+ }
1807
+ return stageOrder[stage] <= stageOrder[pluginStage !== null && pluginStage !== void 0 ? pluginStage : 'instance'];
1808
+ };
1780
1809
  // Runs a channel/instance hook chain. The emitting plugin is excluded by the
1781
1810
  // caller's allow-list filter; additional events fire before any skip is applied
1782
1811
  // (matching the edge), and a skip is reported without mutating the final tag.
@@ -1802,7 +1831,7 @@ const runPluginHook = async (plugins, hookName, pluginScopes, baseParams) => {
1802
1831
  variables: plugin.variables || {},
1803
1832
  });
1804
1833
  if (result === null || result === void 0 ? void 0 : result.additionalEvents) {
1805
- dispatchAdditionalEvents(plugin, pluginScopes, result.additionalEvents);
1834
+ dispatchAdditionalEvents(plugin, pluginScopes, hookName === 'tagChannel' ? 'channel' : 'instance', result.additionalEvents);
1806
1835
  }
1807
1836
  // Mark-and-continue, matching the edge (processPluginsChannel /
1808
1837
  // processPluginsInstance): a skip suppresses only the final tag; later
@@ -1836,7 +1865,7 @@ const runPluginHook = async (plugins, hookName, pluginScopes, baseParams) => {
1836
1865
  // skip is applied, and skips are scoped — a global plugin marks every browser
1837
1866
  // instance, a scoped one only its resolved instances. The skip is a marker:
1838
1867
  // channel/instance hooks still run and only the final tag is suppressed.
1839
- const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredTags, pluginSource) => {
1868
+ const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredTags, pluginSource, pluginStage) => {
1840
1869
  var _a, _b;
1841
1870
  const payload = baseParams['payload'];
1842
1871
  let currentEventName = payload['eventName'];
@@ -1844,8 +1873,9 @@ const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredT
1844
1873
  let currentProviders = baseParams['providers'];
1845
1874
  const skippedInstances = new Set();
1846
1875
  for (const plugin of plugins) {
1847
- if (plugin.name === pluginSource)
1876
+ if (isPluginSuppressed(plugin.name, pluginSource, pluginStage, 'root')) {
1848
1877
  continue;
1878
+ }
1849
1879
  const hook = plugin.rules.tagRoot;
1850
1880
  if (!hook)
1851
1881
  continue;
@@ -1860,7 +1890,7 @@ const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredT
1860
1890
  variables: plugin.variables || {},
1861
1891
  });
1862
1892
  if (result === null || result === void 0 ? void 0 : result.additionalEvents) {
1863
- dispatchAdditionalEvents(plugin, pluginScopes, result.additionalEvents);
1893
+ dispatchAdditionalEvents(plugin, pluginScopes, 'root', result.additionalEvents);
1864
1894
  }
1865
1895
  if (result === null || result === void 0 ? void 0 : result.skipEvent) {
1866
1896
  const scopeSkipKeys = getScopedPluginSkipKeys(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name], configuredTags);
@@ -1899,7 +1929,7 @@ const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredT
1899
1929
  skippedInstances,
1900
1930
  };
1901
1931
  };
1902
- const processTag = async (destination, eventName, data = {}, providers, options, pluginSource) => {
1932
+ const processTag = async (destination, eventName, data = {}, providers, options, pluginSource, pluginStage) => {
1903
1933
  var _a, _b;
1904
1934
  let currentEventName = eventName;
1905
1935
  if (!getSetting(destination, 'initialized')) {
@@ -1970,7 +2000,7 @@ const processTag = async (destination, eventName, data = {}, providers, options,
1970
2000
  providers,
1971
2001
  settings: pluginSettings,
1972
2002
  utilities: pluginUtilities,
1973
- }, pluginScopes, configuredTags, pluginSource);
2003
+ }, pluginScopes, configuredTags, pluginSource, pluginStage);
1974
2004
  if (rootResult.providers !== undefined) {
1975
2005
  // eslint-disable-next-line no-param-reassign
1976
2006
  providers = rootResult.providers;
@@ -2009,8 +2039,7 @@ const processTag = async (destination, eventName, data = {}, providers, options,
2009
2039
  // events still fire even when instances are skipped. All instances
2010
2040
  // share the same channel-level input (eventName/data), so a single
2011
2041
  // invocation is equivalent to running it per instance.
2012
- const channelPlugins = plugins.filter((plugin) => plugin.name !== pluginSource &&
2013
- isPluginAllowed(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name], providerId, null));
2042
+ const channelPlugins = plugins.filter((plugin) => !isPluginSuppressed(plugin.name, pluginSource, pluginStage, 'channel') && isPluginAllowed(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name], providerId, null));
2014
2043
  if (channelPlugins.length > 0) {
2015
2044
  const channelResult = await runPluginHook(channelPlugins, 'tagChannel', pluginScopes, {
2016
2045
  payload: {
@@ -2051,7 +2080,7 @@ const processTag = async (destination, eventName, data = {}, providers, options,
2051
2080
  logger.log('GEO request region does not match the filter, skipping');
2052
2081
  continue;
2053
2082
  }
2054
- const instancePlugins = plugins.filter((plugin) => plugin.name !== pluginSource &&
2083
+ const instancePlugins = plugins.filter((plugin) => !isPluginSuppressed(plugin.name, pluginSource, pluginStage, 'instance') &&
2055
2084
  isPluginAllowed(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name], providerId, variable.tagName));
2056
2085
  if (instancePlugins.length > 0) {
2057
2086
  const instanceResult = await runPluginHook(instancePlugins, 'tagInstance', pluginScopes, {
@@ -2134,13 +2163,13 @@ const processTag = async (destination, eventName, data = {}, providers, options,
2134
2163
  handleTag(additionalEventName, jsonClone(originalData || {}), originalProviders, options);
2135
2164
  }
2136
2165
  };
2137
- const handleTag = (eventName, data = {}, providers, options, pluginSource) => {
2166
+ const handleTag = (eventName, data = {}, providers, options, pluginSource, pluginStage) => {
2138
2167
  if (options === null || options === void 0 ? void 0 : options.destination) {
2139
- processTag(options.destination, eventName, data, providers, options, pluginSource).catch(logger.error);
2168
+ processTag(options.destination, eventName, data, providers, options, pluginSource, pluginStage).catch(logger.error);
2140
2169
  return;
2141
2170
  }
2142
2171
  getInstances().forEach((instance) => {
2143
- processTag(instance, eventName, data, providers, options, pluginSource).catch(logger.error);
2172
+ processTag(instance, eventName, data, providers, options, pluginSource, pluginStage).catch(logger.error);
2144
2173
  });
2145
2174
  };
2146
2175
 
package/index.mjs CHANGED
@@ -1492,7 +1492,7 @@ const getStandardPayload = (destination, payload) => {
1492
1492
  referrer: getReferrer(destination),
1493
1493
  search: getSearch(destination),
1494
1494
  locale: getLocale(),
1495
- sdkVersion: "1.67.0" ,
1495
+ sdkVersion: "1.68.0" ,
1496
1496
  ...(payload || {}),
1497
1497
  };
1498
1498
  let storage = {};
@@ -1616,7 +1616,6 @@ const addStub = (destination, stub) => {
1616
1616
 
1617
1617
  const preparePayloadWithConversion = (payloadData, currencySettings) => {
1618
1618
  const currency = payloadData['currency'];
1619
- const value = parseFloat(payloadData['value']);
1620
1619
  if (!currency || !currencySettings) {
1621
1620
  return null;
1622
1621
  }
@@ -1624,15 +1623,31 @@ const preparePayloadWithConversion = (payloadData, currencySettings) => {
1624
1623
  if (!conversion) {
1625
1624
  return null;
1626
1625
  }
1626
+ // Convert the whole money breakdown, not just `value`: applyPurchaseValueMode
1627
+ // recomputes value from grossValue/taxCost/shippingCost, app-lovin reads the
1628
+ // same fields, and discount_value nets against item_price downstream.
1629
+ const convert = (obj, key) => {
1630
+ const amount = parseFloat(obj[key]);
1631
+ if (amount) {
1632
+ obj[key] = parseFloat((amount * conversion.value).toFixed(3));
1633
+ }
1634
+ };
1627
1635
  payloadData['currency'] = conversion.to;
1628
- if (value) {
1629
- payloadData['value'] = parseFloat((value * conversion.value).toFixed(3));
1636
+ convert(payloadData, 'value');
1637
+ convert(payloadData, 'grossValue');
1638
+ convert(payloadData, 'taxCost');
1639
+ convert(payloadData, 'shippingCost');
1640
+ // predicted_ltv is a monetary string (Facebook custom_data); keep it a string.
1641
+ if (payloadData['predicted_ltv']) {
1642
+ const ltv = parseFloat(payloadData['predicted_ltv']);
1643
+ if (ltv) {
1644
+ payloadData['predicted_ltv'] = parseFloat((ltv * conversion.value).toFixed(3)).toString();
1645
+ }
1630
1646
  }
1631
1647
  if ('contents' in payloadData && Array.isArray(payloadData['contents'])) {
1632
1648
  for (const content of payloadData['contents']) {
1633
- if ('item_price' in content) {
1634
- content.item_price = parseFloat((content.item_price * conversion.value).toFixed(3));
1635
- }
1649
+ convert(content, 'item_price');
1650
+ convert(content, 'discount_value');
1636
1651
  }
1637
1652
  }
1638
1653
  return {
@@ -1767,14 +1782,28 @@ const getPlugins = (destination) => {
1767
1782
  }
1768
1783
  };
1769
1784
  // Dispatches a plugin's additional events. Mirrors the edge: each event is
1770
- // stamped with the emitting plugin (so it cannot reprocess itself) and limited
1771
- // to that plugin's scope.
1772
- const dispatchAdditionalEvents = (plugin, pluginScopes, events) => {
1785
+ // stamped with the emitting plugin and stage (so it cannot reprocess itself at
1786
+ // that stage or an earlier one) and limited to that plugin's scope.
1787
+ const dispatchAdditionalEvents = (plugin, pluginScopes, stage, events) => {
1773
1788
  const providerLimits = getPluginProviderLimits(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name]);
1774
1789
  for (const evt of events) {
1775
- handleTag(evt.eventName, evt.data, providerLimits, undefined, plugin.name);
1790
+ handleTag(evt.eventName, evt.data, providerLimits, undefined, plugin.name, stage);
1776
1791
  }
1777
1792
  };
1793
+ const stageOrder = {
1794
+ root: 0,
1795
+ channel: 1,
1796
+ instance: 2,
1797
+ };
1798
+ // The plugin that emitted an additional
1799
+ // event is suppressed at the emitting stage and every earlier one, but still
1800
+ // runs at later stages so it can post-process its own events.
1801
+ const isPluginSuppressed = (pluginName, pluginSource, pluginStage, stage) => {
1802
+ if (pluginName !== pluginSource) {
1803
+ return false;
1804
+ }
1805
+ return stageOrder[stage] <= stageOrder[pluginStage !== null && pluginStage !== void 0 ? pluginStage : 'instance'];
1806
+ };
1778
1807
  // Runs a channel/instance hook chain. The emitting plugin is excluded by the
1779
1808
  // caller's allow-list filter; additional events fire before any skip is applied
1780
1809
  // (matching the edge), and a skip is reported without mutating the final tag.
@@ -1800,7 +1829,7 @@ const runPluginHook = async (plugins, hookName, pluginScopes, baseParams) => {
1800
1829
  variables: plugin.variables || {},
1801
1830
  });
1802
1831
  if (result === null || result === void 0 ? void 0 : result.additionalEvents) {
1803
- dispatchAdditionalEvents(plugin, pluginScopes, result.additionalEvents);
1832
+ dispatchAdditionalEvents(plugin, pluginScopes, hookName === 'tagChannel' ? 'channel' : 'instance', result.additionalEvents);
1804
1833
  }
1805
1834
  // Mark-and-continue, matching the edge (processPluginsChannel /
1806
1835
  // processPluginsInstance): a skip suppresses only the final tag; later
@@ -1834,7 +1863,7 @@ const runPluginHook = async (plugins, hookName, pluginScopes, baseParams) => {
1834
1863
  // skip is applied, and skips are scoped — a global plugin marks every browser
1835
1864
  // instance, a scoped one only its resolved instances. The skip is a marker:
1836
1865
  // channel/instance hooks still run and only the final tag is suppressed.
1837
- const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredTags, pluginSource) => {
1866
+ const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredTags, pluginSource, pluginStage) => {
1838
1867
  var _a, _b;
1839
1868
  const payload = baseParams['payload'];
1840
1869
  let currentEventName = payload['eventName'];
@@ -1842,8 +1871,9 @@ const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredT
1842
1871
  let currentProviders = baseParams['providers'];
1843
1872
  const skippedInstances = new Set();
1844
1873
  for (const plugin of plugins) {
1845
- if (plugin.name === pluginSource)
1874
+ if (isPluginSuppressed(plugin.name, pluginSource, pluginStage, 'root')) {
1846
1875
  continue;
1876
+ }
1847
1877
  const hook = plugin.rules.tagRoot;
1848
1878
  if (!hook)
1849
1879
  continue;
@@ -1858,7 +1888,7 @@ const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredT
1858
1888
  variables: plugin.variables || {},
1859
1889
  });
1860
1890
  if (result === null || result === void 0 ? void 0 : result.additionalEvents) {
1861
- dispatchAdditionalEvents(plugin, pluginScopes, result.additionalEvents);
1891
+ dispatchAdditionalEvents(plugin, pluginScopes, 'root', result.additionalEvents);
1862
1892
  }
1863
1893
  if (result === null || result === void 0 ? void 0 : result.skipEvent) {
1864
1894
  const scopeSkipKeys = getScopedPluginSkipKeys(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name], configuredTags);
@@ -1897,7 +1927,7 @@ const runRootPluginHooks = async (plugins, baseParams, pluginScopes, configuredT
1897
1927
  skippedInstances,
1898
1928
  };
1899
1929
  };
1900
- const processTag = async (destination, eventName, data = {}, providers, options, pluginSource) => {
1930
+ const processTag = async (destination, eventName, data = {}, providers, options, pluginSource, pluginStage) => {
1901
1931
  var _a, _b;
1902
1932
  let currentEventName = eventName;
1903
1933
  if (!getSetting(destination, 'initialized')) {
@@ -1968,7 +1998,7 @@ const processTag = async (destination, eventName, data = {}, providers, options,
1968
1998
  providers,
1969
1999
  settings: pluginSettings,
1970
2000
  utilities: pluginUtilities,
1971
- }, pluginScopes, configuredTags, pluginSource);
2001
+ }, pluginScopes, configuredTags, pluginSource, pluginStage);
1972
2002
  if (rootResult.providers !== undefined) {
1973
2003
  // eslint-disable-next-line no-param-reassign
1974
2004
  providers = rootResult.providers;
@@ -2007,8 +2037,7 @@ const processTag = async (destination, eventName, data = {}, providers, options,
2007
2037
  // events still fire even when instances are skipped. All instances
2008
2038
  // share the same channel-level input (eventName/data), so a single
2009
2039
  // invocation is equivalent to running it per instance.
2010
- const channelPlugins = plugins.filter((plugin) => plugin.name !== pluginSource &&
2011
- isPluginAllowed(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name], providerId, null));
2040
+ const channelPlugins = plugins.filter((plugin) => !isPluginSuppressed(plugin.name, pluginSource, pluginStage, 'channel') && isPluginAllowed(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name], providerId, null));
2012
2041
  if (channelPlugins.length > 0) {
2013
2042
  const channelResult = await runPluginHook(channelPlugins, 'tagChannel', pluginScopes, {
2014
2043
  payload: {
@@ -2049,7 +2078,7 @@ const processTag = async (destination, eventName, data = {}, providers, options,
2049
2078
  logger.log('GEO request region does not match the filter, skipping');
2050
2079
  continue;
2051
2080
  }
2052
- const instancePlugins = plugins.filter((plugin) => plugin.name !== pluginSource &&
2081
+ const instancePlugins = plugins.filter((plugin) => !isPluginSuppressed(plugin.name, pluginSource, pluginStage, 'instance') &&
2053
2082
  isPluginAllowed(pluginScopes === null || pluginScopes === void 0 ? void 0 : pluginScopes[plugin.name], providerId, variable.tagName));
2054
2083
  if (instancePlugins.length > 0) {
2055
2084
  const instanceResult = await runPluginHook(instancePlugins, 'tagInstance', pluginScopes, {
@@ -2132,13 +2161,13 @@ const processTag = async (destination, eventName, data = {}, providers, options,
2132
2161
  handleTag(additionalEventName, jsonClone(originalData || {}), originalProviders, options);
2133
2162
  }
2134
2163
  };
2135
- const handleTag = (eventName, data = {}, providers, options, pluginSource) => {
2164
+ const handleTag = (eventName, data = {}, providers, options, pluginSource, pluginStage) => {
2136
2165
  if (options === null || options === void 0 ? void 0 : options.destination) {
2137
- processTag(options.destination, eventName, data, providers, options, pluginSource).catch(logger.error);
2166
+ processTag(options.destination, eventName, data, providers, options, pluginSource, pluginStage).catch(logger.error);
2138
2167
  return;
2139
2168
  }
2140
2169
  getInstances().forEach((instance) => {
2141
- processTag(instance, eventName, data, providers, options, pluginSource).catch(logger.error);
2170
+ processTag(instance, eventName, data, providers, options, pluginSource, pluginStage).catch(logger.error);
2142
2171
  });
2143
2172
  };
2144
2173
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blotoutio/edgetag-sdk-js",
3
- "version": "1.67.0",
3
+ "version": "1.68.0",
4
4
  "description": "JS SDK for EdgeTag",
5
5
  "author": "Blotout",
6
6
  "license": "MIT",