mytart 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## Features
8
8
 
9
- - 🔌 **6 providers out of the box**: Google Analytics 4, Mixpanel, Segment, Amplitude, Plausible, PostHog
9
+ - 🔌 **7 providers out of the box**: Google Analytics 4, Mixpanel, Segment, Amplitude, Plausible, PostHog, Meta Pixel
10
10
  - 🌐 **Universal**: works in Node.js, browsers, and any JS framework (Next.js, Remix, Astro, SvelteKit, etc.)
11
11
  - 🔷 **TypeScript-first**: precise typings, object-parameter style for great DX
12
12
  - 📦 **Dual ESM/CJS output**: works with `import` and `require`
@@ -176,6 +176,83 @@ Consent Mode is a no-op in server mode (the Measurement Protocol does not suppor
176
176
  { provider: 'posthog', apiKey: 'phc_YOUR_KEY', apiUrl?: string }
177
177
  ```
178
178
 
179
+ ### Meta Pixel
180
+
181
+ Meta Pixel supports two modes via the `appType` option:
182
+
183
+ #### Server mode (default)
184
+
185
+ Uses the [Meta Conversions API](https://developers.facebook.com/docs/marketing-api/conversions-api) — direct HTTP calls, no browser APIs. Use this for Node.js, API routes, serverless functions, etc.
186
+
187
+ ```typescript
188
+ {
189
+ provider: 'meta-pixel',
190
+ pixelId: '123456789',
191
+ accessToken: 'YOUR_ACCESS_TOKEN',
192
+ enabled: true,
193
+ // appType defaults to 'server'
194
+ }
195
+ ```
196
+
197
+ PII fields in `user_data` (`em`, `ph`, `fn`, `ln`, `ge`, `db`, `ct`, `st`, `zp`, `country`) are automatically SHA-256 hashed before being sent to the Conversions API. Already-hashed values are not double-hashed.
198
+
199
+ #### Browser mode
200
+
201
+ Injects Meta's official [fbevents.js snippet](https://developers.facebook.com/docs/meta-pixel/get-started) into the page. Use this for client-side tracking in any framework.
202
+
203
+ ```typescript
204
+ {
205
+ provider: 'meta-pixel',
206
+ pixelId: '123456789',
207
+ appType: 'browser',
208
+ advancedMatching: { em: 'user@example.com' },
209
+ enabled: true,
210
+ }
211
+ ```
212
+
213
+ When `appType: 'browser'` is set:
214
+
215
+ - The fbevents.js script is loaded once on the first `track()`, `identify()`, or `page()` call
216
+ - Standard Meta events (e.g. `Purchase`, `AddToCart`, `ViewContent`) use `fbq('track', ...)` — custom events use `fbq('trackCustom', ...)`
217
+ - SSR-safe: silently succeeds when `window` is undefined
218
+ - `accessToken` is not required
219
+
220
+ #### Event deduplication
221
+
222
+ Pass `eventId` via context to deduplicate browser + server events:
223
+
224
+ ```typescript
225
+ await analytics.track({
226
+ event: 'Purchase',
227
+ properties: { currency: 'USD', value: 42 },
228
+ context: { eventId: 'order-abc-123' },
229
+ });
230
+ ```
231
+
232
+ In browser mode this passes `{ eventID: 'order-abc-123' }` as the 4th `fbq()` parameter. In server mode it sets the `event_id` field in the Conversions API payload.
233
+
234
+ #### Identify
235
+
236
+ In browser mode, `identify()` re-calls `fbq('init', pixelId, newTraits)` to update Advanced Matching data. In server mode, traits are cached in memory and included as `user_data` in all subsequent `track()` / `page()` calls.
237
+
238
+ #### Consent
239
+
240
+ Meta Pixel uses a simple binary consent model. Access it directly on the provider instance:
241
+
242
+ ```typescript
243
+ import { MetaPixelProvider } from 'mytart';
244
+
245
+ // Grant consent
246
+ const metaProvider = analytics.getProviders().includes('meta-pixel')
247
+ ? (analytics as any) // or access the provider directly
248
+ : null;
249
+
250
+ // Or use the provider directly:
251
+ const provider = new MetaPixelProvider({ provider: 'meta-pixel', pixelId: '123', appType: 'browser' });
252
+ await provider.updatePixelConsent(true); // fbq('consent', 'grant')
253
+ await provider.updatePixelConsent(false); // fbq('consent', 'revoke')
254
+ ```
255
+
179
256
  ## API Reference
180
257
 
181
258
  ### `new Mytart(config: MytartConfig)`
@@ -295,7 +372,8 @@ import type {
295
372
  MytartConfig, BaseProviderConfig, ProviderConfig, TrackOptions, IdentifyOptions, PageOptions,
296
373
  TrackResult, MytartError, EventContext, ProviderName, GoogleAnalyticsAppType,
297
374
  GoogleAnalyticsConfig, ConsentSettings, ConsentState, MixpanelConfig, SegmentConfig,
298
- AmplitudeConfig, PlausibleConfig, PostHogConfig,
375
+ AmplitudeConfig, PlausibleConfig, PostHogConfig, MetaPixelConfig, MetaPixelAppType,
376
+ MetaPixelAdvancedMatching,
299
377
  } from 'mytart';
300
378
  ```
301
379
 
package/dist/index.d.mts CHANGED
@@ -35,7 +35,7 @@ interface ConsentSettings {
35
35
  /** Controls storage for security purposes (e.g. authentication). */
36
36
  security_storage?: ConsentState;
37
37
  }
38
- type ProviderName = 'google-analytics' | 'mixpanel' | 'segment' | 'amplitude' | 'plausible' | 'posthog';
38
+ type ProviderName = 'google-analytics' | 'mixpanel' | 'segment' | 'amplitude' | 'plausible' | 'posthog' | 'meta-pixel';
39
39
  type GoogleAnalyticsAppType = 'browser' | 'server';
40
40
  interface GoogleAnalyticsConfig extends BaseProviderConfig {
41
41
  provider: 'google-analytics';
@@ -110,7 +110,77 @@ interface PostHogConfig extends BaseProviderConfig {
110
110
  apiKey: string;
111
111
  apiUrl?: string;
112
112
  }
113
- type ProviderConfig = GoogleAnalyticsConfig | MixpanelConfig | SegmentConfig | AmplitudeConfig | PlausibleConfig | PostHogConfig;
113
+ /**
114
+ * Advanced Matching data for the Meta Pixel.
115
+ * In browser mode these fields are passed to `fbq('init', pixelId, matchData)`.
116
+ * In server mode they are included in the `user_data` object sent to the
117
+ * Conversions API — plain-text values are automatically SHA-256 hashed before
118
+ * sending.
119
+ */
120
+ interface MetaPixelAdvancedMatching {
121
+ /** Email address */
122
+ em?: string;
123
+ /** Phone number */
124
+ ph?: string;
125
+ /** First name */
126
+ fn?: string;
127
+ /** Last name */
128
+ ln?: string;
129
+ /** Gender (m or f) */
130
+ ge?: string;
131
+ /** Date of birth (YYYYMMDD) */
132
+ db?: string;
133
+ /** City */
134
+ ct?: string;
135
+ /** State / province (2-letter code) */
136
+ st?: string;
137
+ /** Zip / postal code */
138
+ zp?: string;
139
+ /** Country (2-letter ISO code) */
140
+ country?: string;
141
+ /** External ID */
142
+ external_id?: string;
143
+ }
144
+ type MetaPixelAppType = 'browser' | 'server';
145
+ interface MetaPixelConfig extends BaseProviderConfig {
146
+ provider: 'meta-pixel';
147
+ /** The Meta Pixel ID (numeric string). */
148
+ pixelId: string;
149
+ /**
150
+ * Access token for the Conversions API. Required when `appType` is
151
+ * `'server'` — ignored in browser mode.
152
+ */
153
+ accessToken?: string;
154
+ /**
155
+ * `'browser'` injects the official fbevents.js snippet and calls `window.fbq()`.
156
+ * `'server'` (default) sends events via the Conversions API HTTP endpoint.
157
+ */
158
+ appType?: MetaPixelAppType;
159
+ /**
160
+ * Graph API version to use for the Conversions API.
161
+ * Defaults to `'v21.0'`.
162
+ */
163
+ apiVersion?: string;
164
+ /**
165
+ * Test Event Code for the Events Manager Test Events tool.
166
+ * Only used in server mode (Conversions API).
167
+ */
168
+ testEventCode?: string;
169
+ /**
170
+ * Advanced Matching data passed to `fbq('init')` in browser mode.
171
+ * In server mode this populates the initial `user_data` for all events.
172
+ */
173
+ advancedMatching?: MetaPixelAdvancedMatching;
174
+ /**
175
+ * When `true` (default), the Pixel uses Automatic Configuration
176
+ * (auto-detects button clicks, page metadata, etc.).
177
+ * Set to `false` to disable.
178
+ */
179
+ autoConfig?: boolean;
180
+ /** Enable Meta Pixel debug mode (`fbq('set', 'debug', true)`). */
181
+ debug?: boolean;
182
+ }
183
+ type ProviderConfig = GoogleAnalyticsConfig | MixpanelConfig | SegmentConfig | AmplitudeConfig | PlausibleConfig | PostHogConfig | MetaPixelConfig;
114
184
  interface MytartConfig {
115
185
  providers: ProviderConfig[];
116
186
  defaultUserId?: string;
@@ -322,4 +392,82 @@ declare class PostHogProvider extends BaseProvider {
322
392
  page({ name, url, userId, anonymousId, properties }: PageOptions): Promise<TrackResult>;
323
393
  }
324
394
 
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 };
395
+ declare global {
396
+ interface Window {
397
+ fbq: FbqFn & {
398
+ callMethod?: FbqFn;
399
+ queue?: unknown[];
400
+ loaded?: boolean;
401
+ version?: string;
402
+ push?: FbqFn;
403
+ };
404
+ _fbq: Window['fbq'];
405
+ }
406
+ }
407
+ type FbqFn = (...args: unknown[]) => void;
408
+ declare class MetaPixelProvider extends BaseProvider {
409
+ readonly name = "meta-pixel";
410
+ private readonly config;
411
+ private readonly http;
412
+ private readonly isBrowser;
413
+ private readonly apiVersion;
414
+ private fbqReady;
415
+ /**
416
+ * Cached user data for the Conversions API. Updated by `identify()` so
417
+ * that subsequent `track()` and `page()` calls include user information.
418
+ */
419
+ private cachedUserData;
420
+ constructor(config: MetaPixelConfig);
421
+ /**
422
+ * Initialises the Meta Pixel snippet. This mirrors the official snippet from
423
+ * https://developers.facebook.com/docs/meta-pixel/get-started:
424
+ *
425
+ * !function(f,b,e,v,n,t,s){...}(window, document,'script',
426
+ * 'https://connect.facebook.net/en_US/fbevents.js');
427
+ * fbq('init', 'PIXEL_ID');
428
+ * fbq('track', 'PageView');
429
+ *
430
+ * Key details:
431
+ * - The fbq shim queue is set up synchronously before the script loads
432
+ * - `fbq('init', pixelId, advancedMatching?)` is called immediately
433
+ * - autoConfig is respected via `fbq('set', 'autoConfig', false, pixelId)`
434
+ * - debug mode via `fbq('set', 'debug', true)`
435
+ * - The returned promise resolves when the script finishes loading
436
+ */
437
+ private initFbq;
438
+ /**
439
+ * Ensures the Pixel is initialised exactly once. Subsequent calls return
440
+ * the same promise so the script is never injected twice.
441
+ */
442
+ private ensureFbq;
443
+ private trackBrowser;
444
+ private identifyBrowser;
445
+ private pageBrowser;
446
+ private buildFbqResult;
447
+ private get capiEndpoint();
448
+ /**
449
+ * Build the `user_data` object for a CAPI event.
450
+ * Merges cached user data (from `identify()` / config) with any per-event
451
+ * overrides, then SHA-256 hashes known PII fields.
452
+ */
453
+ private buildUserData;
454
+ private trackServer;
455
+ private identifyServer;
456
+ private pageServer;
457
+ /**
458
+ * Meta Pixel consent is a binary grant/revoke model, not the granular
459
+ * settings of Google Consent Mode v2. This method accepts the standard
460
+ * `ConsentSettings` type but is a no-op — use `updatePixelConsent()` for
461
+ * Meta-specific consent control in browser mode.
462
+ */
463
+ /**
464
+ * Grant or revoke consent for the Meta Pixel in browser mode.
465
+ * Calls `fbq('consent', 'grant')` or `fbq('consent', 'revoke')`.
466
+ */
467
+ updatePixelConsent(granted: boolean): Promise<void>;
468
+ track(options: TrackOptions): Promise<TrackResult>;
469
+ identify(options: IdentifyOptions): Promise<TrackResult>;
470
+ page(options: PageOptions): Promise<TrackResult>;
471
+ }
472
+
473
+ export { type AmplitudeConfig, AmplitudeProvider, BaseProvider, type BaseProviderConfig, type ConsentSettings, type ConsentState, type EventContext, type GoogleAnalyticsAppType, type GoogleAnalyticsConfig, GoogleAnalyticsProvider, type IdentifyOptions, type MetaPixelAdvancedMatching, type MetaPixelAppType, type MetaPixelConfig, MetaPixelProvider, 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
@@ -35,7 +35,7 @@ interface ConsentSettings {
35
35
  /** Controls storage for security purposes (e.g. authentication). */
36
36
  security_storage?: ConsentState;
37
37
  }
38
- type ProviderName = 'google-analytics' | 'mixpanel' | 'segment' | 'amplitude' | 'plausible' | 'posthog';
38
+ type ProviderName = 'google-analytics' | 'mixpanel' | 'segment' | 'amplitude' | 'plausible' | 'posthog' | 'meta-pixel';
39
39
  type GoogleAnalyticsAppType = 'browser' | 'server';
40
40
  interface GoogleAnalyticsConfig extends BaseProviderConfig {
41
41
  provider: 'google-analytics';
@@ -110,7 +110,77 @@ interface PostHogConfig extends BaseProviderConfig {
110
110
  apiKey: string;
111
111
  apiUrl?: string;
112
112
  }
113
- type ProviderConfig = GoogleAnalyticsConfig | MixpanelConfig | SegmentConfig | AmplitudeConfig | PlausibleConfig | PostHogConfig;
113
+ /**
114
+ * Advanced Matching data for the Meta Pixel.
115
+ * In browser mode these fields are passed to `fbq('init', pixelId, matchData)`.
116
+ * In server mode they are included in the `user_data` object sent to the
117
+ * Conversions API — plain-text values are automatically SHA-256 hashed before
118
+ * sending.
119
+ */
120
+ interface MetaPixelAdvancedMatching {
121
+ /** Email address */
122
+ em?: string;
123
+ /** Phone number */
124
+ ph?: string;
125
+ /** First name */
126
+ fn?: string;
127
+ /** Last name */
128
+ ln?: string;
129
+ /** Gender (m or f) */
130
+ ge?: string;
131
+ /** Date of birth (YYYYMMDD) */
132
+ db?: string;
133
+ /** City */
134
+ ct?: string;
135
+ /** State / province (2-letter code) */
136
+ st?: string;
137
+ /** Zip / postal code */
138
+ zp?: string;
139
+ /** Country (2-letter ISO code) */
140
+ country?: string;
141
+ /** External ID */
142
+ external_id?: string;
143
+ }
144
+ type MetaPixelAppType = 'browser' | 'server';
145
+ interface MetaPixelConfig extends BaseProviderConfig {
146
+ provider: 'meta-pixel';
147
+ /** The Meta Pixel ID (numeric string). */
148
+ pixelId: string;
149
+ /**
150
+ * Access token for the Conversions API. Required when `appType` is
151
+ * `'server'` — ignored in browser mode.
152
+ */
153
+ accessToken?: string;
154
+ /**
155
+ * `'browser'` injects the official fbevents.js snippet and calls `window.fbq()`.
156
+ * `'server'` (default) sends events via the Conversions API HTTP endpoint.
157
+ */
158
+ appType?: MetaPixelAppType;
159
+ /**
160
+ * Graph API version to use for the Conversions API.
161
+ * Defaults to `'v21.0'`.
162
+ */
163
+ apiVersion?: string;
164
+ /**
165
+ * Test Event Code for the Events Manager Test Events tool.
166
+ * Only used in server mode (Conversions API).
167
+ */
168
+ testEventCode?: string;
169
+ /**
170
+ * Advanced Matching data passed to `fbq('init')` in browser mode.
171
+ * In server mode this populates the initial `user_data` for all events.
172
+ */
173
+ advancedMatching?: MetaPixelAdvancedMatching;
174
+ /**
175
+ * When `true` (default), the Pixel uses Automatic Configuration
176
+ * (auto-detects button clicks, page metadata, etc.).
177
+ * Set to `false` to disable.
178
+ */
179
+ autoConfig?: boolean;
180
+ /** Enable Meta Pixel debug mode (`fbq('set', 'debug', true)`). */
181
+ debug?: boolean;
182
+ }
183
+ type ProviderConfig = GoogleAnalyticsConfig | MixpanelConfig | SegmentConfig | AmplitudeConfig | PlausibleConfig | PostHogConfig | MetaPixelConfig;
114
184
  interface MytartConfig {
115
185
  providers: ProviderConfig[];
116
186
  defaultUserId?: string;
@@ -322,4 +392,82 @@ declare class PostHogProvider extends BaseProvider {
322
392
  page({ name, url, userId, anonymousId, properties }: PageOptions): Promise<TrackResult>;
323
393
  }
324
394
 
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 };
395
+ declare global {
396
+ interface Window {
397
+ fbq: FbqFn & {
398
+ callMethod?: FbqFn;
399
+ queue?: unknown[];
400
+ loaded?: boolean;
401
+ version?: string;
402
+ push?: FbqFn;
403
+ };
404
+ _fbq: Window['fbq'];
405
+ }
406
+ }
407
+ type FbqFn = (...args: unknown[]) => void;
408
+ declare class MetaPixelProvider extends BaseProvider {
409
+ readonly name = "meta-pixel";
410
+ private readonly config;
411
+ private readonly http;
412
+ private readonly isBrowser;
413
+ private readonly apiVersion;
414
+ private fbqReady;
415
+ /**
416
+ * Cached user data for the Conversions API. Updated by `identify()` so
417
+ * that subsequent `track()` and `page()` calls include user information.
418
+ */
419
+ private cachedUserData;
420
+ constructor(config: MetaPixelConfig);
421
+ /**
422
+ * Initialises the Meta Pixel snippet. This mirrors the official snippet from
423
+ * https://developers.facebook.com/docs/meta-pixel/get-started:
424
+ *
425
+ * !function(f,b,e,v,n,t,s){...}(window, document,'script',
426
+ * 'https://connect.facebook.net/en_US/fbevents.js');
427
+ * fbq('init', 'PIXEL_ID');
428
+ * fbq('track', 'PageView');
429
+ *
430
+ * Key details:
431
+ * - The fbq shim queue is set up synchronously before the script loads
432
+ * - `fbq('init', pixelId, advancedMatching?)` is called immediately
433
+ * - autoConfig is respected via `fbq('set', 'autoConfig', false, pixelId)`
434
+ * - debug mode via `fbq('set', 'debug', true)`
435
+ * - The returned promise resolves when the script finishes loading
436
+ */
437
+ private initFbq;
438
+ /**
439
+ * Ensures the Pixel is initialised exactly once. Subsequent calls return
440
+ * the same promise so the script is never injected twice.
441
+ */
442
+ private ensureFbq;
443
+ private trackBrowser;
444
+ private identifyBrowser;
445
+ private pageBrowser;
446
+ private buildFbqResult;
447
+ private get capiEndpoint();
448
+ /**
449
+ * Build the `user_data` object for a CAPI event.
450
+ * Merges cached user data (from `identify()` / config) with any per-event
451
+ * overrides, then SHA-256 hashes known PII fields.
452
+ */
453
+ private buildUserData;
454
+ private trackServer;
455
+ private identifyServer;
456
+ private pageServer;
457
+ /**
458
+ * Meta Pixel consent is a binary grant/revoke model, not the granular
459
+ * settings of Google Consent Mode v2. This method accepts the standard
460
+ * `ConsentSettings` type but is a no-op — use `updatePixelConsent()` for
461
+ * Meta-specific consent control in browser mode.
462
+ */
463
+ /**
464
+ * Grant or revoke consent for the Meta Pixel in browser mode.
465
+ * Calls `fbq('consent', 'grant')` or `fbq('consent', 'revoke')`.
466
+ */
467
+ updatePixelConsent(granted: boolean): Promise<void>;
468
+ track(options: TrackOptions): Promise<TrackResult>;
469
+ identify(options: IdentifyOptions): Promise<TrackResult>;
470
+ page(options: PageOptions): Promise<TrackResult>;
471
+ }
472
+
473
+ export { type AmplitudeConfig, AmplitudeProvider, BaseProvider, type BaseProviderConfig, type ConsentSettings, type ConsentState, type EventContext, type GoogleAnalyticsAppType, type GoogleAnalyticsConfig, GoogleAnalyticsProvider, type IdentifyOptions, type MetaPixelAdvancedMatching, type MetaPixelAppType, type MetaPixelConfig, MetaPixelProvider, 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
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  AmplitudeProvider: () => AmplitudeProvider,
34
34
  BaseProvider: () => BaseProvider,
35
35
  GoogleAnalyticsProvider: () => GoogleAnalyticsProvider,
36
+ MetaPixelProvider: () => MetaPixelProvider,
36
37
  MixpanelProvider: () => MixpanelProvider,
37
38
  Mytart: () => Mytart,
38
39
  PlausibleProvider: () => PlausibleProvider,
@@ -145,7 +146,9 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
145
146
  window.gtag("consent", "default", consentParams);
146
147
  }
147
148
  window.gtag("js", /* @__PURE__ */ new Date());
148
- const configParams = {};
149
+ const configParams = {
150
+ send_page_view: false
151
+ };
149
152
  if (this.config.signals === true) {
150
153
  configParams["allow_google_signals"] = true;
151
154
  configParams["allow_ad_personalization_signals"] = true;
@@ -153,11 +156,7 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
153
156
  configParams["allow_google_signals"] = false;
154
157
  configParams["allow_ad_personalization_signals"] = false;
155
158
  }
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
- }
159
+ window.gtag("config", this.config.measurementId, configParams);
161
160
  return new Promise((resolve) => {
162
161
  const script = document.createElement("script");
163
162
  script.async = true;
@@ -760,6 +759,330 @@ var PostHogProvider = class extends BaseProvider {
760
759
  }
761
760
  };
762
761
 
762
+ // src/utils/hash.ts
763
+ var import_crypto = require("crypto");
764
+ function sha256(value) {
765
+ if (/^[a-f0-9]{64}$/.test(value)) {
766
+ return value;
767
+ }
768
+ return (0, import_crypto.createHash)("sha256").update(value).digest("hex");
769
+ }
770
+ var PII_FIELDS = /* @__PURE__ */ new Set([
771
+ "em",
772
+ "ph",
773
+ "fn",
774
+ "ln",
775
+ "ge",
776
+ "db",
777
+ "ct",
778
+ "st",
779
+ "zp",
780
+ "country"
781
+ ]);
782
+ function hashUserData(userData) {
783
+ const result = {};
784
+ for (const [key, value] of Object.entries(userData)) {
785
+ if (PII_FIELDS.has(key) && typeof value === "string" && value.length > 0) {
786
+ result[key] = sha256(value);
787
+ } else {
788
+ result[key] = value;
789
+ }
790
+ }
791
+ return result;
792
+ }
793
+
794
+ // src/providers/meta-pixel.ts
795
+ var FBEVENTS_SCRIPT_URL = "https://connect.facebook.net/en_US/fbevents.js";
796
+ var DEFAULT_API_VERSION = "v21.0";
797
+ var STANDARD_EVENTS = /* @__PURE__ */ new Set([
798
+ "PageView",
799
+ "Purchase",
800
+ "AddToCart",
801
+ "ViewContent",
802
+ "InitiateCheckout",
803
+ "Search",
804
+ "Lead",
805
+ "CompleteRegistration",
806
+ "AddPaymentInfo",
807
+ "AddToWishlist",
808
+ "Contact",
809
+ "StartTrial",
810
+ "Subscribe",
811
+ "Donate",
812
+ "Schedule",
813
+ "SubmitApplication",
814
+ "CustomizeProduct",
815
+ "FindLocation"
816
+ ]);
817
+ var MetaPixelProvider = class extends BaseProvider {
818
+ constructor(config) {
819
+ super();
820
+ this.name = "meta-pixel";
821
+ this.fbqReady = null;
822
+ /**
823
+ * Cached user data for the Conversions API. Updated by `identify()` so
824
+ * that subsequent `track()` and `page()` calls include user information.
825
+ */
826
+ this.cachedUserData = {};
827
+ this.config = config;
828
+ this.http = createHttpClient();
829
+ this.isBrowser = config.appType === "browser";
830
+ this.apiVersion = config.apiVersion ?? DEFAULT_API_VERSION;
831
+ if (config.advancedMatching) {
832
+ this.cachedUserData = { ...config.advancedMatching };
833
+ }
834
+ }
835
+ // ---------------------------------------------------------------------------
836
+ // Browser mode — fbevents.js injection & fbq() calls
837
+ // ---------------------------------------------------------------------------
838
+ /**
839
+ * Initialises the Meta Pixel snippet. This mirrors the official snippet from
840
+ * https://developers.facebook.com/docs/meta-pixel/get-started:
841
+ *
842
+ * !function(f,b,e,v,n,t,s){...}(window, document,'script',
843
+ * 'https://connect.facebook.net/en_US/fbevents.js');
844
+ * fbq('init', 'PIXEL_ID');
845
+ * fbq('track', 'PageView');
846
+ *
847
+ * Key details:
848
+ * - The fbq shim queue is set up synchronously before the script loads
849
+ * - `fbq('init', pixelId, advancedMatching?)` is called immediately
850
+ * - autoConfig is respected via `fbq('set', 'autoConfig', false, pixelId)`
851
+ * - debug mode via `fbq('set', 'debug', true)`
852
+ * - The returned promise resolves when the script finishes loading
853
+ */
854
+ initFbq() {
855
+ if (typeof window === "undefined" || typeof document === "undefined") {
856
+ return Promise.resolve();
857
+ }
858
+ if (!window.fbq) {
859
+ const queue = [];
860
+ const fbq = Object.assign(
861
+ function fbq2(...args) {
862
+ if (fbq2.callMethod) {
863
+ fbq2.callMethod(...args);
864
+ } else {
865
+ queue.push(args);
866
+ }
867
+ },
868
+ {
869
+ push: void 0,
870
+ loaded: true,
871
+ version: "2.0",
872
+ queue,
873
+ callMethod: void 0
874
+ }
875
+ );
876
+ fbq.push = fbq;
877
+ window.fbq = fbq;
878
+ window._fbq = fbq;
879
+ }
880
+ if (this.config.autoConfig === false) {
881
+ window.fbq("set", "autoConfig", false, this.config.pixelId);
882
+ }
883
+ if (this.config.debug) {
884
+ window.fbq("set", "debug", true);
885
+ }
886
+ const matchData = this.config.advancedMatching;
887
+ if (matchData && Object.keys(matchData).length > 0) {
888
+ window.fbq("init", this.config.pixelId, matchData);
889
+ } else {
890
+ window.fbq("init", this.config.pixelId);
891
+ }
892
+ return new Promise((resolve) => {
893
+ const script = document.createElement("script");
894
+ script.async = true;
895
+ script.src = FBEVENTS_SCRIPT_URL;
896
+ script.onload = () => resolve();
897
+ script.onerror = () => resolve();
898
+ document.head.appendChild(script);
899
+ });
900
+ }
901
+ /**
902
+ * Ensures the Pixel is initialised exactly once. Subsequent calls return
903
+ * the same promise so the script is never injected twice.
904
+ */
905
+ ensureFbq() {
906
+ if (!this.fbqReady) {
907
+ this.fbqReady = this.initFbq();
908
+ }
909
+ return this.fbqReady;
910
+ }
911
+ // -- browser track --
912
+ async trackBrowser(options) {
913
+ if (typeof window === "undefined") {
914
+ return this.buildFbqResult();
915
+ }
916
+ await this.ensureFbq();
917
+ const { event, properties, context } = options;
918
+ const eventId = context?.eventId;
919
+ const isStandard = STANDARD_EVENTS.has(event);
920
+ const command = isStandard ? "track" : "trackCustom";
921
+ if (eventId) {
922
+ window.fbq(command, event, properties ?? {}, { eventID: eventId });
923
+ } else {
924
+ window.fbq(command, event, properties ?? {});
925
+ }
926
+ return this.buildFbqResult();
927
+ }
928
+ // -- browser identify --
929
+ async identifyBrowser(options) {
930
+ if (typeof window === "undefined") {
931
+ return this.buildFbqResult();
932
+ }
933
+ await this.ensureFbq();
934
+ const matchData = {};
935
+ if (options.traits) {
936
+ Object.assign(matchData, options.traits);
937
+ }
938
+ matchData["external_id"] = options.userId;
939
+ window.fbq("init", this.config.pixelId, matchData);
940
+ return this.buildFbqResult();
941
+ }
942
+ // -- browser page --
943
+ async pageBrowser(options) {
944
+ if (typeof window === "undefined") {
945
+ return this.buildFbqResult();
946
+ }
947
+ await this.ensureFbq();
948
+ window.fbq("track", "PageView");
949
+ return this.buildFbqResult();
950
+ }
951
+ buildFbqResult() {
952
+ return {
953
+ provider: this.name,
954
+ success: true,
955
+ statusCode: 200
956
+ };
957
+ }
958
+ // ---------------------------------------------------------------------------
959
+ // Server mode — Meta Conversions API (CAPI)
960
+ // ---------------------------------------------------------------------------
961
+ get capiEndpoint() {
962
+ return `https://graph.facebook.com/${this.apiVersion}/${this.config.pixelId}/events`;
963
+ }
964
+ /**
965
+ * Build the `user_data` object for a CAPI event.
966
+ * Merges cached user data (from `identify()` / config) with any per-event
967
+ * overrides, then SHA-256 hashes known PII fields.
968
+ */
969
+ buildUserData(overrides) {
970
+ const merged = { ...this.cachedUserData, ...overrides };
971
+ return hashUserData(merged);
972
+ }
973
+ // -- server track --
974
+ async trackServer(options) {
975
+ try {
976
+ const { event, properties, context, timestamp, userId } = options;
977
+ const eventId = context?.eventId;
978
+ const userDataOverrides = {};
979
+ if (userId) {
980
+ userDataOverrides["external_id"] = userId;
981
+ }
982
+ const eventData = {
983
+ event_name: event,
984
+ event_time: timestamp ? Math.floor(timestamp.getTime() / 1e3) : Math.floor(Date.now() / 1e3),
985
+ action_source: "website",
986
+ user_data: this.buildUserData(userDataOverrides)
987
+ };
988
+ if (eventId) {
989
+ eventData["event_id"] = eventId;
990
+ }
991
+ const pageUrl = context?.page?.url;
992
+ if (pageUrl) {
993
+ eventData["event_source_url"] = pageUrl;
994
+ }
995
+ if (properties && Object.keys(properties).length > 0) {
996
+ eventData["custom_data"] = properties;
997
+ }
998
+ const body = {
999
+ data: [eventData]
1000
+ };
1001
+ if (this.config.testEventCode) {
1002
+ body["test_event_code"] = this.config.testEventCode;
1003
+ }
1004
+ const response = await this.http.post(this.capiEndpoint, body, {
1005
+ params: { access_token: this.config.accessToken }
1006
+ });
1007
+ return this.buildSuccess(response.status);
1008
+ } catch (error) {
1009
+ if (isAxiosError(error)) {
1010
+ return this.buildError(
1011
+ error.message,
1012
+ `META_PIXEL_HTTP_${error.response?.status ?? "ERROR"}`,
1013
+ error
1014
+ );
1015
+ }
1016
+ return this.buildError(String(error), "META_PIXEL_UNKNOWN_ERROR", error);
1017
+ }
1018
+ }
1019
+ // -- server identify --
1020
+ async identifyServer(options) {
1021
+ if (options.traits) {
1022
+ Object.assign(this.cachedUserData, options.traits);
1023
+ }
1024
+ this.cachedUserData["external_id"] = options.userId;
1025
+ return this.buildSuccess(200);
1026
+ }
1027
+ // -- server page --
1028
+ async pageServer(options) {
1029
+ return this.trackServer({
1030
+ event: "PageView",
1031
+ properties: options.properties,
1032
+ userId: options.userId,
1033
+ anonymousId: options.anonymousId,
1034
+ timestamp: options.timestamp,
1035
+ context: {
1036
+ page: {
1037
+ url: options.url,
1038
+ referrer: options.referrer
1039
+ }
1040
+ }
1041
+ });
1042
+ }
1043
+ // ---------------------------------------------------------------------------
1044
+ // Consent — Meta Pixel's simple grant / revoke model
1045
+ // ---------------------------------------------------------------------------
1046
+ /**
1047
+ * Meta Pixel consent is a binary grant/revoke model, not the granular
1048
+ * settings of Google Consent Mode v2. This method accepts the standard
1049
+ * `ConsentSettings` type but is a no-op — use `updatePixelConsent()` for
1050
+ * Meta-specific consent control in browser mode.
1051
+ */
1052
+ /**
1053
+ * Grant or revoke consent for the Meta Pixel in browser mode.
1054
+ * Calls `fbq('consent', 'grant')` or `fbq('consent', 'revoke')`.
1055
+ */
1056
+ async updatePixelConsent(granted) {
1057
+ if (!this.isBrowser || typeof window === "undefined") {
1058
+ return;
1059
+ }
1060
+ await this.ensureFbq();
1061
+ window.fbq("consent", granted ? "grant" : "revoke");
1062
+ }
1063
+ // ---------------------------------------------------------------------------
1064
+ // Public API — routes to browser or server mode
1065
+ // ---------------------------------------------------------------------------
1066
+ async track(options) {
1067
+ if (this.isBrowser) {
1068
+ return this.trackBrowser(options);
1069
+ }
1070
+ return this.trackServer(options);
1071
+ }
1072
+ async identify(options) {
1073
+ if (this.isBrowser) {
1074
+ return this.identifyBrowser(options);
1075
+ }
1076
+ return this.identifyServer(options);
1077
+ }
1078
+ async page(options) {
1079
+ if (this.isBrowser) {
1080
+ return this.pageBrowser(options);
1081
+ }
1082
+ return this.pageServer(options);
1083
+ }
1084
+ };
1085
+
763
1086
  // src/mytart.ts
764
1087
  function createProvider(config) {
765
1088
  switch (config.provider) {
@@ -775,6 +1098,8 @@ function createProvider(config) {
775
1098
  return new PlausibleProvider(config);
776
1099
  case "posthog":
777
1100
  return new PostHogProvider(config);
1101
+ case "meta-pixel":
1102
+ return new MetaPixelProvider(config);
778
1103
  default: {
779
1104
  const exhaustive = config;
780
1105
  throw new Error(`Unknown provider: ${exhaustive.provider}`);
@@ -836,6 +1161,7 @@ var Mytart = class {
836
1161
  AmplitudeProvider,
837
1162
  BaseProvider,
838
1163
  GoogleAnalyticsProvider,
1164
+ MetaPixelProvider,
839
1165
  MixpanelProvider,
840
1166
  Mytart,
841
1167
  PlausibleProvider,
package/dist/index.mjs CHANGED
@@ -102,7 +102,9 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
102
102
  window.gtag("consent", "default", consentParams);
103
103
  }
104
104
  window.gtag("js", /* @__PURE__ */ new Date());
105
- const configParams = {};
105
+ const configParams = {
106
+ send_page_view: false
107
+ };
106
108
  if (this.config.signals === true) {
107
109
  configParams["allow_google_signals"] = true;
108
110
  configParams["allow_ad_personalization_signals"] = true;
@@ -110,11 +112,7 @@ var GoogleAnalyticsProvider = class extends BaseProvider {
110
112
  configParams["allow_google_signals"] = false;
111
113
  configParams["allow_ad_personalization_signals"] = false;
112
114
  }
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
- }
115
+ window.gtag("config", this.config.measurementId, configParams);
118
116
  return new Promise((resolve) => {
119
117
  const script = document.createElement("script");
120
118
  script.async = true;
@@ -717,6 +715,330 @@ var PostHogProvider = class extends BaseProvider {
717
715
  }
718
716
  };
719
717
 
718
+ // src/utils/hash.ts
719
+ import { createHash } from "crypto";
720
+ function sha256(value) {
721
+ if (/^[a-f0-9]{64}$/.test(value)) {
722
+ return value;
723
+ }
724
+ return createHash("sha256").update(value).digest("hex");
725
+ }
726
+ var PII_FIELDS = /* @__PURE__ */ new Set([
727
+ "em",
728
+ "ph",
729
+ "fn",
730
+ "ln",
731
+ "ge",
732
+ "db",
733
+ "ct",
734
+ "st",
735
+ "zp",
736
+ "country"
737
+ ]);
738
+ function hashUserData(userData) {
739
+ const result = {};
740
+ for (const [key, value] of Object.entries(userData)) {
741
+ if (PII_FIELDS.has(key) && typeof value === "string" && value.length > 0) {
742
+ result[key] = sha256(value);
743
+ } else {
744
+ result[key] = value;
745
+ }
746
+ }
747
+ return result;
748
+ }
749
+
750
+ // src/providers/meta-pixel.ts
751
+ var FBEVENTS_SCRIPT_URL = "https://connect.facebook.net/en_US/fbevents.js";
752
+ var DEFAULT_API_VERSION = "v21.0";
753
+ var STANDARD_EVENTS = /* @__PURE__ */ new Set([
754
+ "PageView",
755
+ "Purchase",
756
+ "AddToCart",
757
+ "ViewContent",
758
+ "InitiateCheckout",
759
+ "Search",
760
+ "Lead",
761
+ "CompleteRegistration",
762
+ "AddPaymentInfo",
763
+ "AddToWishlist",
764
+ "Contact",
765
+ "StartTrial",
766
+ "Subscribe",
767
+ "Donate",
768
+ "Schedule",
769
+ "SubmitApplication",
770
+ "CustomizeProduct",
771
+ "FindLocation"
772
+ ]);
773
+ var MetaPixelProvider = class extends BaseProvider {
774
+ constructor(config) {
775
+ super();
776
+ this.name = "meta-pixel";
777
+ this.fbqReady = null;
778
+ /**
779
+ * Cached user data for the Conversions API. Updated by `identify()` so
780
+ * that subsequent `track()` and `page()` calls include user information.
781
+ */
782
+ this.cachedUserData = {};
783
+ this.config = config;
784
+ this.http = createHttpClient();
785
+ this.isBrowser = config.appType === "browser";
786
+ this.apiVersion = config.apiVersion ?? DEFAULT_API_VERSION;
787
+ if (config.advancedMatching) {
788
+ this.cachedUserData = { ...config.advancedMatching };
789
+ }
790
+ }
791
+ // ---------------------------------------------------------------------------
792
+ // Browser mode — fbevents.js injection & fbq() calls
793
+ // ---------------------------------------------------------------------------
794
+ /**
795
+ * Initialises the Meta Pixel snippet. This mirrors the official snippet from
796
+ * https://developers.facebook.com/docs/meta-pixel/get-started:
797
+ *
798
+ * !function(f,b,e,v,n,t,s){...}(window, document,'script',
799
+ * 'https://connect.facebook.net/en_US/fbevents.js');
800
+ * fbq('init', 'PIXEL_ID');
801
+ * fbq('track', 'PageView');
802
+ *
803
+ * Key details:
804
+ * - The fbq shim queue is set up synchronously before the script loads
805
+ * - `fbq('init', pixelId, advancedMatching?)` is called immediately
806
+ * - autoConfig is respected via `fbq('set', 'autoConfig', false, pixelId)`
807
+ * - debug mode via `fbq('set', 'debug', true)`
808
+ * - The returned promise resolves when the script finishes loading
809
+ */
810
+ initFbq() {
811
+ if (typeof window === "undefined" || typeof document === "undefined") {
812
+ return Promise.resolve();
813
+ }
814
+ if (!window.fbq) {
815
+ const queue = [];
816
+ const fbq = Object.assign(
817
+ function fbq2(...args) {
818
+ if (fbq2.callMethod) {
819
+ fbq2.callMethod(...args);
820
+ } else {
821
+ queue.push(args);
822
+ }
823
+ },
824
+ {
825
+ push: void 0,
826
+ loaded: true,
827
+ version: "2.0",
828
+ queue,
829
+ callMethod: void 0
830
+ }
831
+ );
832
+ fbq.push = fbq;
833
+ window.fbq = fbq;
834
+ window._fbq = fbq;
835
+ }
836
+ if (this.config.autoConfig === false) {
837
+ window.fbq("set", "autoConfig", false, this.config.pixelId);
838
+ }
839
+ if (this.config.debug) {
840
+ window.fbq("set", "debug", true);
841
+ }
842
+ const matchData = this.config.advancedMatching;
843
+ if (matchData && Object.keys(matchData).length > 0) {
844
+ window.fbq("init", this.config.pixelId, matchData);
845
+ } else {
846
+ window.fbq("init", this.config.pixelId);
847
+ }
848
+ return new Promise((resolve) => {
849
+ const script = document.createElement("script");
850
+ script.async = true;
851
+ script.src = FBEVENTS_SCRIPT_URL;
852
+ script.onload = () => resolve();
853
+ script.onerror = () => resolve();
854
+ document.head.appendChild(script);
855
+ });
856
+ }
857
+ /**
858
+ * Ensures the Pixel is initialised exactly once. Subsequent calls return
859
+ * the same promise so the script is never injected twice.
860
+ */
861
+ ensureFbq() {
862
+ if (!this.fbqReady) {
863
+ this.fbqReady = this.initFbq();
864
+ }
865
+ return this.fbqReady;
866
+ }
867
+ // -- browser track --
868
+ async trackBrowser(options) {
869
+ if (typeof window === "undefined") {
870
+ return this.buildFbqResult();
871
+ }
872
+ await this.ensureFbq();
873
+ const { event, properties, context } = options;
874
+ const eventId = context?.eventId;
875
+ const isStandard = STANDARD_EVENTS.has(event);
876
+ const command = isStandard ? "track" : "trackCustom";
877
+ if (eventId) {
878
+ window.fbq(command, event, properties ?? {}, { eventID: eventId });
879
+ } else {
880
+ window.fbq(command, event, properties ?? {});
881
+ }
882
+ return this.buildFbqResult();
883
+ }
884
+ // -- browser identify --
885
+ async identifyBrowser(options) {
886
+ if (typeof window === "undefined") {
887
+ return this.buildFbqResult();
888
+ }
889
+ await this.ensureFbq();
890
+ const matchData = {};
891
+ if (options.traits) {
892
+ Object.assign(matchData, options.traits);
893
+ }
894
+ matchData["external_id"] = options.userId;
895
+ window.fbq("init", this.config.pixelId, matchData);
896
+ return this.buildFbqResult();
897
+ }
898
+ // -- browser page --
899
+ async pageBrowser(options) {
900
+ if (typeof window === "undefined") {
901
+ return this.buildFbqResult();
902
+ }
903
+ await this.ensureFbq();
904
+ window.fbq("track", "PageView");
905
+ return this.buildFbqResult();
906
+ }
907
+ buildFbqResult() {
908
+ return {
909
+ provider: this.name,
910
+ success: true,
911
+ statusCode: 200
912
+ };
913
+ }
914
+ // ---------------------------------------------------------------------------
915
+ // Server mode — Meta Conversions API (CAPI)
916
+ // ---------------------------------------------------------------------------
917
+ get capiEndpoint() {
918
+ return `https://graph.facebook.com/${this.apiVersion}/${this.config.pixelId}/events`;
919
+ }
920
+ /**
921
+ * Build the `user_data` object for a CAPI event.
922
+ * Merges cached user data (from `identify()` / config) with any per-event
923
+ * overrides, then SHA-256 hashes known PII fields.
924
+ */
925
+ buildUserData(overrides) {
926
+ const merged = { ...this.cachedUserData, ...overrides };
927
+ return hashUserData(merged);
928
+ }
929
+ // -- server track --
930
+ async trackServer(options) {
931
+ try {
932
+ const { event, properties, context, timestamp, userId } = options;
933
+ const eventId = context?.eventId;
934
+ const userDataOverrides = {};
935
+ if (userId) {
936
+ userDataOverrides["external_id"] = userId;
937
+ }
938
+ const eventData = {
939
+ event_name: event,
940
+ event_time: timestamp ? Math.floor(timestamp.getTime() / 1e3) : Math.floor(Date.now() / 1e3),
941
+ action_source: "website",
942
+ user_data: this.buildUserData(userDataOverrides)
943
+ };
944
+ if (eventId) {
945
+ eventData["event_id"] = eventId;
946
+ }
947
+ const pageUrl = context?.page?.url;
948
+ if (pageUrl) {
949
+ eventData["event_source_url"] = pageUrl;
950
+ }
951
+ if (properties && Object.keys(properties).length > 0) {
952
+ eventData["custom_data"] = properties;
953
+ }
954
+ const body = {
955
+ data: [eventData]
956
+ };
957
+ if (this.config.testEventCode) {
958
+ body["test_event_code"] = this.config.testEventCode;
959
+ }
960
+ const response = await this.http.post(this.capiEndpoint, body, {
961
+ params: { access_token: this.config.accessToken }
962
+ });
963
+ return this.buildSuccess(response.status);
964
+ } catch (error) {
965
+ if (isAxiosError(error)) {
966
+ return this.buildError(
967
+ error.message,
968
+ `META_PIXEL_HTTP_${error.response?.status ?? "ERROR"}`,
969
+ error
970
+ );
971
+ }
972
+ return this.buildError(String(error), "META_PIXEL_UNKNOWN_ERROR", error);
973
+ }
974
+ }
975
+ // -- server identify --
976
+ async identifyServer(options) {
977
+ if (options.traits) {
978
+ Object.assign(this.cachedUserData, options.traits);
979
+ }
980
+ this.cachedUserData["external_id"] = options.userId;
981
+ return this.buildSuccess(200);
982
+ }
983
+ // -- server page --
984
+ async pageServer(options) {
985
+ return this.trackServer({
986
+ event: "PageView",
987
+ properties: options.properties,
988
+ userId: options.userId,
989
+ anonymousId: options.anonymousId,
990
+ timestamp: options.timestamp,
991
+ context: {
992
+ page: {
993
+ url: options.url,
994
+ referrer: options.referrer
995
+ }
996
+ }
997
+ });
998
+ }
999
+ // ---------------------------------------------------------------------------
1000
+ // Consent — Meta Pixel's simple grant / revoke model
1001
+ // ---------------------------------------------------------------------------
1002
+ /**
1003
+ * Meta Pixel consent is a binary grant/revoke model, not the granular
1004
+ * settings of Google Consent Mode v2. This method accepts the standard
1005
+ * `ConsentSettings` type but is a no-op — use `updatePixelConsent()` for
1006
+ * Meta-specific consent control in browser mode.
1007
+ */
1008
+ /**
1009
+ * Grant or revoke consent for the Meta Pixel in browser mode.
1010
+ * Calls `fbq('consent', 'grant')` or `fbq('consent', 'revoke')`.
1011
+ */
1012
+ async updatePixelConsent(granted) {
1013
+ if (!this.isBrowser || typeof window === "undefined") {
1014
+ return;
1015
+ }
1016
+ await this.ensureFbq();
1017
+ window.fbq("consent", granted ? "grant" : "revoke");
1018
+ }
1019
+ // ---------------------------------------------------------------------------
1020
+ // Public API — routes to browser or server mode
1021
+ // ---------------------------------------------------------------------------
1022
+ async track(options) {
1023
+ if (this.isBrowser) {
1024
+ return this.trackBrowser(options);
1025
+ }
1026
+ return this.trackServer(options);
1027
+ }
1028
+ async identify(options) {
1029
+ if (this.isBrowser) {
1030
+ return this.identifyBrowser(options);
1031
+ }
1032
+ return this.identifyServer(options);
1033
+ }
1034
+ async page(options) {
1035
+ if (this.isBrowser) {
1036
+ return this.pageBrowser(options);
1037
+ }
1038
+ return this.pageServer(options);
1039
+ }
1040
+ };
1041
+
720
1042
  // src/mytart.ts
721
1043
  function createProvider(config) {
722
1044
  switch (config.provider) {
@@ -732,6 +1054,8 @@ function createProvider(config) {
732
1054
  return new PlausibleProvider(config);
733
1055
  case "posthog":
734
1056
  return new PostHogProvider(config);
1057
+ case "meta-pixel":
1058
+ return new MetaPixelProvider(config);
735
1059
  default: {
736
1060
  const exhaustive = config;
737
1061
  throw new Error(`Unknown provider: ${exhaustive.provider}`);
@@ -792,6 +1116,7 @@ export {
792
1116
  AmplitudeProvider,
793
1117
  BaseProvider,
794
1118
  GoogleAnalyticsProvider,
1119
+ MetaPixelProvider,
795
1120
  MixpanelProvider,
796
1121
  Mytart,
797
1122
  PlausibleProvider,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mytart",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Multi-Yield Tracking & Analytics Relay Tool — framework-agnostic analytics for any project",
5
5
  "keywords": [
6
6
  "analytics",