@owlmeans/web-gtm 0.1.18-rc.3 → 0.1.18-rc.30

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 ADDED
@@ -0,0 +1,15 @@
1
+ <!-- owlmeans:agent-guidance:start -->
2
+ ## Agent guidance
3
+
4
+ This package ships embedded agent skills under `agent-meta/`. After installing your
5
+ `@owlmeans/*` packages, run the OwlMeans agent-skills installer to place them into
6
+ your project's skill store (`.agents/skills/`):
7
+
8
+ ```sh
9
+ npx @owlmeans/agent-skills@^0.1.18-rc.39
10
+ ```
11
+
12
+ The embedded files are version-matched to this package release. Do not edit them
13
+ directly — they are regenerated on each publish. To contribute guidance edits,
14
+ open a PR against the source monorepo.
15
+ <!-- owlmeans:agent-guidance:end -->
@@ -0,0 +1,16 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "package": "@owlmeans/web-gtm",
4
+ "version": "0.1.18-rc.30",
5
+ "generatedAt": "2026-09-24T14:00:35.009Z",
6
+ "canonicalRepo": "https://github.com/owlmeans/common",
7
+ "entries": [
8
+ {
9
+ "kind": "skill",
10
+ "name": "web-gtm",
11
+ "category": "package-specific",
12
+ "file": "skills/web-gtm/SKILL.md",
13
+ "canonicalPath": ".agents/skills/web-gtm/SKILL.md"
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,222 @@
1
+ ---
2
+ name: web-gtm
3
+ description: How to use @owlmeans/web-gtm — the Google tag head snippet for every Google id (Tag Manager GTM-, GA4 G-, Google tag GT-, Ads AW-, Floodlight DC-) that declares Consent Mode defaults before any loader runs, the id validator, the CSP host lists, the cookie-policy disclosure for a tag, the noscript frame, and the script-side loader for a host that cannot edit its own HTML. Auto-invoked when adding a Google tag or tag manager to a page, importing googleTagHeadScript, isGoogleTagId, GOOGLE_TAG_CSP_SOURCES, googleTagServices, gtmHeadScript, gtmNoscriptFrame or loadGtm, or debugging why a tag ignores a stored consent decision or is blocked by CSP.
4
+ user-invocable: false
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # @owlmeans/web-gtm
9
+
10
+ **Layer:** Web
11
+ **Install:** `"@owlmeans/web-gtm": "^0.1.18-rc.30"` in `dependencies`
12
+
13
+ The tag half of the consent set. It emits **strings and data**, not components, and it holds no
14
+ state — the decision lives in `@owlmeans/consent`, which this package reads through
15
+ `consentBootstrapScript` and `consentStore`.
16
+
17
+ ## The one idea: order
18
+
19
+ A Google tag — the Tag Manager container or gtag.js alike — decides what it may do from the
20
+ consent state present **when it loads**. A React bundle cannot get there first: by the time an
21
+ island mounts, the tag has been running for hundreds of milliseconds and has already decided. A
22
+ site whose defaults arrive after the loader is not configured differently — it is *unconfigured
23
+ for the window that matters*, and nothing in the page reports it.
24
+
25
+ So every head snippet here emits the consent bootstrap first and the loader after it, as ONE
26
+ inline script. Everything else in this package exists to serve that.
27
+
28
+ ## Loading mode: gated by default
29
+
30
+ `googleTagHeadScript`, `gtmHeadScript` and `loadGtm` all take an optional `mode:
31
+ 'basic' | 'advanced'`, defaulting to `GOOGLE_TAG_DEFAULT_MODE` — currently `'basic'`, one named
32
+ constant so flipping the platform default later is a one-line change:
33
+
34
+ | `mode` | What runs, and when |
35
+ |---|---|
36
+ | `'basic'` (default) | The bootstrap and the ads-redaction flags still run unconditionally — Consent Mode's own denied-by-default signals are declared either way. The LOADER itself — `gtm.js`, or `gtag/js` + `js` + `config` — is wrapped in `@owlmeans/consent`'s `consentGateScript`: it runs immediately if the visitor's stored record already `trackingGranted`, and otherwise waits for a later `CONSENT_EVENT` (from `applyConsent`, i.e. an in-page consent decision) that does. Nothing — not even Google's own IP receipt — reaches the tag before a signal-bearing category (`analytics` or `marketing` by default; a `required` or signal-less category never counts) is granted. |
37
+ | `'advanced'` | The original, only behavior before this mode existed: the loader runs immediately, Consent Mode signals denied by default, so the tag itself starts receiving traffic cookielessly from first paint. This is Google's own recommended default for its conversion modeling, and remains available for a site that has decided that tradeoff is acceptable. |
38
+
39
+ Read `'basic'`'s default as the EU/DE worst-case reading of ePrivacy Art. 5(3): no third party
40
+ receives a connection before consent.
41
+
42
+ `gtmNoscriptFrame` returns `''` in `'basic'` mode rather than an iframe — a browser with JavaScript
43
+ disabled cannot have granted anything, so an unauthenticated `<noscript>` frame would defeat the
44
+ whole point of gating. It keeps emitting the iframe in `'advanced'` mode, unchanged.
45
+
46
+ `loadGtm` gates the same way at the DOM level: it still calls `consentStore.init(opts)` first, but
47
+ only appends the container's `<script>` element once `trackingGranted(consentStore.get().record,
48
+ opts.categories)` is true — immediately if already granted, or via a one-shot `consentStore.subscribe`
49
+ that unsubscribes itself on the first update where it becomes true. The anti-double-load guard (the
50
+ element's own id) is unchanged.
51
+
52
+ None of this is new state: it is one more consumer of `@owlmeans/consent`'s existing surface —
53
+ `trackingGranted(record, categories)` (whether a stored/applied record grants a signal-bearing,
54
+ non-required category) and `consentGateScript(loaderExpr, opts)` (the inline script implementing the
55
+ "run now or wait for `CONSENT_EVENT`" branch). Both live in `@owlmeans/consent`, not here, because a
56
+ target project's own custom loader can reuse the same gate without depending on this package.
57
+
58
+ ## Key Exports
59
+
60
+ | Export | Description |
61
+ |--------|-------------|
62
+ | `googleTagHeadScript(opts)` | The inline `<head>` script for any Google id: consent bootstrap, ads redaction, then the loader the prefix calls for — gated behind `consentGateScript` in `opts.mode`'s default, `'basic'`. **The default for new code** |
63
+ | `isGoogleTagId(id)` / `googleTagKind(id)` | Whether an id is loadable, and whether it takes `gtm` (container) or `gtag` (gtag.js); `null` for anything else |
64
+ | `GoogleTagOptions` / `GoogleTagKind` | `ConsentOptions` plus `id`, optional `dataLayerName` and `mode`; `'gtm' \| 'gtag'` |
65
+ | `GoogleTagMode` / `GOOGLE_TAG_DEFAULT_MODE` | `'basic' \| 'advanced'`; the platform default, `'basic'` — see **Loading mode** below |
66
+ | `GOOGLE_TAG_CSP_SOURCES` | The hosts GA4, Tag Manager and Google Ads need in `script-src`, `connect-src` and `img-src` (7, frozen) |
67
+ | `GOOGLE_TAG_FRAME_SOURCES` | The hosts they frame, for `frame-src`: `https://www.googletagmanager.com`, `https://td.doubleclick.net` |
68
+ | `googleTagServices(id)` | The `ConsentService[]` a cookie policy discloses for that tag — pass to `CookiePolicy`'s `services` |
69
+ | `gtmHeadScript(opts)` | The container-only snippet: consent defaults, stored decision, then `gtm.js` — gated the same way as `googleTagHeadScript` |
70
+ | `gtmNoscriptFrame(opts)` | The hidden `<noscript>` iframe, for the top of `<body>` — `''` in `'basic'` mode |
71
+ | `loadGtm(opts)` | Script-side container load, for a host that cannot emit into its own head — gated the same way |
72
+ | `GtmOptions` | `ConsentOptions` plus `id` (e.g. `GTM-XXXXXXX`), optional `dataLayerName` and `mode` |
73
+
74
+ ## Ids
75
+
76
+ `isGoogleTagId` accepts exactly `GTM-` + 4–12, or `G-` / `GT-` / `AW-` / `DC-` + 4–16, upper-case
77
+ letters and digits. It is strict on purpose: the id is typed into a settings form and ends up in
78
+ an inline script and a script URL, and a value it accepts cannot carry a quote, a tag or a second
79
+ query parameter — so **validate with it where the id is stored**, not only where it is emitted.
80
+ Lower case is rejected rather than folded; normalise in the form if you want to be forgiving.
81
+ `UA-` (Universal Analytics, retired) is rejected: it would promise measurement that never comes.
82
+
83
+ | Prefix | Kind | Loader |
84
+ |---|---|---|
85
+ | `GTM-` | `gtm` | `gtm.js` — Google's container IIFE |
86
+ | `G-` GA4, `GT-` Google tag, `AW-` Google Ads, `DC-` Floodlight | `gtag` | `gtag/js`, then `js` and `config` |
87
+
88
+ ## `googleTagHeadScript` — what it emits
89
+
90
+ 1. `consentBootstrapScript(opts)` — `consent/default` (everything denied except
91
+ `security_storage`) and, for a returning visitor, `consent/update` from the stored record.
92
+ 2. `set ads_data_redaction true` and `set url_passthrough false` — both have to precede the tag's
93
+ `config`. Redaction strips ad-click ids and goes cookieless while `ad_storage` is denied.
94
+ 3. The loader. For `gtag`: a queue function (published as `window.gtag` only when nothing else
95
+ owns it, so app code can send events), `js`, `config`, and the gtag.js `<script async>`
96
+ element created from the inline script — one inline script rather than Google's two elements,
97
+ so a build stamps one thing. The element carries id `owl-gtag-<id>`, so running the snippet
98
+ twice configures the tag once instead of double-counting page views. In `mode`'s default,
99
+ `'basic'`, this whole step 3 is what `consentGateScript` withholds — steps 1 and 2 always run;
100
+ pass `mode: 'advanced'` to run step 3 unconditionally too, as before this mode existed.
101
+
102
+ An **invalid id yields the consent bootstrap alone, regardless of `mode`** — a mistyped tag must
103
+ never cost a page its consent defaults. So a host may stamp `googleTagHeadScript({ id: configured
104
+ })` unconditionally, or stamp `consentBootstrapScript()` when no tag is configured; both are
105
+ correct.
106
+
107
+ **The output is safe to place inline in HTML as-is.** JSON encoding keeps configured values inside
108
+ their string literals, but the HTML parser ends a script element at the first `</script` wherever
109
+ it sits, so every `</` and `<!--` in the output is backslash-escaped (`<\/`, `<\!--` — JavaScript
110
+ reads them unchanged). `gtmHeadScript` predates this and does not escape; prefer
111
+ `googleTagHeadScript` wherever category keys or a storage key come from configuration.
112
+
113
+ ## Stamping it
114
+
115
+ ```typescript
116
+ import { googleTagHeadScript } from '@owlmeans/web-gtm'
117
+
118
+ const head = googleTagHeadScript({ id: 'G-XXXXXXXXXX' }) // inline <script> content, first in <head>
119
+ ```
120
+
121
+ The snippet is stamped **from HTML**, never from the application bundle:
122
+
123
+ - a Vite app — a `transformIndexHtml` plugin substituting a marker, so the snippet cannot drift
124
+ from the package;
125
+ - an Astro site — `owlHeadScripts()` from `@owlmeans/astro`, `set:html` in the base layout;
126
+ - a **generated Viable app** — its Rollup build writes `index.html` and puts
127
+ `googleTagHeadScript({ id })` in the `<head>` when the project owner has set a Google tag
128
+ (`BRANDING_GOOGLE_TAG`), and `consentBootstrapScript()` otherwise. The tag is attached in
129
+ **preview and production** alike; the consent default is first in both.
130
+
131
+ The snippet **reads the stored decision**, so the same `ConsentOptions` (categories, `storageKey`)
132
+ must be passed here and to the dialog: two category sets in one page mean the snippet and the
133
+ component disagree about what was asked.
134
+
135
+ **Cross-domain consent needs nothing new here.** `GtmOptions`/`GoogleTagOptions` both extend
136
+ `ConsentOptions`, so `linker` (see the `consent` skill's plugin-seam section) passed to either head
137
+ script reaches `consentBootstrapScript` unchanged, which embeds the adopt-and-strip fragment right
138
+ after `consent/default` when it is set:
139
+
140
+ ```typescript
141
+ googleTagHeadScript({ id: 'GTM-XXXXXXX', linker: { domains: ['owlmeans.com', 'owlmeans.pl'] } })
142
+ ```
143
+
144
+ An app that does not stamp a tag at all still gets the fragment from a bare
145
+ `consentBootstrapScript({ linker: { domains } })`.
146
+
147
+ ## Content Security Policy
148
+
149
+ A page served with a CSP must allow the tag's hosts, or the loader is blocked and nothing on the
150
+ page says so.
151
+
152
+ - `GOOGLE_TAG_CSP_SOURCES` goes into `script-src`, `connect-src` and `img-src` — one list for all
153
+ three, folded from Google's CSP guide (developers.google.com/tag-platform/security/guides/csp):
154
+ `*.googletagmanager.com`, `*.google-analytics.com`, `*.g.doubleclick.net`, `ad.doubleclick.net`,
155
+ `www.googleadservices.com`, `pagead2.googlesyndication.com`, `*.google.com`. Each is
156
+ `https://` + optional `*.` + a dotted host, the only shape the Viable publisher's source filter
157
+ admits, and few enough to fit its per-slot cap. Google's country domains (`google.<TLD>`) cannot
158
+ be expressed in that shape; what is lost is a remarketing ping, never the tag.
159
+ - `GOOGLE_TAG_FRAME_SOURCES` goes into `frame-src`.
160
+ - The inline head script itself is covered by the document's own script hash — the Viable
161
+ publisher derives `'sha256-…'` from the document it sends — so nothing else is needed for it.
162
+ - **Tag Manager Custom HTML tags do not run under a hash-based CSP**: they inject inline scripts
163
+ no hash names, and `'unsafe-inline'` is not an option. Custom JavaScript variables need
164
+ `'unsafe-eval'` and fail the same way. Build a container for such a page from Google's
165
+ **built-in tag templates** (GA4, Google Ads, Floodlight, Conversion Linker) and Community
166
+ Gallery templates, which load from the allowed hosts.
167
+
168
+ ## Disclosing it
169
+
170
+ `googleTagServices(id)` returns what the cookie policy lists under each category, read off the
171
+ prefix — the page knows nothing more:
172
+
173
+ | Id | Entries |
174
+ |---|---|
175
+ | `G-` | Google Analytics — `analytics`; `_ga`, `_ga_<id without G->` |
176
+ | `AW-` | Google Ads — `marketing`; `_gcl_au`, `_gcl_aw` |
177
+ | `DC-` | Floodlight — `marketing`; `_gcl_au`, `_gcl_dc` |
178
+ | `GTM-`, `GT-` | one `analytics` AND one `marketing` entry, worded as "configured in the container" / "for it at Google" — the policy may not claim less than the tag can do |
179
+ | invalid | `[]` — it loads nothing, so it discloses nothing |
180
+
181
+ Pass them to `CookiePolicy` (`services`) wherever the tag is stamped; a generated Viable app's
182
+ cookie page does so when a Google tag is set. The strings are English data, not translation keys.
183
+
184
+ ## Gotchas
185
+
186
+ - **Consent Mode speaks on `window.dataLayer`.** The bootstrap and the dialog's later
187
+ `consent/update` both push there, so a tag given another `dataLayerName` never hears a consent
188
+ command. Leave `dataLayerName` unset on any consent-gated page; it exists for a page running a
189
+ second, separately-consented container. `googleTagHeadScript` falls back to `dataLayer` when the
190
+ name is not a plain identifier.
191
+ - **Ids and queue names are injected as data, never interpolated raw.** `gtmHeadScript` and
192
+ `googleTagHeadScript` JSON-encode them; `gtmNoscriptFrame` URL-encodes the id so it cannot break
193
+ out of the attribute. Never build either string by hand.
194
+ - **`loadGtm` is the fallback, not the default.** It is for a single-page app whose HTML is not
195
+ yours to edit. It still pushes the defaults first (through `consentStore.init`) and refuses to
196
+ load a second time — it marks its own `<script>` element with an id derived from the container —
197
+ but it runs after the bundle, which is exactly the window the head snippet closes. In `'basic'`
198
+ mode it additionally withholds the element itself until `trackingGranted`, same as the head
199
+ snippets.
200
+ - **The default flipped to `'basic'`.** Code written before this mode existed called
201
+ `googleTagHeadScript({ id })` / `gtmHeadScript({ id })` expecting the tag to load unconditionally.
202
+ It now gates by default; pass `mode: 'advanced'` explicitly to keep the old behavior for a site
203
+ that has made that call deliberately.
204
+ - **A second `consent/default` after a tag has loaded can WIDEN what was already narrowed**, so the
205
+ bootstrap is idempotent through a window flag. A page may therefore carry the call twice — once
206
+ inline, once from the bundle that mounts the dialog — without harm. Do not defeat the flag.
207
+ - `loadGtm` returns immediately when there is no `document`, so it is safe to call from code that
208
+ also runs during server rendering.
209
+
210
+ ## Depends On
211
+
212
+ - `@owlmeans/consent` — `consentBootstrapScript`, `consentStore`, `ConsentOptions`,
213
+ `ConsentService`, the category keys and the Consent Mode signal mapping, plus (for the `'basic'`
214
+ gated mode) `consentGateScript` and `trackingGranted`
215
+
216
+ ## Related
217
+
218
+ - `consent` — categories, the storage record and its migration, Consent Mode v2 signalling, and the
219
+ ordering rule this package implements
220
+ - `web-consent` — the dialog and the cookie-policy page (`services`) that produce and disclose the
221
+ decision
222
+ - `astro` — `owlHeadScripts()`, which composes the head strings for an Astro layout
package/build/index.d.ts CHANGED
@@ -1,9 +1,27 @@
1
- import type { ConsentOptions } from '@owlmeans/consent';
1
+ import type { ConsentOptions, ConsentService } from '@owlmeans/consent';
2
+ /** `'gtm'` container / `'gtag'` — see {@link GtmOptions.mode} / {@link GoogleTagOptions.mode}. */
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;
2
18
  export interface GtmOptions extends ConsentOptions {
3
19
  /** Container id, e.g. `GTM-XXXXXXX`. */
4
20
  id: string;
5
21
  /** The queue name, when a page runs more than one container. */
6
22
  dataLayerName?: string;
23
+ /** See {@link GOOGLE_TAG_DEFAULT_MODE}. Defaults to `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`). */
24
+ mode?: GoogleTagMode;
7
25
  }
8
26
  /**
9
27
  * The inline `<head>` snippet, consent first and the container second.
@@ -15,9 +33,20 @@ export interface GtmOptions extends ConsentOptions {
15
33
  * the window that matters, and nothing in the page reports it.
16
34
  *
17
35
  * Emit this into the document head, above everything else, as an inline script.
36
+ *
37
+ * In `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`) the container is not requested at all until a
38
+ * signal-bearing category is granted — {@link consentGateScript} wraps it, reading any stored
39
+ * decision immediately and otherwise waiting for `@owlmeans/consent`'s `CONSENT_EVENT`. Pass
40
+ * `mode: 'advanced'` for the original, unconditional load.
18
41
  */
19
42
  export declare const gtmHeadScript: (opts: GtmOptions) => string;
20
- /** The `<noscript>` iframe, for the body. */
43
+ /**
44
+ * The `<noscript>` iframe, for the body.
45
+ *
46
+ * In `'basic'` mode this is an empty string: the whole point of gating is that an unauthenticated
47
+ * `<noscript>` iframe would defeat it — a browser with JavaScript disabled cannot have granted
48
+ * anything, so there is nothing lawful left to render. `'advanced'` keeps the frame.
49
+ */
21
50
  export declare const gtmNoscriptFrame: (opts: GtmOptions) => string;
22
51
  /**
23
52
  * Load the container from script, for a host that cannot emit into its own head.
@@ -25,6 +54,94 @@ export declare const gtmNoscriptFrame: (opts: GtmOptions) => string;
25
54
  * The head snippet is strictly better and should be preferred; this exists for a single-page app
26
55
  * whose HTML is not ours to edit. It still pushes the defaults first, and it still refuses to load
27
56
  * a second time.
57
+ *
58
+ * In `'basic'` mode the container element is not appended until `trackingGranted` is true: either
59
+ * the stored record already grants it, or — since this runs after the bundle mounted, past the
60
+ * window the head snippet exists to close — a subscription to `consentStore` catches the first
61
+ * update where it becomes true and unsubscribes. `'advanced'` appends it immediately, as before.
28
62
  */
29
63
  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
+ /** Whether `id` is a Tag Manager container or Google tag id this package can load. */
71
+ export declare const isGoogleTagId: (id: string) => boolean;
72
+ /** The loader `id` takes, or `null` when it is not a loadable id at all. */
73
+ 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
+ /**
87
+ * The one inline `<head>` script for a Google tag of any kind: the consent bootstrap FIRST, then
88
+ * ads redaction, then the loader the id's prefix calls for — the Tag Manager container for `GTM-`,
89
+ * gtag.js with `js` and `config` for `G-`, `GT-`, `AW-` and `DC-`.
90
+ *
91
+ * The order is the rule {@link gtmHeadScript} exists for, and it holds for gtag.js just the same:
92
+ * a tag decides what it may do from the consent state present when it loads. The bootstrap
93
+ * declares everything denied and applies any stored decision in the same turn, so a returning
94
+ * visitor's tag starts granted and a new visitor's starts cookieless.
95
+ *
96
+ * An id that is not a loadable id ({@link isGoogleTagId}) yields the consent bootstrap alone — a
97
+ * page must never lose its consent defaults because someone mistyped a tag. The result is safe to
98
+ * place inline in HTML as-is: `</` and `<!--` inside any configured value are escaped.
99
+ *
100
+ * In `GOOGLE_TAG_DEFAULT_MODE` (`'basic'`) the loader itself — the last piece — is wrapped in
101
+ * `consentGateScript`: it runs immediately for a returning visitor whose stored record already
102
+ * grants a signal-bearing category, and otherwise waits for `@owlmeans/consent`'s `CONSENT_EVENT`
103
+ * before ever requesting the tag. The bootstrap and the redaction flags are unaffected and still
104
+ * run unconditionally — Consent Mode's OWN denied-by-default signals are declared either way, so a
105
+ * tag that later does load still starts from `ad_storage`/`analytics_storage` denied. Pass
106
+ * `mode: 'advanced'` for the original, unconditional load.
107
+ */
108
+ 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
+ /**
137
+ * What a Google tag discloses on the cookie policy, for `CookiePolicy`'s `services`.
138
+ *
139
+ * Read off the id's prefix, because that is all the page knows: `G-` is Google Analytics 4
140
+ * (`_ga`, and `_ga_<id without G->` which carries the session), `AW-` Google Ads, `DC-` Floodlight.
141
+ * A `GTM-` container and a `GT-` Google tag route to whatever their owner configured at Google,
142
+ * so both are disclosed as an analytics AND an advertising entry, worded as configured there —
143
+ * the policy may not claim less than the tag can do. An invalid id discloses nothing, matching
144
+ * {@link googleTagHeadScript}, which loads nothing for it.
145
+ */
146
+ export declare const googleTagServices: (id: string) => ConsentService[];
30
147
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvD,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAA;IACV,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,SAAU,UAAU,KAAG,MAWhD,CAAA;AAED,6CAA6C;AAC7C,eAAO,MAAM,gBAAgB,SAAU,UAAU,KAAG,MAEsB,CAAA;AAE1E;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,SAAU,UAAU,KAAG,IAkB1C,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvE,kGAAkG;AAClG,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,CAAA;AAEhD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAuB,CAAA;AAE7D,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;AAYD;;;;;;;;;;;;;;;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;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,MAAM,CAAA;AAc1C,sFAAsF;AACtF,eAAO,MAAM,aAAa,OAAQ,MAAM,KAAG,OACO,CAAA;AAElD,4EAA4E;AAC5E,eAAO,MAAM,aAAa,OAAQ,MAAM,KAAG,aAAa,GAAG,IACS,CAAA;AAEpE,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;AAyDD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,mBAAmB,SAAU,gBAAgB,KAAG,MAe5D,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAQlD,CAAA;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAGpD,CAAA;AAkBF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,OAAQ,MAAM,KAAG,cAAc,EA8C5D,CAAA"}