@getuserfeedback/adapters 0.4.5 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -43
- package/dist/gtm/core.d.ts +1 -29
- package/dist/gtm/core.d.ts.map +1 -1
- package/dist/gtm/core.js +17 -71
- package/dist/gtm/index.d.ts +2 -2
- package/dist/gtm/index.d.ts.map +1 -1
- package/dist/gtm/index.js +2 -2
- package/dist/gtm/runtime-model.d.ts +28 -0
- package/dist/gtm/runtime-model.d.ts.map +1 -0
- package/dist/gtm/runtime-model.js +216 -0
- package/dist/gtm/template-generation.d.ts +22 -0
- package/dist/gtm/template-generation.d.ts.map +1 -1
- package/dist/gtm/template-generation.js +245 -366
- package/package.json +5 -2
|
@@ -1,31 +1,37 @@
|
|
|
1
|
-
import { DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS
|
|
2
|
-
import {
|
|
1
|
+
import { DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS } from "@getuserfeedback/protocol";
|
|
2
|
+
import { GTM_CONSENT_TYPES } from "./core.js";
|
|
3
|
+
import { BUILD_GTM_IDENTIFY_COMMAND_SOURCE, GTM_QUEUE_CLIENT_META, RESOLVE_GTM_THEME_INPUT_FROM_TEMPLATE_DATA_SOURCE, } from "./runtime-model.js";
|
|
3
4
|
export const DEFAULT_GTM_LOADER_SCRIPT_BASE_URL = "https://cdn.getuserfeedback.com/widget/loader/v1";
|
|
4
5
|
const GTM_TEMPLATE_VERSION = 1;
|
|
5
6
|
const GTM_TEMPLATE_DEFAULT_DISPLAY_NAME = "getuserfeedback Widget";
|
|
6
|
-
const GTM_TEMPLATE_DEFAULT_DESCRIPTION = "Load the getuserfeedback widget with Google Tag Manager, including
|
|
7
|
+
const GTM_TEMPLATE_DEFAULT_DESCRIPTION = "Load the getuserfeedback widget with Google Tag Manager, including color scheme settings and user identity.";
|
|
8
|
+
const GTM_TEMPLATE_DEFAULT_ID = "cvt_temp_public_id";
|
|
9
|
+
const GTM_TEMPLATE_GALLERY_DISPLAY_NAME = "getuserfeedback.com";
|
|
10
|
+
const GTM_TEMPLATE_GALLERY_DESCRIPTION = "In-app user onboarding, surveys, and embedded forms for feedback-fueled product growth";
|
|
11
|
+
const GTM_TEMPLATE_DEFAULT_BRAND_DISPLAY_NAME = "getuserfeedback.com";
|
|
12
|
+
const GTM_TEMPLATE_DEFAULT_BRAND_THUMBNAIL = "data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=";
|
|
13
|
+
const GTM_TEMPLATE_GALLERY_CATEGORIES = [
|
|
14
|
+
"CONVERSIONS",
|
|
15
|
+
"ANALYTICS",
|
|
16
|
+
"SURVEY",
|
|
17
|
+
];
|
|
7
18
|
const GTM_TEMPLATE_CATEGORIES = [
|
|
8
19
|
"SURVEY",
|
|
9
20
|
"PERSONALIZATION",
|
|
10
21
|
"UTILITY",
|
|
11
22
|
];
|
|
12
23
|
const GTM_TEMPLATE_CONTAINER_CONTEXTS = ["WEB"];
|
|
24
|
+
const GTM_TEMPLATE_GALLERY_TERMS_OF_SERVICE = [
|
|
25
|
+
"By creating or modifying this file you agree to Google Tag Manager's Community",
|
|
26
|
+
"Template Gallery Developer Terms of Service available at",
|
|
27
|
+
"https://developers.google.com/tag-manager/gallery-tos (or such other URL as",
|
|
28
|
+
"Google may provide), as modified from time to time.",
|
|
29
|
+
].join("\n");
|
|
30
|
+
const GTM_TEMPLATE_DEFAULT_NOTES = "Generated from @getuserfeedback/adapters/gtm.";
|
|
31
|
+
const GTM_TEMPLATE_DEFAULT_TESTS = "scenarios: []";
|
|
32
|
+
const GTM_TEMPLATE_UTF8_BOM = "\uFEFF";
|
|
13
33
|
const GTM_QUEUE_KEY = "__getuserfeedback_queue";
|
|
14
34
|
const NON_EMPTY_VALIDATOR = { type: "NON_EMPTY" };
|
|
15
|
-
const MANUAL_CONSENT_MODE_CONDITION = [
|
|
16
|
-
{
|
|
17
|
-
paramName: "consentMode",
|
|
18
|
-
paramValue: "manual",
|
|
19
|
-
type: "EQUALS",
|
|
20
|
-
},
|
|
21
|
-
];
|
|
22
|
-
const AUTOMATIC_CONSENT_MODE_CONDITION = [
|
|
23
|
-
{
|
|
24
|
-
paramName: "consentMode",
|
|
25
|
-
paramValue: "automatic",
|
|
26
|
-
type: "EQUALS",
|
|
27
|
-
},
|
|
28
|
-
];
|
|
29
35
|
const IDENTIFY_ADVANCED_MODE_CONDITION = [
|
|
30
36
|
{
|
|
31
37
|
paramName: "identifyMode",
|
|
@@ -49,6 +55,42 @@ const INTELLIGENT_IDENTIFY_EMAIL_KEYS = [
|
|
|
49
55
|
"context.email",
|
|
50
56
|
"context.user.email",
|
|
51
57
|
];
|
|
58
|
+
const INTELLIGENT_IDENTIFY_FIRST_NAME_KEYS = [
|
|
59
|
+
"firstName",
|
|
60
|
+
"first_name",
|
|
61
|
+
"user.firstName",
|
|
62
|
+
"user.first_name",
|
|
63
|
+
"user.givenName",
|
|
64
|
+
"user.given_name",
|
|
65
|
+
"traits.firstName",
|
|
66
|
+
"traits.first_name",
|
|
67
|
+
"traits.givenName",
|
|
68
|
+
"traits.given_name",
|
|
69
|
+
"context.firstName",
|
|
70
|
+
"context.first_name",
|
|
71
|
+
"context.user.firstName",
|
|
72
|
+
"context.user.first_name",
|
|
73
|
+
"context.user.givenName",
|
|
74
|
+
"context.user.given_name",
|
|
75
|
+
];
|
|
76
|
+
const INTELLIGENT_IDENTIFY_LAST_NAME_KEYS = [
|
|
77
|
+
"lastName",
|
|
78
|
+
"last_name",
|
|
79
|
+
"user.lastName",
|
|
80
|
+
"user.last_name",
|
|
81
|
+
"user.familyName",
|
|
82
|
+
"user.family_name",
|
|
83
|
+
"traits.lastName",
|
|
84
|
+
"traits.last_name",
|
|
85
|
+
"traits.familyName",
|
|
86
|
+
"traits.family_name",
|
|
87
|
+
"context.lastName",
|
|
88
|
+
"context.last_name",
|
|
89
|
+
"context.user.lastName",
|
|
90
|
+
"context.user.last_name",
|
|
91
|
+
"context.user.familyName",
|
|
92
|
+
"context.user.family_name",
|
|
93
|
+
];
|
|
52
94
|
const INTELLIGENT_IDENTIFY_PHONE_KEYS = [
|
|
53
95
|
"phone",
|
|
54
96
|
"user.phone",
|
|
@@ -70,31 +112,6 @@ const ADVANCED_PRIMARY_IDENTITY_SELECT_ITEMS = [
|
|
|
70
112
|
value: "email",
|
|
71
113
|
},
|
|
72
114
|
];
|
|
73
|
-
const buildAutomaticDefaultScopeMappings = () => DEFAULT_GTM_SCOPE_MAPPINGS.filter((mapping) => isHostConfigurableGtmScope(mapping.scope)).map((mapping) => ({
|
|
74
|
-
scope: mapping.scope,
|
|
75
|
-
mapping: mapping.consentType,
|
|
76
|
-
}));
|
|
77
|
-
const buildHostConfigurableDefaultScopeMappings = () => [
|
|
78
|
-
...buildAutomaticDefaultScopeMappings(),
|
|
79
|
-
{
|
|
80
|
-
scope: "analytics.measurement",
|
|
81
|
-
mapping: "analytics_storage",
|
|
82
|
-
},
|
|
83
|
-
];
|
|
84
|
-
const buildScopeParameterHelp = () => [
|
|
85
|
-
"Some required widget storage settings are always enabled and do not appear here.",
|
|
86
|
-
`${ESSENTIAL_GTM_SCOPES[0]} and ${ESSENTIAL_GTM_SCOPES[1]} are always enabled so the widget can load and keep itself secure.`,
|
|
87
|
-
"",
|
|
88
|
-
...GTM_TEMPLATE_SCOPE_OPTIONS.filter((option) => isHostConfigurableGtmScope(option.scope)).map((option) => `${option.label}: ${option.description}`),
|
|
89
|
-
].join("\n");
|
|
90
|
-
const buildHostConfigurableScopeSelectItems = () => GTM_TEMPLATE_SCOPE_OPTIONS.filter((option) => isHostConfigurableGtmScope(option.scope)).map((option) => ({
|
|
91
|
-
value: option.scope,
|
|
92
|
-
displayValue: option.label,
|
|
93
|
-
}));
|
|
94
|
-
const buildGtmConsentTypeSelectItems = () => GTM_TEMPLATE_MAPPING_OPTIONS.map((option) => ({
|
|
95
|
-
value: option.consentType,
|
|
96
|
-
displayValue: `${option.label} Granted`,
|
|
97
|
-
}));
|
|
98
115
|
const trimTrailingSlash = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
|
|
99
116
|
const stringValue = (value) => ({
|
|
100
117
|
type: 1,
|
|
@@ -134,29 +151,61 @@ const buildPermission = (publicId, params) => ({
|
|
|
134
151
|
},
|
|
135
152
|
isRequired: true,
|
|
136
153
|
});
|
|
154
|
+
const normalizeAdditionalLoaderScriptUrlPatterns = (patterns) => {
|
|
155
|
+
if (!patterns) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
const normalizedPatterns = new Set();
|
|
159
|
+
for (const pattern of patterns) {
|
|
160
|
+
const normalizedPattern = pattern.trim();
|
|
161
|
+
if (!normalizedPattern) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
normalizedPatterns.add(normalizedPattern);
|
|
165
|
+
}
|
|
166
|
+
return [...normalizedPatterns];
|
|
167
|
+
};
|
|
168
|
+
const renderTemplateSection = (marker, content) => `${marker}\n\n${content}`;
|
|
169
|
+
const normalizeTemplateStringSection = (value, fallback) => {
|
|
170
|
+
const normalizedValue = value?.trim();
|
|
171
|
+
return normalizedValue && normalizedValue.length > 0
|
|
172
|
+
? normalizedValue
|
|
173
|
+
: fallback;
|
|
174
|
+
};
|
|
175
|
+
const normalizeTemplateStringArray = (value, fallback) => {
|
|
176
|
+
if (!value) {
|
|
177
|
+
return [...fallback];
|
|
178
|
+
}
|
|
179
|
+
const normalizedItems = [];
|
|
180
|
+
for (const item of value) {
|
|
181
|
+
const normalizedItem = item.trim();
|
|
182
|
+
if (normalizedItem.length === 0) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
normalizedItems.push(normalizedItem);
|
|
186
|
+
}
|
|
187
|
+
return normalizedItems.length > 0 ? normalizedItems : [...fallback];
|
|
188
|
+
};
|
|
137
189
|
export function buildGtmTemplateParameters(_options) {
|
|
138
|
-
const scopeHelp = buildScopeParameterHelp();
|
|
139
|
-
const scopeSelectItems = buildHostConfigurableScopeSelectItems();
|
|
140
|
-
const mappingSelectItems = buildGtmConsentTypeSelectItems();
|
|
141
190
|
return [
|
|
142
191
|
{
|
|
143
192
|
type: "TEXT",
|
|
144
193
|
name: "apiKey",
|
|
145
194
|
displayName: "API Key",
|
|
146
195
|
simpleValueType: true,
|
|
147
|
-
help: "
|
|
196
|
+
help: "Paste your getuserfeedback API key from Settings → Widget.",
|
|
148
197
|
valueValidators: [NON_EMPTY_VALIDATOR],
|
|
149
198
|
},
|
|
150
199
|
{
|
|
151
200
|
type: "GROUP",
|
|
152
201
|
name: "themeGroup",
|
|
153
|
-
displayName: "
|
|
202
|
+
displayName: "Color Scheme",
|
|
154
203
|
groupStyle: "ZIPPY_CLOSED",
|
|
155
204
|
subParams: [
|
|
156
205
|
{
|
|
157
206
|
type: "SELECT",
|
|
158
207
|
name: "themeMode",
|
|
159
|
-
displayName: "
|
|
208
|
+
displayName: "Color Scheme",
|
|
160
209
|
macrosInSelect: false,
|
|
161
210
|
selectItems: [
|
|
162
211
|
{
|
|
@@ -182,16 +231,16 @@ export function buildGtmTemplateParameters(_options) {
|
|
|
182
231
|
],
|
|
183
232
|
simpleValueType: true,
|
|
184
233
|
defaultValue: "host",
|
|
185
|
-
help: "Choose how the widget
|
|
234
|
+
help: "Choose how the widget color scheme should work.",
|
|
186
235
|
},
|
|
187
236
|
{
|
|
188
237
|
type: "SELECT",
|
|
189
238
|
name: "themeModeVariable",
|
|
190
|
-
displayName: "
|
|
239
|
+
displayName: "Color Scheme Variable",
|
|
191
240
|
macrosInSelect: true,
|
|
192
241
|
selectItems: [],
|
|
193
242
|
simpleValueType: true,
|
|
194
|
-
help: "
|
|
243
|
+
help: "Choose a GTM variable that returns `light`, `dark`, `system`, `auto`, or a comma-separated list of attribute names.",
|
|
195
244
|
enablingConditions: [
|
|
196
245
|
{
|
|
197
246
|
paramName: "themeMode",
|
|
@@ -204,10 +253,10 @@ export function buildGtmTemplateParameters(_options) {
|
|
|
204
253
|
{
|
|
205
254
|
type: "TEXT",
|
|
206
255
|
name: "themeAttr",
|
|
207
|
-
displayName: "
|
|
256
|
+
displayName: "Site Color Scheme Attributes",
|
|
208
257
|
simpleValueType: true,
|
|
209
258
|
defaultValue: DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS.join(","),
|
|
210
|
-
help: "
|
|
259
|
+
help: "Add the HTML or body attributes that show your site's color scheme, separated by commas.",
|
|
211
260
|
enablingConditions: [
|
|
212
261
|
{
|
|
213
262
|
paramName: "themeMode",
|
|
@@ -218,93 +267,6 @@ export function buildGtmTemplateParameters(_options) {
|
|
|
218
267
|
},
|
|
219
268
|
],
|
|
220
269
|
},
|
|
221
|
-
{
|
|
222
|
-
type: "GROUP",
|
|
223
|
-
name: "consentGroup",
|
|
224
|
-
displayName: "Consent Settings",
|
|
225
|
-
groupStyle: "ZIPPY_CLOSED",
|
|
226
|
-
subParams: [
|
|
227
|
-
{
|
|
228
|
-
type: "RADIO",
|
|
229
|
-
name: "consentMode",
|
|
230
|
-
displayName: "Consent Mode",
|
|
231
|
-
radioItems: [
|
|
232
|
-
{
|
|
233
|
-
value: "automatic",
|
|
234
|
-
displayValue: "Automatic (Use GTM Consent Mode)",
|
|
235
|
-
help: "Use GTM consent checks to set the widget's initial consent state. This also lets you decide whether analytics measurement should follow analytics storage or use its own fixed setting.",
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
value: "manual",
|
|
239
|
-
displayValue: "Manual (Map Public Scopes)",
|
|
240
|
-
help: "Choose which widget consent settings are granted for each GTM consent type. Required widget storage settings stay enabled and are not configurable here.",
|
|
241
|
-
},
|
|
242
|
-
],
|
|
243
|
-
simpleValueType: true,
|
|
244
|
-
defaultValue: "automatic",
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
type: "SELECT",
|
|
248
|
-
name: "analyticsMeasurementMode",
|
|
249
|
-
displayName: "Analytics Measurement Policy",
|
|
250
|
-
macrosInSelect: false,
|
|
251
|
-
selectItems: [
|
|
252
|
-
{
|
|
253
|
-
value: "inherit-analytics-storage",
|
|
254
|
-
displayValue: "Inherit analytics_storage",
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
value: "granted",
|
|
258
|
-
displayValue: "Always granted",
|
|
259
|
-
},
|
|
260
|
-
{
|
|
261
|
-
value: "denied",
|
|
262
|
-
displayValue: "Always denied",
|
|
263
|
-
},
|
|
264
|
-
],
|
|
265
|
-
simpleValueType: true,
|
|
266
|
-
defaultValue: "inherit-analytics-storage",
|
|
267
|
-
help: [
|
|
268
|
-
"Analytics measurement is separate from analytics storage so you can control event measurement independently.",
|
|
269
|
-
"Inherit analytics_storage: `analytics.measurement` matches GTM `analytics_storage`.",
|
|
270
|
-
"Always granted: `analytics.measurement` is always enabled.",
|
|
271
|
-
"Always denied: `analytics.measurement` is always disabled.",
|
|
272
|
-
].join("\n"),
|
|
273
|
-
enablingConditions: [...AUTOMATIC_CONSENT_MODE_CONDITION],
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
type: "SIMPLE_TABLE",
|
|
277
|
-
name: "scopeMappings",
|
|
278
|
-
displayName: "Scope Mappings",
|
|
279
|
-
simpleTableColumns: [
|
|
280
|
-
{
|
|
281
|
-
defaultValue: "analytics.storage",
|
|
282
|
-
displayName: "Widget Scope",
|
|
283
|
-
name: "scope",
|
|
284
|
-
type: "SELECT",
|
|
285
|
-
isUnique: true,
|
|
286
|
-
selectItems: scopeSelectItems,
|
|
287
|
-
},
|
|
288
|
-
{
|
|
289
|
-
defaultValue: "analytics_storage",
|
|
290
|
-
displayName: "Granted When",
|
|
291
|
-
name: "mapping",
|
|
292
|
-
type: "SELECT",
|
|
293
|
-
macrosInSelect: true,
|
|
294
|
-
selectItems: mappingSelectItems,
|
|
295
|
-
},
|
|
296
|
-
],
|
|
297
|
-
newRowButtonText: "Add Scope",
|
|
298
|
-
help: scopeHelp,
|
|
299
|
-
enablingConditions: [...MANUAL_CONSENT_MODE_CONDITION],
|
|
300
|
-
valueValidators: [NON_EMPTY_VALIDATOR],
|
|
301
|
-
defaultValue: buildHostConfigurableDefaultScopeMappings().map((row) => ({
|
|
302
|
-
scope: row.scope,
|
|
303
|
-
mapping: row.mapping,
|
|
304
|
-
})),
|
|
305
|
-
},
|
|
306
|
-
],
|
|
307
|
-
},
|
|
308
270
|
{
|
|
309
271
|
type: "GROUP",
|
|
310
272
|
name: "identifyGroup",
|
|
@@ -321,44 +283,44 @@ export function buildGtmTemplateParameters(_options) {
|
|
|
321
283
|
{
|
|
322
284
|
value: "intelligent",
|
|
323
285
|
displayValue: "Intelligent",
|
|
324
|
-
help: "Automatically read common data layer keys for user ID, email,
|
|
286
|
+
help: "Automatically read common data layer keys for user ID, email, phone, first name, and last name.",
|
|
325
287
|
},
|
|
326
288
|
{
|
|
327
289
|
value: "disabled",
|
|
328
290
|
displayValue: "Disabled",
|
|
329
|
-
help: "
|
|
291
|
+
help: "Don't send identity data from this tag.",
|
|
330
292
|
},
|
|
331
293
|
{
|
|
332
294
|
value: "advanced",
|
|
333
|
-
displayValue: "
|
|
334
|
-
help: "Choose
|
|
295
|
+
displayValue: "Custom",
|
|
296
|
+
help: "Choose the main identity value and add any extra fields you want to send.",
|
|
335
297
|
},
|
|
336
298
|
],
|
|
337
299
|
},
|
|
338
300
|
{
|
|
339
301
|
type: "SELECT",
|
|
340
302
|
name: "identifyPrimaryIdentityType",
|
|
341
|
-
displayName: "
|
|
303
|
+
displayName: "Main identity",
|
|
342
304
|
simpleValueType: true,
|
|
343
305
|
macrosInSelect: false,
|
|
344
306
|
defaultValue: "userId",
|
|
345
307
|
selectItems: [...ADVANCED_PRIMARY_IDENTITY_SELECT_ITEMS],
|
|
346
|
-
help: "Choose whether
|
|
308
|
+
help: "Choose whether the main identity should be sent as a user ID, email, or phone number.",
|
|
347
309
|
enablingConditions: [...IDENTIFY_ADVANCED_MODE_CONDITION],
|
|
348
310
|
},
|
|
349
311
|
{
|
|
350
312
|
type: "TEXT",
|
|
351
313
|
name: "identifyPrimaryIdentityValue",
|
|
352
|
-
displayName: "
|
|
314
|
+
displayName: "Main identity value",
|
|
353
315
|
simpleValueType: true,
|
|
354
|
-
help: "
|
|
316
|
+
help: "Choose a GTM variable for the main identity value.",
|
|
355
317
|
enablingConditions: [...IDENTIFY_ADVANCED_MODE_CONDITION],
|
|
356
318
|
},
|
|
357
319
|
{
|
|
358
320
|
type: "SIMPLE_TABLE",
|
|
359
321
|
name: "identifyTraits",
|
|
360
322
|
displayName: "Traits",
|
|
361
|
-
help: "Add any
|
|
323
|
+
help: "Add any extra fields you want to send from GTM variables. Common examples are `email`, `phone`, `firstName`, `lastName`, and `plan`. Empty rows are ignored.",
|
|
362
324
|
newRowButtonText: "Add Trait",
|
|
363
325
|
simpleTableColumns: [
|
|
364
326
|
{
|
|
@@ -380,8 +342,12 @@ export function buildGtmTemplateParameters(_options) {
|
|
|
380
342
|
}
|
|
381
343
|
export function buildGtmRuntimeSource(input) {
|
|
382
344
|
const loaderScriptBaseUrl = trimTrailingSlash(input?.loaderScriptBaseUrl ?? DEFAULT_GTM_LOADER_SCRIPT_BASE_URL);
|
|
383
|
-
const
|
|
384
|
-
|
|
345
|
+
const runtimeEndpoints = input?.runtimeEndpoints === undefined
|
|
346
|
+
? undefined
|
|
347
|
+
: {
|
|
348
|
+
apiUrl: input.runtimeEndpoints.apiUrl.trim(),
|
|
349
|
+
coreUrl: input.runtimeEndpoints.coreUrl.trim(),
|
|
350
|
+
};
|
|
385
351
|
return `
|
|
386
352
|
const log = require("logToConsole");
|
|
387
353
|
const injectScript = require("injectScript");
|
|
@@ -393,14 +359,17 @@ const copyFromDataLayer = require("copyFromDataLayer");
|
|
|
393
359
|
|
|
394
360
|
const loaderScriptBaseUrl = ${JSON.stringify(loaderScriptBaseUrl)};
|
|
395
361
|
const gtmConsentTypes = ${JSON.stringify(GTM_CONSENT_TYPES)};
|
|
396
|
-
const automaticDefaultScopeMappings = ${JSON.stringify(automaticDefaultScopeMappings)};
|
|
397
|
-
const defaultScopeMappings = ${JSON.stringify(defaultTemplateScopeMappings)};
|
|
398
|
-
const hostConfigurableScopes = ${JSON.stringify(defaultTemplateScopeMappings.map((row) => row.scope))};
|
|
399
362
|
const defaultAutoDetectColorSchemeAttrs = ${JSON.stringify(DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS)};
|
|
400
363
|
const queueKey = ${JSON.stringify(GTM_QUEUE_KEY)};
|
|
401
364
|
const intelligentIdentifyUserIdKeys = ${JSON.stringify(INTELLIGENT_IDENTIFY_USER_ID_KEYS)};
|
|
402
365
|
const intelligentIdentifyEmailKeys = ${JSON.stringify(INTELLIGENT_IDENTIFY_EMAIL_KEYS)};
|
|
366
|
+
const intelligentIdentifyFirstNameKeys = ${JSON.stringify(INTELLIGENT_IDENTIFY_FIRST_NAME_KEYS)};
|
|
367
|
+
const intelligentIdentifyLastNameKeys = ${JSON.stringify(INTELLIGENT_IDENTIFY_LAST_NAME_KEYS)};
|
|
403
368
|
const intelligentIdentifyPhoneKeys = ${JSON.stringify(INTELLIGENT_IDENTIFY_PHONE_KEYS)};
|
|
369
|
+
const generatedRuntimeEndpoints = ${JSON.stringify(runtimeEndpoints ?? null)};
|
|
370
|
+
const queueClientMeta = ${JSON.stringify(GTM_QUEUE_CLIENT_META)};
|
|
371
|
+
${RESOLVE_GTM_THEME_INPUT_FROM_TEMPLATE_DATA_SOURCE}
|
|
372
|
+
${BUILD_GTM_IDENTIFY_COMMAND_SOURCE}
|
|
404
373
|
|
|
405
374
|
const trimString = function(value) {
|
|
406
375
|
return typeof value === "string" ? value.trim() : "";
|
|
@@ -485,12 +454,7 @@ const normalizeTraitValue = function(value) {
|
|
|
485
454
|
};
|
|
486
455
|
|
|
487
456
|
const normalizeUserIdValue = function(value) {
|
|
488
|
-
|
|
489
|
-
return undefined;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const trimmedValue = value.trim();
|
|
493
|
-
return trimmedValue ? trimmedValue : undefined;
|
|
457
|
+
return typeof value === "string" ? trimString(value) || undefined : undefined;
|
|
494
458
|
};
|
|
495
459
|
|
|
496
460
|
const addTrait = function(result, name, value) {
|
|
@@ -520,10 +484,12 @@ const getIntelligentIdentifyValue = function(keys) {
|
|
|
520
484
|
};
|
|
521
485
|
|
|
522
486
|
const getIntelligentIdentifyTraits = function() {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
487
|
+
return {
|
|
488
|
+
email: getIntelligentIdentifyValue(intelligentIdentifyEmailKeys),
|
|
489
|
+
firstName: getIntelligentIdentifyValue(intelligentIdentifyFirstNameKeys),
|
|
490
|
+
lastName: getIntelligentIdentifyValue(intelligentIdentifyLastNameKeys),
|
|
491
|
+
phone: getIntelligentIdentifyValue(intelligentIdentifyPhoneKeys),
|
|
492
|
+
};
|
|
527
493
|
};
|
|
528
494
|
|
|
529
495
|
const getAdvancedIdentifyTraits = function(templateData) {
|
|
@@ -539,96 +505,27 @@ const getAdvancedIdentifyTraits = function(templateData) {
|
|
|
539
505
|
return traits;
|
|
540
506
|
};
|
|
541
507
|
|
|
542
|
-
const hasOwnKeys = function(value) {
|
|
543
|
-
for (const key in value) {
|
|
544
|
-
return !!key;
|
|
545
|
-
}
|
|
546
|
-
return false;
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
const getIdentifyCommandFromUserIdAndTraits = function(userId, traits) {
|
|
550
|
-
const hasTraits = hasOwnKeys(traits);
|
|
551
|
-
|
|
552
|
-
if (userId && hasTraits) {
|
|
553
|
-
return {
|
|
554
|
-
kind: "identify",
|
|
555
|
-
userId: userId,
|
|
556
|
-
traits: traits,
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
if (userId) {
|
|
560
|
-
return {
|
|
561
|
-
kind: "identify",
|
|
562
|
-
userId: userId,
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
if (hasTraits) {
|
|
566
|
-
return {
|
|
567
|
-
kind: "identify",
|
|
568
|
-
traits: traits,
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
return undefined;
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
const getIntelligentIdentifyCommand = function() {
|
|
575
|
-
const userId = normalizeUserIdValue(
|
|
576
|
-
getIntelligentIdentifyValue(intelligentIdentifyUserIdKeys),
|
|
577
|
-
);
|
|
578
|
-
const traits = getIntelligentIdentifyTraits();
|
|
579
|
-
return getIdentifyCommandFromUserIdAndTraits(userId, traits);
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
const getAdvancedIdentifyCommand = function(templateData) {
|
|
583
|
-
const traits = getAdvancedIdentifyTraits(templateData);
|
|
584
|
-
const primaryIdentityType = trimString(templateData.identifyPrimaryIdentityType);
|
|
585
|
-
const primaryIdentityValue = normalizeTraitValue(
|
|
586
|
-
templateData.identifyPrimaryIdentityValue,
|
|
587
|
-
);
|
|
588
|
-
|
|
589
|
-
if (primaryIdentityType === "userId") {
|
|
590
|
-
const userId = normalizeUserIdValue(primaryIdentityValue);
|
|
591
|
-
return getIdentifyCommandFromUserIdAndTraits(userId, traits);
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
if (
|
|
595
|
-
primaryIdentityType === "email" ||
|
|
596
|
-
primaryIdentityType === "phone"
|
|
597
|
-
) {
|
|
598
|
-
addTrait(traits, primaryIdentityType, primaryIdentityValue);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
return hasOwnKeys(traits)
|
|
602
|
-
? {
|
|
603
|
-
kind: "identify",
|
|
604
|
-
traits: traits,
|
|
605
|
-
}
|
|
606
|
-
: undefined;
|
|
607
|
-
};
|
|
608
|
-
|
|
609
508
|
const getIdentifyCommand = function(templateData) {
|
|
610
509
|
if (templateData.identifyMode === "intelligent") {
|
|
611
|
-
|
|
510
|
+
const intelligentTraits = getIntelligentIdentifyTraits();
|
|
511
|
+
return buildGtmIdentifyCommand({
|
|
512
|
+
email: intelligentTraits.email,
|
|
513
|
+
firstName: intelligentTraits.firstName,
|
|
514
|
+
lastName: intelligentTraits.lastName,
|
|
515
|
+
mode: "intelligent",
|
|
516
|
+
phone: intelligentTraits.phone,
|
|
517
|
+
userId: getIntelligentIdentifyValue(intelligentIdentifyUserIdKeys),
|
|
518
|
+
});
|
|
612
519
|
}
|
|
613
520
|
if (templateData.identifyMode === "advanced") {
|
|
614
|
-
return
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
}
|
|
623
|
-
if (templateData.themeMode === "host") {
|
|
624
|
-
return templateData.themeAttr;
|
|
625
|
-
}
|
|
626
|
-
if (
|
|
627
|
-
templateData.themeMode === "light" ||
|
|
628
|
-
templateData.themeMode === "dark" ||
|
|
629
|
-
templateData.themeMode === "system"
|
|
630
|
-
) {
|
|
631
|
-
return templateData.themeMode;
|
|
521
|
+
return buildGtmIdentifyCommand({
|
|
522
|
+
mode: "advanced",
|
|
523
|
+
primaryIdentityType: trimString(templateData.identifyPrimaryIdentityType),
|
|
524
|
+
primaryIdentityValue: normalizeTraitValue(
|
|
525
|
+
templateData.identifyPrimaryIdentityValue,
|
|
526
|
+
),
|
|
527
|
+
traits: getAdvancedIdentifyTraits(templateData),
|
|
528
|
+
});
|
|
632
529
|
}
|
|
633
530
|
return undefined;
|
|
634
531
|
};
|
|
@@ -644,144 +541,78 @@ const getConsentState = function() {
|
|
|
644
541
|
return result;
|
|
645
542
|
};
|
|
646
543
|
|
|
647
|
-
const
|
|
648
|
-
const
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
if (
|
|
653
|
-
|
|
544
|
+
const buildDefaultConsent = function(templateData) {
|
|
545
|
+
const consentState = getConsentState();
|
|
546
|
+
const grantedScopeLookup = {};
|
|
547
|
+
const grantedScopes = [];
|
|
548
|
+
const grantScope = function(scope) {
|
|
549
|
+
if (!scope || grantedScopeLookup[scope]) {
|
|
550
|
+
return;
|
|
654
551
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
}
|
|
658
|
-
return result;
|
|
659
|
-
};
|
|
552
|
+
grantedScopeLookup[scope] = true;
|
|
553
|
+
grantedScopes.push(scope);
|
|
554
|
+
};
|
|
660
555
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
return true;
|
|
665
|
-
}
|
|
556
|
+
if (consentState.analytics_storage === "granted") {
|
|
557
|
+
grantScope("analytics.storage");
|
|
558
|
+
grantScope("analytics.measurement");
|
|
666
559
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const isHostConfigurableScope = function(scope) {
|
|
671
|
-
for (let index = 0; index < hostConfigurableScopes.length; index += 1) {
|
|
672
|
-
if (hostConfigurableScopes[index] === scope) {
|
|
673
|
-
return true;
|
|
674
|
-
}
|
|
560
|
+
if (consentState.personalization_storage === "granted") {
|
|
561
|
+
grantScope("personalization.storage");
|
|
675
562
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
const getManualScopeMappings = function(templateData) {
|
|
680
|
-
const rawRows = getSimpleTableRows(templateData.scopeMappings);
|
|
681
|
-
const rows = rawRows.length > 0 ? rawRows : defaultScopeMappings;
|
|
682
|
-
const mappings = [];
|
|
683
|
-
for (let index = 0; index < rows.length; index += 1) {
|
|
684
|
-
const row = rows[index];
|
|
685
|
-
if (!row || typeof row !== "object") {
|
|
686
|
-
continue;
|
|
687
|
-
}
|
|
688
|
-
const scope = trimString(row.scope);
|
|
689
|
-
const consentType = trimString(row.mapping);
|
|
690
|
-
if (!scope || !consentType) {
|
|
691
|
-
continue;
|
|
692
|
-
}
|
|
693
|
-
if (!isHostConfigurableScope(scope) || !isKnownConsentType(consentType)) {
|
|
694
|
-
continue;
|
|
695
|
-
}
|
|
696
|
-
mappings.push({
|
|
697
|
-
scope: scope,
|
|
698
|
-
consentType: consentType,
|
|
699
|
-
});
|
|
563
|
+
if (consentState.ad_storage === "granted") {
|
|
564
|
+
grantScope("ads.storage");
|
|
700
565
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
const getAnalyticsMeasurementMode = function(templateData) {
|
|
705
|
-
if (
|
|
706
|
-
templateData.analyticsMeasurementMode === "granted" ||
|
|
707
|
-
templateData.analyticsMeasurementMode === "denied"
|
|
708
|
-
) {
|
|
709
|
-
return templateData.analyticsMeasurementMode;
|
|
566
|
+
if (consentState.ad_user_data === "granted") {
|
|
567
|
+
grantScope("ads.user_data");
|
|
710
568
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
const buildDefaultConsent = function(templateData) {
|
|
715
|
-
const consentState = getConsentState();
|
|
716
|
-
const grantedScopes = [];
|
|
717
|
-
|
|
718
|
-
if (templateData.consentMode === "manual") {
|
|
719
|
-
const scopeMappings = getManualScopeMappings(templateData);
|
|
720
|
-
for (let index = 0; index < scopeMappings.length; index += 1) {
|
|
721
|
-
const mapping = scopeMappings[index];
|
|
722
|
-
if (consentState[mapping.consentType] === "granted") {
|
|
723
|
-
grantedScopes.push(mapping.scope);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
} else {
|
|
727
|
-
for (let index = 0; index < automaticDefaultScopeMappings.length; index += 1) {
|
|
728
|
-
const mapping = automaticDefaultScopeMappings[index];
|
|
729
|
-
if (consentState[mapping.mapping] === "granted") {
|
|
730
|
-
grantedScopes.push(mapping.scope);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
const analyticsMeasurementMode = getAnalyticsMeasurementMode(templateData);
|
|
735
|
-
if (analyticsMeasurementMode === "granted") {
|
|
736
|
-
grantedScopes.push("analytics.measurement");
|
|
737
|
-
}
|
|
738
|
-
if (
|
|
739
|
-
analyticsMeasurementMode === "inherit-analytics-storage" &&
|
|
740
|
-
consentState.analytics_storage === "granted"
|
|
741
|
-
) {
|
|
742
|
-
grantedScopes.push("analytics.measurement");
|
|
743
|
-
}
|
|
569
|
+
if (consentState.ad_personalization === "granted") {
|
|
570
|
+
grantScope("ads.personalization");
|
|
744
571
|
}
|
|
745
572
|
|
|
746
|
-
|
|
747
|
-
return dedupedScopes.length > 0 ? dedupedScopes : undefined;
|
|
573
|
+
return grantedScopes.length > 0 ? grantedScopes : undefined;
|
|
748
574
|
};
|
|
749
575
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
576
|
+
let requestSequence = 0;
|
|
577
|
+
|
|
578
|
+
const enqueuePublicCommand = function(queuePush, instanceId, command) {
|
|
579
|
+
requestSequence += 1;
|
|
580
|
+
const requestId = "gtm_" + command.kind + "_" + requestSequence;
|
|
581
|
+
queuePush({
|
|
582
|
+
version: "1",
|
|
583
|
+
instanceId: instanceId,
|
|
584
|
+
requestId: requestId,
|
|
585
|
+
idempotencyKey: requestId,
|
|
586
|
+
clientMeta: queueClientMeta,
|
|
587
|
+
command: command,
|
|
588
|
+
});
|
|
754
589
|
};
|
|
755
590
|
|
|
756
|
-
const
|
|
591
|
+
const apiKey = trimString(data.apiKey);
|
|
592
|
+
if (!apiKey) {
|
|
757
593
|
if (typeof data.gtmOnFailure === "function") {
|
|
758
594
|
data.gtmOnFailure();
|
|
759
595
|
}
|
|
760
|
-
};
|
|
761
|
-
|
|
762
|
-
const apiKey = trimString(data.apiKey);
|
|
763
|
-
if (!apiKey) {
|
|
764
|
-
onFailure();
|
|
765
596
|
return;
|
|
766
597
|
}
|
|
767
598
|
|
|
768
599
|
const encodedApiKey = encodeUriComponent(apiKey);
|
|
769
600
|
if (!encodedApiKey) {
|
|
770
|
-
|
|
601
|
+
if (typeof data.gtmOnFailure === "function") {
|
|
602
|
+
data.gtmOnFailure();
|
|
603
|
+
}
|
|
771
604
|
return;
|
|
772
605
|
}
|
|
773
|
-
|
|
774
606
|
const widgetUrl = loaderScriptBaseUrl + "/" + encodedApiKey + "/loader.js";
|
|
775
607
|
const queuePush = createQueue(queueKey);
|
|
608
|
+
const instanceId = "gtm-" + encodedApiKey;
|
|
776
609
|
const initOptions = {
|
|
777
610
|
apiKey: apiKey,
|
|
778
|
-
clientMeta:
|
|
779
|
-
loader: "gtm",
|
|
780
|
-
transport: "tag-manager",
|
|
781
|
-
},
|
|
611
|
+
clientMeta: queueClientMeta,
|
|
782
612
|
};
|
|
783
613
|
|
|
784
|
-
const
|
|
614
|
+
const themeInput = resolveGtmThemeInputFromTemplateData(data);
|
|
615
|
+
const colorScheme = normalizeThemeInput(themeInput);
|
|
785
616
|
if (colorScheme !== undefined) {
|
|
786
617
|
initOptions.colorScheme = colorScheme;
|
|
787
618
|
}
|
|
@@ -791,33 +622,50 @@ if (defaultConsent !== undefined) {
|
|
|
791
622
|
initOptions.defaultConsent = defaultConsent;
|
|
792
623
|
}
|
|
793
624
|
|
|
794
|
-
|
|
625
|
+
if (generatedRuntimeEndpoints !== null) {
|
|
626
|
+
initOptions.runtimeEndpoints = {
|
|
627
|
+
apiUrl: generatedRuntimeEndpoints.apiUrl,
|
|
628
|
+
coreUrl: generatedRuntimeEndpoints.coreUrl,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
enqueuePublicCommand(queuePush, instanceId, {
|
|
795
633
|
kind: "init",
|
|
796
634
|
opts: initOptions,
|
|
797
635
|
});
|
|
798
636
|
|
|
799
637
|
const identifyCommand = getIdentifyCommand(data);
|
|
800
638
|
if (identifyCommand !== undefined) {
|
|
801
|
-
queuePush
|
|
639
|
+
enqueuePublicCommand(queuePush, instanceId, identifyCommand);
|
|
802
640
|
}
|
|
803
641
|
|
|
804
642
|
log("Loading getuserfeedback loader:", widgetUrl);
|
|
805
643
|
|
|
806
644
|
if (queryPermission("inject_script", widgetUrl)) {
|
|
807
|
-
injectScript(
|
|
645
|
+
injectScript(
|
|
646
|
+
widgetUrl,
|
|
647
|
+
typeof data.gtmOnSuccess === "function" ? data.gtmOnSuccess : undefined,
|
|
648
|
+
typeof data.gtmOnFailure === "function" ? data.gtmOnFailure : undefined,
|
|
649
|
+
"getuserfeedback",
|
|
650
|
+
);
|
|
808
651
|
} else {
|
|
809
|
-
|
|
652
|
+
if (typeof data.gtmOnFailure === "function") {
|
|
653
|
+
data.gtmOnFailure();
|
|
654
|
+
}
|
|
810
655
|
}
|
|
811
656
|
`.trim();
|
|
812
657
|
}
|
|
813
658
|
export function buildGtmPermissions(input) {
|
|
814
659
|
const loaderScriptBaseUrl = trimTrailingSlash(input?.loaderScriptBaseUrl ?? DEFAULT_GTM_LOADER_SCRIPT_BASE_URL);
|
|
815
|
-
const
|
|
660
|
+
const loaderUrlPatterns = [
|
|
661
|
+
`${loaderScriptBaseUrl}/*/loader.js`,
|
|
662
|
+
...normalizeAdditionalLoaderScriptUrlPatterns(input?.additionalLoaderScriptUrlPatterns),
|
|
663
|
+
];
|
|
816
664
|
return [
|
|
817
665
|
buildPermission("inject_script", [
|
|
818
666
|
{
|
|
819
667
|
key: "urls",
|
|
820
|
-
value: listValue(
|
|
668
|
+
value: listValue(loaderUrlPatterns.map((pattern) => stringValue(pattern))),
|
|
821
669
|
},
|
|
822
670
|
]),
|
|
823
671
|
buildPermission("logging", [
|
|
@@ -855,6 +703,8 @@ export function buildGtmPermissions(input) {
|
|
|
855
703
|
value: listValue([
|
|
856
704
|
...INTELLIGENT_IDENTIFY_USER_ID_KEYS,
|
|
857
705
|
...INTELLIGENT_IDENTIFY_EMAIL_KEYS,
|
|
706
|
+
...INTELLIGENT_IDENTIFY_FIRST_NAME_KEYS,
|
|
707
|
+
...INTELLIGENT_IDENTIFY_LAST_NAME_KEYS,
|
|
858
708
|
...INTELLIGENT_IDENTIFY_PHONE_KEYS,
|
|
859
709
|
].map((key) => stringValue(key))),
|
|
860
710
|
},
|
|
@@ -882,3 +732,32 @@ export function buildGtmTemplateParts(input) {
|
|
|
882
732
|
export function buildGtmTemplateArtifact(input) {
|
|
883
733
|
return buildGtmTemplateParts(input);
|
|
884
734
|
}
|
|
735
|
+
export function buildGtmTemplateFileSource(input) {
|
|
736
|
+
const artifact = buildGtmTemplateArtifact(input);
|
|
737
|
+
const infoOverrides = input?.infoOverrides;
|
|
738
|
+
const info = {
|
|
739
|
+
type: artifact.info.type,
|
|
740
|
+
id: normalizeTemplateStringSection(infoOverrides?.id, GTM_TEMPLATE_DEFAULT_ID),
|
|
741
|
+
version: artifact.info.version,
|
|
742
|
+
securityGroups: normalizeTemplateStringArray(infoOverrides?.securityGroups, []),
|
|
743
|
+
displayName: normalizeTemplateStringSection(infoOverrides?.displayName, GTM_TEMPLATE_GALLERY_DISPLAY_NAME),
|
|
744
|
+
categories: normalizeTemplateStringArray(infoOverrides?.categories, GTM_TEMPLATE_GALLERY_CATEGORIES),
|
|
745
|
+
brand: {
|
|
746
|
+
displayName: normalizeTemplateStringSection(infoOverrides?.brandDisplayName, GTM_TEMPLATE_DEFAULT_BRAND_DISPLAY_NAME),
|
|
747
|
+
thumbnail: normalizeTemplateStringSection(infoOverrides?.brandThumbnail, GTM_TEMPLATE_DEFAULT_BRAND_THUMBNAIL),
|
|
748
|
+
},
|
|
749
|
+
description: normalizeTemplateStringSection(infoOverrides?.description, GTM_TEMPLATE_GALLERY_DESCRIPTION),
|
|
750
|
+
containerContexts: [...artifact.info.containerContexts],
|
|
751
|
+
};
|
|
752
|
+
const sections = [
|
|
753
|
+
renderTemplateSection("___TERMS_OF_SERVICE___", normalizeTemplateStringSection(input?.termsOfService, GTM_TEMPLATE_GALLERY_TERMS_OF_SERVICE)),
|
|
754
|
+
renderTemplateSection("___INFO___", JSON.stringify(info, null, 2)),
|
|
755
|
+
renderTemplateSection("___TEMPLATE_PARAMETERS___", JSON.stringify(artifact.parameters, null, 2)),
|
|
756
|
+
renderTemplateSection("___SANDBOXED_JS_FOR_WEB_TEMPLATE___", artifact.runtimeSource),
|
|
757
|
+
renderTemplateSection("___WEB_PERMISSIONS___", JSON.stringify(artifact.permissions, null, 2)),
|
|
758
|
+
renderTemplateSection("___TESTS___", normalizeTemplateStringSection(input?.tests, GTM_TEMPLATE_DEFAULT_TESTS)),
|
|
759
|
+
renderTemplateSection("___NOTES___", normalizeTemplateStringSection(input?.notes, GTM_TEMPLATE_DEFAULT_NOTES)),
|
|
760
|
+
"",
|
|
761
|
+
];
|
|
762
|
+
return `${input?.includeUtf8Bom === false ? "" : GTM_TEMPLATE_UTF8_BOM}${sections.join("\n\n")}`;
|
|
763
|
+
}
|