c15t 0.0.1-rc.3

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 (86) hide show
  1. package/.turbo/turbo-build.log +54 -0
  2. package/.turbo/turbo-fmt.log +6 -0
  3. package/.turbo/turbo-lint.log +288 -0
  4. package/.turbo/turbo-test.log +33 -0
  5. package/CHANGELOG.md +20 -0
  6. package/LICENSE.md +595 -0
  7. package/README.md +28 -0
  8. package/dist/index.cjs +118 -0
  9. package/dist/index.d.ts +27 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +19 -0
  12. package/dist/libs/__tests__/tracking-blocker.test.cjs +269 -0
  13. package/dist/libs/__tests__/tracking-blocker.test.d.ts +2 -0
  14. package/dist/libs/__tests__/tracking-blocker.test.d.ts.map +1 -0
  15. package/dist/libs/__tests__/tracking-blocker.test.js +267 -0
  16. package/dist/libs/consent-utils.cjs +68 -0
  17. package/dist/libs/consent-utils.d.ts +49 -0
  18. package/dist/libs/consent-utils.d.ts.map +1 -0
  19. package/dist/libs/consent-utils.js +23 -0
  20. package/dist/libs/tracking-blocker.cjs +167 -0
  21. package/dist/libs/tracking-blocker.d.ts +33 -0
  22. package/dist/libs/tracking-blocker.d.ts.map +1 -0
  23. package/dist/libs/tracking-blocker.js +108 -0
  24. package/dist/libs/tracking-domains.cjs +188 -0
  25. package/dist/libs/tracking-domains.d.ts +7 -0
  26. package/dist/libs/tracking-domains.d.ts.map +1 -0
  27. package/dist/libs/tracking-domains.js +146 -0
  28. package/dist/store.cjs +248 -0
  29. package/dist/store.d.ts +58 -0
  30. package/dist/store.d.ts.map +1 -0
  31. package/dist/store.initial-state.cjs +105 -0
  32. package/dist/store.initial-state.d.ts +43 -0
  33. package/dist/store.initial-state.d.ts.map +1 -0
  34. package/dist/store.initial-state.js +66 -0
  35. package/dist/store.js +219 -0
  36. package/dist/store.type.cjs +22 -0
  37. package/dist/store.type.d.ts +159 -0
  38. package/dist/store.type.d.ts.map +1 -0
  39. package/dist/store.type.js +0 -0
  40. package/dist/translations/en.cjs +96 -0
  41. package/dist/translations/en.d.ts +3 -0
  42. package/dist/translations/en.d.ts.map +1 -0
  43. package/dist/translations/en.js +54 -0
  44. package/dist/translations/index.cjs +51 -0
  45. package/dist/translations/index.d.ts +3 -0
  46. package/dist/translations/index.d.ts.map +1 -0
  47. package/dist/translations/index.js +9 -0
  48. package/dist/types/callbacks.cjs +22 -0
  49. package/dist/types/callbacks.d.ts +146 -0
  50. package/dist/types/callbacks.d.ts.map +1 -0
  51. package/dist/types/callbacks.js +0 -0
  52. package/dist/types/compliance.cjs +22 -0
  53. package/dist/types/compliance.d.ts +196 -0
  54. package/dist/types/compliance.d.ts.map +1 -0
  55. package/dist/types/compliance.js +0 -0
  56. package/dist/types/gdpr.cjs +86 -0
  57. package/dist/types/gdpr.d.ts +168 -0
  58. package/dist/types/gdpr.d.ts.map +1 -0
  59. package/dist/types/gdpr.js +44 -0
  60. package/dist/types/index.cjs +44 -0
  61. package/dist/types/index.d.ts +141 -0
  62. package/dist/types/index.d.ts.map +1 -0
  63. package/dist/types/index.js +4 -0
  64. package/dist/types/translations.cjs +22 -0
  65. package/dist/types/translations.d.ts +52 -0
  66. package/dist/types/translations.d.ts.map +1 -0
  67. package/dist/types/translations.js +0 -0
  68. package/package.json +33 -0
  69. package/rslib.config.ts +28 -0
  70. package/src/index.ts +31 -0
  71. package/src/libs/__tests__/tracking-blocker.test.ts +271 -0
  72. package/src/libs/consent-utils.ts +70 -0
  73. package/src/libs/tracking-blocker.ts +202 -0
  74. package/src/libs/tracking-domains.ts +158 -0
  75. package/src/store.initial-state.ts +123 -0
  76. package/src/store.ts +450 -0
  77. package/src/store.type.ts +187 -0
  78. package/src/translations/en.ts +55 -0
  79. package/src/translations/index.ts +10 -0
  80. package/src/types/callbacks.ts +152 -0
  81. package/src/types/compliance.ts +205 -0
  82. package/src/types/gdpr.ts +217 -0
  83. package/src/types/index.ts +148 -0
  84. package/src/types/translations.ts +60 -0
  85. package/tsconfig.json +12 -0
  86. package/vitest.config.ts +15 -0
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+ var __webpack_require__ = {};
3
+ (() => {
4
+ __webpack_require__.d = function (exports1, definition) {
5
+ for (var key in definition)
6
+ if (
7
+ __webpack_require__.o(definition, key) &&
8
+ !__webpack_require__.o(exports1, key)
9
+ )
10
+ Object.defineProperty(exports1, key, {
11
+ enumerable: true,
12
+ get: definition[key],
13
+ });
14
+ };
15
+ })();
16
+ (() => {
17
+ __webpack_require__.o = function (obj, prop) {
18
+ return Object.prototype.hasOwnProperty.call(obj, prop);
19
+ };
20
+ })();
21
+ (() => {
22
+ __webpack_require__.r = function (exports1) {
23
+ if ('undefined' != typeof Symbol && Symbol.toStringTag)
24
+ Object.defineProperty(exports1, Symbol.toStringTag, {
25
+ value: 'Module',
26
+ });
27
+ Object.defineProperty(exports1, '__esModule', {
28
+ value: true,
29
+ });
30
+ };
31
+ })();
32
+ var __webpack_exports__ = {};
33
+ __webpack_require__.r(__webpack_exports__);
34
+ __webpack_require__.d(__webpack_exports__, {
35
+ consentTypes: () => external_gdpr_cjs_namespaceObject.consentTypes,
36
+ });
37
+ const external_gdpr_cjs_namespaceObject = require('./gdpr.cjs');
38
+ var __webpack_export_target__ = exports;
39
+ for (var __webpack_i__ in __webpack_exports__)
40
+ __webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__];
41
+ if (__webpack_exports__.__esModule)
42
+ Object.defineProperty(__webpack_export_target__, '__esModule', {
43
+ value: true,
44
+ });
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Central export point for all consent management types and interfaces.
4
+ * This module aggregates and re-exports all type definitions needed for implementing
5
+ * GDPR-compliant consent management.
6
+ */
7
+ /**
8
+ * @module
9
+ * Compliance and Privacy Types
10
+ *
11
+ * @remarks
12
+ * Exports types related to privacy compliance and consent management:
13
+ * - Region-specific compliance settings
14
+ * - Consent state tracking
15
+ * - Privacy preferences
16
+ * - Namespace configuration
17
+ *
18
+ * @example
19
+ * Import compliance-related types:
20
+ * ```typescript
21
+ * import type {
22
+ * ComplianceRegion,
23
+ * ComplianceSettings,
24
+ * PrivacySettings
25
+ * } from 'c15t/types';
26
+ *
27
+ * const euSettings: ComplianceSettings = {
28
+ * enabled: true,
29
+ * appliesGlobally: false,
30
+ * applies: true
31
+ * };
32
+ *
33
+ * const region: ComplianceRegion = 'gdpr';
34
+ * ```
35
+ */
36
+ import type {
37
+ ComplianceRegion,
38
+ ComplianceSettings,
39
+ ConsentState,
40
+ HasConsentedProps,
41
+ NamespaceProps,
42
+ PrivacySettings,
43
+ } from './compliance';
44
+ export type {
45
+ ConsentState,
46
+ ComplianceRegion,
47
+ ComplianceSettings,
48
+ PrivacySettings,
49
+ HasConsentedProps,
50
+ NamespaceProps,
51
+ };
52
+ /**
53
+ * @module
54
+ * GDPR Consent Types
55
+ *
56
+ * @remarks
57
+ * Exports types and constants for GDPR-specific consent management:
58
+ * - Consent category definitions
59
+ * - Consent type configurations
60
+ * - Predefined consent settings
61
+ *
62
+ * @example
63
+ * Import and use GDPR-related types:
64
+ * ```typescript
65
+ * import {
66
+ * type AllConsentNames,
67
+ * type ConsentType,
68
+ * consentTypes
69
+ * } from 'c15t/types';
70
+ *
71
+ * function isOptionalConsent(type: AllConsentNames): boolean {
72
+ * const config = consentTypes.find(c => c.name === type);
73
+ * return config ? !config.disabled && !config.defaultValue : false;
74
+ * }
75
+ * ```
76
+ */
77
+ import { type AllConsentNames, type ConsentType, consentTypes } from './gdpr';
78
+ export { consentTypes };
79
+ export type { AllConsentNames, ConsentType };
80
+ /**
81
+ * @module
82
+ * Callback Types
83
+ *
84
+ * @remarks
85
+ * Exports types for consent management callbacks and event handlers:
86
+ * - Generic callback function type
87
+ * - Consent-specific callback configurations
88
+ *
89
+ * @example
90
+ * Import and use callback types:
91
+ * ```typescript
92
+ * import type {
93
+ * CallbackFunction,
94
+ * Callbacks
95
+ * } from 'c15t/types';
96
+ *
97
+ * const callbacks: Callbacks = {
98
+ * onConsentGiven: () => {
99
+ * console.log('Consent granted');
100
+ * initializeAnalytics();
101
+ * },
102
+ * onError: (error) => {
103
+ * console.error('Consent error:', error);
104
+ * }
105
+ * };
106
+ * ```
107
+ *
108
+ * @example
109
+ * Create typed callback functions:
110
+ * ```typescript
111
+ * const errorHandler: CallbackFunction<string> =
112
+ * (message) => console.error(message);
113
+ *
114
+ * const readyHandler: CallbackFunction =
115
+ * () => console.log('System ready');
116
+ * ```
117
+ */
118
+ import type { CallbackFunction, Callbacks } from './callbacks';
119
+ export type { CallbackFunction, Callbacks };
120
+ /**
121
+ * @module
122
+ * Translation Types
123
+ *
124
+ * @remarks
125
+ * Exports types for translation configuration and translations:
126
+ * - Translation configuration
127
+ * - Translation types
128
+ * - Cookie banner translations
129
+ * - Consent manager dialog translations
130
+ * - Consent manager widget translations
131
+ */
132
+ export type {
133
+ ConsentManagerDialogTranslations,
134
+ ConsentManagerWidgetTranslations,
135
+ ConsentTypeTranslations,
136
+ ConsentTypesTranslations,
137
+ CookieBannerTranslations,
138
+ TranslationConfig,
139
+ Translations,
140
+ } from './translations';
141
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,KAAK,EACX,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,MAAM,cAAc,CAAC;AAEtB,YAAY,EACX,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,cAAc,GACd,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAE9E,OAAO,EAAE,YAAY,EAAE,CAAC;AACxB,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE/D,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,CAAC;AAE5C;;;;;;;;;;;GAWG;AAEH,YAAY,EACX,gCAAgC,EAChC,gCAAgC,EAChC,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,iBAAiB,EACjB,YAAY,GACZ,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import * as __WEBPACK_EXTERNAL_MODULE__gdpr_js_09f2378e__ from './gdpr.js';
2
+ var __webpack_exports__consentTypes =
3
+ __WEBPACK_EXTERNAL_MODULE__gdpr_js_09f2378e__.consentTypes;
4
+ export { __webpack_exports__consentTypes as consentTypes };
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+ var __webpack_require__ = {};
3
+ (() => {
4
+ __webpack_require__.r = function (exports1) {
5
+ if ('undefined' != typeof Symbol && Symbol.toStringTag)
6
+ Object.defineProperty(exports1, Symbol.toStringTag, {
7
+ value: 'Module',
8
+ });
9
+ Object.defineProperty(exports1, '__esModule', {
10
+ value: true,
11
+ });
12
+ };
13
+ })();
14
+ var __webpack_exports__ = {};
15
+ __webpack_require__.r(__webpack_exports__);
16
+ var __webpack_export_target__ = exports;
17
+ for (var __webpack_i__ in __webpack_exports__)
18
+ __webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__];
19
+ if (__webpack_exports__.__esModule)
20
+ Object.defineProperty(__webpack_export_target__, '__esModule', {
21
+ value: true,
22
+ });
@@ -0,0 +1,52 @@
1
+ import type { ConsentType } from './gdpr';
2
+ export interface CookieBannerTranslations {
3
+ title: string;
4
+ description: string;
5
+ acceptAll: string;
6
+ rejectAll: string;
7
+ customize: string;
8
+ }
9
+ export interface ConsentManagerDialogTranslations {
10
+ title: string;
11
+ description: string;
12
+ save: string;
13
+ acceptAll: string;
14
+ rejectAll: string;
15
+ close: string;
16
+ }
17
+ export interface ConsentTypeTranslations {
18
+ title: string;
19
+ description: string;
20
+ }
21
+ export interface ConsentManagerWidgetTranslations {
22
+ title: string;
23
+ description: string;
24
+ save: string;
25
+ acceptAll: string;
26
+ rejectAll: string;
27
+ }
28
+ /**
29
+ * Maps consent type names to their respective translations.
30
+ * Uses the name property from ConsentType to ensure type safety.
31
+ */
32
+ export type ConsentTypesTranslations = {
33
+ [key in ConsentType['name']]: ConsentTypeTranslations;
34
+ };
35
+ export interface CompleteTranslations {
36
+ cookieBanner: CookieBannerTranslations;
37
+ consentManagerDialog: ConsentManagerDialogTranslations;
38
+ consentManagerWidget: ConsentManagerWidgetTranslations;
39
+ consentTypes: ConsentTypesTranslations;
40
+ }
41
+ export interface Translations {
42
+ cookieBanner: Partial<CookieBannerTranslations>;
43
+ consentManagerDialog: Partial<ConsentManagerDialogTranslations>;
44
+ consentManagerWidget: Partial<ConsentManagerWidgetTranslations>;
45
+ consentTypes: Partial<ConsentTypesTranslations>;
46
+ }
47
+ export interface TranslationConfig {
48
+ translations: Record<string, Partial<Translations>>;
49
+ defaultLanguage?: string;
50
+ disableAutoLanguageSwitch?: boolean;
51
+ }
52
+ //# sourceMappingURL=translations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../src/types/translations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,MAAM,WAAW,wBAAwB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gCAAgC;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gCAAgC;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;KACrC,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,GAAG,uBAAuB;CACrD,CAAC;AAGF,MAAM,WAAW,oBAAoB;IACpC,YAAY,EAAE,wBAAwB,CAAC;IACvC,oBAAoB,EAAE,gCAAgC,CAAC;IACvD,oBAAoB,EAAE,gCAAgC,CAAC;IACvD,YAAY,EAAE,wBAAwB,CAAC;CACvC;AAGD,MAAM,WAAW,YAAY;IAC5B,YAAY,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAChD,oBAAoB,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;IAChE,oBAAoB,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;IAChE,YAAY,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAChD;AACD,MAAM,WAAW,iBAAiB;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACpC"}
File without changes
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "c15t",
3
+ "version": "0.0.1-rc.3",
4
+ "type": "module",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "browserslist": [
9
+ ">0.2%",
10
+ "not dead",
11
+ "not op_mini all"
12
+ ],
13
+ "dependencies": {
14
+ "zustand": "^5.0.3"
15
+ },
16
+ "devDependencies": {
17
+ "happy-dom": "^17.1.0",
18
+ "@c15t/typescript-config": "0.0.1-beta.1"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "scripts": {
24
+ "build": "rslib build",
25
+ "check-types": "tsc --noEmit",
26
+ "dev": "rslib build --watch",
27
+ "fmt": "pnpm biome format --write .",
28
+ "lint": "pnpm biome lint ./src",
29
+ "test": "vitest run",
30
+ "test:watch": "vitest",
31
+ "test:coverage": "vitest run --coverage"
32
+ }
33
+ }
@@ -0,0 +1,28 @@
1
+ import { pluginReact } from '@rsbuild/plugin-react';
2
+ import { defineConfig } from '@rslib/core';
3
+
4
+ export default defineConfig({
5
+ source: {
6
+ entry: {
7
+ index: ['./src/**'],
8
+ },
9
+ },
10
+ lib: [
11
+ {
12
+ bundle: false,
13
+ dts: true,
14
+ format: 'esm',
15
+ },
16
+ {
17
+ bundle: false,
18
+ dts: true,
19
+ format: 'cjs',
20
+ },
21
+ ],
22
+ output: {
23
+ target: 'web',
24
+
25
+ cleanDistPath: true,
26
+ },
27
+ plugins: [pluginReact()],
28
+ });
package/src/index.ts ADDED
@@ -0,0 +1,31 @@
1
+ // Re-export store
2
+ export { createConsentManagerStore } from './store';
3
+ export type { PrivacyConsentState } from './store.type';
4
+ // Re-export all utilities
5
+ export * from './libs/consent-utils';
6
+ export { createTrackingBlocker } from './libs/tracking-blocker';
7
+ export type { TrackingBlockerConfig } from './libs/tracking-blocker';
8
+
9
+ // Re-export types and constants
10
+ export { consentTypes } from './types';
11
+ export type {
12
+ CallbackFunction,
13
+ Callbacks,
14
+ AllConsentNames,
15
+ ConsentType,
16
+ ConsentState,
17
+ ComplianceRegion,
18
+ ComplianceSettings,
19
+ PrivacySettings,
20
+ HasConsentedProps,
21
+ NamespaceProps,
22
+ TranslationConfig,
23
+ Translations,
24
+ CookieBannerTranslations,
25
+ ConsentManagerDialogTranslations,
26
+ ConsentManagerWidgetTranslations,
27
+ ConsentTypeTranslations,
28
+ ConsentTypesTranslations,
29
+ } from './types';
30
+
31
+ export { defaultTranslationConfig } from './translations';
@@ -0,0 +1,271 @@
1
+ import {
2
+ afterAll,
3
+ beforeAll,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ test,
8
+ vi,
9
+ } from 'vitest';
10
+ import { createTrackingBlocker } from '../tracking-blocker';
11
+
12
+ const BLOCKED_CONSENT = {
13
+ experience: false,
14
+ functionality: false,
15
+ marketing: false,
16
+ measurement: false,
17
+ necessary: true,
18
+ };
19
+
20
+ const ALLOWED_CONSENT = {
21
+ ...BLOCKED_CONSENT,
22
+ measurement: true,
23
+ };
24
+
25
+ describe('TrackingBlocker', () => {
26
+ // Store original globals
27
+ const originalFetch = global.fetch;
28
+ const originalXHR = global.XMLHttpRequest;
29
+
30
+ // Set up test environment before all tests
31
+ beforeAll(() => {
32
+ // Mock fetch to simulate a successful response
33
+ global.fetch = vi
34
+ .fn()
35
+ .mockImplementation(() => Promise.resolve(new Response()));
36
+
37
+ // We'll use the real XMLHttpRequest instead of mocking it
38
+ if (!global.XMLHttpRequest) {
39
+ global.XMLHttpRequest = originalXHR;
40
+ }
41
+ });
42
+
43
+ // Clean up test environment after all tests
44
+ afterAll(() => {
45
+ // Restore globals
46
+ global.fetch = originalFetch;
47
+ global.XMLHttpRequest = originalXHR;
48
+ });
49
+
50
+ beforeEach(() => {
51
+ // Reset all mocks and spies
52
+ vi.clearAllMocks();
53
+ vi.restoreAllMocks();
54
+
55
+ // Reset fetch to a fresh mock for each test
56
+ global.fetch = vi
57
+ .fn()
58
+ .mockImplementation(() => Promise.resolve(new Response()));
59
+ window.fetch = global.fetch;
60
+
61
+ // Reset XMLHttpRequest to original
62
+ window.XMLHttpRequest = originalXHR;
63
+
64
+ // Mock document.dispatchEvent since it's not included in the global setup
65
+ vi.spyOn(document, 'dispatchEvent');
66
+ });
67
+
68
+ test('blocks fetch requests to tracking domains when consent not granted', async () => {
69
+ // Create tracking blocker with default consents (all false except necessary)
70
+ const trackingBlocker = createTrackingBlocker({}, BLOCKED_CONSENT);
71
+
72
+ // Try to fetch from a tracking domain (Google Analytics)
73
+ await expect(
74
+ fetch('https://www.google-analytics.com/analytics.js')
75
+ ).rejects.toThrow(
76
+ 'Request to https://www.google-analytics.com/analytics.js blocked due to missing consent'
77
+ );
78
+
79
+ // Verify blocked event was dispatched
80
+ expect(document.dispatchEvent).toHaveBeenCalledWith(
81
+ expect.objectContaining({
82
+ type: 'ConsentBlockedRequest',
83
+ detail: {
84
+ url: 'https://www.google-analytics.com/analytics.js',
85
+ },
86
+ })
87
+ );
88
+ });
89
+
90
+ test('blocks XMLHttpRequest to tracking domains when consent not granted', () => {
91
+ // Create tracking blocker with default consents
92
+ const trackingBlocker = createTrackingBlocker({}, BLOCKED_CONSENT);
93
+ // Create new XMLHttpRequest
94
+ const xhr = new XMLHttpRequest();
95
+
96
+ // Try to open request to tracking domain
97
+ expect(() => {
98
+ xhr.open('GET', 'https://www.google-analytics.com/analytics.js');
99
+ }).toThrow(
100
+ 'Request to https://www.google-analytics.com/analytics.js blocked due to missing consent'
101
+ );
102
+
103
+ // Verify blocked event was dispatched
104
+ expect(document.dispatchEvent).toHaveBeenCalledWith(
105
+ expect.objectContaining({
106
+ type: 'ConsentBlockedRequest',
107
+ detail: {
108
+ url: 'https://www.google-analytics.com/analytics.js',
109
+ },
110
+ })
111
+ );
112
+ });
113
+
114
+ test('allows fetch requests to tracking domains after consent granted', async () => {
115
+ // Reset fetch to ensure we start with a clean state
116
+ window.fetch = global.fetch;
117
+
118
+ // Create tracking blocker with consent granted
119
+ const trackingBlocker = createTrackingBlocker({}, ALLOWED_CONSENT);
120
+
121
+ // Create a spy on window.fetch after tracking blocker has overridden it
122
+ const fetchSpy = vi.spyOn(window, 'fetch');
123
+
124
+ // Try fetch request - should now be allowed
125
+ await fetch('https://www.google-analytics.com/analytics.js');
126
+
127
+ // Verify fetch was called with correct URL
128
+ expect(fetchSpy).toHaveBeenCalledWith(
129
+ 'https://www.google-analytics.com/analytics.js'
130
+ );
131
+
132
+ // Clean up
133
+ trackingBlocker.destroy();
134
+ });
135
+
136
+ test('allows XMLHttpRequest to tracking domains after consent granted', () => {
137
+ // Create tracking blocker with default consents
138
+ const trackingBlocker = createTrackingBlocker({}, ALLOWED_CONSENT);
139
+
140
+ // Create new XMLHttpRequest
141
+ const xhr = new XMLHttpRequest();
142
+
143
+ // Should not throw error now that consent is granted
144
+ expect(() => {
145
+ xhr.open('GET', 'https://www.google-analytics.com/analytics.js');
146
+ }).not.toThrow();
147
+ });
148
+
149
+ test('allows requests to non-tracking domains regardless of consent', async () => {
150
+ const trackingBlocker = createTrackingBlocker({}, BLOCKED_CONSENT);
151
+
152
+ // Try fetch to non-tracking domain
153
+ const fetchSpy = vi.spyOn(window, 'fetch');
154
+ await fetch('https://api.example.com/data');
155
+ expect(fetchSpy).toHaveBeenCalledWith('https://api.example.com/data');
156
+
157
+ // Try XMLHttpRequest to non-tracking domain
158
+ const xhr = new XMLHttpRequest();
159
+ expect(() => {
160
+ xhr.open('GET', 'https://api.example.com/data');
161
+ }).not.toThrow();
162
+ });
163
+
164
+ test('handles custom domain consent map', async () => {
165
+ // Create tracking blocker with custom domain map
166
+ const trackingBlocker = createTrackingBlocker(
167
+ {
168
+ overrideDomainConsentMap: true,
169
+ domainConsentMap: {
170
+ 'custom-analytics.example.com': 'measurement',
171
+ },
172
+ },
173
+ BLOCKED_CONSENT
174
+ );
175
+
176
+ // Should block request to custom domain
177
+ await expect(
178
+ fetch('https://custom-analytics.example.com/track')
179
+ ).rejects.toThrow(
180
+ 'Request to https://custom-analytics.example.com/track blocked due to missing consent'
181
+ );
182
+
183
+ // Update consent
184
+ trackingBlocker.updateConsents({
185
+ experience: false,
186
+ functionality: false,
187
+ marketing: false,
188
+ measurement: true,
189
+ necessary: true,
190
+ });
191
+ const fetchSpy = vi.spyOn(window, 'fetch');
192
+
193
+ // Should now allow request
194
+ await fetch('https://custom-analytics.example.com/track');
195
+ expect(fetchSpy).toHaveBeenCalledWith(
196
+ 'https://custom-analytics.example.com/track'
197
+ );
198
+ });
199
+
200
+ test('handles subdomains correctly', async () => {
201
+ const trackingBlocker = createTrackingBlocker({}, BLOCKED_CONSENT);
202
+
203
+ // Should block request to subdomain of google-analytics.com
204
+ await expect(
205
+ fetch('https://subdomain.google-analytics.com/track')
206
+ ).rejects.toThrow(
207
+ 'Request to https://subdomain.google-analytics.com/track blocked due to missing consent'
208
+ );
209
+ });
210
+
211
+ test('can be disabled via config', async () => {
212
+ // Create tracking blocker with automatic blocking disabled
213
+ const trackingBlocker = createTrackingBlocker(
214
+ {
215
+ disableAutomaticBlocking: true,
216
+ },
217
+ BLOCKED_CONSENT
218
+ );
219
+
220
+ // Should allow all requests regardless of consent
221
+ await fetch('https://www.google-analytics.com/analytics.js');
222
+ expect(fetch).toHaveBeenCalledWith(
223
+ 'https://www.google-analytics.com/analytics.js'
224
+ );
225
+ });
226
+
227
+ test('cleanup restores original fetch and XMLHttpRequest functionality', async () => {
228
+ // Create tracking blocker which should wrap the implementations
229
+ const trackingBlocker = createTrackingBlocker({}, BLOCKED_CONSENT);
230
+
231
+ // Verify tracking is active by trying a blocked request
232
+ await expect(
233
+ fetch('https://www.google-analytics.com/analytics.js')
234
+ ).rejects.toThrow();
235
+
236
+ // Destroy tracking blocker
237
+ trackingBlocker.destroy();
238
+
239
+ // After cleanup, the blocked request should work
240
+ await fetch('https://www.google-analytics.com/analytics.js');
241
+ expect(fetch).toHaveBeenCalledWith(
242
+ 'https://www.google-analytics.com/analytics.js'
243
+ );
244
+
245
+ // Verify XMLHttpRequest also works after cleanup
246
+ const xhr = new XMLHttpRequest();
247
+ expect(() => {
248
+ xhr.open('GET', 'https://www.google-analytics.com/analytics.js');
249
+ }).not.toThrow();
250
+ });
251
+
252
+ test('handles multiple concurrent requests correctly', async () => {
253
+ createTrackingBlocker({}, BLOCKED_CONSENT);
254
+
255
+ // Create multiple concurrent requests
256
+ const requests = Promise.all([
257
+ expect(
258
+ fetch('https://www.google-analytics.com/analytics.js')
259
+ ).rejects.toThrow(),
260
+ expect(
261
+ fetch('https://www.google-analytics.com/collect')
262
+ ).rejects.toThrow(),
263
+ fetch('https://api.example.com/data'), // Should succeed
264
+ ]);
265
+ await requests;
266
+
267
+ // Verify correct number of blocked events
268
+ // 6 blocked events for the 3 requests due to retries
269
+ expect(document.dispatchEvent).toHaveBeenCalledTimes(6);
270
+ });
271
+ });