@getuserfeedback/adapters 0.3.0 → 0.4.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/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # `@getuserfeedback/adapters`
2
+
3
+ `@getuserfeedback/adapters` publishes external-system translation layers built on top of the public widget protocol.
4
+
5
+ ## GTM
6
+
7
+ The `@getuserfeedback/adapters/gtm` entrypoint is the authoritative source for Google Tag Manager template generation. It owns:
8
+
9
+ - GTM consent mapping logic
10
+ - GTM theme normalization logic
11
+ - GTM template parameter generation
12
+ - GTM runtime source generation
13
+ - GTM permission generation
14
+ - GTM template defaults, help text, and option labels
15
+ - the canonical loader script base URL pattern
16
+
17
+ ### Public exports
18
+
19
+ Core mappings:
20
+
21
+ - `GTM_CONSENT_TYPES`
22
+ - `ESSENTIAL_GTM_SCOPES`
23
+ - `DEFAULT_GTM_SCOPE_MAPPINGS`
24
+ - `GTM_TEMPLATE_SCOPE_OPTIONS`
25
+ - `GTM_TEMPLATE_MAPPING_OPTIONS`
26
+ - `isGtmConsentType(value)`
27
+ - `isHostConfigurableGtmScope(scope)`
28
+ - `normalizeGtmThemeInput(input)`
29
+ - `resolveDefaultConsentFromGtm(input)`
30
+ - `buildInitOptionsFromGtmTemplate(input)`
31
+
32
+ Types:
33
+
34
+ - `GtmConsentType`
35
+ - `GtmConsentValue`
36
+ - `GtmConsentState`
37
+ - `GtmScopeMapping`
38
+ - `GtmAnalyticsMeasurementMode`
39
+ - `GtmTemplateInput`
40
+ - `GtmTemplateScopeOption`
41
+ - `GtmTemplateMappingOption`
42
+
43
+ Template generation:
44
+
45
+ - `DEFAULT_GTM_LOADER_SCRIPT_BASE_URL`
46
+ - `buildGtmTemplateParameters(options?)`
47
+ - `buildGtmRuntimeSource(options?)`
48
+ - `buildGtmPermissions(options?)`
49
+ - `buildGtmTemplateInfo(options?)`
50
+ - `buildGtmTemplateParts(options?)`
51
+ - `buildGtmTemplateArtifact(options?)`
52
+
53
+ ### Essential scope policy
54
+
55
+ `functionality.storage` and `security.storage` are essential baseline scopes.
56
+
57
+ - They remain accepted by the protocol for backward compatibility.
58
+ - They are not host-configurable in GTM-generated consent UI.
59
+ - They are not emitted in GTM-generated host-overridable default consent output.
60
+ - Generated help text states that they are fixed by the loader.
61
+
62
+ ### Analytics measurement policy
63
+
64
+ Automatic GTM consent mode exposes an explicit analytics measurement policy:
65
+
66
+ - `inherit-analytics-storage`
67
+ - `granted`
68
+ - `denied`
69
+
70
+ Manual GTM consent mode does not apply hidden analytics measurement behavior. It only uses the generated host-configurable scope mappings.
71
+
72
+ ### Theme behavior
73
+
74
+ Generated GTM theme support accepts:
75
+
76
+ - fixed `light`
77
+ - fixed `dark`
78
+ - fixed `system`
79
+ - host-sync via protocol auto-detect attributes
80
+ - GTM variable-driven values
81
+ - comma-separated auto-detect attribute lists
82
+
83
+ The adapter normalizes all theme inputs into protocol-valid `colorScheme`.
84
+
85
+ ### Generate a GTM template artifact
86
+
87
+ ```ts
88
+ import { buildGtmTemplateArtifact } from "@getuserfeedback/adapters/gtm";
89
+
90
+ const artifact = buildGtmTemplateArtifact({
91
+ loaderScriptBaseUrl: "https://cdn.getuserfeedback.com/widget/loader/v1",
92
+ });
93
+
94
+ artifact.info;
95
+ artifact.parameters;
96
+ artifact.runtimeSource;
97
+ artifact.permissions;
98
+ ```
99
+
100
+ The returned object is suitable for rendering into GTM `___INFO___`, `___TEMPLATE_PARAMETERS___`, `___SANDBOXED_JS_FOR_WEB_TEMPLATE___`, and `___WEB_PERMISSIONS___` sections.
101
+
102
+ ### What the GTM repo should still own
103
+
104
+ After importing the published package, the GTM repo should only own:
105
+
106
+ - GTM Gallery wrapper file assembly
107
+ - brand thumbnail and base64 image assets
108
+ - `metadata.yaml`
109
+ - publishing and release workflow
110
+
111
+ It should not rebuild parameters, runtime source, permissions, or GTM-specific init payload translation.
112
+
113
+ ### Customizing the loader base URL
114
+
115
+ Pass `loaderScriptBaseUrl` to:
116
+
117
+ - `buildGtmRuntimeSource`
118
+ - `buildGtmPermissions`
119
+ - `buildGtmTemplateParts`
120
+ - `buildGtmTemplateArtifact`
121
+
122
+ The same base URL is used to derive the runtime loader URL and the `inject_script` permission pattern.
123
+
124
+ ### Versioning expectations
125
+
126
+ Any change that affects generated GTM output is a public package change and should be versioned accordingly. This includes:
127
+
128
+ - parameter shape or defaults
129
+ - runtime behavior
130
+ - permission generation
131
+ - default mappings
132
+ - essential-scope exposure
133
+ - analytics measurement behavior
134
+
135
+ Release notes should call out template-affecting changes explicitly.
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/gtm/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,WAAW,EAEhB,KAAK,KAAK,EACV,MAAM,2BAA2B,CAAC;AAEnC,eAAO,MAAM,iBAAiB,EAAE,SAAS;IACxC,uBAAuB;IACvB,kBAAkB;IAClB,mBAAmB;IACnB,yBAAyB;IACzB,YAAY;IACZ,cAAc;IACd,oBAAoB;CASpB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEnD,MAAM,MAAM,2BAA2B,GACpC,2BAA2B,GAC3B,SAAS,GACT,QAAQ,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG;IAC7B,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,cAAc,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC;AAE/E,MAAM,MAAM,gBAAgB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,aAAa,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,wBAAwB,CAAC,EAAE,2BAA2B,CAAC;IACvD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC;IAC3C,gBAAgB,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;CACvC,CAAC;AAEF,KAAK,oBAAoB,GAAG,WAAW,CACtC,OAAO,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,CAC9C,CAAC,MAAM,CAAC,CAAC;AAEV,MAAM,MAAM,sBAAsB,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACtC,WAAW,EAAE,cAAc,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,SAAS;IAC3C,uBAAuB;IACvB,kBAAkB;CAC8B,CAAC;AAIlD,eAAO,MAAM,0BAA0B,GACtC,OAAO,KAAK,KACV,KAAK,IAAI,oBAAmD,CAAC;AAEhE,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,eAAe,CA6BrE,CAAC;AA4BF,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,sBAAsB,CAKzE,CAAC;AAEL,eAAO,MAAM,4BAA4B,EAAE,aAAa,CAAC,wBAAwB,CAI7E,CAAC;AAEL,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAKxE;AAED,wBAAgB,sBAAsB,CACrC,KAAK,EAAE,OAAO,GACZ,WAAW,CAAC,aAAa,CAAC,GAAG,SAAS,CA2CxC;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IACnD,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,aAAa,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,wBAAwB,CAAC,EAAE,2BAA2B,CAAC;CACvD,GAAG,WAAW,CAAC,gBAAgB,CAAC,GAAG,SAAS,CA0B5C;AAED,wBAAgB,+BAA+B,CAC9C,KAAK,EAAE,gBAAgB,GACrB,WAAW,CA8Bb"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/gtm/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,KAAK,WAAW,EAGhB,KAAK,KAAK,EACV,MAAM,2BAA2B,CAAC;AAEnC,eAAO,MAAM,iBAAiB,EAAE,SAAS;IACxC,uBAAuB;IACvB,kBAAkB;IAClB,mBAAmB;IACnB,yBAAyB;IACzB,YAAY;IACZ,cAAc;IACd,oBAAoB;CASpB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEnD,MAAM,MAAM,2BAA2B,GACpC,2BAA2B,GAC3B,SAAS,GACT,QAAQ,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG;IAC7B,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,cAAc,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC;AAE/E,MAAM,MAAM,gBAAgB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,aAAa,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,wBAAwB,CAAC,EAAE,2BAA2B,CAAC;IACvD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC;IAC3C,gBAAgB,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;CACvC,CAAC;AAEF,KAAK,oBAAoB,GAAG,WAAW,CACtC,OAAO,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,CAC9C,CAAC,MAAM,CAAC,CAAC;AAEV,MAAM,MAAM,sBAAsB,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACtC,WAAW,EAAE,cAAc,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,SAAS;IAC3C,uBAAuB;IACvB,kBAAkB;CAC8B,CAAC;AAIlD,eAAO,MAAM,0BAA0B,GACtC,OAAO,KAAK,KACV,KAAK,IAAI,oBAAmD,CAAC;AAEhE,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,eAAe,CA6BrE,CAAC;AA4BF,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,sBAAsB,CAKzE,CAAC;AAEL,eAAO,MAAM,4BAA4B,EAAE,aAAa,CAAC,wBAAwB,CAI7E,CAAC;AAEL,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAKxE;AAED,wBAAgB,sBAAsB,CACrC,KAAK,EAAE,OAAO,GACZ,WAAW,CAAC,aAAa,CAAC,GAAG,SAAS,CA2CxC;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IACnD,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,aAAa,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,wBAAwB,CAAC,EAAE,2BAA2B,CAAC;CACvD,GAAG,WAAW,CAAC,gBAAgB,CAAC,GAAG,SAAS,CA6B5C;AAED,wBAAgB,+BAA+B,CAC9C,KAAK,EAAE,gBAAgB,GACrB,WAAW,CA8Bb"}
package/dist/gtm/core.js CHANGED
@@ -1,4 +1,4 @@
1
- import { DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS, listScopes, } from "@getuserfeedback/protocol";
1
+ import { colorSchemeConfigInputSchema, consentConfigInputSchema, DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS, listScopes, parseInitOptions, } from "@getuserfeedback/protocol";
2
2
  export const GTM_CONSENT_TYPES = [
3
3
  "functionality_storage",
4
4
  "security_storage",
@@ -102,17 +102,17 @@ export function normalizeGtmThemeInput(input) {
102
102
  return normalized;
103
103
  }
104
104
  if (normalized === "auto") {
105
- return {
105
+ return colorSchemeConfigInputSchema.parse({
106
106
  autoDetectColorScheme: [...DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS],
107
- };
107
+ });
108
108
  }
109
109
  const attrs = normalizeAttrList(input);
110
110
  if (attrs.length === 0) {
111
111
  return undefined;
112
112
  }
113
- return {
113
+ return colorSchemeConfigInputSchema.parse({
114
114
  autoDetectColorScheme: attrs,
115
- };
115
+ });
116
116
  }
117
117
  export function resolveDefaultConsentFromGtm(input) {
118
118
  const consentState = input.consentState;
@@ -129,7 +129,10 @@ export function resolveDefaultConsentFromGtm(input) {
129
129
  consentState.analytics_storage === "granted") {
130
130
  grantedScopes.push("analytics.measurement");
131
131
  }
132
- return uniqueScopes(grantedScopes);
132
+ const normalizedGrantedScopes = uniqueScopes(grantedScopes);
133
+ return normalizedGrantedScopes.length > 0
134
+ ? consentConfigInputSchema.parse(normalizedGrantedScopes)
135
+ : undefined;
133
136
  }
134
137
  export function buildInitOptionsFromGtmTemplate(input) {
135
138
  const colorScheme = normalizeGtmThemeInput(input.colorScheme);
@@ -138,7 +141,7 @@ export function buildInitOptionsFromGtmTemplate(input) {
138
141
  scopeMappings: input.scopeMappings,
139
142
  analyticsMeasurementMode: input.analyticsMeasurementMode,
140
143
  });
141
- return {
144
+ return parseInitOptions({
142
145
  apiKey: input.apiKey,
143
146
  ...(colorScheme !== undefined ? { colorScheme } : {}),
144
147
  ...(input.disableTelemetry !== undefined
@@ -159,5 +162,5 @@ export function buildInitOptionsFromGtmTemplate(input) {
159
162
  ...(input.runtimeEndpoints !== undefined
160
163
  ? { runtimeEndpoints: input.runtimeEndpoints }
161
164
  : {}),
162
- };
165
+ });
163
166
  }
@@ -1,3 +1,3 @@
1
1
  export { buildInitOptionsFromGtmTemplate, DEFAULT_GTM_SCOPE_MAPPINGS, ESSENTIAL_GTM_SCOPES, GTM_CONSENT_TYPES, GTM_TEMPLATE_MAPPING_OPTIONS, GTM_TEMPLATE_SCOPE_OPTIONS, type GtmAnalyticsMeasurementMode, type GtmConsentState, type GtmConsentType, type GtmConsentValue, type GtmScopeMapping, type GtmTemplateInput, type GtmTemplateMappingOption, type GtmTemplateScopeOption, isGtmConsentType, isHostConfigurableGtmScope, normalizeGtmThemeInput, resolveDefaultConsentFromGtm, } from "./core.js";
2
- export { buildGtmRuntimeSource, buildGtmTemplateParameters, buildGtmTemplateParts, DEFAULT_GTM_LOADER_SCRIPT_BASE_URL, type GtmTemplateParts, type TemplateCondition, type TemplateParameter, type TemplateSelectItem, type TemplateValueValidator, } from "./template-generation.js";
2
+ export { buildGtmPermissions, buildGtmRuntimeSource, buildGtmTemplateArtifact, buildGtmTemplateInfo, buildGtmTemplateParameters, buildGtmTemplateParts, DEFAULT_GTM_LOADER_SCRIPT_BASE_URL, type GtmTemplateArtifact, type GtmTemplateGenerationOptions, type GtmTemplateInfo, type GtmTemplateParts, type GtmTemplatePermission, type TemplateCondition, type TemplateParameter, type TemplateSelectItem, type TemplateValueValidator, } from "./template-generation.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gtm/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,+BAA+B,EAC/B,0BAA0B,EAC1B,oBAAoB,EACpB,iBAAiB,EACjB,4BAA4B,EAC5B,0BAA0B,EAC1B,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,0BAA0B,EAC1B,sBAAsB,EACtB,4BAA4B,GAC5B,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,qBAAqB,EACrB,0BAA0B,EAC1B,qBAAqB,EACrB,kCAAkC,EAClC,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,GAC3B,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gtm/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,+BAA+B,EAC/B,0BAA0B,EAC1B,oBAAoB,EACpB,iBAAiB,EACjB,4BAA4B,EAC5B,0BAA0B,EAC1B,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,0BAA0B,EAC1B,sBAAsB,EACtB,4BAA4B,GAC5B,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,mBAAmB,EACnB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,kCAAkC,EAClC,KAAK,mBAAmB,EACxB,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,GAC3B,MAAM,0BAA0B,CAAC"}
package/dist/gtm/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export { buildInitOptionsFromGtmTemplate, DEFAULT_GTM_SCOPE_MAPPINGS, ESSENTIAL_GTM_SCOPES, GTM_CONSENT_TYPES, GTM_TEMPLATE_MAPPING_OPTIONS, GTM_TEMPLATE_SCOPE_OPTIONS, isGtmConsentType, isHostConfigurableGtmScope, normalizeGtmThemeInput, resolveDefaultConsentFromGtm, } from "./core.js";
2
- export { buildGtmRuntimeSource, buildGtmTemplateParameters, buildGtmTemplateParts, DEFAULT_GTM_LOADER_SCRIPT_BASE_URL, } from "./template-generation.js";
2
+ export { buildGtmPermissions, buildGtmRuntimeSource, buildGtmTemplateArtifact, buildGtmTemplateInfo, buildGtmTemplateParameters, buildGtmTemplateParts, DEFAULT_GTM_LOADER_SCRIPT_BASE_URL, } from "./template-generation.js";
@@ -64,16 +64,66 @@ export type TemplateParameter = {
64
64
  groupStyle: "ZIPPY_CLOSED";
65
65
  subParams: TemplateParameter[];
66
66
  };
67
+ type GtmPermissionStringValue = {
68
+ type: 1;
69
+ string: string;
70
+ };
71
+ type GtmPermissionBooleanValue = {
72
+ type: 8;
73
+ boolean: boolean;
74
+ };
75
+ type GtmPermissionMapValue = {
76
+ type: 3;
77
+ mapKey: GtmPermissionStringValue[];
78
+ mapValue: Array<GtmPermissionStringValue | GtmPermissionBooleanValue | GtmPermissionListValue | GtmPermissionMapValue>;
79
+ };
80
+ type GtmPermissionListValue = {
81
+ type: 2;
82
+ listItem: Array<GtmPermissionStringValue | GtmPermissionBooleanValue | GtmPermissionMapValue>;
83
+ };
84
+ type GtmPermissionParam = {
85
+ key: string;
86
+ value: GtmPermissionStringValue | GtmPermissionBooleanValue | GtmPermissionListValue | GtmPermissionMapValue;
87
+ };
88
+ export type GtmTemplatePermission = {
89
+ instance: {
90
+ key: {
91
+ publicId: string;
92
+ versionId: "1";
93
+ };
94
+ param: GtmPermissionParam[];
95
+ };
96
+ clientAnnotations?: {
97
+ isEditedByUser: true;
98
+ };
99
+ isRequired: true;
100
+ };
101
+ export type GtmTemplateInfo = {
102
+ type: "TAG";
103
+ version: 1;
104
+ displayName: string;
105
+ description: string;
106
+ categories: readonly ["SURVEY", "PERSONALIZATION", "UTILITY"];
107
+ containerContexts: readonly ["WEB"];
108
+ };
67
109
  export type GtmTemplateParts = {
110
+ info: GtmTemplateInfo;
68
111
  parameters: TemplateParameter[];
69
112
  runtimeSource: string;
113
+ permissions: GtmTemplatePermission[];
70
114
  };
71
- export declare const DEFAULT_GTM_LOADER_SCRIPT_BASE_URL = "https://cdn.getuserfeedback.com/widget/loader/v1";
72
- export declare function buildGtmTemplateParameters(): TemplateParameter[];
73
- export declare function buildGtmRuntimeSource(input?: {
115
+ export type GtmTemplateArtifact = GtmTemplateParts;
116
+ export type GtmTemplateGenerationOptions = {
74
117
  loaderScriptBaseUrl?: string;
75
- }): string;
76
- export declare function buildGtmTemplateParts(input?: {
77
- loaderScriptBaseUrl?: string;
78
- }): GtmTemplateParts;
118
+ displayName?: string;
119
+ description?: string;
120
+ };
121
+ export declare const DEFAULT_GTM_LOADER_SCRIPT_BASE_URL = "https://cdn.getuserfeedback.com/widget/loader/v1";
122
+ export declare function buildGtmTemplateParameters(_options?: GtmTemplateGenerationOptions): TemplateParameter[];
123
+ export declare function buildGtmRuntimeSource(input?: GtmTemplateGenerationOptions): string;
124
+ export declare function buildGtmPermissions(input?: GtmTemplateGenerationOptions): GtmTemplatePermission[];
125
+ export declare function buildGtmTemplateInfo(input?: GtmTemplateGenerationOptions): GtmTemplateInfo;
126
+ export declare function buildGtmTemplateParts(input?: GtmTemplateGenerationOptions): GtmTemplateParts;
127
+ export declare function buildGtmTemplateArtifact(input?: GtmTemplateGenerationOptions): GtmTemplateArtifact;
128
+ export {};
79
129
  //# sourceMappingURL=template-generation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"template-generation.d.ts","sourceRoot":"","sources":["../../src/gtm/template-generation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"template-generation.d.ts","sourceRoot":"","sources":["../../src/gtm/template-generation.ts"],"names":[],"mappings":"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"}
@@ -1,36 +1,47 @@
1
1
  import { DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS, } from "@getuserfeedback/protocol";
2
- import { GTM_CONSENT_TYPES, GTM_TEMPLATE_MAPPING_OPTIONS, GTM_TEMPLATE_SCOPE_OPTIONS, isHostConfigurableGtmScope, } from "./core.js";
2
+ import { DEFAULT_GTM_SCOPE_MAPPINGS, ESSENTIAL_GTM_SCOPES, GTM_CONSENT_TYPES, GTM_TEMPLATE_MAPPING_OPTIONS, GTM_TEMPLATE_SCOPE_OPTIONS, isHostConfigurableGtmScope, } from "./core.js";
3
3
  export const DEFAULT_GTM_LOADER_SCRIPT_BASE_URL = "https://cdn.getuserfeedback.com/widget/loader/v1";
4
+ const GTM_TEMPLATE_VERSION = 1;
5
+ const GTM_TEMPLATE_DEFAULT_DISPLAY_NAME = "getuserfeedback Widget";
6
+ const GTM_TEMPLATE_DEFAULT_DESCRIPTION = "Load the getuserfeedback widget with modern loader, theme, and GTM Consent Mode defaults derived from the public protocol contract.";
7
+ const GTM_TEMPLATE_CATEGORIES = [
8
+ "SURVEY",
9
+ "PERSONALIZATION",
10
+ "UTILITY",
11
+ ];
12
+ const GTM_TEMPLATE_CONTAINER_CONTEXTS = ["WEB"];
13
+ const GTM_QUEUE_KEY = "__getuserfeedback_queue";
4
14
  const NON_EMPTY_VALIDATOR = { type: "NON_EMPTY" };
5
- const buildScopeParameterHelp = () => GTM_TEMPLATE_SCOPE_OPTIONS.filter((option) => isHostConfigurableGtmScope(option.scope))
6
- .map((option) => `${option.label}: ${option.description}`)
7
- .join("\n");
8
- const buildDefaultTemplateScopeMappings = () => [
9
- {
10
- scope: "analytics.storage",
11
- mapping: "analytics_storage",
12
- },
13
- {
14
- scope: "analytics.measurement",
15
- mapping: "analytics_storage",
16
- },
17
- {
18
- scope: "personalization.storage",
19
- mapping: "personalization_storage",
20
- },
15
+ const MANUAL_CONSENT_MODE_CONDITION = [
21
16
  {
22
- scope: "ads.storage",
23
- mapping: "ad_storage",
17
+ paramName: "consentMode",
18
+ paramValue: "manual",
19
+ type: "EQUALS",
24
20
  },
21
+ ];
22
+ const AUTOMATIC_CONSENT_MODE_CONDITION = [
25
23
  {
26
- scope: "ads.user_data",
27
- mapping: "ad_user_data",
24
+ paramName: "consentMode",
25
+ paramValue: "automatic",
26
+ type: "EQUALS",
28
27
  },
28
+ ];
29
+ const buildHostConfigurableDefaultScopeMappings = () => [
30
+ ...DEFAULT_GTM_SCOPE_MAPPINGS.filter((mapping) => isHostConfigurableGtmScope(mapping.scope)).map((mapping) => ({
31
+ scope: mapping.scope,
32
+ mapping: mapping.consentType,
33
+ })),
29
34
  {
30
- scope: "ads.personalization",
31
- mapping: "ad_personalization",
35
+ scope: "analytics.measurement",
36
+ mapping: "analytics_storage",
32
37
  },
33
38
  ];
39
+ const buildScopeParameterHelp = () => [
40
+ "Essential scopes are fixed by the loader and are not exposed for GTM override.",
41
+ `${ESSENTIAL_GTM_SCOPES[0]} and ${ESSENTIAL_GTM_SCOPES[1]} always remain baseline scopes outside the GTM template UI.`,
42
+ "",
43
+ ...GTM_TEMPLATE_SCOPE_OPTIONS.filter((option) => isHostConfigurableGtmScope(option.scope)).map((option) => `${option.label}: ${option.description}`),
44
+ ].join("\n");
34
45
  const buildHostConfigurableScopeSelectItems = () => GTM_TEMPLATE_SCOPE_OPTIONS.filter((option) => isHostConfigurableGtmScope(option.scope)).map((option) => ({
35
46
  value: option.scope,
36
47
  displayValue: option.label,
@@ -40,7 +51,45 @@ const buildGtmConsentTypeSelectItems = () => GTM_TEMPLATE_MAPPING_OPTIONS.map((o
40
51
  displayValue: `${option.label} Granted`,
41
52
  }));
42
53
  const trimTrailingSlash = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
43
- export function buildGtmTemplateParameters() {
54
+ const stringValue = (value) => ({
55
+ type: 1,
56
+ string: value,
57
+ });
58
+ const booleanValue = (value) => ({
59
+ type: 8,
60
+ boolean: value,
61
+ });
62
+ const listValue = (items) => ({
63
+ type: 2,
64
+ listItem: items,
65
+ });
66
+ const mapValue = (entries) => ({
67
+ type: 3,
68
+ mapKey: entries.map(([key]) => stringValue(key)),
69
+ mapValue: entries.map(([, value]) => {
70
+ if (typeof value === "string") {
71
+ return stringValue(value);
72
+ }
73
+ if (typeof value === "boolean") {
74
+ return booleanValue(value);
75
+ }
76
+ return value;
77
+ }),
78
+ });
79
+ const buildPermission = (publicId, params) => ({
80
+ instance: {
81
+ key: {
82
+ publicId,
83
+ versionId: "1",
84
+ },
85
+ param: params,
86
+ },
87
+ clientAnnotations: {
88
+ isEditedByUser: true,
89
+ },
90
+ isRequired: true,
91
+ });
92
+ export function buildGtmTemplateParameters(_options) {
44
93
  const scopeHelp = buildScopeParameterHelp();
45
94
  const scopeSelectItems = buildHostConfigurableScopeSelectItems();
46
95
  const mappingSelectItems = buildGtmConsentTypeSelectItems();
@@ -50,7 +99,7 @@ export function buildGtmTemplateParameters() {
50
99
  name: "apiKey",
51
100
  displayName: "API Key",
52
101
  simpleValueType: true,
53
- help: "Find your API key in getuserfeedback.com under Settings Widget.",
102
+ help: "Find your API key in getuserfeedback.com under Settings -> Widget.",
54
103
  valueValidators: [NON_EMPTY_VALIDATOR],
55
104
  },
56
105
  {
@@ -79,16 +128,16 @@ export function buildGtmTemplateParameters() {
79
128
  },
80
129
  {
81
130
  value: "host",
82
- displayValue: "Sync With Host",
131
+ displayValue: "Sync With Host Attributes",
83
132
  },
84
133
  {
85
134
  value: "variable",
86
- displayValue: "Variable",
135
+ displayValue: "Resolve From GTM Variable",
87
136
  },
88
137
  ],
89
138
  simpleValueType: true,
90
139
  defaultValue: "host",
91
- help: "Choose how the widget resolves `init.opts.colorScheme`.",
140
+ help: "Generate protocol-valid `init.opts.colorScheme` using fixed modes, host auto-detect attributes, or a GTM variable.",
92
141
  },
93
142
  {
94
143
  type: "SELECT",
@@ -113,7 +162,7 @@ export function buildGtmTemplateParameters() {
113
162
  displayName: "Host Theme Attributes",
114
163
  simpleValueType: true,
115
164
  defaultValue: DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS.join(","),
116
- help: "Comma-separated attributes to inspect on `<html>` or `<body>` for host color scheme detection.",
165
+ help: "Comma-separated attributes to inspect on `<html>` or `<body>` when syncing the widget to host theme state.",
117
166
  enablingConditions: [
118
167
  {
119
168
  paramName: "themeMode",
@@ -138,17 +187,41 @@ export function buildGtmTemplateParameters() {
138
187
  {
139
188
  value: "automatic",
140
189
  displayValue: "Automatic (Use GTM Consent Mode)",
141
- help: "Reads GTM Consent Mode and translates granted consent types into widget `defaultConsent`.",
190
+ help: "Read GTM consent mode via `isConsentGranted`, translate granted consent types into widget `defaultConsent`, and apply the explicit analytics measurement policy.",
142
191
  },
143
192
  {
144
193
  value: "manual",
145
194
  displayValue: "Manual (Map Public Scopes)",
146
- help: "Override the public, host-configurable widget consent scopes with explicit GTM consent mappings.",
195
+ help: "Override host-configurable widget consent scopes with explicit GTM consent mappings. Essential scopes remain fixed by the loader and are never configurable here.",
147
196
  },
148
197
  ],
149
198
  simpleValueType: true,
150
199
  defaultValue: "automatic",
151
200
  },
201
+ {
202
+ type: "SELECT",
203
+ name: "analyticsMeasurementMode",
204
+ displayName: "Analytics Measurement Policy",
205
+ macrosInSelect: false,
206
+ selectItems: [
207
+ {
208
+ value: "inherit-analytics-storage",
209
+ displayValue: "Inherit analytics_storage",
210
+ },
211
+ {
212
+ value: "granted",
213
+ displayValue: "Always granted",
214
+ },
215
+ {
216
+ value: "denied",
217
+ displayValue: "Always denied",
218
+ },
219
+ ],
220
+ simpleValueType: true,
221
+ defaultValue: "inherit-analytics-storage",
222
+ help: "Controls whether `analytics.measurement` follows GTM `analytics_storage` or is forced on/off in automatic mode.",
223
+ enablingConditions: [...AUTOMATIC_CONSENT_MODE_CONDITION],
224
+ },
152
225
  {
153
226
  type: "SIMPLE_TABLE",
154
227
  name: "scopeMappings",
@@ -172,16 +245,10 @@ export function buildGtmTemplateParameters() {
172
245
  },
173
246
  ],
174
247
  newRowButtonText: "Add Scope",
175
- help: `Configure public widget consent scopes. Essential scopes are fixed by the loader and are not exposed here.\n\n${scopeHelp}`,
176
- enablingConditions: [
177
- {
178
- paramName: "consentMode",
179
- paramValue: "manual",
180
- type: "EQUALS",
181
- },
182
- ],
248
+ help: scopeHelp,
249
+ enablingConditions: [...MANUAL_CONSENT_MODE_CONDITION],
183
250
  valueValidators: [NON_EMPTY_VALIDATOR],
184
- defaultValue: buildDefaultTemplateScopeMappings().map((row) => ({
251
+ defaultValue: buildHostConfigurableDefaultScopeMappings().map((row) => ({
185
252
  scope: row.scope,
186
253
  mapping: row.mapping,
187
254
  })),
@@ -192,7 +259,7 @@ export function buildGtmTemplateParameters() {
192
259
  }
193
260
  export function buildGtmRuntimeSource(input) {
194
261
  const loaderScriptBaseUrl = trimTrailingSlash(input?.loaderScriptBaseUrl ?? DEFAULT_GTM_LOADER_SCRIPT_BASE_URL);
195
- const defaultTemplateScopeMappings = buildDefaultTemplateScopeMappings();
262
+ const defaultTemplateScopeMappings = buildHostConfigurableDefaultScopeMappings();
196
263
  return `
197
264
  const log = require("logToConsole");
198
265
  const injectScript = require("injectScript");
@@ -203,8 +270,9 @@ const isConsentGranted = require("isConsentGranted");
203
270
  const loaderScriptBaseUrl = ${JSON.stringify(loaderScriptBaseUrl)};
204
271
  const gtmConsentTypes = ${JSON.stringify(GTM_CONSENT_TYPES)};
205
272
  const defaultScopeMappings = ${JSON.stringify(defaultTemplateScopeMappings)};
206
- const hostConfigurableScopes = ${JSON.stringify(buildDefaultTemplateScopeMappings().map((row) => row.scope))};
273
+ const hostConfigurableScopes = ${JSON.stringify(defaultTemplateScopeMappings.map((row) => row.scope))};
207
274
  const defaultAutoDetectColorSchemeAttrs = ${JSON.stringify(DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS)};
275
+ const queueKey = ${JSON.stringify(GTM_QUEUE_KEY)};
208
276
 
209
277
  const trimString = function(value) {
210
278
  return typeof value === "string" ? value.trim() : "";
@@ -221,13 +289,13 @@ const normalizeAttrList = function(value) {
221
289
  });
222
290
  };
223
291
 
224
- const normalizeThemeInput = function(input) {
225
- if (input === undefined || input === null) {
292
+ const normalizeThemeInput = function(inputValue) {
293
+ if (inputValue === undefined || inputValue === null) {
226
294
  return undefined;
227
295
  }
228
296
 
229
- if (typeof input === "string") {
230
- const normalized = input.trim().toLowerCase();
297
+ if (typeof inputValue === "string") {
298
+ const normalized = inputValue.trim().toLowerCase();
231
299
  if (!normalized) {
232
300
  return undefined;
233
301
  }
@@ -243,30 +311,13 @@ const normalizeThemeInput = function(input) {
243
311
  autoDetectColorScheme: defaultAutoDetectColorSchemeAttrs.slice(),
244
312
  };
245
313
  }
246
- const attrs = normalizeAttrList(input);
314
+ const attrs = normalizeAttrList(inputValue);
247
315
  return attrs.length > 0
248
316
  ? { autoDetectColorScheme: attrs }
249
317
  : undefined;
250
318
  }
251
319
 
252
- if (!Array.isArray(input)) {
253
- return undefined;
254
- }
255
-
256
- const attrs = input
257
- .filter(function(value) {
258
- return typeof value === "string";
259
- })
260
- .map(function(value) {
261
- return value.trim();
262
- })
263
- .filter(function(value) {
264
- return value.length > 0;
265
- });
266
-
267
- return attrs.length > 0
268
- ? { autoDetectColorScheme: attrs }
269
- : undefined;
320
+ return undefined;
270
321
  };
271
322
 
272
323
  const getThemeInput = function(templateData) {
@@ -297,6 +348,38 @@ const getConsentState = function() {
297
348
  return result;
298
349
  };
299
350
 
351
+ const dedupeScopes = function(scopes) {
352
+ const seen = {};
353
+ const result = [];
354
+ for (let index = 0; index < scopes.length; index += 1) {
355
+ const scope = scopes[index];
356
+ if (typeof scope !== "string" || seen[scope]) {
357
+ continue;
358
+ }
359
+ seen[scope] = true;
360
+ result.push(scope);
361
+ }
362
+ return result;
363
+ };
364
+
365
+ const isKnownConsentType = function(consentType) {
366
+ for (let index = 0; index < gtmConsentTypes.length; index += 1) {
367
+ if (gtmConsentTypes[index] === consentType) {
368
+ return true;
369
+ }
370
+ }
371
+ return false;
372
+ };
373
+
374
+ const isHostConfigurableScope = function(scope) {
375
+ for (let index = 0; index < hostConfigurableScopes.length; index += 1) {
376
+ if (hostConfigurableScopes[index] === scope) {
377
+ return true;
378
+ }
379
+ }
380
+ return false;
381
+ };
382
+
300
383
  const getManualScopeMappings = function(templateData) {
301
384
  const rawRows = Array.isArray(templateData.scopeMappings)
302
385
  ? templateData.scopeMappings
@@ -308,74 +391,90 @@ const getManualScopeMappings = function(templateData) {
308
391
  return undefined;
309
392
  }
310
393
  const scope = trimString(row.scope);
311
- const mapping = trimString(row.mapping);
312
- if (!scope || !mapping) {
394
+ const consentType = trimString(row.mapping);
395
+ if (!scope || !consentType) {
396
+ return undefined;
397
+ }
398
+ if (!isHostConfigurableScope(scope) || !isKnownConsentType(consentType)) {
313
399
  return undefined;
314
400
  }
315
- return { scope: scope, consentType: mapping };
401
+ return {
402
+ scope: scope,
403
+ consentType: consentType,
404
+ };
316
405
  })
317
406
  .filter(function(row) {
318
- if (!row) {
319
- return false;
320
- }
321
- let isHostConfigurableScope = false;
322
- for (let scopeIndex = 0; scopeIndex < hostConfigurableScopes.length; scopeIndex += 1) {
323
- if (hostConfigurableScopes[scopeIndex] === row.scope) {
324
- isHostConfigurableScope = true;
325
- break;
326
- }
327
- }
328
- if (!isHostConfigurableScope) {
329
- return false;
330
- }
331
- for (let index = 0; index < gtmConsentTypes.length; index += 1) {
332
- if (gtmConsentTypes[index] === row.consentType) {
333
- return true;
334
- }
335
- }
336
- return false;
407
+ return row !== undefined;
337
408
  });
338
409
  };
339
410
 
340
- const dedupeScopes = function(scopes) {
341
- const seen = {};
342
- const result = [];
343
- for (let index = 0; index < scopes.length; index += 1) {
344
- const scope = scopes[index];
345
- if (typeof scope !== "string" || seen[scope]) {
346
- continue;
347
- }
348
- seen[scope] = true;
349
- result.push(scope);
411
+ const getAnalyticsMeasurementMode = function(templateData) {
412
+ if (
413
+ templateData.analyticsMeasurementMode === "granted" ||
414
+ templateData.analyticsMeasurementMode === "denied"
415
+ ) {
416
+ return templateData.analyticsMeasurementMode;
350
417
  }
351
- return result;
418
+ return "inherit-analytics-storage";
352
419
  };
353
420
 
354
421
  const buildDefaultConsent = function(templateData) {
355
422
  const consentState = getConsentState();
356
- const scopeMappings =
357
- templateData.consentMode === "manual"
358
- ? getManualScopeMappings(templateData)
359
- : defaultScopeMappings;
360
423
  const grantedScopes = [];
361
424
 
362
- for (let index = 0; index < scopeMappings.length; index += 1) {
363
- const mapping = scopeMappings[index];
364
- const consentType = mapping.consentType || mapping.mapping;
365
- if (consentState[consentType] === "granted") {
366
- grantedScopes.push(mapping.scope);
425
+ if (templateData.consentMode === "manual") {
426
+ const scopeMappings = getManualScopeMappings(templateData);
427
+ for (let index = 0; index < scopeMappings.length; index += 1) {
428
+ const mapping = scopeMappings[index];
429
+ if (consentState[mapping.consentType] === "granted") {
430
+ grantedScopes.push(mapping.scope);
431
+ }
432
+ }
433
+ } else {
434
+ for (let index = 0; index < defaultScopeMappings.length; index += 1) {
435
+ const mapping = defaultScopeMappings[index];
436
+ if (consentState[mapping.mapping] === "granted") {
437
+ grantedScopes.push(mapping.scope);
438
+ }
367
439
  }
368
- }
369
440
 
370
- if (consentState.analytics_storage === "granted") {
371
- grantedScopes.push("analytics.measurement");
441
+ const analyticsMeasurementMode = getAnalyticsMeasurementMode(templateData);
442
+ if (analyticsMeasurementMode === "granted") {
443
+ grantedScopes.push("analytics.measurement");
444
+ }
445
+ if (
446
+ analyticsMeasurementMode === "inherit-analytics-storage" &&
447
+ consentState.analytics_storage === "granted"
448
+ ) {
449
+ grantedScopes.push("analytics.measurement");
450
+ }
372
451
  }
373
452
 
374
453
  const dedupedScopes = dedupeScopes(grantedScopes);
375
454
  return dedupedScopes.length > 0 ? dedupedScopes : undefined;
376
455
  };
377
456
 
457
+ const onSuccess = function() {
458
+ if (typeof data.gtmOnSuccess === "function") {
459
+ data.gtmOnSuccess();
460
+ }
461
+ };
462
+
463
+ const onFailure = function() {
464
+ if (typeof data.gtmOnFailure === "function") {
465
+ data.gtmOnFailure();
466
+ }
467
+ };
468
+
378
469
  const apiKey = trimString(data.apiKey);
470
+ if (!apiKey) {
471
+ onFailure();
472
+ return;
473
+ }
474
+
475
+ const widgetUrl =
476
+ loaderScriptBaseUrl + "/" + encodeURIComponent(apiKey) + "/loader.js";
477
+ const queuePush = createQueue(queueKey);
379
478
  const initOptions = {
380
479
  apiKey: apiKey,
381
480
  clientMeta: {
@@ -394,10 +493,6 @@ if (defaultConsent !== undefined) {
394
493
  initOptions.defaultConsent = defaultConsent;
395
494
  }
396
495
 
397
- const widgetUrl =
398
- loaderScriptBaseUrl + "/" + encodeURIComponent(apiKey) + "/loader.js";
399
- const queuePush = createQueue("__getuserfeedback_queue");
400
-
401
496
  queuePush({
402
497
  kind: "init",
403
498
  opts: initOptions,
@@ -405,18 +500,6 @@ queuePush({
405
500
 
406
501
  log("Loading getuserfeedback loader:", widgetUrl);
407
502
 
408
- const onSuccess = function() {
409
- if (typeof data.gtmOnSuccess === "function") {
410
- data.gtmOnSuccess();
411
- }
412
- };
413
-
414
- const onFailure = function() {
415
- if (typeof data.gtmOnFailure === "function") {
416
- data.gtmOnFailure();
417
- }
418
- };
419
-
420
503
  if (queryPermission("inject_script", widgetUrl)) {
421
504
  injectScript(widgetUrl, onSuccess, onFailure, "getuserfeedback");
422
505
  } else {
@@ -424,9 +507,65 @@ if (queryPermission("inject_script", widgetUrl)) {
424
507
  }
425
508
  `.trim();
426
509
  }
510
+ export function buildGtmPermissions(input) {
511
+ const loaderScriptBaseUrl = trimTrailingSlash(input?.loaderScriptBaseUrl ?? DEFAULT_GTM_LOADER_SCRIPT_BASE_URL);
512
+ const loaderUrlPattern = `${loaderScriptBaseUrl}/*/loader.js`;
513
+ return [
514
+ buildPermission("inject_script", [
515
+ {
516
+ key: "urls",
517
+ value: listValue([stringValue(loaderUrlPattern)]),
518
+ },
519
+ ]),
520
+ buildPermission("logging", [
521
+ {
522
+ key: "environments",
523
+ value: stringValue("debug"),
524
+ },
525
+ ]),
526
+ buildPermission("access_globals", [
527
+ {
528
+ key: "keys",
529
+ value: listValue([
530
+ mapValue([
531
+ ["key", GTM_QUEUE_KEY],
532
+ ["read", true],
533
+ ["write", true],
534
+ ["execute", false],
535
+ ]),
536
+ ]),
537
+ },
538
+ ]),
539
+ buildPermission("access_consent", [
540
+ {
541
+ key: "consentTypes",
542
+ value: listValue(GTM_CONSENT_TYPES.map((consentType) => mapValue([
543
+ ["consentType", consentType],
544
+ ["read", true],
545
+ ["write", false],
546
+ ]))),
547
+ },
548
+ ]),
549
+ ];
550
+ }
551
+ export function buildGtmTemplateInfo(input) {
552
+ return {
553
+ type: "TAG",
554
+ version: GTM_TEMPLATE_VERSION,
555
+ displayName: input?.displayName ?? GTM_TEMPLATE_DEFAULT_DISPLAY_NAME,
556
+ description: input?.description ?? GTM_TEMPLATE_DEFAULT_DESCRIPTION,
557
+ categories: GTM_TEMPLATE_CATEGORIES,
558
+ containerContexts: GTM_TEMPLATE_CONTAINER_CONTEXTS,
559
+ };
560
+ }
427
561
  export function buildGtmTemplateParts(input) {
428
562
  return {
429
- parameters: buildGtmTemplateParameters(),
563
+ info: buildGtmTemplateInfo(input),
564
+ parameters: buildGtmTemplateParameters(input),
430
565
  runtimeSource: buildGtmRuntimeSource(input),
566
+ permissions: buildGtmPermissions(input),
431
567
  };
432
568
  }
569
+ export function buildGtmTemplateArtifact(input) {
570
+ return buildGtmTemplateParts(input);
571
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getuserfeedback/adapters",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "getuserfeedback integration adapters",
5
5
  "keywords": [
6
6
  "getuserfeedback",