@owlmeans/consent 0.1.18-rc.8 → 0.1.18
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 +229 -0
- package/build/consts.d.ts +84 -23
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +110 -29
- package/build/consts.js.map +1 -1
- package/build/consts.local.d.ts +12 -0
- package/build/consts.local.d.ts.map +1 -0
- package/build/consts.local.js +12 -0
- package/build/consts.local.js.map +1 -0
- package/build/geo/types.d.ts +47 -0
- package/build/geo/types.d.ts.map +1 -0
- package/build/geo/types.js +2 -0
- package/build/geo/types.js.map +1 -0
- package/build/geo.d.ts +4 -0
- package/build/geo.d.ts.map +1 -0
- package/build/geo.js +96 -0
- package/build/geo.js.map +1 -0
- package/build/gtobserver/types.d.ts +75 -0
- package/build/gtobserver/types.d.ts.map +1 -0
- package/build/gtobserver/types.js +2 -0
- package/build/gtobserver/types.js.map +1 -0
- package/build/gtobserver.d.ts +7 -0
- package/build/gtobserver.d.ts.map +1 -0
- package/build/gtobserver.js +142 -0
- package/build/gtobserver.js.map +1 -0
- package/build/i18n/be.json +9 -4
- package/build/i18n/de.json +9 -4
- package/build/i18n/en.json +9 -4
- package/build/i18n/es.json +9 -4
- package/build/i18n/fr.json +9 -4
- package/build/i18n/pl.json +9 -4
- package/build/i18n/ru.json +9 -4
- package/build/i18n/types.d.ts +11 -0
- package/build/i18n/types.d.ts.map +1 -0
- package/build/i18n/types.js +2 -0
- package/build/i18n/types.js.map +1 -0
- package/build/i18n/uk.json +9 -4
- package/build/i18n.d.ts +4 -14
- package/build/i18n.d.ts.map +1 -1
- package/build/i18n.js +12 -27
- package/build/i18n.js.map +1 -1
- package/build/index.d.ts +13 -6
- package/build/index.d.ts.map +1 -1
- package/build/index.js +5 -4
- package/build/index.js.map +1 -1
- package/build/linker/types.d.ts +59 -0
- package/build/linker/types.d.ts.map +1 -0
- package/build/linker/types.js +2 -0
- package/build/linker/types.js.map +1 -0
- package/build/linker.d.ts +5 -76
- package/build/linker.d.ts.map +1 -1
- package/build/linker.js +290 -339
- package/build/linker.js.map +1 -1
- package/build/plugins/types.d.ts +42 -0
- package/build/plugins/types.d.ts.map +1 -0
- package/build/plugins/types.js +2 -0
- package/build/plugins/types.js.map +1 -0
- package/build/plugins.d.ts +4 -69
- package/build/plugins.d.ts.map +1 -1
- package/build/plugins.js +73 -62
- package/build/plugins.js.map +1 -1
- package/build/storage/types.d.ts +31 -0
- package/build/storage/types.d.ts.map +1 -0
- package/build/storage/types.js +2 -0
- package/build/storage/types.js.map +1 -0
- package/build/storage.d.ts +6 -22
- package/build/storage.d.ts.map +1 -1
- package/build/storage.js +72 -81
- package/build/storage.js.map +1 -1
- package/build/store.d.ts +2 -2
- package/build/store.d.ts.map +1 -1
- package/build/store.js +116 -67
- package/build/store.js.map +1 -1
- package/build/types.d.ts +147 -26
- package/build/types.d.ts.map +1 -1
- package/build/types.local.d.ts +9 -0
- package/build/types.local.d.ts.map +1 -0
- package/build/types.local.js +2 -0
- package/build/types.local.js.map +1 -0
- package/package.json +3 -3
- package/src/consts.local.ts +16 -0
- package/src/consts.ts +126 -34
- package/src/geo/types.ts +49 -0
- package/src/geo.ts +122 -0
- package/src/gtobserver/types.ts +73 -0
- package/src/gtobserver.ts +182 -0
- package/src/i18n/be.json +9 -4
- package/src/i18n/de.json +9 -4
- package/src/i18n/en.json +9 -4
- package/src/i18n/es.json +9 -4
- package/src/i18n/fr.json +9 -4
- package/src/i18n/pl.json +9 -4
- package/src/i18n/ru.json +9 -4
- package/src/i18n/types.ts +11 -0
- package/src/i18n/uk.json +9 -4
- package/src/i18n.ts +20 -32
- package/src/index.ts +13 -13
- package/src/linker/types.ts +59 -0
- package/src/linker.ts +301 -364
- package/src/plugins/types.ts +42 -0
- package/src/plugins.ts +82 -106
- package/src/storage/types.ts +31 -0
- package/src/storage.ts +75 -77
- package/src/store.ts +122 -69
- package/src/types.local.ts +9 -0
- package/src/types.ts +153 -25
- package/tests/consent.spec.ts +26 -28
- package/tests/geo.spec.ts +474 -0
- package/tests/linker.spec.ts +144 -309
- package/tests/tracking.spec.ts +14 -14
- package/build/functional.d.ts +0 -35
- package/build/functional.d.ts.map +0 -1
- package/build/functional.js +0 -67
- package/build/functional.js.map +0 -1
- package/build/gtm.d.ts +0 -72
- package/build/gtm.d.ts.map +0 -1
- package/build/gtm.js +0 -182
- package/build/gtm.js.map +0 -1
- package/src/functional.ts +0 -73
- package/src/gtm.ts +0 -220
package/tests/tracking.spec.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { describe, test, expect } from 'bun:test'
|
|
2
2
|
import { CONSENT_ANALYTICS, CONSENT_ESSENTIAL, CONSENT_EVENT, CONSENT_KEY, CONSENT_MARKETING } from '../src/consts.js'
|
|
3
|
-
import {
|
|
3
|
+
import { consentModeHelper } from '../src/gtobserver.js'
|
|
4
4
|
import type { ConsentCategory, ConsentRecord } from '../src/types.js'
|
|
5
5
|
|
|
6
6
|
describe('trackingGranted', () => {
|
|
7
7
|
test('no record at all is not granted', () => {
|
|
8
|
-
expect(trackingGranted(null)).toBe(false)
|
|
8
|
+
expect(consentModeHelper.trackingGranted(null)).toBe(false)
|
|
9
9
|
})
|
|
10
10
|
|
|
11
11
|
test('an essential-only record is not granted — required categories never count', () => {
|
|
@@ -13,19 +13,19 @@ describe('trackingGranted', () => {
|
|
|
13
13
|
[CONSENT_ESSENTIAL]: true, [CONSENT_ANALYTICS]: false, [CONSENT_MARKETING]: false,
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
expect(trackingGranted(record)).toBe(false)
|
|
16
|
+
expect(consentModeHelper.trackingGranted(record)).toBe(false)
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
test('analytics granted is tracking-granted', () => {
|
|
20
20
|
const record: ConsentRecord = { [CONSENT_ESSENTIAL]: true, [CONSENT_ANALYTICS]: true }
|
|
21
21
|
|
|
22
|
-
expect(trackingGranted(record)).toBe(true)
|
|
22
|
+
expect(consentModeHelper.trackingGranted(record)).toBe(true)
|
|
23
23
|
})
|
|
24
24
|
|
|
25
25
|
test('marketing granted is tracking-granted too', () => {
|
|
26
26
|
const record: ConsentRecord = { [CONSENT_ESSENTIAL]: true, [CONSENT_MARKETING]: true }
|
|
27
27
|
|
|
28
|
-
expect(trackingGranted(record)).toBe(true)
|
|
28
|
+
expect(consentModeHelper.trackingGranted(record)).toBe(true)
|
|
29
29
|
})
|
|
30
30
|
|
|
31
31
|
test('a required category is ignored even when the record marks it granted', () => {
|
|
@@ -39,7 +39,7 @@ describe('trackingGranted', () => {
|
|
|
39
39
|
},
|
|
40
40
|
]
|
|
41
41
|
|
|
42
|
-
expect(trackingGranted({ essential: true }, categories)).toBe(false)
|
|
42
|
+
expect(consentModeHelper.trackingGranted({ essential: true }, categories)).toBe(false)
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
test('a category with no signals is ignored even when granted', () => {
|
|
@@ -47,7 +47,7 @@ describe('trackingGranted', () => {
|
|
|
47
47
|
{ key: 'preferences', labelKey: 'a', descriptionKey: 'b' },
|
|
48
48
|
]
|
|
49
49
|
|
|
50
|
-
expect(trackingGranted({ preferences: true }, categories)).toBe(false)
|
|
50
|
+
expect(consentModeHelper.trackingGranted({ preferences: true }, categories)).toBe(false)
|
|
51
51
|
})
|
|
52
52
|
})
|
|
53
53
|
|
|
@@ -91,7 +91,7 @@ describe('consentGateScript — the two reachable paths', () => {
|
|
|
91
91
|
test('a returning visitor with a stored grant runs the loader immediately, synchronously', () => {
|
|
92
92
|
const browser = makeBrowser({ record: { essential: true, analytics: true, v: 2 } })
|
|
93
93
|
browser.win.__loaded = 0
|
|
94
|
-
browser.run(consentGateScript('window.__loaded = (window.__loaded || 0) + 1'))
|
|
94
|
+
browser.run(consentModeHelper.consentGateScript('window.__loaded = (window.__loaded || 0) + 1'))
|
|
95
95
|
|
|
96
96
|
expect(browser.win.__loaded).toBe(1)
|
|
97
97
|
// Nothing left listening — the immediate branch never subscribes.
|
|
@@ -101,7 +101,7 @@ describe('consentGateScript — the two reachable paths', () => {
|
|
|
101
101
|
test('a stored record that grants nothing does not run the loader immediately', () => {
|
|
102
102
|
const browser = makeBrowser({ record: { essential: true, analytics: false, v: 2 } })
|
|
103
103
|
browser.win.__loaded = 0
|
|
104
|
-
browser.run(consentGateScript('window.__loaded = (window.__loaded || 0) + 1'))
|
|
104
|
+
browser.run(consentModeHelper.consentGateScript('window.__loaded = (window.__loaded || 0) + 1'))
|
|
105
105
|
|
|
106
106
|
expect(browser.win.__loaded).toBe(0)
|
|
107
107
|
expect(browser.listenerCount()).toBe(1)
|
|
@@ -110,7 +110,7 @@ describe('consentGateScript — the two reachable paths', () => {
|
|
|
110
110
|
test('no stored record at all falls to the event-listener branch and waits', () => {
|
|
111
111
|
const browser = makeBrowser()
|
|
112
112
|
browser.win.__loaded = 0
|
|
113
|
-
browser.run(consentGateScript('window.__loaded = (window.__loaded || 0) + 1'))
|
|
113
|
+
browser.run(consentModeHelper.consentGateScript('window.__loaded = (window.__loaded || 0) + 1'))
|
|
114
114
|
|
|
115
115
|
expect(browser.win.__loaded).toBe(0)
|
|
116
116
|
|
|
@@ -145,7 +145,7 @@ describe('consentGateScript — the two reachable paths', () => {
|
|
|
145
145
|
const doc = { cookie: `${CONSENT_KEY}=${record}` }
|
|
146
146
|
// eslint-disable-next-line no-new-func
|
|
147
147
|
new Function(
|
|
148
|
-
'window', 'document', consentGateScript('window.__loaded = (window.__loaded || 0) + 1')
|
|
148
|
+
'window', 'document', consentModeHelper.consentGateScript('window.__loaded = (window.__loaded || 0) + 1')
|
|
149
149
|
)(win, doc)
|
|
150
150
|
|
|
151
151
|
expect(win.__loaded).toBe(1)
|
|
@@ -158,7 +158,7 @@ describe('consentGateScript — the two reachable paths', () => {
|
|
|
158
158
|
]
|
|
159
159
|
const browser = makeBrowser({ record: { essential: true, analytics: false, v: 2 } })
|
|
160
160
|
browser.win.__loaded = 0
|
|
161
|
-
browser.run(consentGateScript('window.__loaded = (window.__loaded || 0) + 1', { categories }))
|
|
161
|
+
browser.run(consentModeHelper.consentGateScript('window.__loaded = (window.__loaded || 0) + 1', { categories }))
|
|
162
162
|
|
|
163
163
|
expect(browser.win.__loaded).toBe(0)
|
|
164
164
|
|
|
@@ -167,7 +167,7 @@ describe('consentGateScript — the two reachable paths', () => {
|
|
|
167
167
|
})
|
|
168
168
|
|
|
169
169
|
test('the generated script carries both reachable code paths in its text', () => {
|
|
170
|
-
const script = consentGateScript('window.__loaded = (window.__loaded || 0) + 1')
|
|
170
|
+
const script = consentModeHelper.consentGateScript('window.__loaded = (window.__loaded || 0) + 1')
|
|
171
171
|
|
|
172
172
|
// The immediate branch: `load()` called right after the stored-record check, before any
|
|
173
173
|
// listener is attached.
|
|
@@ -182,7 +182,7 @@ describe('consentGateScript — the two reachable paths', () => {
|
|
|
182
182
|
test('a custom storage key is honoured, matching the bootstrap script\'s own lookup', () => {
|
|
183
183
|
const browser = makeBrowser({ key: 'custom_key', record: { essential: true, analytics: true, v: 2 } })
|
|
184
184
|
browser.win.__loaded = 0
|
|
185
|
-
browser.run(consentGateScript(
|
|
185
|
+
browser.run(consentModeHelper.consentGateScript(
|
|
186
186
|
'window.__loaded = (window.__loaded || 0) + 1', { storageKey: 'custom_key' }
|
|
187
187
|
))
|
|
188
188
|
|
package/build/functional.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { ConsentOptions } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Whether this document's visitor has granted functional storage — a preference they set, such as
|
|
4
|
-
* the interface language, may be remembered on their device.
|
|
5
|
-
*
|
|
6
|
-
* Read from the stored record every time rather than from the store's state, so it is correct
|
|
7
|
-
* before `consentStore.init` has run: the language resolves at start-up, ahead of any component.
|
|
8
|
-
* A record with no `functional` key (one saved before the category existed) is NOT a grant — the
|
|
9
|
-
* visitor was never asked, and a preference is not remembered on a device nobody asked about.
|
|
10
|
-
*/
|
|
11
|
-
export declare const functionalGranted: (opts?: ConsentOptions) => boolean;
|
|
12
|
-
/** Every `localStorage` key that may hold a functional preference for these options. */
|
|
13
|
-
export declare const functionalKeysOf: (opts?: ConsentOptions) => string[];
|
|
14
|
-
/**
|
|
15
|
-
* Remove every functional preference this application keeps. Called whenever functional is not
|
|
16
|
-
* granted — a refusal, a withdrawal, or a record that never answered — because a preference that
|
|
17
|
-
* outlives the consent to remember it is exactly the storage the visitor did not agree to.
|
|
18
|
-
*/
|
|
19
|
-
export declare const purgeFunctionalStorage: (opts?: ConsentOptions) => void;
|
|
20
|
-
/**
|
|
21
|
-
* Remember a functional preference — but only while `functional` is granted. Returns whether it was
|
|
22
|
-
* written. The gate lives here so a caller cannot forget it; an application that is asked to store
|
|
23
|
-
* something functional calls this instead of `localStorage.setItem`.
|
|
24
|
-
*/
|
|
25
|
-
export declare const writeFunctionalPreference: (key: string, value: string, opts?: ConsentOptions) => boolean;
|
|
26
|
-
/**
|
|
27
|
-
* An inline `<head>` fragment that defines `window.owlConsentAllows(category)` — whether the stored
|
|
28
|
-
* decision grants that category — for the scripts of a page that has no bundle yet: a site that
|
|
29
|
-
* remembers the visitor's language choice from a plain inline script (owlmeans.com's language
|
|
30
|
-
* switcher) asks it before it writes anything. Reads the same localStorage-then-cookie pair
|
|
31
|
-
* `readConsent` does, and answers `false` for anything it cannot read or parse — no answer is not a
|
|
32
|
-
* grant. Stamp it before the scripts that call it.
|
|
33
|
-
*/
|
|
34
|
-
export declare const consentAllowsScript: (opts?: ConsentOptions) => string;
|
|
35
|
-
//# sourceMappingURL=functional.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"functional.d.ts","sourceRoot":"","sources":["../src/functional.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,UAAW,cAAc,KAAG,OACR,CAAA;AAElD,wFAAwF;AACxF,eAAO,MAAM,gBAAgB,UAAW,cAAc,KAAG,MAAM,EAK9D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,UAAW,cAAc,KAAG,IAM9D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,QAAS,MAAM,SAAS,MAAM,SAAS,cAAc,KAAG,OAW7F,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,UAAW,cAAc,KAAG,MAS3D,CAAA"}
|
package/build/functional.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { CONSENT_FUNCTIONAL, CONSENT_KEY, CONSENT_LANGUAGE_KEY } from './consts.js';
|
|
2
|
-
import { readConsent } from './storage.js';
|
|
3
|
-
/**
|
|
4
|
-
* Whether this document's visitor has granted functional storage — a preference they set, such as
|
|
5
|
-
* the interface language, may be remembered on their device.
|
|
6
|
-
*
|
|
7
|
-
* Read from the stored record every time rather than from the store's state, so it is correct
|
|
8
|
-
* before `consentStore.init` has run: the language resolves at start-up, ahead of any component.
|
|
9
|
-
* A record with no `functional` key (one saved before the category existed) is NOT a grant — the
|
|
10
|
-
* visitor was never asked, and a preference is not remembered on a device nobody asked about.
|
|
11
|
-
*/
|
|
12
|
-
export const functionalGranted = (opts) => readConsent(opts)?.[CONSENT_FUNCTIONAL] === true;
|
|
13
|
-
/** Every `localStorage` key that may hold a functional preference for these options. */
|
|
14
|
-
export const functionalKeysOf = (opts) => [
|
|
15
|
-
...new Set([
|
|
16
|
-
...(opts?.functionalKeys ?? [CONSENT_LANGUAGE_KEY]),
|
|
17
|
-
...(opts?.linker?.language?.storageKey != null ? [opts.linker.language.storageKey] : []),
|
|
18
|
-
]),
|
|
19
|
-
];
|
|
20
|
-
/**
|
|
21
|
-
* Remove every functional preference this application keeps. Called whenever functional is not
|
|
22
|
-
* granted — a refusal, a withdrawal, or a record that never answered — because a preference that
|
|
23
|
-
* outlives the consent to remember it is exactly the storage the visitor did not agree to.
|
|
24
|
-
*/
|
|
25
|
-
export const purgeFunctionalStorage = (opts) => {
|
|
26
|
-
for (const key of functionalKeysOf(opts)) {
|
|
27
|
-
try {
|
|
28
|
-
localStorage.removeItem(key);
|
|
29
|
-
}
|
|
30
|
-
catch { /* storage blocked: nothing was written there to remove */ }
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* Remember a functional preference — but only while `functional` is granted. Returns whether it was
|
|
35
|
-
* written. The gate lives here so a caller cannot forget it; an application that is asked to store
|
|
36
|
-
* something functional calls this instead of `localStorage.setItem`.
|
|
37
|
-
*/
|
|
38
|
-
export const writeFunctionalPreference = (key, value, opts) => {
|
|
39
|
-
if (!functionalGranted(opts)) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
try {
|
|
43
|
-
localStorage.setItem(key, value);
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* An inline `<head>` fragment that defines `window.owlConsentAllows(category)` — whether the stored
|
|
52
|
-
* decision grants that category — for the scripts of a page that has no bundle yet: a site that
|
|
53
|
-
* remembers the visitor's language choice from a plain inline script (owlmeans.com's language
|
|
54
|
-
* switcher) asks it before it writes anything. Reads the same localStorage-then-cookie pair
|
|
55
|
-
* `readConsent` does, and answers `false` for anything it cannot read or parse — no answer is not a
|
|
56
|
-
* grant. Stamp it before the scripts that call it.
|
|
57
|
-
*/
|
|
58
|
-
export const consentAllowsScript = (opts) => {
|
|
59
|
-
const key = JSON.stringify(opts?.storageKey ?? CONSENT_KEY).replace(/</g, '\\u003c');
|
|
60
|
-
return `(function(w,d){w.owlConsentAllows=function(k){` +
|
|
61
|
-
`var raw=null;try{raw=w.localStorage.getItem(${key})}catch(e){}` +
|
|
62
|
-
`if(!raw){var p=('; '+d.cookie).split('; '+${key}+'=');if(p.length===2)raw=p.pop().split(';').shift()}` +
|
|
63
|
-
`if(!raw)return false;` +
|
|
64
|
-
`try{var r=JSON.parse(raw);return !!r&&typeof r==='object'&&r[k]===true}catch(e){return false}}` +
|
|
65
|
-
`})(window,document)`;
|
|
66
|
-
};
|
|
67
|
-
//# sourceMappingURL=functional.js.map
|
package/build/functional.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"functional.js","sourceRoot":"","sources":["../src/functional.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG1C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAqB,EAAW,EAAE,CAClE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAA;AAElD,wFAAwF;AACxF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAqB,EAAY,EAAE,CAAC;IACnE,GAAG,IAAI,GAAG,CAAC;QACT,GAAG,CAAC,IAAI,EAAE,cAAc,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACnD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACzF,CAAC;CACH,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,IAAqB,EAAQ,EAAE;IACpE,KAAK,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC;YACH,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;QAC9B,CAAC;QAAC,MAAM,CAAC,CAAC,0DAA0D,CAAC,CAAC;IACxE,CAAC;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,IAAqB,EAAW,EAAE;IACtG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAEhC,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAqB,EAAU,EAAE;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,IAAI,WAAW,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IAEpF,OAAO,gDAAgD;QACrD,+CAA+C,GAAG,cAAc;QAChE,6CAA6C,GAAG,uDAAuD;QACvG,uBAAuB;QACvB,gGAAgG;QAClG,qBAAqB,CAAA;AACvB,CAAC,CAAA"}
|
package/build/gtm.d.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { ConsentCategory, ConsentOptions, ConsentRecord } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Push onto the tag-manager queue in the shape Google's own snippet uses.
|
|
4
|
-
*
|
|
5
|
-
* `arguments`, not an array literal. Both work with today's Consent API, but `arguments` is what
|
|
6
|
-
* `gtag.js` itself emits, and a page that carries both this and Google's snippet should not have
|
|
7
|
-
* two shapes in one queue.
|
|
8
|
-
*/
|
|
9
|
-
export declare function gtagConsent(..._args: unknown[]): void;
|
|
10
|
-
/** Every signal the configured categories drive, at its pre-consent value. */
|
|
11
|
-
export declare const consentDefaults: (categories?: ConsentCategory[]) => Record<string, 'granted' | 'denied'>;
|
|
12
|
-
/** Every signal, at the value this record implies. */
|
|
13
|
-
export declare const consentUpdate: (record: ConsentRecord, categories?: ConsentCategory[]) => Record<string, 'granted' | 'denied'>;
|
|
14
|
-
/**
|
|
15
|
-
* Declare what is denied, before any tag can act.
|
|
16
|
-
*
|
|
17
|
-
* Idempotent through a window flag, because the page may carry this call twice — once inline in
|
|
18
|
-
* the document head, once from whatever bundle mounts the dialog — and a second `default` after a
|
|
19
|
-
* tag has loaded is worse than none: it can widen what was already narrowed.
|
|
20
|
-
*/
|
|
21
|
-
export declare const pushConsentDefaults: (opts?: ConsentOptions) => void;
|
|
22
|
-
/**
|
|
23
|
-
* Apply a decision: globals first, then the signal update, then any per-category events.
|
|
24
|
-
*
|
|
25
|
-
* The order is load-bearing. A tag that fires on the update event reads the globals in the same
|
|
26
|
-
* turn, so writing them afterwards would let the first firing see the previous answer.
|
|
27
|
-
*/
|
|
28
|
-
export declare const applyConsent: (record: ConsentRecord, opts?: ConsentOptions) => void;
|
|
29
|
-
/**
|
|
30
|
-
* Whether a stored decision grants tracking at all — a signal-bearing category, not merely the
|
|
31
|
-
* required essential one.
|
|
32
|
-
*
|
|
33
|
-
* "Signal-bearing" is what a required category can never be: it exists to gate whether a LOADER
|
|
34
|
-
* runs, and a required category is disclosure, not a question — everyone gets it. Only a category
|
|
35
|
-
* that both is optional and drives at least one Consent Mode signal counts, which is exactly what
|
|
36
|
-
* `googleTagHeadScript`'s `'basic'` mode gates a Google tag behind.
|
|
37
|
-
*/
|
|
38
|
-
export declare const trackingGranted: (record: ConsentRecord | null, categories?: ConsentCategory[]) => boolean;
|
|
39
|
-
/**
|
|
40
|
-
* The inline script a document stamps ABOVE its tag-manager snippet.
|
|
41
|
-
*
|
|
42
|
-
* It exists because the defaults have to be on the queue before `gtm.js` loads, and a React bundle
|
|
43
|
-
* cannot be: by the time an island mounts, the container has been running for hundreds of
|
|
44
|
-
* milliseconds and has already decided what it may do. Emitting it from the HTML is the only
|
|
45
|
-
* ordering that holds — and it reads the stored record too, so a returning visitor's tags are not
|
|
46
|
-
* denied for the first paint of every page.
|
|
47
|
-
*/
|
|
48
|
-
export declare const consentBootstrapScript: (opts?: ConsentOptions) => string;
|
|
49
|
-
/**
|
|
50
|
-
* Withhold a loader until a stored or later decision grants tracking.
|
|
51
|
-
*
|
|
52
|
-
* The counterpart to `consentBootstrapScript` for the `'basic'` gated loading mode
|
|
53
|
-
* (`@owlmeans/web-gtm`'s `GOOGLE_TAG_DEFAULT_MODE`): where the bootstrap always declares Consent
|
|
54
|
-
* Mode's defaults so a tag may load cookieless, this withholds the tag itself — the loader string
|
|
55
|
-
* is only ever reached once `trackingGranted` is true, either because a returning visitor's stored
|
|
56
|
-
* record already says so, or because `applyConsent` later dispatches {@link CONSENT_EVENT} with
|
|
57
|
-
* one that does. The listener removes itself on the first passing event, so the loader — which
|
|
58
|
-
* marks its own element and refuses to run twice — is asked to run at most once from here.
|
|
59
|
-
*
|
|
60
|
-
* `loaderExpr` is a complete statement or expression that runs the loader when reached — the
|
|
61
|
-
* self-invoking IIFE string `googleTagHeadScript`'s `gtagScript`/`gtmContainerScript` already
|
|
62
|
-
* produce, embedded here verbatim rather than called. It is placed inline exactly as
|
|
63
|
-
* `consentBootstrapScript`'s own output is: dynamic values (the storage key, the category shape,
|
|
64
|
-
* the event name) are JSON-encoded so nothing configured can break out of a string literal, and
|
|
65
|
-
* escaping the RESULT for HTML (`</script`, `<!--`) is left to whoever composes the final inline
|
|
66
|
-
* script — the same discipline `consentBootstrapScript` follows, and `googleTagHeadScript` already
|
|
67
|
-
* applies to the whole script it stamps.
|
|
68
|
-
*/
|
|
69
|
-
export declare const consentGateScript: (loaderExpr: string, opts?: ConsentOptions & {
|
|
70
|
-
categories?: ConsentCategory[];
|
|
71
|
-
}) => string;
|
|
72
|
-
//# sourceMappingURL=gtm.d.ts.map
|
package/build/gtm.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gtm.d.ts","sourceRoot":"","sources":["../src/gtm.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAUhF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAUrD;AAKD,8EAA8E;AAC9E,eAAO,MAAM,eAAe,gBACb,eAAe,EAAE,KAC7B,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAMrC,CAAA;AAED,sDAAsD;AACtD,eAAO,MAAM,aAAa,WAChB,aAAa,eAAe,eAAe,EAAE,KACpD,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAWrC,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,UAAW,cAAc,KAAG,IAO3D,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,WAAY,aAAa,SAAS,cAAc,KAAG,IA6B3E,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,WAClB,aAAa,GAAG,IAAI,eAAc,eAAe,EAAE,KAC1D,OAEF,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,UAAW,cAAc,KAAG,MA+B9D,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,iBAAiB,eAChB,MAAM,SAAS,cAAc,GAAG;IAAE,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;CAAE,KAC7E,MAuBF,CAAA"}
|
package/build/gtm.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { CONSENT_EVENT, CONSENT_KEY, CONSENT_SETUP_FLAG, CONSENT_SIGNAL_DEFAULTS, DEFAULT_CONSENT_CATEGORIES, } from './consts.js';
|
|
2
|
-
import { consentLinkerScript } from './linker.js';
|
|
3
|
-
const global = () => typeof window !== 'undefined' ? window : null;
|
|
4
|
-
/**
|
|
5
|
-
* Push onto the tag-manager queue in the shape Google's own snippet uses.
|
|
6
|
-
*
|
|
7
|
-
* `arguments`, not an array literal. Both work with today's Consent API, but `arguments` is what
|
|
8
|
-
* `gtag.js` itself emits, and a page that carries both this and Google's snippet should not have
|
|
9
|
-
* two shapes in one queue.
|
|
10
|
-
*/
|
|
11
|
-
export function gtagConsent(..._args) {
|
|
12
|
-
const win = global();
|
|
13
|
-
if (win == null) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
win.dataLayer = win.dataLayer ?? [];
|
|
17
|
-
// `arguments`, not `_args`. The rest parameter is only here to type the call sites; what goes on
|
|
18
|
-
// the queue must be the arguments object itself.
|
|
19
|
-
// eslint-disable-next-line prefer-rest-params
|
|
20
|
-
win.dataLayer.push(arguments);
|
|
21
|
-
}
|
|
22
|
-
const categoriesOf = (opts) => opts?.categories ?? DEFAULT_CONSENT_CATEGORIES;
|
|
23
|
-
/** Every signal the configured categories drive, at its pre-consent value. */
|
|
24
|
-
export const consentDefaults = (categories) => {
|
|
25
|
-
const used = new Set((categories ?? DEFAULT_CONSENT_CATEGORIES)
|
|
26
|
-
.flatMap(category => category.signals ?? []));
|
|
27
|
-
return Object.fromEntries(Object.entries(CONSENT_SIGNAL_DEFAULTS)
|
|
28
|
-
.filter(([signal]) => used.has(signal)));
|
|
29
|
-
};
|
|
30
|
-
/** Every signal, at the value this record implies. */
|
|
31
|
-
export const consentUpdate = (record, categories) => {
|
|
32
|
-
const update = {};
|
|
33
|
-
for (const category of categories ?? DEFAULT_CONSENT_CATEGORIES) {
|
|
34
|
-
const granted = category.required === true || record[category.key] === true;
|
|
35
|
-
for (const signal of category.signals ?? []) {
|
|
36
|
-
// A signal named by two categories is granted only when every one of them is.
|
|
37
|
-
update[signal] = granted && update[signal] !== 'denied' ? 'granted' : 'denied';
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return update;
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* Declare what is denied, before any tag can act.
|
|
44
|
-
*
|
|
45
|
-
* Idempotent through a window flag, because the page may carry this call twice — once inline in
|
|
46
|
-
* the document head, once from whatever bundle mounts the dialog — and a second `default` after a
|
|
47
|
-
* tag has loaded is worse than none: it can widen what was already narrowed.
|
|
48
|
-
*/
|
|
49
|
-
export const pushConsentDefaults = (opts) => {
|
|
50
|
-
const win = global();
|
|
51
|
-
if (win == null || opts?.silent === true || win[CONSENT_SETUP_FLAG] === true) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
gtagConsent('consent', 'default', consentDefaults(categoriesOf(opts)));
|
|
55
|
-
win[CONSENT_SETUP_FLAG] = true;
|
|
56
|
-
};
|
|
57
|
-
/**
|
|
58
|
-
* Apply a decision: globals first, then the signal update, then any per-category events.
|
|
59
|
-
*
|
|
60
|
-
* The order is load-bearing. A tag that fires on the update event reads the globals in the same
|
|
61
|
-
* turn, so writing them afterwards would let the first firing see the previous answer.
|
|
62
|
-
*/
|
|
63
|
-
export const applyConsent = (record, opts) => {
|
|
64
|
-
const win = global();
|
|
65
|
-
if (win == null || opts?.silent === true) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const categories = categoriesOf(opts);
|
|
69
|
-
for (const category of categories) {
|
|
70
|
-
if (category.globalVar != null) {
|
|
71
|
-
win[category.globalVar] = category.required === true || record[category.key] === true;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
gtagConsent('consent', 'update', consentUpdate(record, categories));
|
|
75
|
-
for (const category of categories) {
|
|
76
|
-
if (category.event != null && (category.required === true || record[category.key] === true)) {
|
|
77
|
-
win.dataLayer = win.dataLayer ?? [];
|
|
78
|
-
win.dataLayer.push({ event: category.event });
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
// A gate script (`consentGateScript`) that already decided NOT to load, because nothing was
|
|
82
|
-
// granted at the time it ran, has no other way to hear this. Consent Mode itself speaks only on
|
|
83
|
-
// `dataLayer`, which a tag that never loaded is not listening to.
|
|
84
|
-
if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function'
|
|
85
|
-
&& typeof CustomEvent === 'function') {
|
|
86
|
-
window.dispatchEvent(new CustomEvent(CONSENT_EVENT, { detail: { record } }));
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
/**
|
|
90
|
-
* Whether a stored decision grants tracking at all — a signal-bearing category, not merely the
|
|
91
|
-
* required essential one.
|
|
92
|
-
*
|
|
93
|
-
* "Signal-bearing" is what a required category can never be: it exists to gate whether a LOADER
|
|
94
|
-
* runs, and a required category is disclosure, not a question — everyone gets it. Only a category
|
|
95
|
-
* that both is optional and drives at least one Consent Mode signal counts, which is exactly what
|
|
96
|
-
* `googleTagHeadScript`'s `'basic'` mode gates a Google tag behind.
|
|
97
|
-
*/
|
|
98
|
-
export const trackingGranted = (record, categories = DEFAULT_CONSENT_CATEGORIES) => record != null && categories.some(category => !category.required && (category.signals?.length ?? 0) > 0 && record[category.key] === true);
|
|
99
|
-
/**
|
|
100
|
-
* The inline script a document stamps ABOVE its tag-manager snippet.
|
|
101
|
-
*
|
|
102
|
-
* It exists because the defaults have to be on the queue before `gtm.js` loads, and a React bundle
|
|
103
|
-
* cannot be: by the time an island mounts, the container has been running for hundreds of
|
|
104
|
-
* milliseconds and has already decided what it may do. Emitting it from the HTML is the only
|
|
105
|
-
* ordering that holds — and it reads the stored record too, so a returning visitor's tags are not
|
|
106
|
-
* denied for the first paint of every page.
|
|
107
|
-
*/
|
|
108
|
-
export const consentBootstrapScript = (opts) => {
|
|
109
|
-
const defaults = JSON.stringify(consentDefaults(categoriesOf(opts)));
|
|
110
|
-
const categories = JSON.stringify(categoriesOf(opts).map(category => ({
|
|
111
|
-
key: category.key,
|
|
112
|
-
required: category.required === true,
|
|
113
|
-
signals: category.signals ?? [],
|
|
114
|
-
globalVar: category.globalVar ?? null,
|
|
115
|
-
})));
|
|
116
|
-
const storageKey = JSON.stringify(opts?.storageKey ?? CONSENT_KEY);
|
|
117
|
-
const flag = JSON.stringify(CONSENT_SETUP_FLAG);
|
|
118
|
-
// Right after the default is pushed and before EITHER storage read below (this one, and the
|
|
119
|
-
// `consentGateScript` that is concatenated after this whole IIFE) — an adopted decision must
|
|
120
|
-
// already be in storage by the time anything reads it, so `gtm.js`'s own `page_view` never sees
|
|
121
|
-
// the parameter and a granted decision loads the tag on arrival. A page with no `opts.linker`
|
|
122
|
-
// gets an empty string here, unchanged from before this existed.
|
|
123
|
-
const linker = opts?.linker != null ? `${consentLinkerScript(opts)};` : '';
|
|
124
|
-
return `(function(w,d){` +
|
|
125
|
-
`w.dataLayer=w.dataLayer||[];function g(){w.dataLayer.push(arguments)}` +
|
|
126
|
-
`if(w[${flag}])return;g('consent','default',${defaults});w[${flag}]=true;` +
|
|
127
|
-
linker +
|
|
128
|
-
`var raw=null;try{raw=w.localStorage.getItem(${storageKey})}catch(e){}` +
|
|
129
|
-
`if(!raw){var p=('; '+d.cookie).split('; '+${storageKey}+'=');` +
|
|
130
|
-
`if(p.length===2){raw=p.pop().split(';').shift()}}` +
|
|
131
|
-
`if(!raw)return;var r;try{r=JSON.parse(raw)}catch(e){return}` +
|
|
132
|
-
`var cs=${categories},u={};` +
|
|
133
|
-
`for(var i=0;i<cs.length;i++){var c=cs[i];var ok=c.required||r[c.key]===true;` +
|
|
134
|
-
`if(c.globalVar){w[c.globalVar]=ok}` +
|
|
135
|
-
`for(var j=0;j<c.signals.length;j++){var s=c.signals[j];` +
|
|
136
|
-
`u[s]=ok&&u[s]!=='denied'?'granted':'denied'}}` +
|
|
137
|
-
`g('consent','update',u)})(window,document)`;
|
|
138
|
-
};
|
|
139
|
-
/**
|
|
140
|
-
* Withhold a loader until a stored or later decision grants tracking.
|
|
141
|
-
*
|
|
142
|
-
* The counterpart to `consentBootstrapScript` for the `'basic'` gated loading mode
|
|
143
|
-
* (`@owlmeans/web-gtm`'s `GOOGLE_TAG_DEFAULT_MODE`): where the bootstrap always declares Consent
|
|
144
|
-
* Mode's defaults so a tag may load cookieless, this withholds the tag itself — the loader string
|
|
145
|
-
* is only ever reached once `trackingGranted` is true, either because a returning visitor's stored
|
|
146
|
-
* record already says so, or because `applyConsent` later dispatches {@link CONSENT_EVENT} with
|
|
147
|
-
* one that does. The listener removes itself on the first passing event, so the loader — which
|
|
148
|
-
* marks its own element and refuses to run twice — is asked to run at most once from here.
|
|
149
|
-
*
|
|
150
|
-
* `loaderExpr` is a complete statement or expression that runs the loader when reached — the
|
|
151
|
-
* self-invoking IIFE string `googleTagHeadScript`'s `gtagScript`/`gtmContainerScript` already
|
|
152
|
-
* produce, embedded here verbatim rather than called. It is placed inline exactly as
|
|
153
|
-
* `consentBootstrapScript`'s own output is: dynamic values (the storage key, the category shape,
|
|
154
|
-
* the event name) are JSON-encoded so nothing configured can break out of a string literal, and
|
|
155
|
-
* escaping the RESULT for HTML (`</script`, `<!--`) is left to whoever composes the final inline
|
|
156
|
-
* script — the same discipline `consentBootstrapScript` follows, and `googleTagHeadScript` already
|
|
157
|
-
* applies to the whole script it stamps.
|
|
158
|
-
*/
|
|
159
|
-
export const consentGateScript = (loaderExpr, opts) => {
|
|
160
|
-
const gate = JSON.stringify(categoriesOf(opts).map(category => ({
|
|
161
|
-
key: category.key,
|
|
162
|
-
required: category.required === true,
|
|
163
|
-
hasSignal: (category.signals ?? []).length > 0,
|
|
164
|
-
})));
|
|
165
|
-
const storageKey = JSON.stringify(opts?.storageKey ?? CONSENT_KEY);
|
|
166
|
-
const event = JSON.stringify(CONSENT_EVENT);
|
|
167
|
-
return `(function(w,d){` +
|
|
168
|
-
`function ok(r){if(!r)return false;var cs=${gate};` +
|
|
169
|
-
`for(var i=0;i<cs.length;i++){var c=cs[i];` +
|
|
170
|
-
`if(!c.required&&c.hasSignal&&r[c.key]===true)return true}return false}` +
|
|
171
|
-
`function load(){${loaderExpr}}` +
|
|
172
|
-
`var raw=null;try{raw=w.localStorage.getItem(${storageKey})}catch(e){}` +
|
|
173
|
-
`if(!raw){var p=('; '+d.cookie).split('; '+${storageKey}+'=');` +
|
|
174
|
-
`if(p.length===2){raw=p.pop().split(';').shift()}}` +
|
|
175
|
-
`var r=null;if(raw){try{r=JSON.parse(raw)}catch(e){r=null}}` +
|
|
176
|
-
`if(ok(r)){load();return}` +
|
|
177
|
-
`function h(ev){if(ok(ev&&ev.detail&&ev.detail.record)){` +
|
|
178
|
-
`w.removeEventListener(${event},h);load()}}` +
|
|
179
|
-
`w.addEventListener(${event},h)` +
|
|
180
|
-
`})(window,document)`;
|
|
181
|
-
};
|
|
182
|
-
//# sourceMappingURL=gtm.js.map
|
package/build/gtm.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gtm.js","sourceRoot":"","sources":["../src/gtm.ts"],"names":[],"mappings":"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"}
|
package/src/functional.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { CONSENT_FUNCTIONAL, CONSENT_KEY, CONSENT_LANGUAGE_KEY } from './consts.js'
|
|
2
|
-
import { readConsent } from './storage.js'
|
|
3
|
-
import type { ConsentOptions } from './types.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Whether this document's visitor has granted functional storage — a preference they set, such as
|
|
7
|
-
* the interface language, may be remembered on their device.
|
|
8
|
-
*
|
|
9
|
-
* Read from the stored record every time rather than from the store's state, so it is correct
|
|
10
|
-
* before `consentStore.init` has run: the language resolves at start-up, ahead of any component.
|
|
11
|
-
* A record with no `functional` key (one saved before the category existed) is NOT a grant — the
|
|
12
|
-
* visitor was never asked, and a preference is not remembered on a device nobody asked about.
|
|
13
|
-
*/
|
|
14
|
-
export const functionalGranted = (opts?: ConsentOptions): boolean =>
|
|
15
|
-
readConsent(opts)?.[CONSENT_FUNCTIONAL] === true
|
|
16
|
-
|
|
17
|
-
/** Every `localStorage` key that may hold a functional preference for these options. */
|
|
18
|
-
export const functionalKeysOf = (opts?: ConsentOptions): string[] => [
|
|
19
|
-
...new Set([
|
|
20
|
-
...(opts?.functionalKeys ?? [CONSENT_LANGUAGE_KEY]),
|
|
21
|
-
...(opts?.linker?.language?.storageKey != null ? [opts.linker.language.storageKey] : []),
|
|
22
|
-
]),
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Remove every functional preference this application keeps. Called whenever functional is not
|
|
27
|
-
* granted — a refusal, a withdrawal, or a record that never answered — because a preference that
|
|
28
|
-
* outlives the consent to remember it is exactly the storage the visitor did not agree to.
|
|
29
|
-
*/
|
|
30
|
-
export const purgeFunctionalStorage = (opts?: ConsentOptions): void => {
|
|
31
|
-
for (const key of functionalKeysOf(opts)) {
|
|
32
|
-
try {
|
|
33
|
-
localStorage.removeItem(key)
|
|
34
|
-
} catch { /* storage blocked: nothing was written there to remove */ }
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Remember a functional preference — but only while `functional` is granted. Returns whether it was
|
|
40
|
-
* written. The gate lives here so a caller cannot forget it; an application that is asked to store
|
|
41
|
-
* something functional calls this instead of `localStorage.setItem`.
|
|
42
|
-
*/
|
|
43
|
-
export const writeFunctionalPreference = (key: string, value: string, opts?: ConsentOptions): boolean => {
|
|
44
|
-
if (!functionalGranted(opts)) {
|
|
45
|
-
return false
|
|
46
|
-
}
|
|
47
|
-
try {
|
|
48
|
-
localStorage.setItem(key, value)
|
|
49
|
-
|
|
50
|
-
return true
|
|
51
|
-
} catch {
|
|
52
|
-
return false
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* An inline `<head>` fragment that defines `window.owlConsentAllows(category)` — whether the stored
|
|
58
|
-
* decision grants that category — for the scripts of a page that has no bundle yet: a site that
|
|
59
|
-
* remembers the visitor's language choice from a plain inline script (owlmeans.com's language
|
|
60
|
-
* switcher) asks it before it writes anything. Reads the same localStorage-then-cookie pair
|
|
61
|
-
* `readConsent` does, and answers `false` for anything it cannot read or parse — no answer is not a
|
|
62
|
-
* grant. Stamp it before the scripts that call it.
|
|
63
|
-
*/
|
|
64
|
-
export const consentAllowsScript = (opts?: ConsentOptions): string => {
|
|
65
|
-
const key = JSON.stringify(opts?.storageKey ?? CONSENT_KEY).replace(/</g, '\\u003c')
|
|
66
|
-
|
|
67
|
-
return `(function(w,d){w.owlConsentAllows=function(k){` +
|
|
68
|
-
`var raw=null;try{raw=w.localStorage.getItem(${key})}catch(e){}` +
|
|
69
|
-
`if(!raw){var p=('; '+d.cookie).split('; '+${key}+'=');if(p.length===2)raw=p.pop().split(';').shift()}` +
|
|
70
|
-
`if(!raw)return false;` +
|
|
71
|
-
`try{var r=JSON.parse(raw);return !!r&&typeof r==='object'&&r[k]===true}catch(e){return false}}` +
|
|
72
|
-
`})(window,document)`
|
|
73
|
-
}
|