94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
import { dev } from '$app/environment';
|
|
import log from 'loglevel';
|
|
import type { CAPIEvent } from './event.ts';
|
|
import * as v from 'valibot';
|
|
import { capiResponseBodySchema, type CAPIResponseBody } from './handle.ts';
|
|
|
|
const GRAPH_VERSION = 'v24.0';
|
|
|
|
/**
|
|
* Connector class for Meta Conversion API (CAPI). Abstraction over direct HTTP
|
|
* requests to Meta's CAPI endpoint.
|
|
*
|
|
* See https://developers.facebook.com/docs/marketing-api/conversions-api/get-started
|
|
* for more information.
|
|
*/
|
|
export class CAPIConnector {
|
|
private _accessToken: string;
|
|
private _pixelID: string;
|
|
private _testEventCode?: string;
|
|
|
|
/**
|
|
* Creates a new MCAPIControl instance.
|
|
*
|
|
* @param accessToken - Your Meta Pixel Conversion API access token.
|
|
* @param pixelID - Your Meta Pixel ID.
|
|
* @param testEventCode - Optional test event code used for all events if provided.
|
|
*/
|
|
constructor(accessToken: string, pixelID: string, testEventCode?: string) {
|
|
this._accessToken = accessToken;
|
|
this._pixelID = pixelID;
|
|
this._testEventCode = testEventCode;
|
|
}
|
|
|
|
/**
|
|
* Sends conversion events to the Meta Conversion API.
|
|
*
|
|
* @param events - Array of CAPIEvent instances to send.
|
|
* @returns The response from the Meta CAPI.
|
|
* @throws Will throw an error if the request fails or the API returns an error.
|
|
*/
|
|
async sendEvents(events: CAPIEvent[]): Promise<CAPIResponseBody> {
|
|
if (dev && !this._testEventCode) {
|
|
log.warn(
|
|
`[CAPIConnector] Sending ${events.length} event(s) in dev mode without a test event code. ` +
|
|
'Consider providing a test event code to avoid affecting real data.'
|
|
);
|
|
}
|
|
|
|
const url = `https://graph.facebook.com/${GRAPH_VERSION}/${this._pixelID}/events`;
|
|
|
|
const body = {
|
|
data: events.map((e) => e.toObject()),
|
|
test_event_code: this._testEventCode
|
|
};
|
|
log.debug(
|
|
`[CAPIConnector] [${this._pixelID}] Sending ${events.length} event(s) to Meta CAPI at ${url} with body: ${JSON.stringify(body, null, 2)}`
|
|
);
|
|
|
|
const resp = await fetch(`${url}?access_token=${this._accessToken}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(body)
|
|
});
|
|
const json = await resp.json();
|
|
|
|
if (!resp.ok) {
|
|
throw new Error(`Meta CAPI error ${resp.status}: ${JSON.stringify(json, null, 2)}`);
|
|
}
|
|
|
|
try {
|
|
const parsed = v.parse(capiResponseBodySchema, json);
|
|
log.info(
|
|
`[CAPIConnector] [${this._pixelID}] Successfully sent ${events.length} event(s) to Meta CAPI.`
|
|
);
|
|
return parsed as CAPIResponseBody;
|
|
} catch (err) {
|
|
throw new Error(`[CAPIConnector] Invalid response shape: ${(err as Error).message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shorthand for sending a single event to the Meta Conversion API.
|
|
*
|
|
* @param event - The CAPIEvent instance to send.
|
|
* @returns The response from the Meta CAPI.
|
|
* @throws Will throw an error if the request fails or the API returns an error.
|
|
*/
|
|
async trackEvent(event: CAPIEvent): Promise<CAPIResponseBody> {
|
|
return this.sendEvents([event]);
|
|
}
|
|
}
|