@bacano/sdk 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/STOREFRONT_MARKETING.md +58 -2
- package/index.cjs +69 -2
- package/index.js +69 -2
- package/marketing/gtm.d.ts +6 -0
- package/package.json +1 -1
- package/react.cjs +69 -2
- package/react.js +69 -2
- package/types.d.ts +4 -0
package/README.md
CHANGED
|
@@ -945,6 +945,10 @@ contains only browser-safe media metadata and a public Storage URL.
|
|
|
945
945
|
|
|
946
946
|
GA4, Meta, and Microsoft Clarity are configured per website in Bacano and are
|
|
947
947
|
disabled by default.
|
|
948
|
+
An optional Google Tag Manager web container is also supported via
|
|
949
|
+
`marketing.destinations.gtm.containerId`. It adds `bacano_*` browser events to
|
|
950
|
+
the data layer without replacing native destinations or server conversions.
|
|
951
|
+
See [GTM setup, limitations and duplication safeguards](./STOREFRONT_MARKETING.md#google-tag-manager-contenedor-web-opcional).
|
|
948
952
|
The existing public-config request returns only active public destination IDs;
|
|
949
953
|
provider secrets never reach the SDK. The runtime remains a no-op outside the
|
|
950
954
|
website's canonical production domain.
|
package/STOREFRONT_MARKETING.md
CHANGED
|
@@ -6,9 +6,65 @@ identificadores, adaptadores ni llamadas directas a proveedores.
|
|
|
6
6
|
|
|
7
7
|
## Regla principal
|
|
8
8
|
|
|
9
|
+
### Google Tag Manager (contenedor web opcional)
|
|
10
|
+
|
|
11
|
+
En Bacano, la tarjeta Google Tag Manager permite guardar un ID `GTM-…`, validar
|
|
12
|
+
que el script publicado sea accesible y activar o pausar la integración por sitio.
|
|
13
|
+
Validar no comprueba propiedad del contenedor, etiquetas, consentimiento ni
|
|
14
|
+
recepción de eventos: comprobar esto en Tag Assistant con el administrador del
|
|
15
|
+
contenedor. No se solicitan secretos, HTML ni URLs de scripts personalizados.
|
|
16
|
+
|
|
17
|
+
Requiere desplegar API/migración y actualizar `@bacano/sdk` en el storefront.
|
|
18
|
+
El contrato público añade `marketing.destinations.gtm.containerId`; SDKs anteriores
|
|
19
|
+
lo ignoran. Un sitio con configuración estática debe reconstruirse para incorporar
|
|
20
|
+
cambios. No se modifica ni se publica automáticamente ningún contenedor de Google.
|
|
21
|
+
|
|
22
|
+
El mismo `BacanoMarketingProvider` carga `gtm.js` una sola vez, conserva la cola
|
|
23
|
+
`window.dataLayer` existente y respeta las barreras de dominio, preview y
|
|
24
|
+
`measurementAllowed`, además de la carga diferida. No instalar otro snippet.
|
|
25
|
+
Esta versión requiere JavaScript; no incorpora un iframe `noscript` ni GTM server-side.
|
|
26
|
+
Pausar evita nuevas cargas con la configuración actualizada; no puede deshacer
|
|
27
|
+
etiquetas de terceros ya ejecutadas en pestañas abiertas. Recargar para comprobar.
|
|
28
|
+
|
|
29
|
+
Los eventos oficiales de navegador se publican con prefijo `bacano_`:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
{ event: "bacano_add_to_cart", bacano: {
|
|
33
|
+
schema_version: 1, event_name: "add_to_cart", event_id: "…",
|
|
34
|
+
currency: "COP", value: 50000,
|
|
35
|
+
items: [{ item_id: "product-variant-id", item_name: "Producto", quantity: 1 }]
|
|
36
|
+
} }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Configurar activadores de evento personalizado `bacano_*` y variables de capa de
|
|
40
|
+
datos como `bacano.event_name`, `bacano.value` y `bacano.items`. El objeto `bacano`
|
|
41
|
+
se limpia antes de cada evento para no arrastrar artículos ni parámetros anteriores.
|
|
42
|
+
Incluye URL saneada, parámetros de comercio e identificadores técnicos de
|
|
43
|
+
interacciones; no copia datos de clientes, valores de formularios, atribución,
|
|
44
|
+
títulos de página ni texto libre de búsqueda. Los eventos `search` no llevan el término.
|
|
45
|
+
No emitir desde GTM `purchase`, `refund` u `order_placed` basándose en la página de
|
|
46
|
+
gracias: siguen siendo autoritativos del servidor y no se publican en este puente.
|
|
47
|
+
|
|
48
|
+
GA4, Meta y Clarity nativos permanecen sin cambios. **No repetir esos destinos o
|
|
49
|
+
conversiones dentro del contenedor**. Esta fase no es un modo alternativo completo
|
|
50
|
+
de medición ni realiza deduplicación entre proveedores y etiquetas configuradas
|
|
51
|
+
externamente. El administrador de GTM puede ejecutar scripts con acceso a la página:
|
|
52
|
+
otorgar acceso solo a personas de confianza y revisar cada publicación.
|
|
53
|
+
El aviso de cookies informativo de Bacano no es un CMP y no autoriza las etiquetas;
|
|
54
|
+
el responsable del contenedor debe configurar el consentimiento aplicable.
|
|
55
|
+
|
|
56
|
+
CSP: permitir `https://www.googletagmanager.com` en `script-src`. Los destinos
|
|
57
|
+
adicionales y Tag Assistant requieren revisar sus propios permisos de CSP; no
|
|
58
|
+
habilitar comodines ni `unsafe-eval` globalmente para acomodar un contenedor.
|
|
59
|
+
|
|
60
|
+
Referencias: [configuración de GTM](https://support.google.com/tagmanager/answer/14842164)
|
|
61
|
+
y [contrato dataLayer](https://developers.google.com/tag-platform/tag-manager/datalayer).
|
|
62
|
+
|
|
63
|
+
### Eventos comunes
|
|
64
|
+
|
|
9
65
|
El storefront expresa hechos mediante `useMarketing().track()`. El SDK decide
|
|
10
|
-
si la medición está activa, sanea la URL y adapta el evento a GA4, Meta
|
|
11
|
-
Microsoft Clarity. Nunca
|
|
66
|
+
si la medición está activa, sanea la URL y adapta el evento a GA4, Meta,
|
|
67
|
+
Microsoft Clarity y, si se configuró, GTM. Nunca
|
|
12
68
|
se debe llamar directamente a `gtag`, `dataLayer`, `fbq`, Measurement Protocol
|
|
13
69
|
o Conversions API.
|
|
14
70
|
Clarity también se carga desde este runtime; no se instala su snippet de forma
|
package/index.cjs
CHANGED
|
@@ -394,7 +394,8 @@ async function getWebsitePublicConfig(options) {
|
|
|
394
394
|
pixelId: marketing.destinations.meta.pixel_id,
|
|
395
395
|
diagnosticMode: marketing.destinations.meta.diagnostic_mode ?? false
|
|
396
396
|
} : null,
|
|
397
|
-
clarity: marketing.destinations.clarity ? { projectId: marketing.destinations.clarity.project_id } : null
|
|
397
|
+
clarity: marketing.destinations.clarity ? { projectId: marketing.destinations.clarity.project_id } : null,
|
|
398
|
+
...marketing.destinations.gtm ? { gtm: { containerId: marketing.destinations.gtm.container_id } } : {}
|
|
398
399
|
}
|
|
399
400
|
},
|
|
400
401
|
legal: {
|
|
@@ -6066,6 +6067,61 @@ function matchesCanonicalMarketingHost(canonicalDomain, currentHostname) {
|
|
|
6066
6067
|
}
|
|
6067
6068
|
}
|
|
6068
6069
|
|
|
6070
|
+
// libs/sdk/src/marketing/gtm.ts
|
|
6071
|
+
var CONTAINER_ID = /^GTM-[A-Z0-9]{4,32}$/;
|
|
6072
|
+
var preparedQueues = /* @__PURE__ */ new WeakMap();
|
|
6073
|
+
function prepareGtm(containerId) {
|
|
6074
|
+
const target = marketingWindow();
|
|
6075
|
+
if (!target || !CONTAINER_ID.test(containerId)) return;
|
|
6076
|
+
target.dataLayer ??= [];
|
|
6077
|
+
const containers = preparedQueues.get(target.dataLayer) ?? /* @__PURE__ */ new Set();
|
|
6078
|
+
if (containers.has(containerId)) return;
|
|
6079
|
+
target.dataLayer.push({ "gtm.start": Date.now(), event: "gtm.js" });
|
|
6080
|
+
containers.add(containerId);
|
|
6081
|
+
preparedQueues.set(target.dataLayer, containers);
|
|
6082
|
+
}
|
|
6083
|
+
function loadGtmScript(containerId, onError) {
|
|
6084
|
+
const target = marketingWindow();
|
|
6085
|
+
if (!target || typeof document === "undefined" || !CONTAINER_ID.test(containerId))
|
|
6086
|
+
return;
|
|
6087
|
+
const id = `bacano-gtm-${containerId}`;
|
|
6088
|
+
if (document.getElementById(id)) return;
|
|
6089
|
+
prepareGtm(containerId);
|
|
6090
|
+
const script = document.createElement("script");
|
|
6091
|
+
script.id = id;
|
|
6092
|
+
script.async = true;
|
|
6093
|
+
script.src = `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(containerId)}`;
|
|
6094
|
+
script.referrerPolicy = "strict-origin-when-cross-origin";
|
|
6095
|
+
script.onerror = () => onError();
|
|
6096
|
+
document.head.appendChild(script);
|
|
6097
|
+
}
|
|
6098
|
+
function sendGtm(event) {
|
|
6099
|
+
const target = marketingWindow();
|
|
6100
|
+
if (!target?.dataLayer) return;
|
|
6101
|
+
target.dataLayer.push({ bacano: null });
|
|
6102
|
+
target.dataLayer.push({
|
|
6103
|
+
event: `bacano_${event.eventName}`,
|
|
6104
|
+
bacano: {
|
|
6105
|
+
schema_version: event.schemaVersion,
|
|
6106
|
+
event_name: event.eventName,
|
|
6107
|
+
event_id: event.eventId,
|
|
6108
|
+
...interactionParameters(event),
|
|
6109
|
+
page_location: event.pageLocation,
|
|
6110
|
+
currency: event.currency,
|
|
6111
|
+
value: event.value,
|
|
6112
|
+
items: mapGa4Items(event.items),
|
|
6113
|
+
item_list_id: event.itemListId,
|
|
6114
|
+
item_list_name: event.itemListName,
|
|
6115
|
+
shipping_tier: event.shippingTier,
|
|
6116
|
+
payment_type: event.paymentType,
|
|
6117
|
+
method: event.contactMethod,
|
|
6118
|
+
contact_placement: event.eventName === "contact" && WEBSITE_MARKETING_CONTACT_PLACEMENTS.some(
|
|
6119
|
+
(value) => value === event.contactPlacement
|
|
6120
|
+
) ? event.contactPlacement : void 0
|
|
6121
|
+
}
|
|
6122
|
+
});
|
|
6123
|
+
}
|
|
6124
|
+
|
|
6069
6125
|
// libs/sdk/src/marketing/runtime.ts
|
|
6070
6126
|
function createWebsiteMarketingRuntime(options) {
|
|
6071
6127
|
let destroyed = false;
|
|
@@ -6086,7 +6142,7 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
6086
6142
|
target.location.hostname
|
|
6087
6143
|
)
|
|
6088
6144
|
) && Boolean(
|
|
6089
|
-
options.config.destinations.ga4 || options.config.destinations.meta || options.config.destinations.clarity
|
|
6145
|
+
options.config.destinations.ga4 || options.config.destinations.meta || options.config.destinations.clarity || options.config.destinations.gtm
|
|
6090
6146
|
);
|
|
6091
6147
|
if (!enabled)
|
|
6092
6148
|
diagnostic({
|
|
@@ -6127,6 +6183,10 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
6127
6183
|
const { projectId } = options.config.destinations.clarity;
|
|
6128
6184
|
guard("Clarity", () => prepareClarity(projectId));
|
|
6129
6185
|
}
|
|
6186
|
+
if (options.config.destinations.gtm) {
|
|
6187
|
+
const { containerId } = options.config.destinations.gtm;
|
|
6188
|
+
guard("GTM", () => prepareGtm(containerId));
|
|
6189
|
+
}
|
|
6130
6190
|
cancelScheduledLoad = scheduleScriptLoad(() => {
|
|
6131
6191
|
if (destroyed) return;
|
|
6132
6192
|
if (options.config.destinations.ga4) {
|
|
@@ -6142,6 +6202,10 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
6142
6202
|
() => loadClarityScript(projectId, scriptError("Clarity"))
|
|
6143
6203
|
);
|
|
6144
6204
|
}
|
|
6205
|
+
if (options.config.destinations.gtm) {
|
|
6206
|
+
const { containerId } = options.config.destinations.gtm;
|
|
6207
|
+
guard("GTM", () => loadGtmScript(containerId, scriptError("GTM")));
|
|
6208
|
+
}
|
|
6145
6209
|
}, options.scriptLoadStrategy);
|
|
6146
6210
|
captureWebsiteMarketingAttribution(
|
|
6147
6211
|
options.websiteId,
|
|
@@ -6261,6 +6325,9 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
6261
6325
|
if (options.config.destinations.clarity) {
|
|
6262
6326
|
dispatch("Clarity", () => sendClarityEvent(event.eventName));
|
|
6263
6327
|
}
|
|
6328
|
+
if (options.config.destinations.gtm && /^GTM-[A-Z0-9]{4,32}$/.test(options.config.destinations.gtm.containerId)) {
|
|
6329
|
+
dispatch("GTM", () => sendGtm(event));
|
|
6330
|
+
}
|
|
6264
6331
|
},
|
|
6265
6332
|
captureAttribution() {
|
|
6266
6333
|
if (!enabled || destroyed) return null;
|
package/index.js
CHANGED
|
@@ -337,7 +337,8 @@ async function getWebsitePublicConfig(options) {
|
|
|
337
337
|
pixelId: marketing.destinations.meta.pixel_id,
|
|
338
338
|
diagnosticMode: marketing.destinations.meta.diagnostic_mode ?? false
|
|
339
339
|
} : null,
|
|
340
|
-
clarity: marketing.destinations.clarity ? { projectId: marketing.destinations.clarity.project_id } : null
|
|
340
|
+
clarity: marketing.destinations.clarity ? { projectId: marketing.destinations.clarity.project_id } : null,
|
|
341
|
+
...marketing.destinations.gtm ? { gtm: { containerId: marketing.destinations.gtm.container_id } } : {}
|
|
341
342
|
}
|
|
342
343
|
},
|
|
343
344
|
legal: {
|
|
@@ -6009,6 +6010,61 @@ function matchesCanonicalMarketingHost(canonicalDomain, currentHostname) {
|
|
|
6009
6010
|
}
|
|
6010
6011
|
}
|
|
6011
6012
|
|
|
6013
|
+
// libs/sdk/src/marketing/gtm.ts
|
|
6014
|
+
var CONTAINER_ID = /^GTM-[A-Z0-9]{4,32}$/;
|
|
6015
|
+
var preparedQueues = /* @__PURE__ */ new WeakMap();
|
|
6016
|
+
function prepareGtm(containerId) {
|
|
6017
|
+
const target = marketingWindow();
|
|
6018
|
+
if (!target || !CONTAINER_ID.test(containerId)) return;
|
|
6019
|
+
target.dataLayer ??= [];
|
|
6020
|
+
const containers = preparedQueues.get(target.dataLayer) ?? /* @__PURE__ */ new Set();
|
|
6021
|
+
if (containers.has(containerId)) return;
|
|
6022
|
+
target.dataLayer.push({ "gtm.start": Date.now(), event: "gtm.js" });
|
|
6023
|
+
containers.add(containerId);
|
|
6024
|
+
preparedQueues.set(target.dataLayer, containers);
|
|
6025
|
+
}
|
|
6026
|
+
function loadGtmScript(containerId, onError) {
|
|
6027
|
+
const target = marketingWindow();
|
|
6028
|
+
if (!target || typeof document === "undefined" || !CONTAINER_ID.test(containerId))
|
|
6029
|
+
return;
|
|
6030
|
+
const id = `bacano-gtm-${containerId}`;
|
|
6031
|
+
if (document.getElementById(id)) return;
|
|
6032
|
+
prepareGtm(containerId);
|
|
6033
|
+
const script = document.createElement("script");
|
|
6034
|
+
script.id = id;
|
|
6035
|
+
script.async = true;
|
|
6036
|
+
script.src = `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(containerId)}`;
|
|
6037
|
+
script.referrerPolicy = "strict-origin-when-cross-origin";
|
|
6038
|
+
script.onerror = () => onError();
|
|
6039
|
+
document.head.appendChild(script);
|
|
6040
|
+
}
|
|
6041
|
+
function sendGtm(event) {
|
|
6042
|
+
const target = marketingWindow();
|
|
6043
|
+
if (!target?.dataLayer) return;
|
|
6044
|
+
target.dataLayer.push({ bacano: null });
|
|
6045
|
+
target.dataLayer.push({
|
|
6046
|
+
event: `bacano_${event.eventName}`,
|
|
6047
|
+
bacano: {
|
|
6048
|
+
schema_version: event.schemaVersion,
|
|
6049
|
+
event_name: event.eventName,
|
|
6050
|
+
event_id: event.eventId,
|
|
6051
|
+
...interactionParameters(event),
|
|
6052
|
+
page_location: event.pageLocation,
|
|
6053
|
+
currency: event.currency,
|
|
6054
|
+
value: event.value,
|
|
6055
|
+
items: mapGa4Items(event.items),
|
|
6056
|
+
item_list_id: event.itemListId,
|
|
6057
|
+
item_list_name: event.itemListName,
|
|
6058
|
+
shipping_tier: event.shippingTier,
|
|
6059
|
+
payment_type: event.paymentType,
|
|
6060
|
+
method: event.contactMethod,
|
|
6061
|
+
contact_placement: event.eventName === "contact" && WEBSITE_MARKETING_CONTACT_PLACEMENTS.some(
|
|
6062
|
+
(value) => value === event.contactPlacement
|
|
6063
|
+
) ? event.contactPlacement : void 0
|
|
6064
|
+
}
|
|
6065
|
+
});
|
|
6066
|
+
}
|
|
6067
|
+
|
|
6012
6068
|
// libs/sdk/src/marketing/runtime.ts
|
|
6013
6069
|
function createWebsiteMarketingRuntime(options) {
|
|
6014
6070
|
let destroyed = false;
|
|
@@ -6029,7 +6085,7 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
6029
6085
|
target.location.hostname
|
|
6030
6086
|
)
|
|
6031
6087
|
) && Boolean(
|
|
6032
|
-
options.config.destinations.ga4 || options.config.destinations.meta || options.config.destinations.clarity
|
|
6088
|
+
options.config.destinations.ga4 || options.config.destinations.meta || options.config.destinations.clarity || options.config.destinations.gtm
|
|
6033
6089
|
);
|
|
6034
6090
|
if (!enabled)
|
|
6035
6091
|
diagnostic({
|
|
@@ -6070,6 +6126,10 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
6070
6126
|
const { projectId } = options.config.destinations.clarity;
|
|
6071
6127
|
guard("Clarity", () => prepareClarity(projectId));
|
|
6072
6128
|
}
|
|
6129
|
+
if (options.config.destinations.gtm) {
|
|
6130
|
+
const { containerId } = options.config.destinations.gtm;
|
|
6131
|
+
guard("GTM", () => prepareGtm(containerId));
|
|
6132
|
+
}
|
|
6073
6133
|
cancelScheduledLoad = scheduleScriptLoad(() => {
|
|
6074
6134
|
if (destroyed) return;
|
|
6075
6135
|
if (options.config.destinations.ga4) {
|
|
@@ -6085,6 +6145,10 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
6085
6145
|
() => loadClarityScript(projectId, scriptError("Clarity"))
|
|
6086
6146
|
);
|
|
6087
6147
|
}
|
|
6148
|
+
if (options.config.destinations.gtm) {
|
|
6149
|
+
const { containerId } = options.config.destinations.gtm;
|
|
6150
|
+
guard("GTM", () => loadGtmScript(containerId, scriptError("GTM")));
|
|
6151
|
+
}
|
|
6088
6152
|
}, options.scriptLoadStrategy);
|
|
6089
6153
|
captureWebsiteMarketingAttribution(
|
|
6090
6154
|
options.websiteId,
|
|
@@ -6204,6 +6268,9 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
6204
6268
|
if (options.config.destinations.clarity) {
|
|
6205
6269
|
dispatch("Clarity", () => sendClarityEvent(event.eventName));
|
|
6206
6270
|
}
|
|
6271
|
+
if (options.config.destinations.gtm && /^GTM-[A-Z0-9]{4,32}$/.test(options.config.destinations.gtm.containerId)) {
|
|
6272
|
+
dispatch("GTM", () => sendGtm(event));
|
|
6273
|
+
}
|
|
6207
6274
|
},
|
|
6208
6275
|
captureAttribution() {
|
|
6209
6276
|
if (!enabled || destroyed) return null;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { WebsiteMarketingBrowserEvent } from "./types";
|
|
2
|
+
/** Preserve the shared Google queue, including an existing native gtag.js. */
|
|
3
|
+
export declare function prepareGtm(containerId: string): void;
|
|
4
|
+
export declare function loadGtmScript(containerId: string, onError: () => void): void;
|
|
5
|
+
/** Explicit allowlist: never spread caller input, customer data or attribution. */
|
|
6
|
+
export declare function sendGtm(event: WebsiteMarketingBrowserEvent): void;
|
package/package.json
CHANGED
package/react.cjs
CHANGED
|
@@ -5105,6 +5105,61 @@ function matchesCanonicalMarketingHost(canonicalDomain, currentHostname) {
|
|
|
5105
5105
|
}
|
|
5106
5106
|
}
|
|
5107
5107
|
|
|
5108
|
+
// libs/sdk/src/marketing/gtm.ts
|
|
5109
|
+
var CONTAINER_ID = /^GTM-[A-Z0-9]{4,32}$/;
|
|
5110
|
+
var preparedQueues = /* @__PURE__ */ new WeakMap();
|
|
5111
|
+
function prepareGtm(containerId) {
|
|
5112
|
+
const target = marketingWindow();
|
|
5113
|
+
if (!target || !CONTAINER_ID.test(containerId)) return;
|
|
5114
|
+
target.dataLayer ??= [];
|
|
5115
|
+
const containers = preparedQueues.get(target.dataLayer) ?? /* @__PURE__ */ new Set();
|
|
5116
|
+
if (containers.has(containerId)) return;
|
|
5117
|
+
target.dataLayer.push({ "gtm.start": Date.now(), event: "gtm.js" });
|
|
5118
|
+
containers.add(containerId);
|
|
5119
|
+
preparedQueues.set(target.dataLayer, containers);
|
|
5120
|
+
}
|
|
5121
|
+
function loadGtmScript(containerId, onError) {
|
|
5122
|
+
const target = marketingWindow();
|
|
5123
|
+
if (!target || typeof document === "undefined" || !CONTAINER_ID.test(containerId))
|
|
5124
|
+
return;
|
|
5125
|
+
const id = `bacano-gtm-${containerId}`;
|
|
5126
|
+
if (document.getElementById(id)) return;
|
|
5127
|
+
prepareGtm(containerId);
|
|
5128
|
+
const script = document.createElement("script");
|
|
5129
|
+
script.id = id;
|
|
5130
|
+
script.async = true;
|
|
5131
|
+
script.src = `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(containerId)}`;
|
|
5132
|
+
script.referrerPolicy = "strict-origin-when-cross-origin";
|
|
5133
|
+
script.onerror = () => onError();
|
|
5134
|
+
document.head.appendChild(script);
|
|
5135
|
+
}
|
|
5136
|
+
function sendGtm(event) {
|
|
5137
|
+
const target = marketingWindow();
|
|
5138
|
+
if (!target?.dataLayer) return;
|
|
5139
|
+
target.dataLayer.push({ bacano: null });
|
|
5140
|
+
target.dataLayer.push({
|
|
5141
|
+
event: `bacano_${event.eventName}`,
|
|
5142
|
+
bacano: {
|
|
5143
|
+
schema_version: event.schemaVersion,
|
|
5144
|
+
event_name: event.eventName,
|
|
5145
|
+
event_id: event.eventId,
|
|
5146
|
+
...interactionParameters(event),
|
|
5147
|
+
page_location: event.pageLocation,
|
|
5148
|
+
currency: event.currency,
|
|
5149
|
+
value: event.value,
|
|
5150
|
+
items: mapGa4Items(event.items),
|
|
5151
|
+
item_list_id: event.itemListId,
|
|
5152
|
+
item_list_name: event.itemListName,
|
|
5153
|
+
shipping_tier: event.shippingTier,
|
|
5154
|
+
payment_type: event.paymentType,
|
|
5155
|
+
method: event.contactMethod,
|
|
5156
|
+
contact_placement: event.eventName === "contact" && WEBSITE_MARKETING_CONTACT_PLACEMENTS.some(
|
|
5157
|
+
(value) => value === event.contactPlacement
|
|
5158
|
+
) ? event.contactPlacement : void 0
|
|
5159
|
+
}
|
|
5160
|
+
});
|
|
5161
|
+
}
|
|
5162
|
+
|
|
5108
5163
|
// libs/sdk/src/marketing/runtime.ts
|
|
5109
5164
|
function createWebsiteMarketingRuntime(options) {
|
|
5110
5165
|
let destroyed = false;
|
|
@@ -5125,7 +5180,7 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
5125
5180
|
target.location.hostname
|
|
5126
5181
|
)
|
|
5127
5182
|
) && Boolean(
|
|
5128
|
-
options.config.destinations.ga4 || options.config.destinations.meta || options.config.destinations.clarity
|
|
5183
|
+
options.config.destinations.ga4 || options.config.destinations.meta || options.config.destinations.clarity || options.config.destinations.gtm
|
|
5129
5184
|
);
|
|
5130
5185
|
if (!enabled)
|
|
5131
5186
|
diagnostic({
|
|
@@ -5166,6 +5221,10 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
5166
5221
|
const { projectId } = options.config.destinations.clarity;
|
|
5167
5222
|
guard("Clarity", () => prepareClarity(projectId));
|
|
5168
5223
|
}
|
|
5224
|
+
if (options.config.destinations.gtm) {
|
|
5225
|
+
const { containerId } = options.config.destinations.gtm;
|
|
5226
|
+
guard("GTM", () => prepareGtm(containerId));
|
|
5227
|
+
}
|
|
5169
5228
|
cancelScheduledLoad = scheduleScriptLoad(() => {
|
|
5170
5229
|
if (destroyed) return;
|
|
5171
5230
|
if (options.config.destinations.ga4) {
|
|
@@ -5181,6 +5240,10 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
5181
5240
|
() => loadClarityScript(projectId, scriptError("Clarity"))
|
|
5182
5241
|
);
|
|
5183
5242
|
}
|
|
5243
|
+
if (options.config.destinations.gtm) {
|
|
5244
|
+
const { containerId } = options.config.destinations.gtm;
|
|
5245
|
+
guard("GTM", () => loadGtmScript(containerId, scriptError("GTM")));
|
|
5246
|
+
}
|
|
5184
5247
|
}, options.scriptLoadStrategy);
|
|
5185
5248
|
captureWebsiteMarketingAttribution(
|
|
5186
5249
|
options.websiteId,
|
|
@@ -5300,6 +5363,9 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
5300
5363
|
if (options.config.destinations.clarity) {
|
|
5301
5364
|
dispatch("Clarity", () => sendClarityEvent(event.eventName));
|
|
5302
5365
|
}
|
|
5366
|
+
if (options.config.destinations.gtm && /^GTM-[A-Z0-9]{4,32}$/.test(options.config.destinations.gtm.containerId)) {
|
|
5367
|
+
dispatch("GTM", () => sendGtm(event));
|
|
5368
|
+
}
|
|
5303
5369
|
},
|
|
5304
5370
|
captureAttribution() {
|
|
5305
5371
|
if (!enabled || destroyed) return null;
|
|
@@ -5574,7 +5640,8 @@ async function getWebsitePublicConfig(options) {
|
|
|
5574
5640
|
pixelId: marketing.destinations.meta.pixel_id,
|
|
5575
5641
|
diagnosticMode: marketing.destinations.meta.diagnostic_mode ?? false
|
|
5576
5642
|
} : null,
|
|
5577
|
-
clarity: marketing.destinations.clarity ? { projectId: marketing.destinations.clarity.project_id } : null
|
|
5643
|
+
clarity: marketing.destinations.clarity ? { projectId: marketing.destinations.clarity.project_id } : null,
|
|
5644
|
+
...marketing.destinations.gtm ? { gtm: { containerId: marketing.destinations.gtm.container_id } } : {}
|
|
5578
5645
|
}
|
|
5579
5646
|
},
|
|
5580
5647
|
legal: {
|
package/react.js
CHANGED
|
@@ -5052,6 +5052,61 @@ function matchesCanonicalMarketingHost(canonicalDomain, currentHostname) {
|
|
|
5052
5052
|
}
|
|
5053
5053
|
}
|
|
5054
5054
|
|
|
5055
|
+
// libs/sdk/src/marketing/gtm.ts
|
|
5056
|
+
var CONTAINER_ID = /^GTM-[A-Z0-9]{4,32}$/;
|
|
5057
|
+
var preparedQueues = /* @__PURE__ */ new WeakMap();
|
|
5058
|
+
function prepareGtm(containerId) {
|
|
5059
|
+
const target = marketingWindow();
|
|
5060
|
+
if (!target || !CONTAINER_ID.test(containerId)) return;
|
|
5061
|
+
target.dataLayer ??= [];
|
|
5062
|
+
const containers = preparedQueues.get(target.dataLayer) ?? /* @__PURE__ */ new Set();
|
|
5063
|
+
if (containers.has(containerId)) return;
|
|
5064
|
+
target.dataLayer.push({ "gtm.start": Date.now(), event: "gtm.js" });
|
|
5065
|
+
containers.add(containerId);
|
|
5066
|
+
preparedQueues.set(target.dataLayer, containers);
|
|
5067
|
+
}
|
|
5068
|
+
function loadGtmScript(containerId, onError) {
|
|
5069
|
+
const target = marketingWindow();
|
|
5070
|
+
if (!target || typeof document === "undefined" || !CONTAINER_ID.test(containerId))
|
|
5071
|
+
return;
|
|
5072
|
+
const id = `bacano-gtm-${containerId}`;
|
|
5073
|
+
if (document.getElementById(id)) return;
|
|
5074
|
+
prepareGtm(containerId);
|
|
5075
|
+
const script = document.createElement("script");
|
|
5076
|
+
script.id = id;
|
|
5077
|
+
script.async = true;
|
|
5078
|
+
script.src = `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(containerId)}`;
|
|
5079
|
+
script.referrerPolicy = "strict-origin-when-cross-origin";
|
|
5080
|
+
script.onerror = () => onError();
|
|
5081
|
+
document.head.appendChild(script);
|
|
5082
|
+
}
|
|
5083
|
+
function sendGtm(event) {
|
|
5084
|
+
const target = marketingWindow();
|
|
5085
|
+
if (!target?.dataLayer) return;
|
|
5086
|
+
target.dataLayer.push({ bacano: null });
|
|
5087
|
+
target.dataLayer.push({
|
|
5088
|
+
event: `bacano_${event.eventName}`,
|
|
5089
|
+
bacano: {
|
|
5090
|
+
schema_version: event.schemaVersion,
|
|
5091
|
+
event_name: event.eventName,
|
|
5092
|
+
event_id: event.eventId,
|
|
5093
|
+
...interactionParameters(event),
|
|
5094
|
+
page_location: event.pageLocation,
|
|
5095
|
+
currency: event.currency,
|
|
5096
|
+
value: event.value,
|
|
5097
|
+
items: mapGa4Items(event.items),
|
|
5098
|
+
item_list_id: event.itemListId,
|
|
5099
|
+
item_list_name: event.itemListName,
|
|
5100
|
+
shipping_tier: event.shippingTier,
|
|
5101
|
+
payment_type: event.paymentType,
|
|
5102
|
+
method: event.contactMethod,
|
|
5103
|
+
contact_placement: event.eventName === "contact" && WEBSITE_MARKETING_CONTACT_PLACEMENTS.some(
|
|
5104
|
+
(value) => value === event.contactPlacement
|
|
5105
|
+
) ? event.contactPlacement : void 0
|
|
5106
|
+
}
|
|
5107
|
+
});
|
|
5108
|
+
}
|
|
5109
|
+
|
|
5055
5110
|
// libs/sdk/src/marketing/runtime.ts
|
|
5056
5111
|
function createWebsiteMarketingRuntime(options) {
|
|
5057
5112
|
let destroyed = false;
|
|
@@ -5072,7 +5127,7 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
5072
5127
|
target.location.hostname
|
|
5073
5128
|
)
|
|
5074
5129
|
) && Boolean(
|
|
5075
|
-
options.config.destinations.ga4 || options.config.destinations.meta || options.config.destinations.clarity
|
|
5130
|
+
options.config.destinations.ga4 || options.config.destinations.meta || options.config.destinations.clarity || options.config.destinations.gtm
|
|
5076
5131
|
);
|
|
5077
5132
|
if (!enabled)
|
|
5078
5133
|
diagnostic({
|
|
@@ -5113,6 +5168,10 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
5113
5168
|
const { projectId } = options.config.destinations.clarity;
|
|
5114
5169
|
guard("Clarity", () => prepareClarity(projectId));
|
|
5115
5170
|
}
|
|
5171
|
+
if (options.config.destinations.gtm) {
|
|
5172
|
+
const { containerId } = options.config.destinations.gtm;
|
|
5173
|
+
guard("GTM", () => prepareGtm(containerId));
|
|
5174
|
+
}
|
|
5116
5175
|
cancelScheduledLoad = scheduleScriptLoad(() => {
|
|
5117
5176
|
if (destroyed) return;
|
|
5118
5177
|
if (options.config.destinations.ga4) {
|
|
@@ -5128,6 +5187,10 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
5128
5187
|
() => loadClarityScript(projectId, scriptError("Clarity"))
|
|
5129
5188
|
);
|
|
5130
5189
|
}
|
|
5190
|
+
if (options.config.destinations.gtm) {
|
|
5191
|
+
const { containerId } = options.config.destinations.gtm;
|
|
5192
|
+
guard("GTM", () => loadGtmScript(containerId, scriptError("GTM")));
|
|
5193
|
+
}
|
|
5131
5194
|
}, options.scriptLoadStrategy);
|
|
5132
5195
|
captureWebsiteMarketingAttribution(
|
|
5133
5196
|
options.websiteId,
|
|
@@ -5247,6 +5310,9 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
5247
5310
|
if (options.config.destinations.clarity) {
|
|
5248
5311
|
dispatch("Clarity", () => sendClarityEvent(event.eventName));
|
|
5249
5312
|
}
|
|
5313
|
+
if (options.config.destinations.gtm && /^GTM-[A-Z0-9]{4,32}$/.test(options.config.destinations.gtm.containerId)) {
|
|
5314
|
+
dispatch("GTM", () => sendGtm(event));
|
|
5315
|
+
}
|
|
5250
5316
|
},
|
|
5251
5317
|
captureAttribution() {
|
|
5252
5318
|
if (!enabled || destroyed) return null;
|
|
@@ -5521,7 +5587,8 @@ async function getWebsitePublicConfig(options) {
|
|
|
5521
5587
|
pixelId: marketing.destinations.meta.pixel_id,
|
|
5522
5588
|
diagnosticMode: marketing.destinations.meta.diagnostic_mode ?? false
|
|
5523
5589
|
} : null,
|
|
5524
|
-
clarity: marketing.destinations.clarity ? { projectId: marketing.destinations.clarity.project_id } : null
|
|
5590
|
+
clarity: marketing.destinations.clarity ? { projectId: marketing.destinations.clarity.project_id } : null,
|
|
5591
|
+
...marketing.destinations.gtm ? { gtm: { containerId: marketing.destinations.gtm.container_id } } : {}
|
|
5525
5592
|
}
|
|
5526
5593
|
},
|
|
5527
5594
|
legal: {
|
package/types.d.ts
CHANGED
|
@@ -267,6 +267,10 @@ export interface WebsiteMarketingPublicConfig {
|
|
|
267
267
|
clarity?: {
|
|
268
268
|
projectId: string;
|
|
269
269
|
} | null;
|
|
270
|
+
/** Optional web container. Does not replace native GA4/Meta destinations. */
|
|
271
|
+
gtm?: {
|
|
272
|
+
containerId: string;
|
|
273
|
+
} | null;
|
|
270
274
|
};
|
|
271
275
|
}
|
|
272
276
|
export type WebsiteCustomerIdentifierType = "AUTO" | "EMAIL" | "PHONE" | "DOCUMENT";
|