@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.
Files changed (120) hide show
  1. package/README.md +229 -0
  2. package/build/consts.d.ts +84 -23
  3. package/build/consts.d.ts.map +1 -1
  4. package/build/consts.js +110 -29
  5. package/build/consts.js.map +1 -1
  6. package/build/consts.local.d.ts +12 -0
  7. package/build/consts.local.d.ts.map +1 -0
  8. package/build/consts.local.js +12 -0
  9. package/build/consts.local.js.map +1 -0
  10. package/build/geo/types.d.ts +47 -0
  11. package/build/geo/types.d.ts.map +1 -0
  12. package/build/geo/types.js +2 -0
  13. package/build/geo/types.js.map +1 -0
  14. package/build/geo.d.ts +4 -0
  15. package/build/geo.d.ts.map +1 -0
  16. package/build/geo.js +96 -0
  17. package/build/geo.js.map +1 -0
  18. package/build/gtobserver/types.d.ts +75 -0
  19. package/build/gtobserver/types.d.ts.map +1 -0
  20. package/build/gtobserver/types.js +2 -0
  21. package/build/gtobserver/types.js.map +1 -0
  22. package/build/gtobserver.d.ts +7 -0
  23. package/build/gtobserver.d.ts.map +1 -0
  24. package/build/gtobserver.js +142 -0
  25. package/build/gtobserver.js.map +1 -0
  26. package/build/i18n/be.json +9 -4
  27. package/build/i18n/de.json +9 -4
  28. package/build/i18n/en.json +9 -4
  29. package/build/i18n/es.json +9 -4
  30. package/build/i18n/fr.json +9 -4
  31. package/build/i18n/pl.json +9 -4
  32. package/build/i18n/ru.json +9 -4
  33. package/build/i18n/types.d.ts +11 -0
  34. package/build/i18n/types.d.ts.map +1 -0
  35. package/build/i18n/types.js +2 -0
  36. package/build/i18n/types.js.map +1 -0
  37. package/build/i18n/uk.json +9 -4
  38. package/build/i18n.d.ts +4 -14
  39. package/build/i18n.d.ts.map +1 -1
  40. package/build/i18n.js +12 -27
  41. package/build/i18n.js.map +1 -1
  42. package/build/index.d.ts +13 -6
  43. package/build/index.d.ts.map +1 -1
  44. package/build/index.js +5 -4
  45. package/build/index.js.map +1 -1
  46. package/build/linker/types.d.ts +59 -0
  47. package/build/linker/types.d.ts.map +1 -0
  48. package/build/linker/types.js +2 -0
  49. package/build/linker/types.js.map +1 -0
  50. package/build/linker.d.ts +5 -76
  51. package/build/linker.d.ts.map +1 -1
  52. package/build/linker.js +290 -339
  53. package/build/linker.js.map +1 -1
  54. package/build/plugins/types.d.ts +42 -0
  55. package/build/plugins/types.d.ts.map +1 -0
  56. package/build/plugins/types.js +2 -0
  57. package/build/plugins/types.js.map +1 -0
  58. package/build/plugins.d.ts +4 -69
  59. package/build/plugins.d.ts.map +1 -1
  60. package/build/plugins.js +73 -62
  61. package/build/plugins.js.map +1 -1
  62. package/build/storage/types.d.ts +31 -0
  63. package/build/storage/types.d.ts.map +1 -0
  64. package/build/storage/types.js +2 -0
  65. package/build/storage/types.js.map +1 -0
  66. package/build/storage.d.ts +6 -22
  67. package/build/storage.d.ts.map +1 -1
  68. package/build/storage.js +72 -81
  69. package/build/storage.js.map +1 -1
  70. package/build/store.d.ts +2 -2
  71. package/build/store.d.ts.map +1 -1
  72. package/build/store.js +116 -67
  73. package/build/store.js.map +1 -1
  74. package/build/types.d.ts +147 -26
  75. package/build/types.d.ts.map +1 -1
  76. package/build/types.local.d.ts +9 -0
  77. package/build/types.local.d.ts.map +1 -0
  78. package/build/types.local.js +2 -0
  79. package/build/types.local.js.map +1 -0
  80. package/package.json +3 -3
  81. package/src/consts.local.ts +16 -0
  82. package/src/consts.ts +126 -34
  83. package/src/geo/types.ts +49 -0
  84. package/src/geo.ts +122 -0
  85. package/src/gtobserver/types.ts +73 -0
  86. package/src/gtobserver.ts +182 -0
  87. package/src/i18n/be.json +9 -4
  88. package/src/i18n/de.json +9 -4
  89. package/src/i18n/en.json +9 -4
  90. package/src/i18n/es.json +9 -4
  91. package/src/i18n/fr.json +9 -4
  92. package/src/i18n/pl.json +9 -4
  93. package/src/i18n/ru.json +9 -4
  94. package/src/i18n/types.ts +11 -0
  95. package/src/i18n/uk.json +9 -4
  96. package/src/i18n.ts +20 -32
  97. package/src/index.ts +13 -13
  98. package/src/linker/types.ts +59 -0
  99. package/src/linker.ts +301 -364
  100. package/src/plugins/types.ts +42 -0
  101. package/src/plugins.ts +82 -106
  102. package/src/storage/types.ts +31 -0
  103. package/src/storage.ts +75 -77
  104. package/src/store.ts +122 -69
  105. package/src/types.local.ts +9 -0
  106. package/src/types.ts +153 -25
  107. package/tests/consent.spec.ts +26 -28
  108. package/tests/geo.spec.ts +474 -0
  109. package/tests/linker.spec.ts +144 -309
  110. package/tests/tracking.spec.ts +14 -14
  111. package/build/functional.d.ts +0 -35
  112. package/build/functional.d.ts.map +0 -1
  113. package/build/functional.js +0 -67
  114. package/build/functional.js.map +0 -1
  115. package/build/gtm.d.ts +0 -72
  116. package/build/gtm.d.ts.map +0 -1
  117. package/build/gtm.js +0 -182
  118. package/build/gtm.js.map +0 -1
  119. package/src/functional.ts +0 -73
  120. package/src/gtm.ts +0 -220
@@ -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 { consentGateScript, trackingGranted } from '../src/gtm.js'
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
 
@@ -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"}
@@ -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
@@ -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
@@ -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
- }