mytart 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 CHANGED
@@ -86,6 +86,64 @@ When `appType: 'browser'` is set:
86
86
  - SSR-safe: silently succeeds when `window` is undefined (e.g. during server-side rendering)
87
87
  - `apiSecret` is not required (and not used)
88
88
 
89
+ #### Google Signals (demographics)
90
+
91
+ To enable demographic data (age, gender, interests) in GA4 reports, set `signals: true`:
92
+
93
+ ```typescript
94
+ {
95
+ provider: 'google-analytics',
96
+ measurementId: 'G-XXXXXXXXXX',
97
+ appType: 'browser',
98
+ enabled: true,
99
+ signals: true,
100
+ }
101
+ ```
102
+
103
+ This does two things automatically:
104
+
105
+ 1. Passes `allow_google_signals: true` and `allow_ad_personalization_signals: true` to `gtag('config')`
106
+ 2. Sets Consent Mode v2 defaults granting `ad_personalization`, `ad_user_data`, `ad_storage`, and `analytics_storage`
107
+
108
+ Set `signals: false` to explicitly disable Google Signals. Omit the flag entirely to use Google's default behaviour.
109
+
110
+ > **Note**: You must also enable Google Signals in the GA4 admin panel (Admin > Data Settings > Data Collection) for demographic data to appear.
111
+
112
+ #### Consent Mode v2
113
+
114
+ For GDPR/privacy compliance you can control Consent Mode v2 directly. Use `defaultConsent` to set the initial consent state (emitted before `gtag('config')`), and `updateConsent()` to change it at runtime when the user interacts with a cookie banner.
115
+
116
+ ```typescript
117
+ const analytics = new Mytart({
118
+ providers: [{
119
+ provider: 'google-analytics',
120
+ measurementId: 'G-XXXXXXXXXX',
121
+ appType: 'browser',
122
+ enabled: true,
123
+ signals: true,
124
+ defaultConsent: {
125
+ ad_storage: 'denied',
126
+ analytics_storage: 'denied',
127
+ ad_user_data: 'denied',
128
+ ad_personalization: 'denied',
129
+ },
130
+ consentWaitForUpdate: 500, // wait 500ms for consent banner
131
+ }],
132
+ });
133
+
134
+ // After the user accepts the cookie banner:
135
+ await analytics.updateConsent({
136
+ ad_storage: 'granted',
137
+ analytics_storage: 'granted',
138
+ ad_user_data: 'granted',
139
+ ad_personalization: 'granted',
140
+ });
141
+ ```
142
+
143
+ When both `signals: true` and `defaultConsent` are set, the explicit `defaultConsent` takes precedence over the auto-consent that `signals` would generate. This lets you combine `signals: true` (for the config flags) with a GDPR-safe denied-by-default consent flow.
144
+
145
+ Consent Mode is a no-op in server mode (the Measurement Protocol does not support it).
146
+
89
147
  ### Mixpanel
90
148
 
91
149
  ```typescript
@@ -180,6 +238,22 @@ interface PageOptions {
180
238
  }
181
239
  ```
182
240
 
241
+ ### `analytics.updateConsent(consent: ConsentSettings): Promise<void>`
242
+
243
+ Updates Google Consent Mode v2 state at runtime. Call this when the user interacts with a cookie/consent banner. Only affects Google Analytics in browser mode; all other providers ignore it.
244
+
245
+ ```typescript
246
+ interface ConsentSettings {
247
+ ad_storage?: 'granted' | 'denied';
248
+ analytics_storage?: 'granted' | 'denied';
249
+ ad_user_data?: 'granted' | 'denied';
250
+ ad_personalization?: 'granted' | 'denied';
251
+ functionality_storage?: 'granted' | 'denied';
252
+ personalization_storage?: 'granted' | 'denied';
253
+ security_storage?: 'granted' | 'denied';
254
+ }
255
+ ```
256
+
183
257
  ### `analytics.addProvider(config: ProviderConfig): void`
184
258
 
185
259
  Dynamically add a provider at runtime.
@@ -220,7 +294,7 @@ All types are exported:
220
294
  import type {
221
295
  MytartConfig, BaseProviderConfig, ProviderConfig, TrackOptions, IdentifyOptions, PageOptions,
222
296
  TrackResult, MytartError, EventContext, ProviderName, GoogleAnalyticsAppType,
223
- GoogleAnalyticsConfig, MixpanelConfig, SegmentConfig,
297
+ GoogleAnalyticsConfig, ConsentSettings, ConsentState, MixpanelConfig, SegmentConfig,
224
298
  AmplitudeConfig, PlausibleConfig, PostHogConfig,
225
299
  } from 'mytart';
226
300
  ```
package/dist/index.d.mts CHANGED
@@ -8,6 +8,33 @@ interface BaseProviderConfig {
8
8
  /** Whether this provider is active. Defaults to `false` when omitted. */
9
9
  enabled?: boolean;
10
10
  }
11
+ type ConsentState = 'granted' | 'denied';
12
+ /**
13
+ * Google Consent Mode v2 settings.
14
+ * Controls how Google tags behave based on user consent.
15
+ *
16
+ * Key fields for Google Signals demographics (age, gender, interests):
17
+ * - `ad_personalization` — must be `'granted'` for Google Signals to attribute
18
+ * demographic data to sessions.
19
+ * - `ad_user_data` — must be `'granted'` for user data to be sent to Google
20
+ * for advertising purposes.
21
+ */
22
+ interface ConsentSettings {
23
+ /** Controls storage of advertising-related cookies. */
24
+ ad_storage?: ConsentState;
25
+ /** Controls storage of analytics-related cookies. */
26
+ analytics_storage?: ConsentState;
27
+ /** Controls whether user data can be sent to Google for advertising. */
28
+ ad_user_data?: ConsentState;
29
+ /** Controls whether data can be used for personalized advertising. */
30
+ ad_personalization?: ConsentState;
31
+ /** Controls storage for functional purposes (e.g. language settings). */
32
+ functionality_storage?: ConsentState;
33
+ /** Controls storage for personalization (e.g. video recommendations). */
34
+ personalization_storage?: ConsentState;
35
+ /** Controls storage for security purposes (e.g. authentication). */
36
+ security_storage?: ConsentState;
37
+ }
11
38
  type ProviderName = 'google-analytics' | 'mixpanel' | 'segment' | 'amplitude' | 'plausible' | 'posthog';
12
39
  type GoogleAnalyticsAppType = 'browser' | 'server';
13
40
  interface GoogleAnalyticsConfig extends BaseProviderConfig {
@@ -17,6 +44,44 @@ interface GoogleAnalyticsConfig extends BaseProviderConfig {
17
44
  clientId?: string;
18
45
  debug?: boolean;
19
46
  appType?: GoogleAnalyticsAppType;
47
+ /**
48
+ * Default consent state set before gtag('config'). In browser mode this
49
+ * emits `gtag('consent', 'default', ...)` so Google tags respect user
50
+ * consent from the very first hit. Use `Mytart.updateConsent()` to change
51
+ * consent at runtime (e.g. after a cookie banner interaction).
52
+ *
53
+ * For Google Signals demographics, `ad_personalization` and `ad_user_data`
54
+ * must eventually be set to `'granted'`.
55
+ */
56
+ defaultConsent?: ConsentSettings;
57
+ /**
58
+ * When `true`, sets `wait_for_update` (in milliseconds) on the default
59
+ * consent command. This tells Google tags to wait the specified number of
60
+ * milliseconds for a consent update before sending the first hit. Useful
61
+ * when a consent management platform loads asynchronously.
62
+ * Defaults to `undefined` (no wait).
63
+ */
64
+ consentWaitForUpdate?: number;
65
+ /**
66
+ * Convenience flag to enable or disable Google Signals for demographics
67
+ * (age, gender, interests).
68
+ *
69
+ * - `true` — passes `allow_google_signals: true` and
70
+ * `allow_ad_personalization_signals: true` in the `gtag('config')`
71
+ * call. If `defaultConsent` is not explicitly set, automatically
72
+ * configures Consent Mode v2 to grant `ad_personalization`,
73
+ * `ad_user_data`, `ad_storage`, and `analytics_storage`.
74
+ * - `false` — passes `allow_google_signals: false` and
75
+ * `allow_ad_personalization_signals: false` to explicitly disable
76
+ * Google Signals.
77
+ * - `undefined` (default) — uses Google's default behaviour (Signals
78
+ * enabled when the GA4 property has it turned on in Admin).
79
+ *
80
+ * **Note**: Google Signals must also be enabled in the GA4 admin panel
81
+ * (Admin › Data Settings › Data Collection) for demographic data to
82
+ * appear. This flag controls the client-side consent and config only.
83
+ */
84
+ signals?: boolean;
20
85
  }
21
86
  interface MixpanelConfig extends BaseProviderConfig {
22
87
  provider: 'mixpanel';
@@ -106,6 +171,16 @@ declare class Mytart {
106
171
  track(options: TrackOptions): Promise<TrackResult[]>;
107
172
  identify(options: IdentifyOptions): Promise<TrackResult[]>;
108
173
  page(options: PageOptions): Promise<TrackResult[]>;
174
+ /**
175
+ * Update consent state across all providers that support consent management.
176
+ * Currently this is meaningful for Google Analytics (Consent Mode v2) in
177
+ * browser mode. Call this when the user interacts with a cookie/consent
178
+ * banner.
179
+ *
180
+ * To enable Google Signals demographics (age, gender, interests), grant
181
+ * at least `ad_personalization` and `ad_user_data`.
182
+ */
183
+ updateConsent(consent: ConsentSettings): Promise<void>;
109
184
  addProvider(config: ProviderConfig): void;
110
185
  removeProvider(name: string): void;
111
186
  getProviders(): string[];
@@ -116,6 +191,11 @@ declare abstract class BaseProvider {
116
191
  abstract track(options: TrackOptions): Promise<TrackResult>;
117
192
  abstract identify(options: IdentifyOptions): Promise<TrackResult>;
118
193
  abstract page(options: PageOptions): Promise<TrackResult>;
194
+ /**
195
+ * Update consent state. Only meaningful for providers that support consent
196
+ * management (e.g. Google Analytics Consent Mode v2). Default is a no-op.
197
+ */
198
+ updateConsent(_consent: ConsentSettings): Promise<void>;
119
199
  protected buildError(message: string, code: string, originalError?: unknown): TrackResult;
120
200
  protected buildSuccess(statusCode?: number): TrackResult;
121
201
  }
@@ -147,6 +227,10 @@ declare class GoogleAnalyticsProvider extends BaseProvider {
147
227
  * gtag('config', 'TAG_ID');
148
228
  * </script>
149
229
  *
230
+ * When `defaultConsent` is configured, a `gtag('consent', 'default', ...)`
231
+ * call is emitted **before** `gtag('js')` and `gtag('config')`, as required
232
+ * by Google's Consent Mode v2 specification.
233
+ *
150
234
  * Key details:
151
235
  * - The script URL MUST include ?id=TAG_ID for Google Tag Tester detection
152
236
  * - dataLayer and the gtag shim are set up BEFORE the script loads
@@ -164,6 +248,18 @@ declare class GoogleAnalyticsProvider extends BaseProvider {
164
248
  private identifyBrowser;
165
249
  private pageBrowser;
166
250
  private buildGtagResult;
251
+ /**
252
+ * Updates the consent state at runtime. In browser mode this emits
253
+ * `gtag('consent', 'update', ...)`. Call this when the user interacts
254
+ * with a cookie/consent banner.
255
+ *
256
+ * To enable Google Signals demographics (age, gender, interests), grant
257
+ * at least `ad_personalization` and `ad_user_data`.
258
+ *
259
+ * In server mode this is a no-op — the Measurement Protocol does not
260
+ * support Consent Mode.
261
+ */
262
+ updateConsent(consent: ConsentSettings): Promise<void>;
167
263
  track({ event, properties, userId, anonymousId, timestamp }: TrackOptions): Promise<TrackResult>;
168
264
  identify({ userId, traits }: IdentifyOptions): Promise<TrackResult>;
169
265
  page({ name, url, referrer, userId, anonymousId }: PageOptions): Promise<TrackResult>;
@@ -226,4 +322,4 @@ declare class PostHogProvider extends BaseProvider {
226
322
  page({ name, url, userId, anonymousId, properties }: PageOptions): Promise<TrackResult>;
227
323
  }
228
324
 
229
- export { type AmplitudeConfig, AmplitudeProvider, BaseProvider, type BaseProviderConfig, type EventContext, type GoogleAnalyticsAppType, type GoogleAnalyticsConfig, GoogleAnalyticsProvider, type IdentifyOptions, type MixpanelConfig, MixpanelProvider, Mytart, type MytartConfig, type MytartError, type PageOptions, type PlausibleConfig, PlausibleProvider, type PostHogConfig, PostHogProvider, type ProviderConfig, type ProviderName, type SegmentConfig, SegmentProvider, type TrackOptions, type TrackResult };
325
+ export { type AmplitudeConfig, AmplitudeProvider, BaseProvider, type BaseProviderConfig, type ConsentSettings, type ConsentState, type EventContext, type GoogleAnalyticsAppType, type GoogleAnalyticsConfig, GoogleAnalyticsProvider, type IdentifyOptions, type MixpanelConfig, MixpanelProvider, Mytart, type MytartConfig, type MytartError, type PageOptions, type PlausibleConfig, PlausibleProvider, type PostHogConfig, PostHogProvider, type ProviderConfig, type ProviderName, type SegmentConfig, SegmentProvider, type TrackOptions, type TrackResult };
package/dist/index.d.ts CHANGED
@@ -8,6 +8,33 @@ interface BaseProviderConfig {
8
8
  /** Whether this provider is active. Defaults to `false` when omitted. */
9
9
  enabled?: boolean;
10
10
  }
11
+ type ConsentState = 'granted' | 'denied';
12
+ /**
13
+ * Google Consent Mode v2 settings.
14
+ * Controls how Google tags behave based on user consent.
15
+ *
16
+ * Key fields for Google Signals demographics (age, gender, interests):
17
+ * - `ad_personalization` — must be `'granted'` for Google Signals to attribute
18
+ * demographic data to sessions.
19
+ * - `ad_user_data` — must be `'granted'` for user data to be sent to Google
20
+ * for advertising purposes.
21
+ */
22
+ interface ConsentSettings {
23
+ /** Controls storage of advertising-related cookies. */
24
+ ad_storage?: ConsentState;
25
+ /** Controls storage of analytics-related cookies. */
26
+ analytics_storage?: ConsentState;
27
+ /** Controls whether user data can be sent to Google for advertising. */
28
+ ad_user_data?: ConsentState;
29
+ /** Controls whether data can be used for personalized advertising. */
30
+ ad_personalization?: ConsentState;
31
+ /** Controls storage for functional purposes (e.g. language settings). */
32
+ functionality_storage?: ConsentState;
33
+ /** Controls storage for personalization (e.g. video recommendations). */
34
+ personalization_storage?: ConsentState;
35
+ /** Controls storage for security purposes (e.g. authentication). */
36
+ security_storage?: ConsentState;
37
+ }
11
38
  type ProviderName = 'google-analytics' | 'mixpanel' | 'segment' | 'amplitude' | 'plausible' | 'posthog';
12
39
  type GoogleAnalyticsAppType = 'browser' | 'server';
13
40
  interface GoogleAnalyticsConfig extends BaseProviderConfig {
@@ -17,6 +44,44 @@ interface GoogleAnalyticsConfig extends BaseProviderConfig {
17
44
  clientId?: string;
18
45
  debug?: boolean;
19
46
  appType?: GoogleAnalyticsAppType;
47
+ /**
48
+ * Default consent state set before gtag('config'). In browser mode this
49
+ * emits `gtag('consent', 'default', ...)` so Google tags respect user
50
+ * consent from the very first hit. Use `Mytart.updateConsent()` to change
51
+ * consent at runtime (e.g. after a cookie banner interaction).
52
+ *
53
+ * For Google Signals demographics, `ad_personalization` and `ad_user_data`
54
+ * must eventually be set to `'granted'`.
55
+ */
56
+ defaultConsent?: ConsentSettings;
57
+ /**
58
+ * When `true`, sets `wait_for_update` (in milliseconds) on the default
59
+ * consent command. This tells Google tags to wait the specified number of
60
+ * milliseconds for a consent update before sending the first hit. Useful
61
+ * when a consent management platform loads asynchronously.
62
+ * Defaults to `undefined` (no wait).
63
+ */
64
+ consentWaitForUpdate?: number;
65
+ /**
66
+ * Convenience flag to enable or disable Google Signals for demographics
67
+ * (age, gender, interests).
68
+ *
69
+ * - `true` — passes `allow_google_signals: true` and
70
+ * `allow_ad_personalization_signals: true` in the `gtag('config')`
71
+ * call. If `defaultConsent` is not explicitly set, automatically
72
+ * configures Consent Mode v2 to grant `ad_personalization`,
73
+ * `ad_user_data`, `ad_storage`, and `analytics_storage`.
74
+ * - `false` — passes `allow_google_signals: false` and
75
+ * `allow_ad_personalization_signals: false` to explicitly disable
76
+ * Google Signals.
77
+ * - `undefined` (default) — uses Google's default behaviour (Signals
78
+ * enabled when the GA4 property has it turned on in Admin).
79
+ *
80
+ * **Note**: Google Signals must also be enabled in the GA4 admin panel
81
+ * (Admin › Data Settings › Data Collection) for demographic data to
82
+ * appear. This flag controls the client-side consent and config only.
83
+ */
84
+ signals?: boolean;
20
85
  }
21
86
  interface MixpanelConfig extends BaseProviderConfig {
22
87
  provider: 'mixpanel';
@@ -106,6 +171,16 @@ declare class Mytart {
106
171
  track(options: TrackOptions): Promise<TrackResult[]>;
107
172
  identify(options: IdentifyOptions): Promise<TrackResult[]>;
108
173
  page(options: PageOptions): Promise<TrackResult[]>;
174
+ /**
175
+ * Update consent state across all providers that support consent management.
176
+ * Currently this is meaningful for Google Analytics (Consent Mode v2) in
177
+ * browser mode. Call this when the user interacts with a cookie/consent
178
+ * banner.
179
+ *
180
+ * To enable Google Signals demographics (age, gender, interests), grant
181
+ * at least `ad_personalization` and `ad_user_data`.
182
+ */
183
+ updateConsent(consent: ConsentSettings): Promise<void>;
109
184
  addProvider(config: ProviderConfig): void;
110
185
  removeProvider(name: string): void;
111
186
  getProviders(): string[];
@@ -116,6 +191,11 @@ declare abstract class BaseProvider {
116
191
  abstract track(options: TrackOptions): Promise<TrackResult>;
117
192
  abstract identify(options: IdentifyOptions): Promise<TrackResult>;
118
193
  abstract page(options: PageOptions): Promise<TrackResult>;
194
+ /**
195
+ * Update consent state. Only meaningful for providers that support consent
196
+ * management (e.g. Google Analytics Consent Mode v2). Default is a no-op.
197
+ */
198
+ updateConsent(_consent: ConsentSettings): Promise<void>;
119
199
  protected buildError(message: string, code: string, originalError?: unknown): TrackResult;
120
200
  protected buildSuccess(statusCode?: number): TrackResult;
121
201
  }
@@ -147,6 +227,10 @@ declare class GoogleAnalyticsProvider extends BaseProvider {
147
227
  * gtag('config', 'TAG_ID');
148
228
  * </script>
149
229
  *
230
+ * When `defaultConsent` is configured, a `gtag('consent', 'default', ...)`
231
+ * call is emitted **before** `gtag('js')` and `gtag('config')`, as required
232
+ * by Google's Consent Mode v2 specification.
233
+ *
150
234
  * Key details:
151
235
  * - The script URL MUST include ?id=TAG_ID for Google Tag Tester detection
152
236
  * - dataLayer and the gtag shim are set up BEFORE the script loads
@@ -164,6 +248,18 @@ declare class GoogleAnalyticsProvider extends BaseProvider {
164
248
  private identifyBrowser;
165
249
  private pageBrowser;
166
250
  private buildGtagResult;
251
+ /**
252
+ * Updates the consent state at runtime. In browser mode this emits
253
+ * `gtag('consent', 'update', ...)`. Call this when the user interacts
254
+ * with a cookie/consent banner.
255
+ *
256
+ * To enable Google Signals demographics (age, gender, interests), grant
257
+ * at least `ad_personalization` and `ad_user_data`.
258
+ *
259
+ * In server mode this is a no-op — the Measurement Protocol does not
260
+ * support Consent Mode.
261
+ */
262
+ updateConsent(consent: ConsentSettings): Promise<void>;
167
263
  track({ event, properties, userId, anonymousId, timestamp }: TrackOptions): Promise<TrackResult>;
168
264
  identify({ userId, traits }: IdentifyOptions): Promise<TrackResult>;
169
265
  page({ name, url, referrer, userId, anonymousId }: PageOptions): Promise<TrackResult>;
@@ -226,4 +322,4 @@ declare class PostHogProvider extends BaseProvider {
226
322
  page({ name, url, userId, anonymousId, properties }: PageOptions): Promise<TrackResult>;
227
323
  }
228
324
 
229
- export { type AmplitudeConfig, AmplitudeProvider, BaseProvider, type BaseProviderConfig, type EventContext, type GoogleAnalyticsAppType, type GoogleAnalyticsConfig, GoogleAnalyticsProvider, type IdentifyOptions, type MixpanelConfig, MixpanelProvider, Mytart, type MytartConfig, type MytartError, type PageOptions, type PlausibleConfig, PlausibleProvider, type PostHogConfig, PostHogProvider, type ProviderConfig, type ProviderName, type SegmentConfig, SegmentProvider, type TrackOptions, type TrackResult };
325
+ export { type AmplitudeConfig, AmplitudeProvider, BaseProvider, type BaseProviderConfig, type ConsentSettings, type ConsentState, type EventContext, type GoogleAnalyticsAppType, type GoogleAnalyticsConfig, GoogleAnalyticsProvider, type IdentifyOptions, type MixpanelConfig, MixpanelProvider, Mytart, type MytartConfig, type MytartError, type PageOptions, type PlausibleConfig, PlausibleProvider, type PostHogConfig, PostHogProvider, type ProviderConfig, type ProviderName, type SegmentConfig, SegmentProvider, type TrackOptions, type TrackResult };
package/dist/index.js CHANGED
@@ -43,6 +43,13 @@ module.exports = __toCommonJS(index_exports);
43
43
 
44
44
  // src/providers/base.ts
45
45
  var BaseProvider = class {
46
+ /**
47
+ * Update consent state. Only meaningful for providers that support consent
48
+ * management (e.g. Google Analytics Consent Mode v2). Default is a no-op.
49
+ */
50
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
51
+ async updateConsent(_consent) {
52
+ }
46
53
  buildError(message, code, originalError) {
47
54
  return {
48
55
  provider: this.name,
@@ -105,6 +112,10 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
105
112
  * gtag('config', 'TAG_ID');
106
113
  * </script>
107
114
  *
115
+ * When `defaultConsent` is configured, a `gtag('consent', 'default', ...)`
116
+ * call is emitted **before** `gtag('js')` and `gtag('config')`, as required
117
+ * by Google's Consent Mode v2 specification.
118
+ *
108
119
  * Key details:
109
120
  * - The script URL MUST include ?id=TAG_ID for Google Tag Tester detection
110
121
  * - dataLayer and the gtag shim are set up BEFORE the script loads
@@ -120,8 +131,33 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
120
131
  window.gtag = function gtag() {
121
132
  window.dataLayer.push(arguments);
122
133
  };
134
+ const resolvedConsent = this.config.defaultConsent ?? (this.config.signals === true ? {
135
+ ad_storage: "granted",
136
+ analytics_storage: "granted",
137
+ ad_user_data: "granted",
138
+ ad_personalization: "granted"
139
+ } : void 0);
140
+ if (resolvedConsent) {
141
+ const consentParams = { ...resolvedConsent };
142
+ if (this.config.consentWaitForUpdate !== void 0) {
143
+ consentParams["wait_for_update"] = this.config.consentWaitForUpdate;
144
+ }
145
+ window.gtag("consent", "default", consentParams);
146
+ }
123
147
  window.gtag("js", /* @__PURE__ */ new Date());
124
- window.gtag("config", this.config.measurementId);
148
+ const configParams = {};
149
+ if (this.config.signals === true) {
150
+ configParams["allow_google_signals"] = true;
151
+ configParams["allow_ad_personalization_signals"] = true;
152
+ } else if (this.config.signals === false) {
153
+ configParams["allow_google_signals"] = false;
154
+ configParams["allow_ad_personalization_signals"] = false;
155
+ }
156
+ if (Object.keys(configParams).length > 0) {
157
+ window.gtag("config", this.config.measurementId, configParams);
158
+ } else {
159
+ window.gtag("config", this.config.measurementId);
160
+ }
125
161
  return new Promise((resolve) => {
126
162
  const script = document.createElement("script");
127
163
  script.async = true;
@@ -189,6 +225,27 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
189
225
  };
190
226
  }
191
227
  // ---------------------------------------------------------------------------
228
+ // Consent Mode v2
229
+ // ---------------------------------------------------------------------------
230
+ /**
231
+ * Updates the consent state at runtime. In browser mode this emits
232
+ * `gtag('consent', 'update', ...)`. Call this when the user interacts
233
+ * with a cookie/consent banner.
234
+ *
235
+ * To enable Google Signals demographics (age, gender, interests), grant
236
+ * at least `ad_personalization` and `ad_user_data`.
237
+ *
238
+ * In server mode this is a no-op — the Measurement Protocol does not
239
+ * support Consent Mode.
240
+ */
241
+ async updateConsent(consent) {
242
+ if (!this.isBrowser || typeof window === "undefined") {
243
+ return;
244
+ }
245
+ await this.ensureGtag();
246
+ window.gtag("consent", "update", consent);
247
+ }
248
+ // ---------------------------------------------------------------------------
192
249
  // Public API — routes to browser or server mode
193
250
  // ---------------------------------------------------------------------------
194
251
  async track({ event, properties, userId, anonymousId, timestamp }) {
@@ -748,6 +805,18 @@ var Mytart = class {
748
805
  };
749
806
  return Promise.all(this.providers.map((p) => p.page(enriched)));
750
807
  }
808
+ /**
809
+ * Update consent state across all providers that support consent management.
810
+ * Currently this is meaningful for Google Analytics (Consent Mode v2) in
811
+ * browser mode. Call this when the user interacts with a cookie/consent
812
+ * banner.
813
+ *
814
+ * To enable Google Signals demographics (age, gender, interests), grant
815
+ * at least `ad_personalization` and `ad_user_data`.
816
+ */
817
+ async updateConsent(consent) {
818
+ await Promise.all(this.providers.map((p) => p.updateConsent(consent)));
819
+ }
751
820
  addProvider(config) {
752
821
  if (config.enabled !== true) return;
753
822
  this.providers.push(createProvider(config));
package/dist/index.mjs CHANGED
@@ -1,5 +1,12 @@
1
1
  // src/providers/base.ts
2
2
  var BaseProvider = class {
3
+ /**
4
+ * Update consent state. Only meaningful for providers that support consent
5
+ * management (e.g. Google Analytics Consent Mode v2). Default is a no-op.
6
+ */
7
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
8
+ async updateConsent(_consent) {
9
+ }
3
10
  buildError(message, code, originalError) {
4
11
  return {
5
12
  provider: this.name,
@@ -62,6 +69,10 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
62
69
  * gtag('config', 'TAG_ID');
63
70
  * </script>
64
71
  *
72
+ * When `defaultConsent` is configured, a `gtag('consent', 'default', ...)`
73
+ * call is emitted **before** `gtag('js')` and `gtag('config')`, as required
74
+ * by Google's Consent Mode v2 specification.
75
+ *
65
76
  * Key details:
66
77
  * - The script URL MUST include ?id=TAG_ID for Google Tag Tester detection
67
78
  * - dataLayer and the gtag shim are set up BEFORE the script loads
@@ -77,8 +88,33 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
77
88
  window.gtag = function gtag() {
78
89
  window.dataLayer.push(arguments);
79
90
  };
91
+ const resolvedConsent = this.config.defaultConsent ?? (this.config.signals === true ? {
92
+ ad_storage: "granted",
93
+ analytics_storage: "granted",
94
+ ad_user_data: "granted",
95
+ ad_personalization: "granted"
96
+ } : void 0);
97
+ if (resolvedConsent) {
98
+ const consentParams = { ...resolvedConsent };
99
+ if (this.config.consentWaitForUpdate !== void 0) {
100
+ consentParams["wait_for_update"] = this.config.consentWaitForUpdate;
101
+ }
102
+ window.gtag("consent", "default", consentParams);
103
+ }
80
104
  window.gtag("js", /* @__PURE__ */ new Date());
81
- window.gtag("config", this.config.measurementId);
105
+ const configParams = {};
106
+ if (this.config.signals === true) {
107
+ configParams["allow_google_signals"] = true;
108
+ configParams["allow_ad_personalization_signals"] = true;
109
+ } else if (this.config.signals === false) {
110
+ configParams["allow_google_signals"] = false;
111
+ configParams["allow_ad_personalization_signals"] = false;
112
+ }
113
+ if (Object.keys(configParams).length > 0) {
114
+ window.gtag("config", this.config.measurementId, configParams);
115
+ } else {
116
+ window.gtag("config", this.config.measurementId);
117
+ }
82
118
  return new Promise((resolve) => {
83
119
  const script = document.createElement("script");
84
120
  script.async = true;
@@ -146,6 +182,27 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
146
182
  };
147
183
  }
148
184
  // ---------------------------------------------------------------------------
185
+ // Consent Mode v2
186
+ // ---------------------------------------------------------------------------
187
+ /**
188
+ * Updates the consent state at runtime. In browser mode this emits
189
+ * `gtag('consent', 'update', ...)`. Call this when the user interacts
190
+ * with a cookie/consent banner.
191
+ *
192
+ * To enable Google Signals demographics (age, gender, interests), grant
193
+ * at least `ad_personalization` and `ad_user_data`.
194
+ *
195
+ * In server mode this is a no-op — the Measurement Protocol does not
196
+ * support Consent Mode.
197
+ */
198
+ async updateConsent(consent) {
199
+ if (!this.isBrowser || typeof window === "undefined") {
200
+ return;
201
+ }
202
+ await this.ensureGtag();
203
+ window.gtag("consent", "update", consent);
204
+ }
205
+ // ---------------------------------------------------------------------------
149
206
  // Public API — routes to browser or server mode
150
207
  // ---------------------------------------------------------------------------
151
208
  async track({ event, properties, userId, anonymousId, timestamp }) {
@@ -705,6 +762,18 @@ var Mytart = class {
705
762
  };
706
763
  return Promise.all(this.providers.map((p) => p.page(enriched)));
707
764
  }
765
+ /**
766
+ * Update consent state across all providers that support consent management.
767
+ * Currently this is meaningful for Google Analytics (Consent Mode v2) in
768
+ * browser mode. Call this when the user interacts with a cookie/consent
769
+ * banner.
770
+ *
771
+ * To enable Google Signals demographics (age, gender, interests), grant
772
+ * at least `ad_personalization` and `ad_user_data`.
773
+ */
774
+ async updateConsent(consent) {
775
+ await Promise.all(this.providers.map((p) => p.updateConsent(consent)));
776
+ }
708
777
  addProvider(config) {
709
778
  if (config.enabled !== true) return;
710
779
  this.providers.push(createProvider(config));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mytart",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Multi-Yield Tracking & Analytics Relay Tool — framework-agnostic analytics for any project",
5
5
  "keywords": [
6
6
  "analytics",