@owlmeans/web-gtm 0.1.18-rc.32 → 0.1.18-rc.34
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 +1 -1
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/web-gtm/SKILL.md +8 -1
- package/build/consts.d.ts +43 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +53 -0
- package/build/consts.js.map +1 -0
- package/build/consts.local.d.ts +18 -0
- package/build/consts.local.d.ts.map +1 -0
- package/build/consts.local.js +20 -0
- package/build/consts.local.js.map +1 -0
- package/build/index.d.ts +4 -70
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -72
- package/build/index.js.map +1 -1
- package/build/types.d.ts +30 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/package.json +3 -3
- package/src/consts.local.ts +24 -0
- package/src/consts.ts +56 -0
- package/src/index.ts +7 -113
- package/src/types.ts +33 -0
- package/tests/tag.spec.ts +3 -8
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
6
6
|
your project's skill store (`.agents/skills/`):
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
9
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.48
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/web-gtm",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18-rc.34",
|
|
5
|
+
"generatedAt": "2026-10-05T08:32:19.091Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/web-gtm
|
|
9
9
|
|
|
10
10
|
**Layer:** Web
|
|
11
|
-
**Install:** `"@owlmeans/web-gtm": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/web-gtm": "^0.1.18-rc.34"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
The tag half of the consent set. It emits **strings and data**, not components, and it holds no
|
|
14
14
|
state — the decision lives in `@owlmeans/consent`, which this package reads through
|
|
@@ -165,6 +165,13 @@ page says so.
|
|
|
165
165
|
**built-in tag templates** (GA4, Google Ads, Floodlight, Conversion Linker) and Community
|
|
166
166
|
Gallery templates, which load from the allowed hosts.
|
|
167
167
|
|
|
168
|
+
## Sending events
|
|
169
|
+
|
|
170
|
+
This package emits the head snippet and decides when the loader may run; it has no event-push API. An
|
|
171
|
+
application sends its own events through `@owlmeans/log` calls that carry an `analytics` option, and
|
|
172
|
+
`@owlmeans/web-log`'s `gtmAnalyticsPlugin` pushes them onto `window.dataLayer` only while analytics
|
|
173
|
+
consent is granted (`/web-log`).
|
|
174
|
+
|
|
168
175
|
## Disclosing it
|
|
169
176
|
|
|
170
177
|
`googleTagServices(id)` returns what the cookie policy lists under each category, read off the
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { GoogleTagMode } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The platform default for {@link GtmOptions.mode} / {@link GoogleTagOptions.mode}.
|
|
4
|
+
*
|
|
5
|
+
* One named constant so flipping it later is a one-line change.
|
|
6
|
+
*
|
|
7
|
+
* - `'basic'` withholds the loader until a signal-bearing category is granted — which holds back
|
|
8
|
+
* Google's own IP receipt too, not only what a granted tag may then do with it.
|
|
9
|
+
* - `'advanced'` loads immediately with Consent Mode signals denied — today's only behavior, and
|
|
10
|
+
* Google's own recommended default for its own conversion modeling.
|
|
11
|
+
*
|
|
12
|
+
* Default is `'basic'`: read it as the EU/DE worst-case reading of ePrivacy Art. 5(3) — no third
|
|
13
|
+
* party receives a connection before consent.
|
|
14
|
+
*/
|
|
15
|
+
export declare const GOOGLE_TAG_DEFAULT_MODE: GoogleTagMode;
|
|
16
|
+
/**
|
|
17
|
+
* The hosts a page running a Google tag has to allow in `script-src`, `connect-src` and
|
|
18
|
+
* `img-src`, for Google Analytics 4, Tag Manager and Google Ads together.
|
|
19
|
+
*
|
|
20
|
+
* Taken from Google's CSP guide (developers.google.com/tag-platform/security/guides/csp) and
|
|
21
|
+
* folded into one list, because the platform adds one list to all three directives:
|
|
22
|
+
*
|
|
23
|
+
* - `*.googletagmanager.com` — gtm.js and gtag/js (`www.`), and their measurement pings;
|
|
24
|
+
* - `*.google-analytics.com` — GA4 collection (`region1.`, `www.`);
|
|
25
|
+
* - `*.g.doubleclick.net` — GA4 with Google signals and Ads (`stats.g.`, `googleads.g.`);
|
|
26
|
+
* - `ad.doubleclick.net` — Ads and Floodlight conversions;
|
|
27
|
+
* - `www.googleadservices.com` — the Ads conversion script;
|
|
28
|
+
* - `pagead2.googlesyndication.com` — Ads and GA4-with-ads pings;
|
|
29
|
+
* - `*.google.com` — `www.google.com` (the Ads script, consent-mode pings), `adservice.`, and the
|
|
30
|
+
* `analytics.google.com` collection endpoints.
|
|
31
|
+
*
|
|
32
|
+
* Every entry is an `https://` scheme, an optional `*.` label and a dotted host — the only shape
|
|
33
|
+
* the publisher's CSP filter lets through — and there are few enough to stay inside its per-slot
|
|
34
|
+
* cap. Google's country domains (`google.<TLD>`) cannot be named in that shape and are left out:
|
|
35
|
+
* what is lost is a remarketing ping from visitors on those domains, never the tag itself.
|
|
36
|
+
*/
|
|
37
|
+
export declare const GOOGLE_TAG_CSP_SOURCES: readonly string[];
|
|
38
|
+
/**
|
|
39
|
+
* The hosts a Google tag frames: Tag Manager's service-worker and preview frames, and the Ads
|
|
40
|
+
* remarketing frame. For `frame-src`, which otherwise stays `'none'`.
|
|
41
|
+
*/
|
|
42
|
+
export declare const GOOGLE_TAG_FRAME_SOURCES: readonly string[];
|
|
43
|
+
//# sourceMappingURL=consts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAuB,CAAA;AAE7D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAQlD,CAAA;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAGpD,CAAA"}
|
package/build/consts.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform default for {@link GtmOptions.mode} / {@link GoogleTagOptions.mode}.
|
|
3
|
+
*
|
|
4
|
+
* One named constant so flipping it later is a one-line change.
|
|
5
|
+
*
|
|
6
|
+
* - `'basic'` withholds the loader until a signal-bearing category is granted — which holds back
|
|
7
|
+
* Google's own IP receipt too, not only what a granted tag may then do with it.
|
|
8
|
+
* - `'advanced'` loads immediately with Consent Mode signals denied — today's only behavior, and
|
|
9
|
+
* Google's own recommended default for its own conversion modeling.
|
|
10
|
+
*
|
|
11
|
+
* Default is `'basic'`: read it as the EU/DE worst-case reading of ePrivacy Art. 5(3) — no third
|
|
12
|
+
* party receives a connection before consent.
|
|
13
|
+
*/
|
|
14
|
+
export const GOOGLE_TAG_DEFAULT_MODE = 'basic';
|
|
15
|
+
/**
|
|
16
|
+
* The hosts a page running a Google tag has to allow in `script-src`, `connect-src` and
|
|
17
|
+
* `img-src`, for Google Analytics 4, Tag Manager and Google Ads together.
|
|
18
|
+
*
|
|
19
|
+
* Taken from Google's CSP guide (developers.google.com/tag-platform/security/guides/csp) and
|
|
20
|
+
* folded into one list, because the platform adds one list to all three directives:
|
|
21
|
+
*
|
|
22
|
+
* - `*.googletagmanager.com` — gtm.js and gtag/js (`www.`), and their measurement pings;
|
|
23
|
+
* - `*.google-analytics.com` — GA4 collection (`region1.`, `www.`);
|
|
24
|
+
* - `*.g.doubleclick.net` — GA4 with Google signals and Ads (`stats.g.`, `googleads.g.`);
|
|
25
|
+
* - `ad.doubleclick.net` — Ads and Floodlight conversions;
|
|
26
|
+
* - `www.googleadservices.com` — the Ads conversion script;
|
|
27
|
+
* - `pagead2.googlesyndication.com` — Ads and GA4-with-ads pings;
|
|
28
|
+
* - `*.google.com` — `www.google.com` (the Ads script, consent-mode pings), `adservice.`, and the
|
|
29
|
+
* `analytics.google.com` collection endpoints.
|
|
30
|
+
*
|
|
31
|
+
* Every entry is an `https://` scheme, an optional `*.` label and a dotted host — the only shape
|
|
32
|
+
* the publisher's CSP filter lets through — and there are few enough to stay inside its per-slot
|
|
33
|
+
* cap. Google's country domains (`google.<TLD>`) cannot be named in that shape and are left out:
|
|
34
|
+
* what is lost is a remarketing ping from visitors on those domains, never the tag itself.
|
|
35
|
+
*/
|
|
36
|
+
export const GOOGLE_TAG_CSP_SOURCES = Object.freeze([
|
|
37
|
+
'https://*.googletagmanager.com',
|
|
38
|
+
'https://*.google-analytics.com',
|
|
39
|
+
'https://*.g.doubleclick.net',
|
|
40
|
+
'https://ad.doubleclick.net',
|
|
41
|
+
'https://www.googleadservices.com',
|
|
42
|
+
'https://pagead2.googlesyndication.com',
|
|
43
|
+
'https://*.google.com',
|
|
44
|
+
]);
|
|
45
|
+
/**
|
|
46
|
+
* The hosts a Google tag frames: Tag Manager's service-worker and preview frames, and the Ads
|
|
47
|
+
* remarketing frame. For `frame-src`, which otherwise stays `'none'`.
|
|
48
|
+
*/
|
|
49
|
+
export const GOOGLE_TAG_FRAME_SOURCES = Object.freeze([
|
|
50
|
+
'https://www.googletagmanager.com',
|
|
51
|
+
'https://td.doubleclick.net',
|
|
52
|
+
]);
|
|
53
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAkB,OAAO,CAAA;AAE7D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACrE,gCAAgC;IAChC,gCAAgC;IAChC,6BAA6B;IAC7B,4BAA4B;IAC5B,kCAAkC;IAClC,uCAAuC;IACvC,sBAAsB;CACvB,CAAC,CAAA;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACvE,kCAAkC;IAClC,4BAA4B;CAC7B,CAAC,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The id shapes Google issues today, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately strict — upper case, a known prefix, a bounded run of letters and digits — because
|
|
5
|
+
* the id is typed by a person into a settings form and ends up inside an inline script and a
|
|
6
|
+
* script URL. A value this pattern accepts cannot carry a quote, a tag or a second parameter, so
|
|
7
|
+
* the check is what makes it safe to store, not only to emit. `UA-` is not here: Universal
|
|
8
|
+
* Analytics stopped processing data in 2024, and accepting its ids would promise measurement that
|
|
9
|
+
* never arrives.
|
|
10
|
+
*/
|
|
11
|
+
export declare const GOOGLE_TAG_ID: RegExp;
|
|
12
|
+
/** A queue name has to be a plain identifier: it becomes `window[name]` in the emitted script. */
|
|
13
|
+
export declare const JS_IDENTIFIER: RegExp;
|
|
14
|
+
export declare const GOOGLE = "Google LLC";
|
|
15
|
+
export declare const GOOGLE_PRIVACY = "https://policies.google.com/privacy";
|
|
16
|
+
export declare const UNTIL_ANALYTICS: string;
|
|
17
|
+
export declare const UNTIL_MARKETING: string;
|
|
18
|
+
//# sourceMappingURL=consts.local.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,QAAuD,CAAA;AAEjF,kGAAkG;AAClG,eAAO,MAAM,aAAa,QAA+B,CAAA;AAEzD,eAAO,MAAM,MAAM,eAAe,CAAA;AAElC,eAAO,MAAM,cAAc,wCAAwC,CAAA;AAEnE,eAAO,MAAM,eAAe,QACS,CAAA;AAErC,eAAO,MAAM,eAAe,QACG,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The id shapes Google issues today, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately strict — upper case, a known prefix, a bounded run of letters and digits — because
|
|
5
|
+
* the id is typed by a person into a settings form and ends up inside an inline script and a
|
|
6
|
+
* script URL. A value this pattern accepts cannot carry a quote, a tag or a second parameter, so
|
|
7
|
+
* the check is what makes it safe to store, not only to emit. `UA-` is not here: Universal
|
|
8
|
+
* Analytics stopped processing data in 2024, and accepting its ids would promise measurement that
|
|
9
|
+
* never arrives.
|
|
10
|
+
*/
|
|
11
|
+
export const GOOGLE_TAG_ID = /^(GTM-[A-Z0-9]{4,12}|(G|GT|AW|DC)-[A-Z0-9]{4,16})$/;
|
|
12
|
+
/** A queue name has to be a plain identifier: it becomes `window[name]` in the emitted script. */
|
|
13
|
+
export const JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
14
|
+
export const GOOGLE = 'Google LLC';
|
|
15
|
+
export const GOOGLE_PRIVACY = 'https://policies.google.com/privacy';
|
|
16
|
+
export const UNTIL_ANALYTICS = 'Until analytics cookies are accepted it sets no cookies and sends only '
|
|
17
|
+
+ 'cookieless measurement signals.';
|
|
18
|
+
export const UNTIL_MARKETING = 'Until marketing cookies are accepted it sets no cookies and ad click '
|
|
19
|
+
+ 'identifiers are redacted.';
|
|
20
|
+
//# sourceMappingURL=consts.local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,oDAAoD,CAAA;AAEjF,kGAAkG;AAClG,MAAM,CAAC,MAAM,aAAa,GAAG,4BAA4B,CAAA;AAEzD,MAAM,CAAC,MAAM,MAAM,GAAG,YAAY,CAAA;AAElC,MAAM,CAAC,MAAM,cAAc,GAAG,qCAAqC,CAAA;AAEnE,MAAM,CAAC,MAAM,eAAe,GAAG,yEAAyE;MACpG,iCAAiC,CAAA;AAErC,MAAM,CAAC,MAAM,eAAe,GAAG,uEAAuE;MAClG,2BAA2B,CAAA"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,28 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export type GoogleTagMode = 'basic' | 'advanced';
|
|
4
|
-
/**
|
|
5
|
-
* The platform default for {@link GtmOptions.mode} / {@link GoogleTagOptions.mode}.
|
|
6
|
-
*
|
|
7
|
-
* One named constant so flipping it later is a one-line change.
|
|
8
|
-
*
|
|
9
|
-
* - `'basic'` withholds the loader until a signal-bearing category is granted — which holds back
|
|
10
|
-
* Google's own IP receipt too, not only what a granted tag may then do with it.
|
|
11
|
-
* - `'advanced'` loads immediately with Consent Mode signals denied — today's only behavior, and
|
|
12
|
-
* Google's own recommended default for its own conversion modeling.
|
|
13
|
-
*
|
|
14
|
-
* Default is `'basic'`: read it as the EU/DE worst-case reading of ePrivacy Art. 5(3) — no third
|
|
15
|
-
* party receives a connection before consent.
|
|
16
|
-
*/
|
|
17
|
-
export declare const GOOGLE_TAG_DEFAULT_MODE: GoogleTagMode;
|
|
18
|
-
export interface GtmOptions extends ConsentOptions {
|
|
19
|
-
/** Container id, e.g. `GTM-XXXXXXX`. */
|
|
20
|
-
id: string;
|
|
21
|
-
/** The queue name, when a page runs more than one container. */
|
|
22
|
-
dataLayerName?: string;
|
|
23
|
-
/** See {@link GOOGLE_TAG_DEFAULT_MODE}. Defaults to `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`). */
|
|
24
|
-
mode?: GoogleTagMode;
|
|
25
|
-
}
|
|
1
|
+
import { type ConsentService } from '@owlmeans/consent';
|
|
2
|
+
import type { GoogleTagKind, GoogleTagOptions, GtmOptions } from './types.js';
|
|
26
3
|
/**
|
|
27
4
|
* The inline `<head>` snippet, consent first and the container second.
|
|
28
5
|
*
|
|
@@ -61,28 +38,10 @@ export declare const gtmNoscriptFrame: (opts: GtmOptions) => string;
|
|
|
61
38
|
* update where it becomes true and unsubscribes. `'advanced'` appends it immediately, as before.
|
|
62
39
|
*/
|
|
63
40
|
export declare const loadGtm: (opts: GtmOptions) => void;
|
|
64
|
-
/**
|
|
65
|
-
* Which loader an id takes: `gtm` is the Tag Manager container (`gtm.js`), `gtag` the Google tag
|
|
66
|
-
* (`gtag.js`) — Google Analytics 4 (`G-`), a Google tag (`GT-`), Google Ads (`AW-`) and
|
|
67
|
-
* Floodlight (`DC-`).
|
|
68
|
-
*/
|
|
69
|
-
export type GoogleTagKind = 'gtm' | 'gtag';
|
|
70
41
|
/** Whether `id` is a Tag Manager container or Google tag id this package can load. */
|
|
71
42
|
export declare const isGoogleTagId: (id: string) => boolean;
|
|
72
43
|
/** The loader `id` takes, or `null` when it is not a loadable id at all. */
|
|
73
44
|
export declare const googleTagKind: (id: string) => GoogleTagKind | null;
|
|
74
|
-
export interface GoogleTagOptions extends ConsentOptions {
|
|
75
|
-
/** `GTM-…`, `G-…`, `GT-…`, `AW-…` or `DC-…` — see {@link isGoogleTagId}. */
|
|
76
|
-
id: string;
|
|
77
|
-
/**
|
|
78
|
-
* The queue name, when a page runs more than one tag. Consent Mode from `@owlmeans/consent` —
|
|
79
|
-
* the bootstrap AND the dialog's later updates — always speaks on `window.dataLayer`, so a tag
|
|
80
|
-
* given another queue never hears a consent command. Leave it unset on a consent-gated page.
|
|
81
|
-
*/
|
|
82
|
-
dataLayerName?: string;
|
|
83
|
-
/** See {@link GOOGLE_TAG_DEFAULT_MODE}. Defaults to `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`). */
|
|
84
|
-
mode?: GoogleTagMode;
|
|
85
|
-
}
|
|
86
45
|
/**
|
|
87
46
|
* The one inline `<head>` script for a Google tag of any kind: the consent bootstrap FIRST, then
|
|
88
47
|
* ads redaction, then the loader the id's prefix calls for — the Tag Manager container for `GTM-`,
|
|
@@ -106,33 +65,6 @@ export interface GoogleTagOptions extends ConsentOptions {
|
|
|
106
65
|
* `mode: 'advanced'` for the original, unconditional load.
|
|
107
66
|
*/
|
|
108
67
|
export declare const googleTagHeadScript: (opts: GoogleTagOptions) => string;
|
|
109
|
-
/**
|
|
110
|
-
* The hosts a page running a Google tag has to allow in `script-src`, `connect-src` and
|
|
111
|
-
* `img-src`, for Google Analytics 4, Tag Manager and Google Ads together.
|
|
112
|
-
*
|
|
113
|
-
* Taken from Google's CSP guide (developers.google.com/tag-platform/security/guides/csp) and
|
|
114
|
-
* folded into one list, because the platform adds one list to all three directives:
|
|
115
|
-
*
|
|
116
|
-
* - `*.googletagmanager.com` — gtm.js and gtag/js (`www.`), and their measurement pings;
|
|
117
|
-
* - `*.google-analytics.com` — GA4 collection (`region1.`, `www.`);
|
|
118
|
-
* - `*.g.doubleclick.net` — GA4 with Google signals and Ads (`stats.g.`, `googleads.g.`);
|
|
119
|
-
* - `ad.doubleclick.net` — Ads and Floodlight conversions;
|
|
120
|
-
* - `www.googleadservices.com` — the Ads conversion script;
|
|
121
|
-
* - `pagead2.googlesyndication.com` — Ads and GA4-with-ads pings;
|
|
122
|
-
* - `*.google.com` — `www.google.com` (the Ads script, consent-mode pings), `adservice.`, and the
|
|
123
|
-
* `analytics.google.com` collection endpoints.
|
|
124
|
-
*
|
|
125
|
-
* Every entry is an `https://` scheme, an optional `*.` label and a dotted host — the only shape
|
|
126
|
-
* the publisher's CSP filter lets through — and there are few enough to stay inside its per-slot
|
|
127
|
-
* cap. Google's country domains (`google.<TLD>`) cannot be named in that shape and are left out:
|
|
128
|
-
* what is lost is a remarketing ping from visitors on those domains, never the tag itself.
|
|
129
|
-
*/
|
|
130
|
-
export declare const GOOGLE_TAG_CSP_SOURCES: readonly string[];
|
|
131
|
-
/**
|
|
132
|
-
* The hosts a Google tag frames: Tag Manager's service-worker and preview frames, and the Ads
|
|
133
|
-
* remarketing frame. For `frame-src`, which otherwise stays `'none'`.
|
|
134
|
-
*/
|
|
135
|
-
export declare const GOOGLE_TAG_FRAME_SOURCES: readonly string[];
|
|
136
68
|
/**
|
|
137
69
|
* What a Google tag discloses on the cookie policy, for `CookiePolicy`'s `services`.
|
|
138
70
|
*
|
|
@@ -144,4 +76,6 @@ export declare const GOOGLE_TAG_FRAME_SOURCES: readonly string[];
|
|
|
144
76
|
* {@link googleTagHeadScript}, which loads nothing for it.
|
|
145
77
|
*/
|
|
146
78
|
export declare const googleTagServices: (id: string) => ConsentService[];
|
|
79
|
+
export type { GoogleTagKind, GoogleTagMode, GoogleTagOptions, GtmOptions } from './types.js';
|
|
80
|
+
export { GOOGLE_TAG_CSP_SOURCES, GOOGLE_TAG_DEFAULT_MODE, GOOGLE_TAG_FRAME_SOURCES } from './consts.js';
|
|
147
81
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkH,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAGvK,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAY7E;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,SAAU,UAAU,KAAG,MAShD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,SAAU,UAAU,KAAG,MAOnD,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,SAAU,UAAU,KAAG,IA0C1C,CAAA;AAED,sFAAsF;AACtF,eAAO,MAAM,aAAa,OAAQ,MAAM,KAAG,OACO,CAAA;AAElD,4EAA4E;AAC5E,eAAO,MAAM,aAAa,OAAQ,MAAM,KAAG,aAAa,GAAG,IACS,CAAA;AAsDpE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,mBAAmB,SAAU,gBAAgB,KAAG,MAe5D,CAAA;AAYD;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,OAAQ,MAAM,KAAG,cAAc,EA8C5D,CAAA;AAED,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5F,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
import { CONSENT_ANALYTICS, CONSENT_MARKETING, consentBootstrapScript, consentGateScript, consentStore, trackingGranted
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* One named constant so flipping it later is a one-line change.
|
|
6
|
-
*
|
|
7
|
-
* - `'basic'` withholds the loader until a signal-bearing category is granted — which holds back
|
|
8
|
-
* Google's own IP receipt too, not only what a granted tag may then do with it.
|
|
9
|
-
* - `'advanced'` loads immediately with Consent Mode signals denied — today's only behavior, and
|
|
10
|
-
* Google's own recommended default for its own conversion modeling.
|
|
11
|
-
*
|
|
12
|
-
* Default is `'basic'`: read it as the EU/DE worst-case reading of ePrivacy Art. 5(3) — no third
|
|
13
|
-
* party receives a connection before consent.
|
|
14
|
-
*/
|
|
15
|
-
export const GOOGLE_TAG_DEFAULT_MODE = 'basic';
|
|
1
|
+
import { CONSENT_ANALYTICS, CONSENT_MARKETING, consentBootstrapScript, consentGateScript, consentStore, trackingGranted } from '@owlmeans/consent';
|
|
2
|
+
import { GOOGLE, GOOGLE_PRIVACY, GOOGLE_TAG_ID, JS_IDENTIFIER, UNTIL_ANALYTICS, UNTIL_MARKETING } from './consts.local.js';
|
|
3
|
+
import { GOOGLE_TAG_DEFAULT_MODE } from './consts.js';
|
|
16
4
|
/** Google's own container IIFE, the id and queue name injected as JSON data. */
|
|
17
5
|
const gtmContainerScript = (layer, id) => `(function(w,d,s,l,i){w[l]=w[l]||[];` +
|
|
18
6
|
`w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});` +
|
|
@@ -108,23 +96,10 @@ export const loadGtm = (opts) => {
|
|
|
108
96
|
}
|
|
109
97
|
});
|
|
110
98
|
};
|
|
111
|
-
/**
|
|
112
|
-
* The id shapes Google issues today, and nothing else.
|
|
113
|
-
*
|
|
114
|
-
* Deliberately strict — upper case, a known prefix, a bounded run of letters and digits — because
|
|
115
|
-
* the id is typed by a person into a settings form and ends up inside an inline script and a
|
|
116
|
-
* script URL. A value this pattern accepts cannot carry a quote, a tag or a second parameter, so
|
|
117
|
-
* the check is what makes it safe to store, not only to emit. `UA-` is not here: Universal
|
|
118
|
-
* Analytics stopped processing data in 2024, and accepting its ids would promise measurement that
|
|
119
|
-
* never arrives.
|
|
120
|
-
*/
|
|
121
|
-
const GOOGLE_TAG_ID = /^(GTM-[A-Z0-9]{4,12}|(G|GT|AW|DC)-[A-Z0-9]{4,16})$/;
|
|
122
99
|
/** Whether `id` is a Tag Manager container or Google tag id this package can load. */
|
|
123
100
|
export const isGoogleTagId = (id) => typeof id === 'string' && GOOGLE_TAG_ID.test(id);
|
|
124
101
|
/** The loader `id` takes, or `null` when it is not a loadable id at all. */
|
|
125
102
|
export const googleTagKind = (id) => !isGoogleTagId(id) ? null : id.startsWith('GTM-') ? 'gtm' : 'gtag';
|
|
126
|
-
/** A queue name has to be a plain identifier: it becomes `window[name]` in the emitted script. */
|
|
127
|
-
const JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
128
103
|
const layerOf = (opts) => opts.dataLayerName != null && JS_IDENTIFIER.test(opts.dataLayerName)
|
|
129
104
|
? opts.dataLayerName
|
|
130
105
|
: 'dataLayer';
|
|
@@ -205,56 +180,12 @@ export const googleTagHeadScript = (opts) => {
|
|
|
205
180
|
}
|
|
206
181
|
return inlineSafe(`${bootstrap};${redaction};${loader}`);
|
|
207
182
|
};
|
|
208
|
-
/**
|
|
209
|
-
* The hosts a page running a Google tag has to allow in `script-src`, `connect-src` and
|
|
210
|
-
* `img-src`, for Google Analytics 4, Tag Manager and Google Ads together.
|
|
211
|
-
*
|
|
212
|
-
* Taken from Google's CSP guide (developers.google.com/tag-platform/security/guides/csp) and
|
|
213
|
-
* folded into one list, because the platform adds one list to all three directives:
|
|
214
|
-
*
|
|
215
|
-
* - `*.googletagmanager.com` — gtm.js and gtag/js (`www.`), and their measurement pings;
|
|
216
|
-
* - `*.google-analytics.com` — GA4 collection (`region1.`, `www.`);
|
|
217
|
-
* - `*.g.doubleclick.net` — GA4 with Google signals and Ads (`stats.g.`, `googleads.g.`);
|
|
218
|
-
* - `ad.doubleclick.net` — Ads and Floodlight conversions;
|
|
219
|
-
* - `www.googleadservices.com` — the Ads conversion script;
|
|
220
|
-
* - `pagead2.googlesyndication.com` — Ads and GA4-with-ads pings;
|
|
221
|
-
* - `*.google.com` — `www.google.com` (the Ads script, consent-mode pings), `adservice.`, and the
|
|
222
|
-
* `analytics.google.com` collection endpoints.
|
|
223
|
-
*
|
|
224
|
-
* Every entry is an `https://` scheme, an optional `*.` label and a dotted host — the only shape
|
|
225
|
-
* the publisher's CSP filter lets through — and there are few enough to stay inside its per-slot
|
|
226
|
-
* cap. Google's country domains (`google.<TLD>`) cannot be named in that shape and are left out:
|
|
227
|
-
* what is lost is a remarketing ping from visitors on those domains, never the tag itself.
|
|
228
|
-
*/
|
|
229
|
-
export const GOOGLE_TAG_CSP_SOURCES = Object.freeze([
|
|
230
|
-
'https://*.googletagmanager.com',
|
|
231
|
-
'https://*.google-analytics.com',
|
|
232
|
-
'https://*.g.doubleclick.net',
|
|
233
|
-
'https://ad.doubleclick.net',
|
|
234
|
-
'https://www.googleadservices.com',
|
|
235
|
-
'https://pagead2.googlesyndication.com',
|
|
236
|
-
'https://*.google.com',
|
|
237
|
-
]);
|
|
238
|
-
/**
|
|
239
|
-
* The hosts a Google tag frames: Tag Manager's service-worker and preview frames, and the Ads
|
|
240
|
-
* remarketing frame. For `frame-src`, which otherwise stays `'none'`.
|
|
241
|
-
*/
|
|
242
|
-
export const GOOGLE_TAG_FRAME_SOURCES = Object.freeze([
|
|
243
|
-
'https://www.googletagmanager.com',
|
|
244
|
-
'https://td.doubleclick.net',
|
|
245
|
-
]);
|
|
246
|
-
const GOOGLE = 'Google LLC';
|
|
247
|
-
const GOOGLE_PRIVACY = 'https://policies.google.com/privacy';
|
|
248
183
|
const analyticsService = (name, purpose, cookies) => ({
|
|
249
184
|
name, provider: GOOGLE, category: CONSENT_ANALYTICS, purpose, cookies, privacyHref: GOOGLE_PRIVACY,
|
|
250
185
|
});
|
|
251
186
|
const adsService = (name, purpose, cookies) => ({
|
|
252
187
|
name, provider: GOOGLE, category: CONSENT_MARKETING, purpose, cookies, privacyHref: GOOGLE_PRIVACY,
|
|
253
188
|
});
|
|
254
|
-
const UNTIL_ANALYTICS = 'Until analytics cookies are accepted it sets no cookies and sends only '
|
|
255
|
-
+ 'cookieless measurement signals.';
|
|
256
|
-
const UNTIL_MARKETING = 'Until marketing cookies are accepted it sets no cookies and ad click '
|
|
257
|
-
+ 'identifiers are redacted.';
|
|
258
189
|
/**
|
|
259
190
|
* What a Google tag discloses on the cookie policy, for `CookiePolicy`'s `services`.
|
|
260
191
|
*
|
|
@@ -291,4 +222,5 @@ export const googleTagServices = (id) => {
|
|
|
291
222
|
}
|
|
292
223
|
}
|
|
293
224
|
};
|
|
225
|
+
export { GOOGLE_TAG_CSP_SOURCES, GOOGLE_TAG_DEFAULT_MODE, GOOGLE_TAG_FRAME_SOURCES } from './consts.js';
|
|
294
226
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ConsentOptions } from '@owlmeans/consent';
|
|
2
|
+
/** `'gtm'` container / `'gtag'` — see {@link GtmOptions.mode} / {@link GoogleTagOptions.mode}. */
|
|
3
|
+
export type GoogleTagMode = 'basic' | 'advanced';
|
|
4
|
+
export interface GtmOptions extends ConsentOptions {
|
|
5
|
+
/** Container id, e.g. `GTM-XXXXXXX`. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** The queue name, when a page runs more than one container. */
|
|
8
|
+
dataLayerName?: string;
|
|
9
|
+
/** See {@link GOOGLE_TAG_DEFAULT_MODE}. Defaults to `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`). */
|
|
10
|
+
mode?: GoogleTagMode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Which loader an id takes: `gtm` is the Tag Manager container (`gtm.js`), `gtag` the Google tag
|
|
14
|
+
* (`gtag.js`) — Google Analytics 4 (`G-`), a Google tag (`GT-`), Google Ads (`AW-`) and
|
|
15
|
+
* Floodlight (`DC-`).
|
|
16
|
+
*/
|
|
17
|
+
export type GoogleTagKind = 'gtm' | 'gtag';
|
|
18
|
+
export interface GoogleTagOptions extends ConsentOptions {
|
|
19
|
+
/** `GTM-…`, `G-…`, `GT-…`, `AW-…` or `DC-…` — see {@link isGoogleTagId}. */
|
|
20
|
+
id: string;
|
|
21
|
+
/**
|
|
22
|
+
* The queue name, when a page runs more than one tag. Consent Mode from `@owlmeans/consent` —
|
|
23
|
+
* the bootstrap AND the dialog's later updates — always speaks on `window.dataLayer`, so a tag
|
|
24
|
+
* given another queue never hears a consent command. Leave it unset on a consent-gated page.
|
|
25
|
+
*/
|
|
26
|
+
dataLayerName?: string;
|
|
27
|
+
/** See {@link GOOGLE_TAG_DEFAULT_MODE}. Defaults to `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`). */
|
|
28
|
+
mode?: GoogleTagMode;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvD,kGAAkG;AAClG,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,CAAA;AAEhD,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAA;IACV,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,8FAA8F;IAC9F,IAAI,CAAC,EAAE,aAAa,CAAA;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,MAAM,CAAA;AAE1C,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,4EAA4E;IAC5E,EAAE,EAAE,MAAM,CAAA;IACV;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,8FAA8F;IAC9F,IAAI,CAAC,EAAE,aAAa,CAAA;CACrB"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/web-gtm",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.34",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@owlmeans/consent": "^0.1.18-rc.
|
|
25
|
+
"@owlmeans/consent": "^0.1.18-rc.10"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@owlmeans/dep-config": "workspace:*",
|
|
29
|
-
"@types/bun": "^1.4.
|
|
29
|
+
"@types/bun": "^1.4.2",
|
|
30
30
|
"nodemon": "^3.1.14",
|
|
31
31
|
"typescript": "^7.0.2"
|
|
32
32
|
},
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The id shapes Google issues today, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately strict — upper case, a known prefix, a bounded run of letters and digits — because
|
|
5
|
+
* the id is typed by a person into a settings form and ends up inside an inline script and a
|
|
6
|
+
* script URL. A value this pattern accepts cannot carry a quote, a tag or a second parameter, so
|
|
7
|
+
* the check is what makes it safe to store, not only to emit. `UA-` is not here: Universal
|
|
8
|
+
* Analytics stopped processing data in 2024, and accepting its ids would promise measurement that
|
|
9
|
+
* never arrives.
|
|
10
|
+
*/
|
|
11
|
+
export const GOOGLE_TAG_ID = /^(GTM-[A-Z0-9]{4,12}|(G|GT|AW|DC)-[A-Z0-9]{4,16})$/
|
|
12
|
+
|
|
13
|
+
/** A queue name has to be a plain identifier: it becomes `window[name]` in the emitted script. */
|
|
14
|
+
export const JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/
|
|
15
|
+
|
|
16
|
+
export const GOOGLE = 'Google LLC'
|
|
17
|
+
|
|
18
|
+
export const GOOGLE_PRIVACY = 'https://policies.google.com/privacy'
|
|
19
|
+
|
|
20
|
+
export const UNTIL_ANALYTICS = 'Until analytics cookies are accepted it sets no cookies and sends only '
|
|
21
|
+
+ 'cookieless measurement signals.'
|
|
22
|
+
|
|
23
|
+
export const UNTIL_MARKETING = 'Until marketing cookies are accepted it sets no cookies and ad click '
|
|
24
|
+
+ 'identifiers are redacted.'
|
package/src/consts.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { GoogleTagMode } from './types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The platform default for {@link GtmOptions.mode} / {@link GoogleTagOptions.mode}.
|
|
5
|
+
*
|
|
6
|
+
* One named constant so flipping it later is a one-line change.
|
|
7
|
+
*
|
|
8
|
+
* - `'basic'` withholds the loader until a signal-bearing category is granted — which holds back
|
|
9
|
+
* Google's own IP receipt too, not only what a granted tag may then do with it.
|
|
10
|
+
* - `'advanced'` loads immediately with Consent Mode signals denied — today's only behavior, and
|
|
11
|
+
* Google's own recommended default for its own conversion modeling.
|
|
12
|
+
*
|
|
13
|
+
* Default is `'basic'`: read it as the EU/DE worst-case reading of ePrivacy Art. 5(3) — no third
|
|
14
|
+
* party receives a connection before consent.
|
|
15
|
+
*/
|
|
16
|
+
export const GOOGLE_TAG_DEFAULT_MODE: GoogleTagMode = 'basic'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The hosts a page running a Google tag has to allow in `script-src`, `connect-src` and
|
|
20
|
+
* `img-src`, for Google Analytics 4, Tag Manager and Google Ads together.
|
|
21
|
+
*
|
|
22
|
+
* Taken from Google's CSP guide (developers.google.com/tag-platform/security/guides/csp) and
|
|
23
|
+
* folded into one list, because the platform adds one list to all three directives:
|
|
24
|
+
*
|
|
25
|
+
* - `*.googletagmanager.com` — gtm.js and gtag/js (`www.`), and their measurement pings;
|
|
26
|
+
* - `*.google-analytics.com` — GA4 collection (`region1.`, `www.`);
|
|
27
|
+
* - `*.g.doubleclick.net` — GA4 with Google signals and Ads (`stats.g.`, `googleads.g.`);
|
|
28
|
+
* - `ad.doubleclick.net` — Ads and Floodlight conversions;
|
|
29
|
+
* - `www.googleadservices.com` — the Ads conversion script;
|
|
30
|
+
* - `pagead2.googlesyndication.com` — Ads and GA4-with-ads pings;
|
|
31
|
+
* - `*.google.com` — `www.google.com` (the Ads script, consent-mode pings), `adservice.`, and the
|
|
32
|
+
* `analytics.google.com` collection endpoints.
|
|
33
|
+
*
|
|
34
|
+
* Every entry is an `https://` scheme, an optional `*.` label and a dotted host — the only shape
|
|
35
|
+
* the publisher's CSP filter lets through — and there are few enough to stay inside its per-slot
|
|
36
|
+
* cap. Google's country domains (`google.<TLD>`) cannot be named in that shape and are left out:
|
|
37
|
+
* what is lost is a remarketing ping from visitors on those domains, never the tag itself.
|
|
38
|
+
*/
|
|
39
|
+
export const GOOGLE_TAG_CSP_SOURCES: readonly string[] = Object.freeze([
|
|
40
|
+
'https://*.googletagmanager.com',
|
|
41
|
+
'https://*.google-analytics.com',
|
|
42
|
+
'https://*.g.doubleclick.net',
|
|
43
|
+
'https://ad.doubleclick.net',
|
|
44
|
+
'https://www.googleadservices.com',
|
|
45
|
+
'https://pagead2.googlesyndication.com',
|
|
46
|
+
'https://*.google.com',
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The hosts a Google tag frames: Tag Manager's service-worker and preview frames, and the Ads
|
|
51
|
+
* remarketing frame. For `frame-src`, which otherwise stays `'none'`.
|
|
52
|
+
*/
|
|
53
|
+
export const GOOGLE_TAG_FRAME_SOURCES: readonly string[] = Object.freeze([
|
|
54
|
+
'https://www.googletagmanager.com',
|
|
55
|
+
'https://td.doubleclick.net',
|
|
56
|
+
])
|
package/src/index.ts
CHANGED
|
@@ -1,35 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '
|
|
5
|
-
import type { ConsentOptions, ConsentService } from '@owlmeans/consent'
|
|
6
|
-
|
|
7
|
-
/** `'gtm'` container / `'gtag'` — see {@link GtmOptions.mode} / {@link GoogleTagOptions.mode}. */
|
|
8
|
-
export type GoogleTagMode = 'basic' | 'advanced'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* The platform default for {@link GtmOptions.mode} / {@link GoogleTagOptions.mode}.
|
|
12
|
-
*
|
|
13
|
-
* One named constant so flipping it later is a one-line change.
|
|
14
|
-
*
|
|
15
|
-
* - `'basic'` withholds the loader until a signal-bearing category is granted — which holds back
|
|
16
|
-
* Google's own IP receipt too, not only what a granted tag may then do with it.
|
|
17
|
-
* - `'advanced'` loads immediately with Consent Mode signals denied — today's only behavior, and
|
|
18
|
-
* Google's own recommended default for its own conversion modeling.
|
|
19
|
-
*
|
|
20
|
-
* Default is `'basic'`: read it as the EU/DE worst-case reading of ePrivacy Art. 5(3) — no third
|
|
21
|
-
* party receives a connection before consent.
|
|
22
|
-
*/
|
|
23
|
-
export const GOOGLE_TAG_DEFAULT_MODE: GoogleTagMode = 'basic'
|
|
24
|
-
|
|
25
|
-
export interface GtmOptions extends ConsentOptions {
|
|
26
|
-
/** Container id, e.g. `GTM-XXXXXXX`. */
|
|
27
|
-
id: string
|
|
28
|
-
/** The queue name, when a page runs more than one container. */
|
|
29
|
-
dataLayerName?: string
|
|
30
|
-
/** See {@link GOOGLE_TAG_DEFAULT_MODE}. Defaults to `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`). */
|
|
31
|
-
mode?: GoogleTagMode
|
|
32
|
-
}
|
|
1
|
+
import { CONSENT_ANALYTICS, CONSENT_MARKETING, consentBootstrapScript, consentGateScript, consentStore, trackingGranted, type ConsentService } from '@owlmeans/consent'
|
|
2
|
+
import { GOOGLE, GOOGLE_PRIVACY, GOOGLE_TAG_ID, JS_IDENTIFIER, UNTIL_ANALYTICS, UNTIL_MARKETING } from './consts.local.js'
|
|
3
|
+
import { GOOGLE_TAG_DEFAULT_MODE } from './consts.js'
|
|
4
|
+
import type { GoogleTagKind, GoogleTagOptions, GtmOptions } from './types.js'
|
|
33
5
|
|
|
34
6
|
/** Google's own container IIFE, the id and queue name injected as JSON data. */
|
|
35
7
|
const gtmContainerScript = (layer: string, id: string): string =>
|
|
@@ -140,25 +112,6 @@ export const loadGtm = (opts: GtmOptions): void => {
|
|
|
140
112
|
})
|
|
141
113
|
}
|
|
142
114
|
|
|
143
|
-
/**
|
|
144
|
-
* Which loader an id takes: `gtm` is the Tag Manager container (`gtm.js`), `gtag` the Google tag
|
|
145
|
-
* (`gtag.js`) — Google Analytics 4 (`G-`), a Google tag (`GT-`), Google Ads (`AW-`) and
|
|
146
|
-
* Floodlight (`DC-`).
|
|
147
|
-
*/
|
|
148
|
-
export type GoogleTagKind = 'gtm' | 'gtag'
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* The id shapes Google issues today, and nothing else.
|
|
152
|
-
*
|
|
153
|
-
* Deliberately strict — upper case, a known prefix, a bounded run of letters and digits — because
|
|
154
|
-
* the id is typed by a person into a settings form and ends up inside an inline script and a
|
|
155
|
-
* script URL. A value this pattern accepts cannot carry a quote, a tag or a second parameter, so
|
|
156
|
-
* the check is what makes it safe to store, not only to emit. `UA-` is not here: Universal
|
|
157
|
-
* Analytics stopped processing data in 2024, and accepting its ids would promise measurement that
|
|
158
|
-
* never arrives.
|
|
159
|
-
*/
|
|
160
|
-
const GOOGLE_TAG_ID = /^(GTM-[A-Z0-9]{4,12}|(G|GT|AW|DC)-[A-Z0-9]{4,16})$/
|
|
161
|
-
|
|
162
115
|
/** Whether `id` is a Tag Manager container or Google tag id this package can load. */
|
|
163
116
|
export const isGoogleTagId = (id: string): boolean =>
|
|
164
117
|
typeof id === 'string' && GOOGLE_TAG_ID.test(id)
|
|
@@ -167,22 +120,6 @@ export const isGoogleTagId = (id: string): boolean =>
|
|
|
167
120
|
export const googleTagKind = (id: string): GoogleTagKind | null =>
|
|
168
121
|
!isGoogleTagId(id) ? null : id.startsWith('GTM-') ? 'gtm' : 'gtag'
|
|
169
122
|
|
|
170
|
-
export interface GoogleTagOptions extends ConsentOptions {
|
|
171
|
-
/** `GTM-…`, `G-…`, `GT-…`, `AW-…` or `DC-…` — see {@link isGoogleTagId}. */
|
|
172
|
-
id: string
|
|
173
|
-
/**
|
|
174
|
-
* The queue name, when a page runs more than one tag. Consent Mode from `@owlmeans/consent` —
|
|
175
|
-
* the bootstrap AND the dialog's later updates — always speaks on `window.dataLayer`, so a tag
|
|
176
|
-
* given another queue never hears a consent command. Leave it unset on a consent-gated page.
|
|
177
|
-
*/
|
|
178
|
-
dataLayerName?: string
|
|
179
|
-
/** See {@link GOOGLE_TAG_DEFAULT_MODE}. Defaults to `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`). */
|
|
180
|
-
mode?: GoogleTagMode
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** A queue name has to be a plain identifier: it becomes `window[name]` in the emitted script. */
|
|
184
|
-
const JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/
|
|
185
|
-
|
|
186
123
|
const layerOf = (opts: GoogleTagOptions): string =>
|
|
187
124
|
opts.dataLayerName != null && JS_IDENTIFIER.test(opts.dataLayerName)
|
|
188
125
|
? opts.dataLayerName
|
|
@@ -274,48 +211,6 @@ export const googleTagHeadScript = (opts: GoogleTagOptions): string => {
|
|
|
274
211
|
return inlineSafe(`${bootstrap};${redaction};${loader}`)
|
|
275
212
|
}
|
|
276
213
|
|
|
277
|
-
/**
|
|
278
|
-
* The hosts a page running a Google tag has to allow in `script-src`, `connect-src` and
|
|
279
|
-
* `img-src`, for Google Analytics 4, Tag Manager and Google Ads together.
|
|
280
|
-
*
|
|
281
|
-
* Taken from Google's CSP guide (developers.google.com/tag-platform/security/guides/csp) and
|
|
282
|
-
* folded into one list, because the platform adds one list to all three directives:
|
|
283
|
-
*
|
|
284
|
-
* - `*.googletagmanager.com` — gtm.js and gtag/js (`www.`), and their measurement pings;
|
|
285
|
-
* - `*.google-analytics.com` — GA4 collection (`region1.`, `www.`);
|
|
286
|
-
* - `*.g.doubleclick.net` — GA4 with Google signals and Ads (`stats.g.`, `googleads.g.`);
|
|
287
|
-
* - `ad.doubleclick.net` — Ads and Floodlight conversions;
|
|
288
|
-
* - `www.googleadservices.com` — the Ads conversion script;
|
|
289
|
-
* - `pagead2.googlesyndication.com` — Ads and GA4-with-ads pings;
|
|
290
|
-
* - `*.google.com` — `www.google.com` (the Ads script, consent-mode pings), `adservice.`, and the
|
|
291
|
-
* `analytics.google.com` collection endpoints.
|
|
292
|
-
*
|
|
293
|
-
* Every entry is an `https://` scheme, an optional `*.` label and a dotted host — the only shape
|
|
294
|
-
* the publisher's CSP filter lets through — and there are few enough to stay inside its per-slot
|
|
295
|
-
* cap. Google's country domains (`google.<TLD>`) cannot be named in that shape and are left out:
|
|
296
|
-
* what is lost is a remarketing ping from visitors on those domains, never the tag itself.
|
|
297
|
-
*/
|
|
298
|
-
export const GOOGLE_TAG_CSP_SOURCES: readonly string[] = Object.freeze([
|
|
299
|
-
'https://*.googletagmanager.com',
|
|
300
|
-
'https://*.google-analytics.com',
|
|
301
|
-
'https://*.g.doubleclick.net',
|
|
302
|
-
'https://ad.doubleclick.net',
|
|
303
|
-
'https://www.googleadservices.com',
|
|
304
|
-
'https://pagead2.googlesyndication.com',
|
|
305
|
-
'https://*.google.com',
|
|
306
|
-
])
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* The hosts a Google tag frames: Tag Manager's service-worker and preview frames, and the Ads
|
|
310
|
-
* remarketing frame. For `frame-src`, which otherwise stays `'none'`.
|
|
311
|
-
*/
|
|
312
|
-
export const GOOGLE_TAG_FRAME_SOURCES: readonly string[] = Object.freeze([
|
|
313
|
-
'https://www.googletagmanager.com',
|
|
314
|
-
'https://td.doubleclick.net',
|
|
315
|
-
])
|
|
316
|
-
|
|
317
|
-
const GOOGLE = 'Google LLC'
|
|
318
|
-
const GOOGLE_PRIVACY = 'https://policies.google.com/privacy'
|
|
319
214
|
|
|
320
215
|
const analyticsService = (name: string, purpose: string, cookies: string[]): ConsentService => ({
|
|
321
216
|
name, provider: GOOGLE, category: CONSENT_ANALYTICS, purpose, cookies, privacyHref: GOOGLE_PRIVACY,
|
|
@@ -325,10 +220,6 @@ const adsService = (name: string, purpose: string, cookies: string[]): ConsentSe
|
|
|
325
220
|
name, provider: GOOGLE, category: CONSENT_MARKETING, purpose, cookies, privacyHref: GOOGLE_PRIVACY,
|
|
326
221
|
})
|
|
327
222
|
|
|
328
|
-
const UNTIL_ANALYTICS = 'Until analytics cookies are accepted it sets no cookies and sends only '
|
|
329
|
-
+ 'cookieless measurement signals.'
|
|
330
|
-
const UNTIL_MARKETING = 'Until marketing cookies are accepted it sets no cookies and ad click '
|
|
331
|
-
+ 'identifiers are redacted.'
|
|
332
223
|
|
|
333
224
|
/**
|
|
334
225
|
* What a Google tag discloses on the cookie policy, for `CookiePolicy`'s `services`.
|
|
@@ -387,3 +278,6 @@ export const googleTagServices = (id: string): ConsentService[] => {
|
|
|
387
278
|
}
|
|
388
279
|
}
|
|
389
280
|
}
|
|
281
|
+
|
|
282
|
+
export type { GoogleTagKind, GoogleTagMode, GoogleTagOptions, GtmOptions } from './types.js'
|
|
283
|
+
export { GOOGLE_TAG_CSP_SOURCES, GOOGLE_TAG_DEFAULT_MODE, GOOGLE_TAG_FRAME_SOURCES } from './consts.js'
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ConsentOptions } from '@owlmeans/consent'
|
|
2
|
+
|
|
3
|
+
/** `'gtm'` container / `'gtag'` — see {@link GtmOptions.mode} / {@link GoogleTagOptions.mode}. */
|
|
4
|
+
export type GoogleTagMode = 'basic' | 'advanced'
|
|
5
|
+
|
|
6
|
+
export interface GtmOptions extends ConsentOptions {
|
|
7
|
+
/** Container id, e.g. `GTM-XXXXXXX`. */
|
|
8
|
+
id: string
|
|
9
|
+
/** The queue name, when a page runs more than one container. */
|
|
10
|
+
dataLayerName?: string
|
|
11
|
+
/** See {@link GOOGLE_TAG_DEFAULT_MODE}. Defaults to `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`). */
|
|
12
|
+
mode?: GoogleTagMode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Which loader an id takes: `gtm` is the Tag Manager container (`gtm.js`), `gtag` the Google tag
|
|
17
|
+
* (`gtag.js`) — Google Analytics 4 (`G-`), a Google tag (`GT-`), Google Ads (`AW-`) and
|
|
18
|
+
* Floodlight (`DC-`).
|
|
19
|
+
*/
|
|
20
|
+
export type GoogleTagKind = 'gtm' | 'gtag'
|
|
21
|
+
|
|
22
|
+
export interface GoogleTagOptions extends ConsentOptions {
|
|
23
|
+
/** `GTM-…`, `G-…`, `GT-…`, `AW-…` or `DC-…` — see {@link isGoogleTagId}. */
|
|
24
|
+
id: string
|
|
25
|
+
/**
|
|
26
|
+
* The queue name, when a page runs more than one tag. Consent Mode from `@owlmeans/consent` —
|
|
27
|
+
* the bootstrap AND the dialog's later updates — always speaks on `window.dataLayer`, so a tag
|
|
28
|
+
* given another queue never hears a consent command. Leave it unset on a consent-gated page.
|
|
29
|
+
*/
|
|
30
|
+
dataLayerName?: string
|
|
31
|
+
/** See {@link GOOGLE_TAG_DEFAULT_MODE}. Defaults to `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`). */
|
|
32
|
+
mode?: GoogleTagMode
|
|
33
|
+
}
|
package/tests/tag.spec.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
} from '
|
|
5
|
-
import type { ConsentCategory } from '@owlmeans/consent'
|
|
6
|
-
import {
|
|
7
|
-
GOOGLE_TAG_CSP_SOURCES, GOOGLE_TAG_FRAME_SOURCES, googleTagHeadScript, googleTagKind,
|
|
8
|
-
googleTagServices, isGoogleTagId,
|
|
9
|
-
} from '../src/index.js'
|
|
2
|
+
import { CONSENT_EVENT, CONSENT_KEY, DEFAULT_CONSENT_CATEGORIES, consentBootstrapScript, type ConsentCategory } from '@owlmeans/consent'
|
|
3
|
+
import { googleTagHeadScript, googleTagKind, googleTagServices, isGoogleTagId } from '../src/index.js'
|
|
4
|
+
import { GOOGLE_TAG_CSP_SOURCES, GOOGLE_TAG_FRAME_SOURCES } from '../src/consts.js'
|
|
10
5
|
|
|
11
6
|
/**
|
|
12
7
|
* The publisher's filter for slot-supplied CSP sources (`CSP_SOURCE_SHAPE` in viable's
|