@blotoutio/providers-google-analytics-4-sdk 0.72.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.
- package/index.cjs.js +72 -10
- package/index.js +72 -10
- package/index.mjs +72 -10
- package/package.json +1 -1
package/index.cjs.js
CHANGED
|
@@ -3,6 +3,40 @@
|
|
|
3
3
|
const packageName = 'googleAnalytics4';
|
|
4
4
|
const tagManagerUrl = 'https://www.googletagmanager.com/gtag/js';
|
|
5
5
|
|
|
6
|
+
const isBool = (v) => typeof v == 'boolean';
|
|
7
|
+
const isRecord = (v) => !!v && typeof v == 'object' && !Array.isArray(v);
|
|
8
|
+
/**
|
|
9
|
+
* This function validates user consent for a given provider type, not based on tagName.
|
|
10
|
+
*/
|
|
11
|
+
const hasUserConsentForProvider = (consent, provider) => {
|
|
12
|
+
if (!isRecord(consent)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
let allowed = isBool(consent.all) ? consent.all : false;
|
|
16
|
+
if (provider in consent) {
|
|
17
|
+
const providerSpecific = consent[provider];
|
|
18
|
+
if (isBool(providerSpecific)) {
|
|
19
|
+
allowed = providerSpecific;
|
|
20
|
+
}
|
|
21
|
+
else if (isRecord(providerSpecific)) {
|
|
22
|
+
return Object.keys(providerSpecific).some((instance) => providerSpecific[instance] === true);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return allowed;
|
|
26
|
+
};
|
|
27
|
+
const isCategoryConsented = (consentCategories, category) => {
|
|
28
|
+
if (!consentCategories)
|
|
29
|
+
return false;
|
|
30
|
+
let allowed = isBool(consentCategories.all) ? consentCategories.all : false;
|
|
31
|
+
if (category in consentCategories) {
|
|
32
|
+
const categorySpecific = consentCategories[category];
|
|
33
|
+
if (isBool(categorySpecific)) {
|
|
34
|
+
allowed = categorySpecific;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return allowed;
|
|
38
|
+
};
|
|
39
|
+
|
|
6
40
|
const upsert = (map, key, update, createDefault) => {
|
|
7
41
|
const currentValue = map.has(key)
|
|
8
42
|
? map.get(key)
|
|
@@ -304,7 +338,30 @@ const isoCountries = new Map([
|
|
|
304
338
|
new Set(isoCountries.keys());
|
|
305
339
|
|
|
306
340
|
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
307
|
-
const
|
|
341
|
+
const getGoogleConsentFromCategories = (categories) => {
|
|
342
|
+
const advertisingConsent = isCategoryConsented(categories, 'advertising');
|
|
343
|
+
const analyticsConsent = isCategoryConsented(categories, 'analytics');
|
|
344
|
+
return {
|
|
345
|
+
analytics_storage: analyticsConsent ? 'granted' : 'denied',
|
|
346
|
+
ad_storage: advertisingConsent ? 'granted' : 'denied',
|
|
347
|
+
ad_user_data: advertisingConsent ? 'granted' : 'denied',
|
|
348
|
+
ad_personalization: advertisingConsent ? 'granted' : 'denied',
|
|
349
|
+
};
|
|
350
|
+
};
|
|
351
|
+
// TODO: once categories becomes a stable field, we need to remove this and start using categories data for google consent mode.
|
|
352
|
+
const getGoogleConsentFromChannels = (consent) => {
|
|
353
|
+
const gadsConsent = hasUserConsentForProvider(consent, 'googleAdsClicks');
|
|
354
|
+
const ga4Consent = hasUserConsentForProvider(consent, 'googleAnalytics4');
|
|
355
|
+
return {
|
|
356
|
+
analytics_storage: ga4Consent ? 'granted' : 'denied',
|
|
357
|
+
ad_storage: gadsConsent ? 'granted' : 'denied',
|
|
358
|
+
ad_user_data: gadsConsent ? 'granted' : 'denied',
|
|
359
|
+
ad_personalization: gadsConsent ? 'granted' : 'denied',
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
364
|
+
const initGA4 = (ID, advancedConsentMode, consentData, executionContext) => {
|
|
308
365
|
var _a;
|
|
309
366
|
window.dataLayer = window.dataLayer || [];
|
|
310
367
|
window.gtag = function gtag() {
|
|
@@ -318,14 +375,15 @@ const initGA4 = (ID, executionContext) => {
|
|
|
318
375
|
break;
|
|
319
376
|
}
|
|
320
377
|
}
|
|
378
|
+
const data = advancedConsentMode === '1'
|
|
379
|
+
? getGoogleConsentFromCategories(consentData.categories)
|
|
380
|
+
: getGoogleConsentFromChannels(consentData.consent);
|
|
321
381
|
if (isConsentInitialised) {
|
|
322
|
-
window.gtag('consent', 'update',
|
|
323
|
-
analytics_storage: 'granted',
|
|
324
|
-
});
|
|
382
|
+
window.gtag('consent', 'update', data);
|
|
325
383
|
}
|
|
326
384
|
else {
|
|
327
385
|
window.gtag('consent', 'default', {
|
|
328
|
-
|
|
386
|
+
...data,
|
|
329
387
|
functional_storage: 'granted',
|
|
330
388
|
personalization_storage: 'granted',
|
|
331
389
|
security_storage: 'granted',
|
|
@@ -343,7 +401,7 @@ const initGA4 = (ID, executionContext) => {
|
|
|
343
401
|
script.parentNode.insertBefore(element, script);
|
|
344
402
|
}
|
|
345
403
|
};
|
|
346
|
-
const init = ({ manifest, userId, executionContext }) => {
|
|
404
|
+
const init = ({ manifest, userId, executionContext, consentData, }) => {
|
|
347
405
|
if (!window ||
|
|
348
406
|
!manifest.variables ||
|
|
349
407
|
!manifest.variables['measurementId'] ||
|
|
@@ -352,7 +410,7 @@ const init = ({ manifest, userId, executionContext }) => {
|
|
|
352
410
|
}
|
|
353
411
|
if (!window.google_tag_manager ||
|
|
354
412
|
!window.google_tag_manager[manifest.variables['measurementId']]) {
|
|
355
|
-
initGA4(manifest.variables['measurementId'], executionContext);
|
|
413
|
+
initGA4(manifest.variables['measurementId'], manifest.variables['advancedConsentMode'], consentData, executionContext);
|
|
356
414
|
}
|
|
357
415
|
if (window.gtag) {
|
|
358
416
|
window.gtag('config', manifest.variables['measurementId'], {
|
|
@@ -613,16 +671,20 @@ const tag = ({ data, eventName, manifestVariables, eventId }) => {
|
|
|
613
671
|
}
|
|
614
672
|
return {
|
|
615
673
|
loaded: isLoaded,
|
|
616
|
-
sdkVersion: "0.
|
|
674
|
+
sdkVersion: "1.0.0" ,
|
|
617
675
|
};
|
|
618
676
|
};
|
|
619
677
|
|
|
620
|
-
const consent = ({
|
|
678
|
+
const consent = ({ consentData, variables }) => {
|
|
621
679
|
if (!(window === null || window === void 0 ? void 0 : window.gtag)) {
|
|
622
680
|
return;
|
|
623
681
|
}
|
|
682
|
+
const isAdvancedConsentModeEnabled = variables.some((variable) => { var _a; return ((_a = variable.variableSet) === null || _a === void 0 ? void 0 : _a['advancedConsentMode']) === '1'; });
|
|
683
|
+
const data = isAdvancedConsentModeEnabled
|
|
684
|
+
? getGoogleConsentFromCategories(consentData.categories)
|
|
685
|
+
: getGoogleConsentFromChannels(consentData.consent);
|
|
624
686
|
window.gtag('consent', 'update', {
|
|
625
|
-
|
|
687
|
+
...data,
|
|
626
688
|
functional_storage: 'granted',
|
|
627
689
|
personalization_storage: 'granted',
|
|
628
690
|
security_storage: 'granted',
|
package/index.js
CHANGED
|
@@ -4,6 +4,40 @@ var ProvidersGoogleAnalytics4Sdk = (function () {
|
|
|
4
4
|
const packageName = 'googleAnalytics4';
|
|
5
5
|
const tagManagerUrl = 'https://www.googletagmanager.com/gtag/js';
|
|
6
6
|
|
|
7
|
+
const isBool = (v) => typeof v == 'boolean';
|
|
8
|
+
const isRecord = (v) => !!v && typeof v == 'object' && !Array.isArray(v);
|
|
9
|
+
/**
|
|
10
|
+
* This function validates user consent for a given provider type, not based on tagName.
|
|
11
|
+
*/
|
|
12
|
+
const hasUserConsentForProvider = (consent, provider) => {
|
|
13
|
+
if (!isRecord(consent)) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
let allowed = isBool(consent.all) ? consent.all : false;
|
|
17
|
+
if (provider in consent) {
|
|
18
|
+
const providerSpecific = consent[provider];
|
|
19
|
+
if (isBool(providerSpecific)) {
|
|
20
|
+
allowed = providerSpecific;
|
|
21
|
+
}
|
|
22
|
+
else if (isRecord(providerSpecific)) {
|
|
23
|
+
return Object.keys(providerSpecific).some((instance) => providerSpecific[instance] === true);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return allowed;
|
|
27
|
+
};
|
|
28
|
+
const isCategoryConsented = (consentCategories, category) => {
|
|
29
|
+
if (!consentCategories)
|
|
30
|
+
return false;
|
|
31
|
+
let allowed = isBool(consentCategories.all) ? consentCategories.all : false;
|
|
32
|
+
if (category in consentCategories) {
|
|
33
|
+
const categorySpecific = consentCategories[category];
|
|
34
|
+
if (isBool(categorySpecific)) {
|
|
35
|
+
allowed = categorySpecific;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return allowed;
|
|
39
|
+
};
|
|
40
|
+
|
|
7
41
|
const upsert = (map, key, update, createDefault) => {
|
|
8
42
|
const currentValue = map.has(key)
|
|
9
43
|
? map.get(key)
|
|
@@ -305,7 +339,30 @@ var ProvidersGoogleAnalytics4Sdk = (function () {
|
|
|
305
339
|
new Set(isoCountries.keys());
|
|
306
340
|
|
|
307
341
|
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
308
|
-
const
|
|
342
|
+
const getGoogleConsentFromCategories = (categories) => {
|
|
343
|
+
const advertisingConsent = isCategoryConsented(categories, 'advertising');
|
|
344
|
+
const analyticsConsent = isCategoryConsented(categories, 'analytics');
|
|
345
|
+
return {
|
|
346
|
+
analytics_storage: analyticsConsent ? 'granted' : 'denied',
|
|
347
|
+
ad_storage: advertisingConsent ? 'granted' : 'denied',
|
|
348
|
+
ad_user_data: advertisingConsent ? 'granted' : 'denied',
|
|
349
|
+
ad_personalization: advertisingConsent ? 'granted' : 'denied',
|
|
350
|
+
};
|
|
351
|
+
};
|
|
352
|
+
// TODO: once categories becomes a stable field, we need to remove this and start using categories data for google consent mode.
|
|
353
|
+
const getGoogleConsentFromChannels = (consent) => {
|
|
354
|
+
const gadsConsent = hasUserConsentForProvider(consent, 'googleAdsClicks');
|
|
355
|
+
const ga4Consent = hasUserConsentForProvider(consent, 'googleAnalytics4');
|
|
356
|
+
return {
|
|
357
|
+
analytics_storage: ga4Consent ? 'granted' : 'denied',
|
|
358
|
+
ad_storage: gadsConsent ? 'granted' : 'denied',
|
|
359
|
+
ad_user_data: gadsConsent ? 'granted' : 'denied',
|
|
360
|
+
ad_personalization: gadsConsent ? 'granted' : 'denied',
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
365
|
+
const initGA4 = (ID, advancedConsentMode, consentData, executionContext) => {
|
|
309
366
|
var _a;
|
|
310
367
|
window.dataLayer = window.dataLayer || [];
|
|
311
368
|
window.gtag = function gtag() {
|
|
@@ -319,14 +376,15 @@ var ProvidersGoogleAnalytics4Sdk = (function () {
|
|
|
319
376
|
break;
|
|
320
377
|
}
|
|
321
378
|
}
|
|
379
|
+
const data = advancedConsentMode === '1'
|
|
380
|
+
? getGoogleConsentFromCategories(consentData.categories)
|
|
381
|
+
: getGoogleConsentFromChannels(consentData.consent);
|
|
322
382
|
if (isConsentInitialised) {
|
|
323
|
-
window.gtag('consent', 'update',
|
|
324
|
-
analytics_storage: 'granted',
|
|
325
|
-
});
|
|
383
|
+
window.gtag('consent', 'update', data);
|
|
326
384
|
}
|
|
327
385
|
else {
|
|
328
386
|
window.gtag('consent', 'default', {
|
|
329
|
-
|
|
387
|
+
...data,
|
|
330
388
|
functional_storage: 'granted',
|
|
331
389
|
personalization_storage: 'granted',
|
|
332
390
|
security_storage: 'granted',
|
|
@@ -344,7 +402,7 @@ var ProvidersGoogleAnalytics4Sdk = (function () {
|
|
|
344
402
|
script.parentNode.insertBefore(element, script);
|
|
345
403
|
}
|
|
346
404
|
};
|
|
347
|
-
const init = ({ manifest, userId, executionContext }) => {
|
|
405
|
+
const init = ({ manifest, userId, executionContext, consentData, }) => {
|
|
348
406
|
if (!window ||
|
|
349
407
|
!manifest.variables ||
|
|
350
408
|
!manifest.variables['measurementId'] ||
|
|
@@ -353,7 +411,7 @@ var ProvidersGoogleAnalytics4Sdk = (function () {
|
|
|
353
411
|
}
|
|
354
412
|
if (!window.google_tag_manager ||
|
|
355
413
|
!window.google_tag_manager[manifest.variables['measurementId']]) {
|
|
356
|
-
initGA4(manifest.variables['measurementId'], executionContext);
|
|
414
|
+
initGA4(manifest.variables['measurementId'], manifest.variables['advancedConsentMode'], consentData, executionContext);
|
|
357
415
|
}
|
|
358
416
|
if (window.gtag) {
|
|
359
417
|
window.gtag('config', manifest.variables['measurementId'], {
|
|
@@ -614,16 +672,20 @@ var ProvidersGoogleAnalytics4Sdk = (function () {
|
|
|
614
672
|
}
|
|
615
673
|
return {
|
|
616
674
|
loaded: isLoaded,
|
|
617
|
-
sdkVersion: "0.
|
|
675
|
+
sdkVersion: "1.0.0" ,
|
|
618
676
|
};
|
|
619
677
|
};
|
|
620
678
|
|
|
621
|
-
const consent = ({
|
|
679
|
+
const consent = ({ consentData, variables }) => {
|
|
622
680
|
if (!(window === null || window === void 0 ? void 0 : window.gtag)) {
|
|
623
681
|
return;
|
|
624
682
|
}
|
|
683
|
+
const isAdvancedConsentModeEnabled = variables.some((variable) => { var _a; return ((_a = variable.variableSet) === null || _a === void 0 ? void 0 : _a['advancedConsentMode']) === '1'; });
|
|
684
|
+
const data = isAdvancedConsentModeEnabled
|
|
685
|
+
? getGoogleConsentFromCategories(consentData.categories)
|
|
686
|
+
: getGoogleConsentFromChannels(consentData.consent);
|
|
625
687
|
window.gtag('consent', 'update', {
|
|
626
|
-
|
|
688
|
+
...data,
|
|
627
689
|
functional_storage: 'granted',
|
|
628
690
|
personalization_storage: 'granted',
|
|
629
691
|
security_storage: 'granted',
|
package/index.mjs
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
const packageName = 'googleAnalytics4';
|
|
2
2
|
const tagManagerUrl = 'https://www.googletagmanager.com/gtag/js';
|
|
3
3
|
|
|
4
|
+
const isBool = (v) => typeof v == 'boolean';
|
|
5
|
+
const isRecord = (v) => !!v && typeof v == 'object' && !Array.isArray(v);
|
|
6
|
+
/**
|
|
7
|
+
* This function validates user consent for a given provider type, not based on tagName.
|
|
8
|
+
*/
|
|
9
|
+
const hasUserConsentForProvider = (consent, provider) => {
|
|
10
|
+
if (!isRecord(consent)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
let allowed = isBool(consent.all) ? consent.all : false;
|
|
14
|
+
if (provider in consent) {
|
|
15
|
+
const providerSpecific = consent[provider];
|
|
16
|
+
if (isBool(providerSpecific)) {
|
|
17
|
+
allowed = providerSpecific;
|
|
18
|
+
}
|
|
19
|
+
else if (isRecord(providerSpecific)) {
|
|
20
|
+
return Object.keys(providerSpecific).some((instance) => providerSpecific[instance] === true);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return allowed;
|
|
24
|
+
};
|
|
25
|
+
const isCategoryConsented = (consentCategories, category) => {
|
|
26
|
+
if (!consentCategories)
|
|
27
|
+
return false;
|
|
28
|
+
let allowed = isBool(consentCategories.all) ? consentCategories.all : false;
|
|
29
|
+
if (category in consentCategories) {
|
|
30
|
+
const categorySpecific = consentCategories[category];
|
|
31
|
+
if (isBool(categorySpecific)) {
|
|
32
|
+
allowed = categorySpecific;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return allowed;
|
|
36
|
+
};
|
|
37
|
+
|
|
4
38
|
const upsert = (map, key, update, createDefault) => {
|
|
5
39
|
const currentValue = map.has(key)
|
|
6
40
|
? map.get(key)
|
|
@@ -302,7 +336,30 @@ const isoCountries = new Map([
|
|
|
302
336
|
new Set(isoCountries.keys());
|
|
303
337
|
|
|
304
338
|
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
305
|
-
const
|
|
339
|
+
const getGoogleConsentFromCategories = (categories) => {
|
|
340
|
+
const advertisingConsent = isCategoryConsented(categories, 'advertising');
|
|
341
|
+
const analyticsConsent = isCategoryConsented(categories, 'analytics');
|
|
342
|
+
return {
|
|
343
|
+
analytics_storage: analyticsConsent ? 'granted' : 'denied',
|
|
344
|
+
ad_storage: advertisingConsent ? 'granted' : 'denied',
|
|
345
|
+
ad_user_data: advertisingConsent ? 'granted' : 'denied',
|
|
346
|
+
ad_personalization: advertisingConsent ? 'granted' : 'denied',
|
|
347
|
+
};
|
|
348
|
+
};
|
|
349
|
+
// TODO: once categories becomes a stable field, we need to remove this and start using categories data for google consent mode.
|
|
350
|
+
const getGoogleConsentFromChannels = (consent) => {
|
|
351
|
+
const gadsConsent = hasUserConsentForProvider(consent, 'googleAdsClicks');
|
|
352
|
+
const ga4Consent = hasUserConsentForProvider(consent, 'googleAnalytics4');
|
|
353
|
+
return {
|
|
354
|
+
analytics_storage: ga4Consent ? 'granted' : 'denied',
|
|
355
|
+
ad_storage: gadsConsent ? 'granted' : 'denied',
|
|
356
|
+
ad_user_data: gadsConsent ? 'granted' : 'denied',
|
|
357
|
+
ad_personalization: gadsConsent ? 'granted' : 'denied',
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
362
|
+
const initGA4 = (ID, advancedConsentMode, consentData, executionContext) => {
|
|
306
363
|
var _a;
|
|
307
364
|
window.dataLayer = window.dataLayer || [];
|
|
308
365
|
window.gtag = function gtag() {
|
|
@@ -316,14 +373,15 @@ const initGA4 = (ID, executionContext) => {
|
|
|
316
373
|
break;
|
|
317
374
|
}
|
|
318
375
|
}
|
|
376
|
+
const data = advancedConsentMode === '1'
|
|
377
|
+
? getGoogleConsentFromCategories(consentData.categories)
|
|
378
|
+
: getGoogleConsentFromChannels(consentData.consent);
|
|
319
379
|
if (isConsentInitialised) {
|
|
320
|
-
window.gtag('consent', 'update',
|
|
321
|
-
analytics_storage: 'granted',
|
|
322
|
-
});
|
|
380
|
+
window.gtag('consent', 'update', data);
|
|
323
381
|
}
|
|
324
382
|
else {
|
|
325
383
|
window.gtag('consent', 'default', {
|
|
326
|
-
|
|
384
|
+
...data,
|
|
327
385
|
functional_storage: 'granted',
|
|
328
386
|
personalization_storage: 'granted',
|
|
329
387
|
security_storage: 'granted',
|
|
@@ -341,7 +399,7 @@ const initGA4 = (ID, executionContext) => {
|
|
|
341
399
|
script.parentNode.insertBefore(element, script);
|
|
342
400
|
}
|
|
343
401
|
};
|
|
344
|
-
const init = ({ manifest, userId, executionContext }) => {
|
|
402
|
+
const init = ({ manifest, userId, executionContext, consentData, }) => {
|
|
345
403
|
if (!window ||
|
|
346
404
|
!manifest.variables ||
|
|
347
405
|
!manifest.variables['measurementId'] ||
|
|
@@ -350,7 +408,7 @@ const init = ({ manifest, userId, executionContext }) => {
|
|
|
350
408
|
}
|
|
351
409
|
if (!window.google_tag_manager ||
|
|
352
410
|
!window.google_tag_manager[manifest.variables['measurementId']]) {
|
|
353
|
-
initGA4(manifest.variables['measurementId'], executionContext);
|
|
411
|
+
initGA4(manifest.variables['measurementId'], manifest.variables['advancedConsentMode'], consentData, executionContext);
|
|
354
412
|
}
|
|
355
413
|
if (window.gtag) {
|
|
356
414
|
window.gtag('config', manifest.variables['measurementId'], {
|
|
@@ -611,16 +669,20 @@ const tag = ({ data, eventName, manifestVariables, eventId }) => {
|
|
|
611
669
|
}
|
|
612
670
|
return {
|
|
613
671
|
loaded: isLoaded,
|
|
614
|
-
sdkVersion: "0.
|
|
672
|
+
sdkVersion: "1.0.0" ,
|
|
615
673
|
};
|
|
616
674
|
};
|
|
617
675
|
|
|
618
|
-
const consent = ({
|
|
676
|
+
const consent = ({ consentData, variables }) => {
|
|
619
677
|
if (!(window === null || window === void 0 ? void 0 : window.gtag)) {
|
|
620
678
|
return;
|
|
621
679
|
}
|
|
680
|
+
const isAdvancedConsentModeEnabled = variables.some((variable) => { var _a; return ((_a = variable.variableSet) === null || _a === void 0 ? void 0 : _a['advancedConsentMode']) === '1'; });
|
|
681
|
+
const data = isAdvancedConsentModeEnabled
|
|
682
|
+
? getGoogleConsentFromCategories(consentData.categories)
|
|
683
|
+
: getGoogleConsentFromChannels(consentData.consent);
|
|
622
684
|
window.gtag('consent', 'update', {
|
|
623
|
-
|
|
685
|
+
...data,
|
|
624
686
|
functional_storage: 'granted',
|
|
625
687
|
personalization_storage: 'granted',
|
|
626
688
|
security_storage: 'granted',
|