@getuserfeedback/adapters 0.2.1 → 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.
@@ -0,0 +1,58 @@
1
+ import { type InitOptions, type Scope } from "@getuserfeedback/protocol";
2
+ export declare const GTM_CONSENT_TYPES: readonly [
3
+ "functionality_storage",
4
+ "security_storage",
5
+ "analytics_storage",
6
+ "personalization_storage",
7
+ "ad_storage",
8
+ "ad_user_data",
9
+ "ad_personalization"
10
+ ];
11
+ export type GtmConsentType = (typeof GTM_CONSENT_TYPES)[number];
12
+ export type GtmConsentValue = "granted" | "denied";
13
+ export type GtmAnalyticsMeasurementMode = "inherit-analytics-storage" | "granted" | "denied";
14
+ export type GtmScopeMapping = {
15
+ scope: Scope;
16
+ consentType: GtmConsentType;
17
+ };
18
+ export type GtmConsentState = Partial<Record<GtmConsentType, GtmConsentValue>>;
19
+ export type GtmTemplateInput = {
20
+ apiKey: string;
21
+ consentState?: GtmConsentState;
22
+ scopeMappings?: ReadonlyArray<GtmScopeMapping>;
23
+ analyticsMeasurementMode?: GtmAnalyticsMeasurementMode;
24
+ colorScheme?: unknown;
25
+ disableTelemetry?: boolean;
26
+ enableDebug?: InitOptions["enableDebug"];
27
+ capabilities?: InitOptions["capabilities"];
28
+ runtimeEndpoints?: InitOptions["runtimeEndpoints"];
29
+ clientMeta?: InitOptions["clientMeta"];
30
+ };
31
+ type ProtocolConsentScope = NonNullable<Exclude<InitOptions["defaultConsent"], string>>[number];
32
+ export type GtmTemplateScopeOption = {
33
+ label: string;
34
+ scope: Scope;
35
+ description: string;
36
+ };
37
+ export type GtmTemplateMappingOption = {
38
+ consentType: GtmConsentType;
39
+ label: string;
40
+ };
41
+ export declare const ESSENTIAL_GTM_SCOPES: readonly [
42
+ "functionality.storage",
43
+ "security.storage"
44
+ ];
45
+ export declare const isHostConfigurableGtmScope: (scope: Scope) => scope is ProtocolConsentScope;
46
+ export declare const DEFAULT_GTM_SCOPE_MAPPINGS: ReadonlyArray<GtmScopeMapping>;
47
+ export declare const GTM_TEMPLATE_SCOPE_OPTIONS: ReadonlyArray<GtmTemplateScopeOption>;
48
+ export declare const GTM_TEMPLATE_MAPPING_OPTIONS: ReadonlyArray<GtmTemplateMappingOption>;
49
+ export declare function isGtmConsentType(value: unknown): value is GtmConsentType;
50
+ export declare function normalizeGtmThemeInput(input: unknown): InitOptions["colorScheme"] | undefined;
51
+ export declare function resolveDefaultConsentFromGtm(input: {
52
+ consentState?: GtmConsentState;
53
+ scopeMappings?: ReadonlyArray<GtmScopeMapping>;
54
+ analyticsMeasurementMode?: GtmAnalyticsMeasurementMode;
55
+ }): InitOptions["defaultConsent"] | undefined;
56
+ export declare function buildInitOptionsFromGtmTemplate(input: GtmTemplateInput): InitOptions;
57
+ export {};
58
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,166 @@
1
+ import { colorSchemeConfigInputSchema, consentConfigInputSchema, DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS, listScopes, parseInitOptions, } from "@getuserfeedback/protocol";
2
+ export const GTM_CONSENT_TYPES = [
3
+ "functionality_storage",
4
+ "security_storage",
5
+ "analytics_storage",
6
+ "personalization_storage",
7
+ "ad_storage",
8
+ "ad_user_data",
9
+ "ad_personalization",
10
+ ];
11
+ export const ESSENTIAL_GTM_SCOPES = ["functionality.storage", "security.storage"];
12
+ const essentialScopes = new Set(ESSENTIAL_GTM_SCOPES);
13
+ export const isHostConfigurableGtmScope = (scope) => !essentialScopes.has(scope);
14
+ export const DEFAULT_GTM_SCOPE_MAPPINGS = [
15
+ {
16
+ scope: "functionality.storage",
17
+ consentType: "functionality_storage",
18
+ },
19
+ {
20
+ scope: "security.storage",
21
+ consentType: "security_storage",
22
+ },
23
+ {
24
+ scope: "analytics.storage",
25
+ consentType: "analytics_storage",
26
+ },
27
+ {
28
+ scope: "personalization.storage",
29
+ consentType: "personalization_storage",
30
+ },
31
+ {
32
+ scope: "ads.storage",
33
+ consentType: "ad_storage",
34
+ },
35
+ {
36
+ scope: "ads.user_data",
37
+ consentType: "ad_user_data",
38
+ },
39
+ {
40
+ scope: "ads.personalization",
41
+ consentType: "ad_personalization",
42
+ },
43
+ ];
44
+ const toTitleCase = (value) => value
45
+ .split(/[_\-.]/g)
46
+ .filter((part) => part.length > 0)
47
+ .map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`)
48
+ .join(" ");
49
+ const uniqueScopes = (scopes) => {
50
+ const deduped = new Set();
51
+ for (const scope of scopes) {
52
+ if (!isHostConfigurableGtmScope(scope)) {
53
+ continue;
54
+ }
55
+ deduped.add(scope);
56
+ }
57
+ return [...deduped];
58
+ };
59
+ const normalizeAttrList = (value) => value
60
+ .split(",")
61
+ .map((entry) => entry.trim())
62
+ .filter((entry) => entry.length > 0);
63
+ export const GTM_TEMPLATE_SCOPE_OPTIONS = listScopes().map((scopeMeta) => ({
64
+ label: toTitleCase(scopeMeta.id),
65
+ scope: scopeMeta.id,
66
+ description: scopeMeta.description,
67
+ }));
68
+ export const GTM_TEMPLATE_MAPPING_OPTIONS = GTM_CONSENT_TYPES.map((consentType) => ({
69
+ consentType,
70
+ label: toTitleCase(consentType),
71
+ }));
72
+ export function isGtmConsentType(value) {
73
+ if (typeof value !== "string") {
74
+ return false;
75
+ }
76
+ return GTM_CONSENT_TYPES.some((consentType) => consentType === value);
77
+ }
78
+ export function normalizeGtmThemeInput(input) {
79
+ if (input === undefined || input === null) {
80
+ return undefined;
81
+ }
82
+ if (Array.isArray(input)) {
83
+ const attrs = input
84
+ .filter((value) => typeof value === "string")
85
+ .map((value) => value.trim())
86
+ .filter((value) => value.length > 0);
87
+ if (attrs.length === 0) {
88
+ return undefined;
89
+ }
90
+ return { autoDetectColorScheme: attrs };
91
+ }
92
+ if (typeof input !== "string") {
93
+ return undefined;
94
+ }
95
+ const normalized = input.trim().toLowerCase();
96
+ if (normalized.length === 0) {
97
+ return undefined;
98
+ }
99
+ if (normalized === "light" ||
100
+ normalized === "dark" ||
101
+ normalized === "system") {
102
+ return normalized;
103
+ }
104
+ if (normalized === "auto") {
105
+ return colorSchemeConfigInputSchema.parse({
106
+ autoDetectColorScheme: [...DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS],
107
+ });
108
+ }
109
+ const attrs = normalizeAttrList(input);
110
+ if (attrs.length === 0) {
111
+ return undefined;
112
+ }
113
+ return colorSchemeConfigInputSchema.parse({
114
+ autoDetectColorScheme: attrs,
115
+ });
116
+ }
117
+ export function resolveDefaultConsentFromGtm(input) {
118
+ const consentState = input.consentState;
119
+ if (!consentState) {
120
+ return undefined;
121
+ }
122
+ const mappings = input.scopeMappings ?? DEFAULT_GTM_SCOPE_MAPPINGS;
123
+ const grantedScopes = uniqueScopes(mappings.flatMap((mapping) => consentState[mapping.consentType] === "granted" ? [mapping.scope] : []));
124
+ const analyticsMeasurementMode = input.analyticsMeasurementMode ?? "inherit-analytics-storage";
125
+ if (analyticsMeasurementMode === "granted") {
126
+ grantedScopes.push("analytics.measurement");
127
+ }
128
+ if (analyticsMeasurementMode === "inherit-analytics-storage" &&
129
+ consentState.analytics_storage === "granted") {
130
+ grantedScopes.push("analytics.measurement");
131
+ }
132
+ const normalizedGrantedScopes = uniqueScopes(grantedScopes);
133
+ return normalizedGrantedScopes.length > 0
134
+ ? consentConfigInputSchema.parse(normalizedGrantedScopes)
135
+ : undefined;
136
+ }
137
+ export function buildInitOptionsFromGtmTemplate(input) {
138
+ const colorScheme = normalizeGtmThemeInput(input.colorScheme);
139
+ const defaultConsent = resolveDefaultConsentFromGtm({
140
+ consentState: input.consentState,
141
+ scopeMappings: input.scopeMappings,
142
+ analyticsMeasurementMode: input.analyticsMeasurementMode,
143
+ });
144
+ return parseInitOptions({
145
+ apiKey: input.apiKey,
146
+ ...(colorScheme !== undefined ? { colorScheme } : {}),
147
+ ...(input.disableTelemetry !== undefined
148
+ ? { disableTelemetry: input.disableTelemetry }
149
+ : {}),
150
+ ...(input.enableDebug !== undefined
151
+ ? { enableDebug: input.enableDebug }
152
+ : {}),
153
+ ...(defaultConsent !== undefined ? { defaultConsent } : {}),
154
+ clientMeta: {
155
+ ...(input.clientMeta ?? {}),
156
+ loader: "gtm",
157
+ transport: input.clientMeta?.transport ?? "tag-manager",
158
+ },
159
+ ...(input.capabilities !== undefined
160
+ ? { capabilities: input.capabilities }
161
+ : {}),
162
+ ...(input.runtimeEndpoints !== undefined
163
+ ? { runtimeEndpoints: input.runtimeEndpoints }
164
+ : {}),
165
+ });
166
+ }
@@ -1,52 +1,3 @@
1
- import { type InitOptions, type Scope } from "@getuserfeedback/protocol";
2
- export declare const GTM_CONSENT_TYPES: readonly [
3
- "functionality_storage",
4
- "security_storage",
5
- "analytics_storage",
6
- "personalization_storage",
7
- "ad_storage",
8
- "ad_user_data",
9
- "ad_personalization"
10
- ];
11
- export type GtmConsentType = (typeof GTM_CONSENT_TYPES)[number];
12
- export type GtmConsentValue = "granted" | "denied";
13
- export type GtmAnalyticsMeasurementMode = "inherit-analytics-storage" | "granted" | "denied";
14
- export type GtmScopeMapping = {
15
- scope: Scope;
16
- consentType: GtmConsentType;
17
- };
18
- export type GtmConsentState = Partial<Record<GtmConsentType, GtmConsentValue>>;
19
- export type GtmTemplateInput = {
20
- apiKey: string;
21
- consentState?: GtmConsentState;
22
- scopeMappings?: ReadonlyArray<GtmScopeMapping>;
23
- analyticsMeasurementMode?: GtmAnalyticsMeasurementMode;
24
- colorScheme?: unknown;
25
- disableTelemetry?: boolean;
26
- enableDebug?: InitOptions["enableDebug"];
27
- capabilities?: InitOptions["capabilities"];
28
- runtimeEndpoints?: InitOptions["runtimeEndpoints"];
29
- clientMeta?: InitOptions["clientMeta"];
30
- };
31
- type GtmTemplateScopeOption = {
32
- label: string;
33
- scope: Scope;
34
- description: string;
35
- };
36
- type GtmTemplateMappingOption = {
37
- consentType: GtmConsentType;
38
- label: string;
39
- };
40
- export declare const DEFAULT_GTM_SCOPE_MAPPINGS: ReadonlyArray<GtmScopeMapping>;
41
- export declare const GTM_TEMPLATE_SCOPE_OPTIONS: ReadonlyArray<GtmTemplateScopeOption>;
42
- export declare const GTM_TEMPLATE_MAPPING_OPTIONS: ReadonlyArray<GtmTemplateMappingOption>;
43
- export declare function isGtmConsentType(value: unknown): value is GtmConsentType;
44
- export declare function normalizeGtmThemeInput(input: unknown): InitOptions["colorScheme"] | undefined;
45
- export declare function resolveDefaultConsentFromGtm(input: {
46
- consentState?: GtmConsentState;
47
- scopeMappings?: ReadonlyArray<GtmScopeMapping>;
48
- analyticsMeasurementMode?: GtmAnalyticsMeasurementMode;
49
- }): InitOptions["defaultConsent"] | undefined;
50
- export declare function buildInitOptionsFromGtmTemplate(input: GtmTemplateInput): InitOptions;
51
- export {};
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 { 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";
52
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,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;AAMF,KAAK,sBAAsB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC/B,WAAW,EAAE,cAAc,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAUF,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":"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,165 +1,2 @@
1
- import { DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS, listScopes, } from "@getuserfeedback/protocol";
2
- export const GTM_CONSENT_TYPES = [
3
- "functionality_storage",
4
- "security_storage",
5
- "analytics_storage",
6
- "personalization_storage",
7
- "ad_storage",
8
- "ad_user_data",
9
- "ad_personalization",
10
- ];
11
- const essentialScopes = new Set([
12
- "functionality.storage",
13
- "security.storage",
14
- ]);
15
- const isProtocolConsentScope = (scope) => !essentialScopes.has(scope);
16
- export const DEFAULT_GTM_SCOPE_MAPPINGS = [
17
- {
18
- scope: "functionality.storage",
19
- consentType: "functionality_storage",
20
- },
21
- {
22
- scope: "security.storage",
23
- consentType: "security_storage",
24
- },
25
- {
26
- scope: "analytics.storage",
27
- consentType: "analytics_storage",
28
- },
29
- {
30
- scope: "personalization.storage",
31
- consentType: "personalization_storage",
32
- },
33
- {
34
- scope: "ads.storage",
35
- consentType: "ad_storage",
36
- },
37
- {
38
- scope: "ads.user_data",
39
- consentType: "ad_user_data",
40
- },
41
- {
42
- scope: "ads.personalization",
43
- consentType: "ad_personalization",
44
- },
45
- ];
46
- const toTitleCase = (value) => value
47
- .split(/[_\-.]/g)
48
- .filter((part) => part.length > 0)
49
- .map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`)
50
- .join(" ");
51
- const uniqueScopes = (scopes) => {
52
- const deduped = new Set();
53
- for (const scope of scopes) {
54
- if (!isProtocolConsentScope(scope)) {
55
- continue;
56
- }
57
- deduped.add(scope);
58
- }
59
- return [...deduped];
60
- };
61
- const normalizeAttrList = (value) => value
62
- .split(",")
63
- .map((entry) => entry.trim())
64
- .filter((entry) => entry.length > 0);
65
- export const GTM_TEMPLATE_SCOPE_OPTIONS = listScopes().map((scopeMeta) => ({
66
- label: toTitleCase(scopeMeta.id),
67
- scope: scopeMeta.id,
68
- description: scopeMeta.description,
69
- }));
70
- export const GTM_TEMPLATE_MAPPING_OPTIONS = GTM_CONSENT_TYPES.map((consentType) => ({
71
- consentType,
72
- label: toTitleCase(consentType),
73
- }));
74
- export function isGtmConsentType(value) {
75
- if (typeof value !== "string") {
76
- return false;
77
- }
78
- return GTM_CONSENT_TYPES.some((consentType) => consentType === value);
79
- }
80
- export function normalizeGtmThemeInput(input) {
81
- if (input === undefined || input === null) {
82
- return undefined;
83
- }
84
- if (Array.isArray(input)) {
85
- const attrs = input
86
- .filter((value) => typeof value === "string")
87
- .map((value) => value.trim())
88
- .filter((value) => value.length > 0);
89
- if (attrs.length === 0) {
90
- return undefined;
91
- }
92
- return { autoDetectColorScheme: attrs };
93
- }
94
- if (typeof input !== "string") {
95
- return undefined;
96
- }
97
- const normalized = input.trim().toLowerCase();
98
- if (normalized.length === 0) {
99
- return undefined;
100
- }
101
- if (normalized === "light" ||
102
- normalized === "dark" ||
103
- normalized === "system") {
104
- return normalized;
105
- }
106
- if (normalized === "auto") {
107
- return {
108
- autoDetectColorScheme: [...DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS],
109
- };
110
- }
111
- const attrs = normalizeAttrList(input);
112
- if (attrs.length === 0) {
113
- return undefined;
114
- }
115
- return {
116
- autoDetectColorScheme: attrs,
117
- };
118
- }
119
- export function resolveDefaultConsentFromGtm(input) {
120
- const consentState = input.consentState;
121
- if (!consentState) {
122
- return undefined;
123
- }
124
- const mappings = input.scopeMappings ?? DEFAULT_GTM_SCOPE_MAPPINGS;
125
- const grantedScopes = uniqueScopes(mappings.flatMap((mapping) => consentState[mapping.consentType] === "granted" ? [mapping.scope] : []));
126
- const analyticsMeasurementMode = input.analyticsMeasurementMode ?? "inherit-analytics-storage";
127
- if (analyticsMeasurementMode === "granted") {
128
- grantedScopes.push("analytics.measurement");
129
- }
130
- if (analyticsMeasurementMode === "inherit-analytics-storage" &&
131
- consentState.analytics_storage === "granted") {
132
- grantedScopes.push("analytics.measurement");
133
- }
134
- return uniqueScopes(grantedScopes);
135
- }
136
- export function buildInitOptionsFromGtmTemplate(input) {
137
- const colorScheme = normalizeGtmThemeInput(input.colorScheme);
138
- const defaultConsent = resolveDefaultConsentFromGtm({
139
- consentState: input.consentState,
140
- scopeMappings: input.scopeMappings,
141
- analyticsMeasurementMode: input.analyticsMeasurementMode,
142
- });
143
- return {
144
- apiKey: input.apiKey,
145
- ...(colorScheme !== undefined ? { colorScheme } : {}),
146
- ...(input.disableTelemetry !== undefined
147
- ? { disableTelemetry: input.disableTelemetry }
148
- : {}),
149
- ...(input.enableDebug !== undefined
150
- ? { enableDebug: input.enableDebug }
151
- : {}),
152
- ...(defaultConsent !== undefined ? { defaultConsent } : {}),
153
- clientMeta: {
154
- ...(input.clientMeta ?? {}),
155
- loader: "gtm",
156
- transport: input.clientMeta?.transport ?? "tag-manager",
157
- },
158
- ...(input.capabilities !== undefined
159
- ? { capabilities: input.capabilities }
160
- : {}),
161
- ...(input.runtimeEndpoints !== undefined
162
- ? { runtimeEndpoints: input.runtimeEndpoints }
163
- : {}),
164
- };
165
- }
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 { buildGtmPermissions, buildGtmRuntimeSource, buildGtmTemplateArtifact, buildGtmTemplateInfo, buildGtmTemplateParameters, buildGtmTemplateParts, DEFAULT_GTM_LOADER_SCRIPT_BASE_URL, } from "./template-generation.js";
@@ -0,0 +1,129 @@
1
+ export type TemplateSelectItem = {
2
+ value: string;
3
+ displayValue: string;
4
+ };
5
+ export type TemplateCondition = {
6
+ paramName: string;
7
+ paramValue: string;
8
+ type: "EQUALS";
9
+ };
10
+ export type TemplateValueValidator = {
11
+ type: "NON_EMPTY";
12
+ };
13
+ export type TemplateParameter = {
14
+ type: "TEXT";
15
+ name: string;
16
+ displayName: string;
17
+ simpleValueType: true;
18
+ help?: string;
19
+ defaultValue?: string;
20
+ valueValidators?: TemplateValueValidator[];
21
+ enablingConditions?: TemplateCondition[];
22
+ } | {
23
+ type: "SELECT";
24
+ name: string;
25
+ displayName: string;
26
+ simpleValueType: true;
27
+ help?: string;
28
+ defaultValue?: string;
29
+ macrosInSelect: boolean;
30
+ selectItems: TemplateSelectItem[];
31
+ valueValidators?: TemplateValueValidator[];
32
+ enablingConditions?: TemplateCondition[];
33
+ } | {
34
+ type: "RADIO";
35
+ name: string;
36
+ displayName: string;
37
+ simpleValueType: true;
38
+ defaultValue: string;
39
+ radioItems: Array<TemplateSelectItem & {
40
+ help?: string;
41
+ }>;
42
+ } | {
43
+ type: "SIMPLE_TABLE";
44
+ name: string;
45
+ displayName: string;
46
+ help?: string;
47
+ defaultValue?: Array<Record<string, string>>;
48
+ newRowButtonText: string;
49
+ simpleTableColumns: Array<{
50
+ defaultValue?: string;
51
+ displayName: string;
52
+ name: string;
53
+ type: "SELECT";
54
+ isUnique?: boolean;
55
+ macrosInSelect?: boolean;
56
+ selectItems: TemplateSelectItem[];
57
+ }>;
58
+ valueValidators?: TemplateValueValidator[];
59
+ enablingConditions?: TemplateCondition[];
60
+ } | {
61
+ type: "GROUP";
62
+ name: string;
63
+ displayName: string;
64
+ groupStyle: "ZIPPY_CLOSED";
65
+ subParams: TemplateParameter[];
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
+ };
109
+ export type GtmTemplateParts = {
110
+ info: GtmTemplateInfo;
111
+ parameters: TemplateParameter[];
112
+ runtimeSource: string;
113
+ permissions: GtmTemplatePermission[];
114
+ };
115
+ export type GtmTemplateArtifact = GtmTemplateParts;
116
+ export type GtmTemplateGenerationOptions = {
117
+ loaderScriptBaseUrl?: string;
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 {};
129
+ //# sourceMappingURL=template-generation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-generation.d.ts","sourceRoot":"","sources":["../../src/gtm/template-generation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,571 @@
1
+ import { DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS, } from "@getuserfeedback/protocol";
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
+ 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";
14
+ 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
+ const buildHostConfigurableDefaultScopeMappings = () => [
30
+ ...DEFAULT_GTM_SCOPE_MAPPINGS.filter((mapping) => isHostConfigurableGtmScope(mapping.scope)).map((mapping) => ({
31
+ scope: mapping.scope,
32
+ mapping: mapping.consentType,
33
+ })),
34
+ {
35
+ scope: "analytics.measurement",
36
+ mapping: "analytics_storage",
37
+ },
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");
45
+ const buildHostConfigurableScopeSelectItems = () => GTM_TEMPLATE_SCOPE_OPTIONS.filter((option) => isHostConfigurableGtmScope(option.scope)).map((option) => ({
46
+ value: option.scope,
47
+ displayValue: option.label,
48
+ }));
49
+ const buildGtmConsentTypeSelectItems = () => GTM_TEMPLATE_MAPPING_OPTIONS.map((option) => ({
50
+ value: option.consentType,
51
+ displayValue: `${option.label} Granted`,
52
+ }));
53
+ const trimTrailingSlash = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
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) {
93
+ const scopeHelp = buildScopeParameterHelp();
94
+ const scopeSelectItems = buildHostConfigurableScopeSelectItems();
95
+ const mappingSelectItems = buildGtmConsentTypeSelectItems();
96
+ return [
97
+ {
98
+ type: "TEXT",
99
+ name: "apiKey",
100
+ displayName: "API Key",
101
+ simpleValueType: true,
102
+ help: "Find your API key in getuserfeedback.com under Settings -> Widget.",
103
+ valueValidators: [NON_EMPTY_VALIDATOR],
104
+ },
105
+ {
106
+ type: "GROUP",
107
+ name: "themeGroup",
108
+ displayName: "Theme Settings",
109
+ groupStyle: "ZIPPY_CLOSED",
110
+ subParams: [
111
+ {
112
+ type: "SELECT",
113
+ name: "themeMode",
114
+ displayName: "Theme Mode",
115
+ macrosInSelect: false,
116
+ selectItems: [
117
+ {
118
+ value: "light",
119
+ displayValue: "Always Light",
120
+ },
121
+ {
122
+ value: "dark",
123
+ displayValue: "Always Dark",
124
+ },
125
+ {
126
+ value: "system",
127
+ displayValue: "Follow System Preference",
128
+ },
129
+ {
130
+ value: "host",
131
+ displayValue: "Sync With Host Attributes",
132
+ },
133
+ {
134
+ value: "variable",
135
+ displayValue: "Resolve From GTM Variable",
136
+ },
137
+ ],
138
+ simpleValueType: true,
139
+ defaultValue: "host",
140
+ help: "Generate protocol-valid `init.opts.colorScheme` using fixed modes, host auto-detect attributes, or a GTM variable.",
141
+ },
142
+ {
143
+ type: "SELECT",
144
+ name: "themeModeVariable",
145
+ displayName: "Theme Mode Variable",
146
+ macrosInSelect: true,
147
+ selectItems: [],
148
+ simpleValueType: true,
149
+ help: "Variable must resolve to `light`, `dark`, `system`, `auto`, or a comma-separated attribute list.",
150
+ enablingConditions: [
151
+ {
152
+ paramName: "themeMode",
153
+ paramValue: "variable",
154
+ type: "EQUALS",
155
+ },
156
+ ],
157
+ valueValidators: [NON_EMPTY_VALIDATOR],
158
+ },
159
+ {
160
+ type: "TEXT",
161
+ name: "themeAttr",
162
+ displayName: "Host Theme Attributes",
163
+ simpleValueType: true,
164
+ defaultValue: DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS.join(","),
165
+ help: "Comma-separated attributes to inspect on `<html>` or `<body>` when syncing the widget to host theme state.",
166
+ enablingConditions: [
167
+ {
168
+ paramName: "themeMode",
169
+ paramValue: "host",
170
+ type: "EQUALS",
171
+ },
172
+ ],
173
+ },
174
+ ],
175
+ },
176
+ {
177
+ type: "GROUP",
178
+ name: "consentGroup",
179
+ displayName: "Consent Settings",
180
+ groupStyle: "ZIPPY_CLOSED",
181
+ subParams: [
182
+ {
183
+ type: "RADIO",
184
+ name: "consentMode",
185
+ displayName: "Consent Mode",
186
+ radioItems: [
187
+ {
188
+ value: "automatic",
189
+ displayValue: "Automatic (Use GTM Consent Mode)",
190
+ help: "Read GTM consent mode via `isConsentGranted`, translate granted consent types into widget `defaultConsent`, and apply the explicit analytics measurement policy.",
191
+ },
192
+ {
193
+ value: "manual",
194
+ displayValue: "Manual (Map Public Scopes)",
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.",
196
+ },
197
+ ],
198
+ simpleValueType: true,
199
+ defaultValue: "automatic",
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
+ },
225
+ {
226
+ type: "SIMPLE_TABLE",
227
+ name: "scopeMappings",
228
+ displayName: "Scope Mappings",
229
+ simpleTableColumns: [
230
+ {
231
+ defaultValue: "analytics.storage",
232
+ displayName: "Widget Scope",
233
+ name: "scope",
234
+ type: "SELECT",
235
+ isUnique: true,
236
+ selectItems: scopeSelectItems,
237
+ },
238
+ {
239
+ defaultValue: "analytics_storage",
240
+ displayName: "Granted When",
241
+ name: "mapping",
242
+ type: "SELECT",
243
+ macrosInSelect: true,
244
+ selectItems: mappingSelectItems,
245
+ },
246
+ ],
247
+ newRowButtonText: "Add Scope",
248
+ help: scopeHelp,
249
+ enablingConditions: [...MANUAL_CONSENT_MODE_CONDITION],
250
+ valueValidators: [NON_EMPTY_VALIDATOR],
251
+ defaultValue: buildHostConfigurableDefaultScopeMappings().map((row) => ({
252
+ scope: row.scope,
253
+ mapping: row.mapping,
254
+ })),
255
+ },
256
+ ],
257
+ },
258
+ ];
259
+ }
260
+ export function buildGtmRuntimeSource(input) {
261
+ const loaderScriptBaseUrl = trimTrailingSlash(input?.loaderScriptBaseUrl ?? DEFAULT_GTM_LOADER_SCRIPT_BASE_URL);
262
+ const defaultTemplateScopeMappings = buildHostConfigurableDefaultScopeMappings();
263
+ return `
264
+ const log = require("logToConsole");
265
+ const injectScript = require("injectScript");
266
+ const queryPermission = require("queryPermission");
267
+ const createQueue = require("createQueue");
268
+ const isConsentGranted = require("isConsentGranted");
269
+
270
+ const loaderScriptBaseUrl = ${JSON.stringify(loaderScriptBaseUrl)};
271
+ const gtmConsentTypes = ${JSON.stringify(GTM_CONSENT_TYPES)};
272
+ const defaultScopeMappings = ${JSON.stringify(defaultTemplateScopeMappings)};
273
+ const hostConfigurableScopes = ${JSON.stringify(defaultTemplateScopeMappings.map((row) => row.scope))};
274
+ const defaultAutoDetectColorSchemeAttrs = ${JSON.stringify(DEFAULT_AUTO_DETECT_COLOR_SCHEME_ATTRS)};
275
+ const queueKey = ${JSON.stringify(GTM_QUEUE_KEY)};
276
+
277
+ const trimString = function(value) {
278
+ return typeof value === "string" ? value.trim() : "";
279
+ };
280
+
281
+ const normalizeAttrList = function(value) {
282
+ return value
283
+ .split(",")
284
+ .map(function(entry) {
285
+ return entry.trim();
286
+ })
287
+ .filter(function(entry) {
288
+ return entry.length > 0;
289
+ });
290
+ };
291
+
292
+ const normalizeThemeInput = function(inputValue) {
293
+ if (inputValue === undefined || inputValue === null) {
294
+ return undefined;
295
+ }
296
+
297
+ if (typeof inputValue === "string") {
298
+ const normalized = inputValue.trim().toLowerCase();
299
+ if (!normalized) {
300
+ return undefined;
301
+ }
302
+ if (
303
+ normalized === "light" ||
304
+ normalized === "dark" ||
305
+ normalized === "system"
306
+ ) {
307
+ return normalized;
308
+ }
309
+ if (normalized === "auto") {
310
+ return {
311
+ autoDetectColorScheme: defaultAutoDetectColorSchemeAttrs.slice(),
312
+ };
313
+ }
314
+ const attrs = normalizeAttrList(inputValue);
315
+ return attrs.length > 0
316
+ ? { autoDetectColorScheme: attrs }
317
+ : undefined;
318
+ }
319
+
320
+ return undefined;
321
+ };
322
+
323
+ const getThemeInput = function(templateData) {
324
+ if (templateData.themeMode === "variable") {
325
+ return templateData.themeModeVariable;
326
+ }
327
+ if (templateData.themeMode === "host") {
328
+ return templateData.themeAttr;
329
+ }
330
+ if (
331
+ templateData.themeMode === "light" ||
332
+ templateData.themeMode === "dark" ||
333
+ templateData.themeMode === "system"
334
+ ) {
335
+ return templateData.themeMode;
336
+ }
337
+ return undefined;
338
+ };
339
+
340
+ const getConsentState = function() {
341
+ const result = {};
342
+ for (let index = 0; index < gtmConsentTypes.length; index += 1) {
343
+ const consentType = gtmConsentTypes[index];
344
+ result[consentType] = isConsentGranted(consentType)
345
+ ? "granted"
346
+ : "denied";
347
+ }
348
+ return result;
349
+ };
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
+
383
+ const getManualScopeMappings = function(templateData) {
384
+ const rawRows = Array.isArray(templateData.scopeMappings)
385
+ ? templateData.scopeMappings
386
+ : [];
387
+ const rows = rawRows.length > 0 ? rawRows : defaultScopeMappings;
388
+ return rows
389
+ .map(function(row) {
390
+ if (!row || typeof row !== "object") {
391
+ return undefined;
392
+ }
393
+ const scope = trimString(row.scope);
394
+ const consentType = trimString(row.mapping);
395
+ if (!scope || !consentType) {
396
+ return undefined;
397
+ }
398
+ if (!isHostConfigurableScope(scope) || !isKnownConsentType(consentType)) {
399
+ return undefined;
400
+ }
401
+ return {
402
+ scope: scope,
403
+ consentType: consentType,
404
+ };
405
+ })
406
+ .filter(function(row) {
407
+ return row !== undefined;
408
+ });
409
+ };
410
+
411
+ const getAnalyticsMeasurementMode = function(templateData) {
412
+ if (
413
+ templateData.analyticsMeasurementMode === "granted" ||
414
+ templateData.analyticsMeasurementMode === "denied"
415
+ ) {
416
+ return templateData.analyticsMeasurementMode;
417
+ }
418
+ return "inherit-analytics-storage";
419
+ };
420
+
421
+ const buildDefaultConsent = function(templateData) {
422
+ const consentState = getConsentState();
423
+ const grantedScopes = [];
424
+
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
+ }
439
+ }
440
+
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
+ }
451
+ }
452
+
453
+ const dedupedScopes = dedupeScopes(grantedScopes);
454
+ return dedupedScopes.length > 0 ? dedupedScopes : undefined;
455
+ };
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
+
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);
478
+ const initOptions = {
479
+ apiKey: apiKey,
480
+ clientMeta: {
481
+ loader: "gtm",
482
+ transport: "tag-manager",
483
+ },
484
+ };
485
+
486
+ const colorScheme = normalizeThemeInput(getThemeInput(data));
487
+ if (colorScheme !== undefined) {
488
+ initOptions.colorScheme = colorScheme;
489
+ }
490
+
491
+ const defaultConsent = buildDefaultConsent(data);
492
+ if (defaultConsent !== undefined) {
493
+ initOptions.defaultConsent = defaultConsent;
494
+ }
495
+
496
+ queuePush({
497
+ kind: "init",
498
+ opts: initOptions,
499
+ });
500
+
501
+ log("Loading getuserfeedback loader:", widgetUrl);
502
+
503
+ if (queryPermission("inject_script", widgetUrl)) {
504
+ injectScript(widgetUrl, onSuccess, onFailure, "getuserfeedback");
505
+ } else {
506
+ onFailure();
507
+ }
508
+ `.trim();
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
+ }
561
+ export function buildGtmTemplateParts(input) {
562
+ return {
563
+ info: buildGtmTemplateInfo(input),
564
+ parameters: buildGtmTemplateParameters(input),
565
+ runtimeSource: buildGtmRuntimeSource(input),
566
+ permissions: buildGtmPermissions(input),
567
+ };
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.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "getuserfeedback integration adapters",
5
5
  "keywords": [
6
6
  "getuserfeedback",