@freshjuice/zest 0.1.0 → 1.0.0

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 (76) hide show
  1. package/README.md +46 -0
  2. package/dist/zest.de.js +104 -1
  3. package/dist/zest.de.js.map +1 -1
  4. package/dist/zest.de.min.js +1 -1
  5. package/dist/zest.en.js +104 -1
  6. package/dist/zest.en.js.map +1 -1
  7. package/dist/zest.en.min.js +1 -1
  8. package/dist/zest.es.js +104 -1
  9. package/dist/zest.es.js.map +1 -1
  10. package/dist/zest.es.min.js +1 -1
  11. package/dist/zest.esm.js +104 -1
  12. package/dist/zest.esm.js.map +1 -1
  13. package/dist/zest.esm.min.js +1 -1
  14. package/dist/zest.fr.js +104 -1
  15. package/dist/zest.fr.js.map +1 -1
  16. package/dist/zest.fr.min.js +1 -1
  17. package/dist/zest.it.js +104 -1
  18. package/dist/zest.it.js.map +1 -1
  19. package/dist/zest.it.min.js +1 -1
  20. package/dist/zest.ja.js +104 -1
  21. package/dist/zest.ja.js.map +1 -1
  22. package/dist/zest.ja.min.js +1 -1
  23. package/dist/zest.js +104 -1
  24. package/dist/zest.js.map +1 -1
  25. package/dist/zest.min.js +1 -1
  26. package/dist/zest.nl.js +104 -1
  27. package/dist/zest.nl.js.map +1 -1
  28. package/dist/zest.nl.min.js +1 -1
  29. package/dist/zest.pl.js +104 -1
  30. package/dist/zest.pl.js.map +1 -1
  31. package/dist/zest.pl.min.js +1 -1
  32. package/dist/zest.pt.js +104 -1
  33. package/dist/zest.pt.js.map +1 -1
  34. package/dist/zest.pt.min.js +1 -1
  35. package/dist/zest.ru.js +104 -1
  36. package/dist/zest.ru.js.map +1 -1
  37. package/dist/zest.ru.min.js +1 -1
  38. package/dist/zest.uk.js +104 -1
  39. package/dist/zest.uk.js.map +1 -1
  40. package/dist/zest.uk.min.js +1 -1
  41. package/dist/zest.zh.js +104 -1
  42. package/dist/zest.zh.js.map +1 -1
  43. package/dist/zest.zh.min.js +1 -1
  44. package/package.json +5 -4
  45. package/src/api/public-api.js +97 -0
  46. package/src/config/defaults.js +150 -0
  47. package/src/config/parser.js +104 -0
  48. package/src/core/categories.js +52 -0
  49. package/src/core/cookie-interceptor.js +116 -0
  50. package/src/core/dnt.js +56 -0
  51. package/src/core/known-trackers.js +168 -0
  52. package/src/core/pattern-matcher.js +96 -0
  53. package/src/core/script-blocker.js +308 -0
  54. package/src/core/storage-interceptor.js +169 -0
  55. package/src/i18n/lang-en.js +54 -0
  56. package/src/i18n/single/lang-de.js +55 -0
  57. package/src/i18n/single/lang-en.js +55 -0
  58. package/src/i18n/single/lang-es.js +55 -0
  59. package/src/i18n/single/lang-fr.js +55 -0
  60. package/src/i18n/single/lang-it.js +55 -0
  61. package/src/i18n/single/lang-ja.js +55 -0
  62. package/src/i18n/single/lang-nl.js +55 -0
  63. package/src/i18n/single/lang-pl.js +55 -0
  64. package/src/i18n/single/lang-pt.js +55 -0
  65. package/src/i18n/single/lang-ru.js +55 -0
  66. package/src/i18n/single/lang-uk.js +55 -0
  67. package/src/i18n/single/lang-zh.js +55 -0
  68. package/src/i18n/translations.js +546 -0
  69. package/src/index.js +377 -0
  70. package/src/integrations/consent-signals.js +71 -0
  71. package/src/storage/consent-store.js +177 -0
  72. package/src/storage/events.js +84 -0
  73. package/src/ui/banner.js +130 -0
  74. package/src/ui/modal.js +211 -0
  75. package/src/ui/styles.js +498 -0
  76. package/src/ui/widget.js +103 -0
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Zest 🍋
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@freshjuice/zest)](https://www.npmjs.com/package/@freshjuice/zest)
4
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
5
+ [![GitHub stars](https://img.shields.io/github/stars/freshjuice-dev/zest)](https://github.com/freshjuice-dev/zest/stargazers)
6
+ [![GitHub forks](https://img.shields.io/github/forks/freshjuice-dev/zest)](https://github.com/freshjuice-dev/zest/network/members)
7
+
3
8
  A lightweight cookie consent toolkit for GDPR/CCPA compliance.
4
9
 
5
10
  - **Lightweight** - ~9KB gzipped (single language) / ~14KB (all 12 languages)
@@ -187,6 +192,47 @@ document.addEventListener('zest:ready', (e) => {
187
192
  });
188
193
  ```
189
194
 
195
+ ## Google Consent Mode v2 / Microsoft UET Consent Mode
196
+
197
+ Zest can optionally push consent signals to Google and Microsoft advertising APIs. Both are **disabled by default**.
198
+
199
+ ### Enable via JavaScript
200
+
201
+ ```javascript
202
+ window.ZestConfig = {
203
+ consentModeGoogle: true, // Google Consent Mode v2
204
+ consentModeMicrosoft: true // Microsoft UET Consent Mode
205
+ };
206
+ ```
207
+
208
+ ### Enable via data attributes
209
+
210
+ ```html
211
+ <script
212
+ src="zest.min.js"
213
+ data-consent-mode-google="true"
214
+ data-consent-mode-microsoft="true"
215
+ ></script>
216
+ ```
217
+
218
+ ### How it works
219
+
220
+ When enabled, Zest automatically:
221
+
222
+ 1. Pushes a `'default'` denied state on page load (before any tracking scripts fire)
223
+ 2. Pushes an `'update'` with the correct consent state whenever the user makes a choice
224
+
225
+ ### Category mapping
226
+
227
+ | Zest Category | Google Consent Mode v2 Signals | Microsoft UET Signal |
228
+ |---|---|---|
229
+ | `essential` | `functionality_storage: 'granted'` (always) | — |
230
+ | `functional` | `personalization_storage` | — |
231
+ | `analytics` | `analytics_storage` | — |
232
+ | `marketing` | `ad_storage`, `ad_user_data`, `ad_personalization` | `ad_storage` |
233
+
234
+ No additional setup is needed — just include your Google Analytics/GTM or Microsoft UET tags as usual and Zest will handle the consent signaling.
235
+
190
236
  ## Localization
191
237
 
192
238
  Zest has **built-in translations** with auto-detection.
package/dist/zest.de.js CHANGED
@@ -842,6 +842,78 @@ var Zest = (function () {
842
842
  return { enabled: false, source: null };
843
843
  }
844
844
 
845
+ /**
846
+ * Consent Signals - Optional vendor consent mode integrations
847
+ *
848
+ * Pushes consent state to Google Consent Mode v2 and/or Microsoft UET
849
+ * Consent Mode when enabled via config.
850
+ */
851
+
852
+ /**
853
+ * Map Zest consent state to Google Consent Mode v2 signals
854
+ */
855
+ function toGoogleSignals(consent) {
856
+ const g = (val) => val ? 'granted' : 'denied';
857
+ return {
858
+ ad_storage: g(consent.marketing),
859
+ ad_user_data: g(consent.marketing),
860
+ ad_personalization: g(consent.marketing),
861
+ analytics_storage: g(consent.analytics),
862
+ functionality_storage: 'granted', // essential is always true
863
+ personalization_storage: g(consent.functional)
864
+ };
865
+ }
866
+
867
+ /**
868
+ * Push consent signal to Google via gtag or dataLayer fallback.
869
+ * Uses a local function to preserve the `arguments` object shape
870
+ * that gtag/dataLayer expects (not an array).
871
+ */
872
+ function pushGoogle(type, signals) {
873
+ window.dataLayer = window.dataLayer || [];
874
+ if (typeof window.gtag === 'function') {
875
+ window.gtag('consent', type, signals);
876
+ } else {
877
+ function gtagFallback() { window.dataLayer.push(arguments); }
878
+ gtagFallback('consent', type, signals);
879
+ }
880
+ }
881
+
882
+ /**
883
+ * Map Zest consent state to Microsoft UET signal.
884
+ * Microsoft UET only exposes ad_storage.
885
+ */
886
+ function toMicrosoftSignals(consent) {
887
+ return { ad_storage: consent.marketing ? 'granted' : 'denied' };
888
+ }
889
+
890
+ /**
891
+ * Push consent signal to Microsoft UET
892
+ */
893
+ function pushMicrosoft(type, signals) {
894
+ window.uetq = window.uetq || [];
895
+ window.uetq.push('consent', type, signals);
896
+ }
897
+
898
+ /**
899
+ * Apply consent signals to enabled vendor integrations.
900
+ *
901
+ * @param {Object} consent Current Zest consent state
902
+ * @param {Object} config Merged Zest config
903
+ * @param {boolean} isDefault true on first call (pushes 'default'), false for updates
904
+ */
905
+ function applyConsentSignals(consent, config, isDefault) {
906
+ const type = isDefault ? 'default' : 'update';
907
+
908
+ if (config.consentModeGoogle) {
909
+ pushGoogle(type, toGoogleSignals(consent));
910
+ }
911
+
912
+ if (config.consentModeMicrosoft) {
913
+ pushMicrosoft(type, toMicrosoftSignals(consent));
914
+ }
915
+ }
916
+
845
917
  /**
846
918
  * DE only translation - auto-generated
847
919
  * Do not edit manually, run: npm run build
@@ -953,6 +1025,10 @@ var Zest = (function () {
953
1025
  // Custom styles to inject into Shadow DOM
954
1026
  customStyles: '',
955
1027
 
1028
+ // Vendor consent mode integrations (optional)
1029
+ consentModeGoogle: false,
1030
+ consentModeMicrosoft: false,
1031
+
956
1032
  // Blocking mode: 'manual' | 'safe' | 'strict' | 'doomsday'
957
1033
  mode: 'safe',
958
1034
 
@@ -983,7 +1059,7 @@ var Zest = (function () {
983
1059
  }
984
1060
 
985
1061
  // Simple properties
986
- const simpleKeys = ['lang', 'position', 'theme', 'accentColor', 'autoInit', 'showWidget', 'expiration', 'policyUrl', 'imprintUrl', 'customStyles', 'mode', 'blockedDomains', 'respectDNT', 'dntBehavior'];
1062
+ const simpleKeys = ['lang', 'position', 'theme', 'accentColor', 'autoInit', 'showWidget', 'expiration', 'policyUrl', 'imprintUrl', 'customStyles', 'mode', 'blockedDomains', 'respectDNT', 'dntBehavior', 'consentModeGoogle', 'consentModeMicrosoft'];
987
1063
  for (const key of simpleKeys) {
988
1064
  if (userConfig[key] !== undefined) {
989
1065
  config[key] = userConfig[key];
@@ -1093,6 +1169,13 @@ var Zest = (function () {
1093
1169
  const expiration = script.getAttribute('data-expiration');
1094
1170
  if (expiration) config.expiration = parseInt(expiration, 10);
1095
1171
 
1172
+ // Consent mode integrations
1173
+ const consentModeGoogle = script.getAttribute('data-consent-mode-google');
1174
+ if (consentModeGoogle !== null) config.consentModeGoogle = consentModeGoogle !== 'false';
1175
+
1176
+ const consentModeMicrosoft = script.getAttribute('data-consent-mode-microsoft');
1177
+ if (consentModeMicrosoft !== null) config.consentModeMicrosoft = consentModeMicrosoft !== 'false';
1178
+
1096
1179
  return config;
1097
1180
  }
1098
1181
 
@@ -2324,6 +2407,8 @@ ${config.customStyles || ''}
2324
2407
  const result = acceptAll(config.expiration);
2325
2408
  const categories = getCategoryIds();
2326
2409
 
2410
+ applyConsentSignals(result.current, config, false);
2411
+
2327
2412
  hideBanner();
2328
2413
  hideModal();
2329
2414
 
@@ -2345,6 +2430,8 @@ ${config.customStyles || ''}
2345
2430
  function handleRejectAll() {
2346
2431
  const result = rejectAll(config.expiration);
2347
2432
 
2433
+ applyConsentSignals(result.current, config, false);
2434
+
2348
2435
  hideBanner();
2349
2436
  hideModal();
2350
2437
 
@@ -2364,6 +2451,8 @@ ${config.customStyles || ''}
2364
2451
  function handleSavePreferences(selections) {
2365
2452
  const result = updateConsent(selections, config.expiration);
2366
2453
 
2454
+ applyConsentSignals(result.current, config, false);
2455
+
2367
2456
  // Find newly allowed categories
2368
2457
  const newlyAllowed = Object.keys(result.current).filter(
2369
2458
  cat => result.current[cat] && !result.previous[cat]
@@ -2442,6 +2531,13 @@ ${config.customStyles || ''}
2442
2531
  // Merge config
2443
2532
  config = setConfig(userConfig);
2444
2533
 
2534
+ // Push default denied state to vendor consent mode APIs (must happen before scripts load)
2535
+ applyConsentSignals(
2536
+ { functional: false, analytics: false, marketing: false },
2537
+ config,
2538
+ true
2539
+ );
2540
+
2445
2541
  // Set patterns if provided
2446
2542
  if (config.patterns) {
2447
2543
  setPatterns(config.patterns);
@@ -2462,6 +2558,11 @@ ${config.customStyles || ''}
2462
2558
 
2463
2559
  initialized = true;
2464
2560
 
2561
+ // Push update for returning visitors with saved consent
2562
+ if (hasConsentDecision()) {
2563
+ applyConsentSignals(consent, config, false);
2564
+ }
2565
+
2465
2566
  // Check Do Not Track / Global Privacy Control
2466
2567
  const dntEnabled = isDoNotTrackEnabled();
2467
2568
  let dntApplied = false;
@@ -2472,6 +2573,8 @@ ${config.customStyles || ''}
2472
2573
  const result = rejectAll(config.expiration);
2473
2574
  dntApplied = true;
2474
2575
 
2576
+ applyConsentSignals(result.current, config, false);
2577
+
2475
2578
  // Emit events
2476
2579
  emitReject(result.current);
2477
2580
  emitChange(result.current, result.previous);