consentlayer 0.0.0 → 1.0.0-rc.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/LICENSE +21 -0
- package/README.md +87 -1
- package/dist/adapters/google-consent-mode.d.ts +20 -0
- package/dist/adapters/google-consent-mode.js +18 -0
- package/dist/adapters/google-consent-mode.js.map +1 -0
- package/dist/adapters/shopify.d.ts +49 -0
- package/dist/adapters/shopify.js +85 -0
- package/dist/adapters/shopify.js.map +1 -0
- package/dist/adapters/tcf.d.ts +20 -0
- package/dist/adapters/tcf.js +64 -0
- package/dist/adapters/tcf.js.map +1 -0
- package/dist/analytics-provider-07mxUgIK.d.ts +36 -0
- package/dist/angular.d.ts +56 -0
- package/dist/angular.js +120 -0
- package/dist/angular.js.map +1 -0
- package/dist/auto-tracking.d.ts +11 -0
- package/dist/auto-tracking.js +72 -0
- package/dist/auto-tracking.js.map +1 -0
- package/dist/chunk-7XLGT6U3.js +34 -0
- package/dist/chunk-7XLGT6U3.js.map +1 -0
- package/dist/chunk-BWPWDN4G.js +61 -0
- package/dist/chunk-BWPWDN4G.js.map +1 -0
- package/dist/chunk-DK45XPEY.js +77 -0
- package/dist/chunk-DK45XPEY.js.map +1 -0
- package/dist/chunk-ETU64ZV3.js +98 -0
- package/dist/chunk-ETU64ZV3.js.map +1 -0
- package/dist/chunk-FDJMIJFV.js +367 -0
- package/dist/chunk-FDJMIJFV.js.map +1 -0
- package/dist/chunk-HJGBVDPM.js +47 -0
- package/dist/chunk-HJGBVDPM.js.map +1 -0
- package/dist/chunk-J3KUPF4R.js +69 -0
- package/dist/chunk-J3KUPF4R.js.map +1 -0
- package/dist/chunk-LBEIEAJX.js +55 -0
- package/dist/chunk-LBEIEAJX.js.map +1 -0
- package/dist/chunk-LEAQJXJU.js +582 -0
- package/dist/chunk-LEAQJXJU.js.map +1 -0
- package/dist/chunk-R6I7OYQB.js +69 -0
- package/dist/chunk-R6I7OYQB.js.map +1 -0
- package/dist/chunk-RK2M57GW.js +54 -0
- package/dist/chunk-RK2M57GW.js.map +1 -0
- package/dist/chunk-RZ4TYRNY.js +128 -0
- package/dist/chunk-RZ4TYRNY.js.map +1 -0
- package/dist/chunk-TVSZJLRC.js +112 -0
- package/dist/chunk-TVSZJLRC.js.map +1 -0
- package/dist/chunk-TYOQYV45.js +79 -0
- package/dist/chunk-TYOQYV45.js.map +1 -0
- package/dist/chunk-UAIZU5WD.js +52 -0
- package/dist/chunk-UAIZU5WD.js.map +1 -0
- package/dist/chunk-VIIGBALD.js +14 -0
- package/dist/chunk-VIIGBALD.js.map +1 -0
- package/dist/consent-controller-DyHPNfyT.d.ts +47 -0
- package/dist/consentlayer-banner.global.js +20 -0
- package/dist/consentlayer.global.js +9 -0
- package/dist/create-analytics-Bzvm1UpE.d.ts +44 -0
- package/dist/ecommerce.d.ts +41 -0
- package/dist/ecommerce.js +43 -0
- package/dist/ecommerce.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/next.d.ts +16 -0
- package/dist/next.js +27 -0
- package/dist/next.js.map +1 -0
- package/dist/nuxt/config.d.ts +7 -0
- package/dist/nuxt/config.js +6 -0
- package/dist/nuxt/config.js.map +1 -0
- package/dist/nuxt/index.d.ts +11 -0
- package/dist/nuxt/index.js +35 -0
- package/dist/nuxt/index.js.map +1 -0
- package/dist/nuxt/plugin.d.ts +5 -0
- package/dist/nuxt/plugin.js +24 -0
- package/dist/nuxt/plugin.js.map +1 -0
- package/dist/providers/contentsquare.d.ts +27 -0
- package/dist/providers/contentsquare.js +58 -0
- package/dist/providers/contentsquare.js.map +1 -0
- package/dist/providers/ga4.d.ts +46 -0
- package/dist/providers/ga4.js +3 -0
- package/dist/providers/ga4.js.map +1 -0
- package/dist/providers/gtm.d.ts +28 -0
- package/dist/providers/gtm.js +83 -0
- package/dist/providers/gtm.js.map +1 -0
- package/dist/providers/hotjar.d.ts +38 -0
- package/dist/providers/hotjar.js +68 -0
- package/dist/providers/hotjar.js.map +1 -0
- package/dist/providers/meta.d.ts +42 -0
- package/dist/providers/meta.js +65 -0
- package/dist/providers/meta.js.map +1 -0
- package/dist/providers/posthog.d.ts +42 -0
- package/dist/providers/posthog.js +91 -0
- package/dist/providers/posthog.js.map +1 -0
- package/dist/providers/sentry.d.ts +38 -0
- package/dist/providers/sentry.js +82 -0
- package/dist/providers/sentry.js.map +1 -0
- package/dist/providers/tiktok.d.ts +46 -0
- package/dist/providers/tiktok.js +72 -0
- package/dist/providers/tiktok.js.map +1 -0
- package/dist/providers/vercel.d.ts +14 -0
- package/dist/providers/vercel.js +17 -0
- package/dist/providers/vercel.js.map +1 -0
- package/dist/react/index.d.ts +78 -0
- package/dist/react/index.js +79 -0
- package/dist/react/index.js.map +1 -0
- package/dist/sentry/next.d.ts +1 -0
- package/dist/sentry/next.js +3 -0
- package/dist/sentry/next.js.map +1 -0
- package/dist/sentry/server.d.ts +24 -0
- package/dist/sentry/server.js +28 -0
- package/dist/sentry/server.js.map +1 -0
- package/dist/sentry-replay-HSFVEFCM.js +3 -0
- package/dist/sentry-replay-HSFVEFCM.js.map +1 -0
- package/dist/sentry-sdk-YQOGKGWD.js +3 -0
- package/dist/sentry-sdk-YQOGKGWD.js.map +1 -0
- package/dist/sentry-tracing-KWT7ITHF.js +3 -0
- package/dist/sentry-tracing-KWT7ITHF.js.map +1 -0
- package/dist/server/ga4.d.ts +15 -0
- package/dist/server/ga4.js +29 -0
- package/dist/server/ga4.js.map +1 -0
- package/dist/server/index.d.ts +116 -0
- package/dist/server/index.js +206 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/meta.d.ts +14 -0
- package/dist/server/meta.js +63 -0
- package/dist/server/meta.js.map +1 -0
- package/dist/server/sgtm.d.ts +18 -0
- package/dist/server/sgtm.js +34 -0
- package/dist/server/sgtm.js.map +1 -0
- package/dist/server/tiktok.d.ts +15 -0
- package/dist/server/tiktok.js +64 -0
- package/dist/server/tiktok.js.map +1 -0
- package/dist/solid.d.ts +59 -0
- package/dist/solid.js +3 -0
- package/dist/solid.js.map +1 -0
- package/dist/solidstart.d.ts +16 -0
- package/dist/solidstart.js +22 -0
- package/dist/solidstart.js.map +1 -0
- package/dist/svelte/GtmNoscript.svelte +18 -0
- package/dist/svelte/MetaPixelNoscript.svelte +18 -0
- package/dist/svelte.d.ts +55 -0
- package/dist/svelte.js +3 -0
- package/dist/svelte.js.map +1 -0
- package/dist/sveltekit.d.ts +18 -0
- package/dist/sveltekit.js +16 -0
- package/dist/sveltekit.js.map +1 -0
- package/dist/testing.d.ts +11 -0
- package/dist/testing.js +50 -0
- package/dist/testing.js.map +1 -0
- package/dist/types-BV_AFZKQ.d.ts +407 -0
- package/dist/vue.d.ts +92 -0
- package/dist/vue.js +97 -0
- package/dist/vue.js.map +1 -0
- package/package.json +278 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/meta.ts"],"names":[],"mappings":"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","file":"meta.js","sourcesContent":["import { toMetaEvent } from '../core/meta-events'\nimport { isValidEmail, isValidPhoneE164 } from '../core/validation'\nimport type { ServerDestination } from './handler'\nimport { sha256 } from './http'\n\nconst isPlainValue = (value: unknown) =>\n value === null ||\n ['string', 'number', 'boolean'].includes(typeof value) ||\n Array.isArray(value) ||\n (typeof value === 'object' && value.constructor === Object)\n\n/** Meta Conversions API. Receives browser events too; Meta dedupes them by event ID. Off (`undefined`) while `pixelId` or `accessToken` is unset. */\nexport function metaCapi(options: {\n pixelId: string | undefined\n accessToken: string | undefined\n testEventCode?: string\n /** Graph API version. */\n apiVersion?: string\n fetchImpl?: typeof fetch\n}): ServerDestination<true> | undefined {\n const pixelId = options.pixelId?.trim()\n const accessToken = options.accessToken?.trim()\n if (!pixelId || !accessToken) {\n if (pixelId || accessToken) console.warn('[consentlayer] metaCapi is off: set both pixelId and accessToken.')\n return undefined\n }\n const fetchImpl = options.fetchImpl ?? globalThis.fetch\n return {\n id: 'meta',\n requiredConsent: 'marketing',\n forwardBrowserEvents: true,\n async send(event, request) {\n const { name, params } = toMetaEvent({\n id: event.id ?? '',\n name: event.name,\n params: event.params ?? {},\n options: { providers: event.providers },\n })\n const email = event.identity?.email?.trim().toLowerCase()\n const phone = event.identity?.phone && `+${event.identity.phone.replace(/\\D/g, '')}`\n const url = new URL(`https://graph.facebook.com/${options.apiVersion ?? 'v26.0'}/${pixelId}/events`)\n url.searchParams.set('access_token', accessToken)\n if (options.testEventCode) url.searchParams.set('test_event_code', options.testEventCode)\n const response = await fetchImpl(url.href, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n data: [\n {\n event_name: name,\n event_time: Math.floor(Date.now() / 1000),\n event_id: event.id,\n event_source_url: event.url,\n action_source: 'website',\n custom_data: Object.fromEntries(Object.entries(params).filter(([, value]) => isPlainValue(value))),\n user_data: {\n client_ip_address: request?.ip,\n client_user_agent: request?.userAgent,\n fbp: request?.cookies._fbp,\n fbc: request?.cookies._fbc,\n em: email && isValidEmail(email) ? await sha256(email) : undefined,\n ph: phone && isValidPhoneE164(phone) ? await sha256(phone.slice(1)) : undefined,\n external_id: await sha256(event.identity?.userId),\n },\n },\n ],\n }),\n })\n if (!response.ok) throw new Error(`Meta CAPI ${response.status}: ${await response.text()}`)\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ServerDestination } from './index.js';
|
|
2
|
+
import '../types-BV_AFZKQ.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Server-side Google Tag Manager: events go, as GA4 Measurement Protocol
|
|
6
|
+
* requests, to the Measurement Protocol client of your server container.
|
|
7
|
+
* Receives browser events too. Off (`undefined`) while `url` or `measurementId` is unset.
|
|
8
|
+
*/
|
|
9
|
+
declare function sgtm(options: {
|
|
10
|
+
/** The client's endpoint: your tagging server and the client's activation path, e.g. `https://sgtm.example.com/mp/collect`. */
|
|
11
|
+
url: string | undefined;
|
|
12
|
+
measurementId: string | undefined;
|
|
13
|
+
/** Sent as `api_secret`, for a client or tag that checks it. */
|
|
14
|
+
apiSecret?: string;
|
|
15
|
+
fetchImpl?: typeof fetch;
|
|
16
|
+
}): ServerDestination<true> | undefined;
|
|
17
|
+
|
|
18
|
+
export { sgtm };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { sendMeasurementProtocol } from '../chunk-HJGBVDPM.js';
|
|
2
|
+
import { isHttpsOrRelativeUrl } from '../chunk-J3KUPF4R.js';
|
|
3
|
+
|
|
4
|
+
// src/server/sgtm.ts
|
|
5
|
+
function sgtm(options) {
|
|
6
|
+
const url = options.url?.trim();
|
|
7
|
+
const measurementId = options.measurementId?.trim();
|
|
8
|
+
if (!url || !measurementId) {
|
|
9
|
+
if (url || measurementId) console.warn("[consentlayer] sgtm is off: set both url and measurementId.");
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
if (url.startsWith("/") || !isHttpsOrRelativeUrl(url)) {
|
|
13
|
+
throw new Error(`[consentlayer] sgtm url must be an HTTPS URL, received "${url}".`);
|
|
14
|
+
}
|
|
15
|
+
const endpoint = new URL(url);
|
|
16
|
+
endpoint.searchParams.set("measurement_id", measurementId);
|
|
17
|
+
if (options.apiSecret?.trim()) endpoint.searchParams.set("api_secret", options.apiSecret.trim());
|
|
18
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
19
|
+
return {
|
|
20
|
+
id: "sgtm",
|
|
21
|
+
requiredConsent: "analytics",
|
|
22
|
+
forwardBrowserEvents: true,
|
|
23
|
+
send: (event, request) => sendMeasurementProtocol(
|
|
24
|
+
{ url: endpoint.href, sender: "sgtm", receiver: "Server-side GTM" },
|
|
25
|
+
event,
|
|
26
|
+
request,
|
|
27
|
+
fetchImpl
|
|
28
|
+
)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { sgtm };
|
|
33
|
+
//# sourceMappingURL=sgtm.js.map
|
|
34
|
+
//# sourceMappingURL=sgtm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/sgtm.ts"],"names":[],"mappings":";;;;AASO,SAAS,KAAK,OAAA,EAOmB;AACtC,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,EAAK,IAAA,EAAK;AAC9B,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,aAAA,EAAe,IAAA,EAAK;AAClD,EAAA,IAAI,CAAC,GAAA,IAAO,CAAC,aAAA,EAAe;AAC1B,IAAA,IAAI,GAAA,IAAO,aAAA,EAAe,OAAA,CAAQ,IAAA,CAAK,6DAA6D,CAAA;AACpG,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,IAAI,IAAI,UAAA,CAAW,GAAG,KAAK,CAAC,oBAAA,CAAqB,GAAG,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wDAAA,EAA2D,GAAG,CAAA,EAAA,CAAI,CAAA;AAAA,EACpF;AACA,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,GAAG,CAAA;AAC5B,EAAA,QAAA,CAAS,YAAA,CAAa,GAAA,CAAI,gBAAA,EAAkB,aAAa,CAAA;AACzD,EAAA,IAAI,OAAA,CAAQ,SAAA,EAAW,IAAA,EAAK,EAAG,QAAA,CAAS,YAAA,CAAa,GAAA,CAAI,YAAA,EAAc,OAAA,CAAQ,SAAA,CAAU,IAAA,EAAM,CAAA;AAC/F,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,SAAA,IAAa,UAAA,CAAW,KAAA;AAClD,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,MAAA;AAAA,IACJ,eAAA,EAAiB,WAAA;AAAA,IACjB,oBAAA,EAAsB,IAAA;AAAA,IACtB,IAAA,EAAM,CAAC,KAAA,EAAO,OAAA,KACZ,uBAAA;AAAA,MACE,EAAE,GAAA,EAAK,QAAA,CAAS,MAAM,MAAA,EAAQ,MAAA,EAAQ,UAAU,iBAAA,EAAkB;AAAA,MAClE,KAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA;AACF,GACJ;AACF","file":"sgtm.js","sourcesContent":["import { isHttpsOrRelativeUrl } from '../core/validation'\nimport type { ServerDestination } from './handler'\nimport { sendMeasurementProtocol } from './measurement-protocol'\n\n/**\n * Server-side Google Tag Manager: events go, as GA4 Measurement Protocol\n * requests, to the Measurement Protocol client of your server container.\n * Receives browser events too. Off (`undefined`) while `url` or `measurementId` is unset.\n */\nexport function sgtm(options: {\n /** The client's endpoint: your tagging server and the client's activation path, e.g. `https://sgtm.example.com/mp/collect`. */\n url: string | undefined\n measurementId: string | undefined\n /** Sent as `api_secret`, for a client or tag that checks it. */\n apiSecret?: string\n fetchImpl?: typeof fetch\n}): ServerDestination<true> | undefined {\n const url = options.url?.trim()\n const measurementId = options.measurementId?.trim()\n if (!url || !measurementId) {\n if (url || measurementId) console.warn('[consentlayer] sgtm is off: set both url and measurementId.')\n return undefined\n }\n if (url.startsWith('/') || !isHttpsOrRelativeUrl(url)) {\n throw new Error(`[consentlayer] sgtm url must be an HTTPS URL, received \"${url}\".`)\n }\n const endpoint = new URL(url)\n endpoint.searchParams.set('measurement_id', measurementId)\n if (options.apiSecret?.trim()) endpoint.searchParams.set('api_secret', options.apiSecret.trim())\n const fetchImpl = options.fetchImpl ?? globalThis.fetch\n return {\n id: 'sgtm',\n requiredConsent: 'analytics',\n forwardBrowserEvents: true,\n send: (event, request) =>\n sendMeasurementProtocol(\n { url: endpoint.href, sender: 'sgtm', receiver: 'Server-side GTM' },\n event,\n request,\n fetchImpl,\n ),\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ServerDestination } from './index.js';
|
|
2
|
+
import '../types-BV_AFZKQ.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TikTok Events API. Receives browser events too; TikTok dedupes them by event ID.
|
|
6
|
+
* Page views stay with the pixel. Off (`undefined`) while `pixelId` or `accessToken` is unset.
|
|
7
|
+
*/
|
|
8
|
+
declare function tiktokEvents(options: {
|
|
9
|
+
pixelId: string | undefined;
|
|
10
|
+
accessToken: string | undefined;
|
|
11
|
+
testEventCode?: string;
|
|
12
|
+
fetchImpl?: typeof fetch;
|
|
13
|
+
}): ServerDestination<true> | undefined;
|
|
14
|
+
|
|
15
|
+
export { tiktokEvents };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { sha256 } from '../chunk-DK45XPEY.js';
|
|
2
|
+
import { toTikTokEvent, tiktokUserData } from '../chunk-RK2M57GW.js';
|
|
3
|
+
import { isValidPhoneE164, isValidEmail } from '../chunk-J3KUPF4R.js';
|
|
4
|
+
|
|
5
|
+
// src/server/tiktok.ts
|
|
6
|
+
function tiktokEvents(options) {
|
|
7
|
+
const pixelId = options.pixelId?.trim();
|
|
8
|
+
const accessToken = options.accessToken?.trim();
|
|
9
|
+
if (!pixelId || !accessToken) {
|
|
10
|
+
if (pixelId || accessToken) console.warn("[consentlayer] tiktokEvents is off: set both pixelId and accessToken.");
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
14
|
+
return {
|
|
15
|
+
id: "tiktok",
|
|
16
|
+
requiredConsent: "marketing",
|
|
17
|
+
forwardBrowserEvents: true,
|
|
18
|
+
async send(event, request) {
|
|
19
|
+
if (event.name === "page_view") return;
|
|
20
|
+
const { name, params } = toTikTokEvent({
|
|
21
|
+
id: event.id ?? "",
|
|
22
|
+
name: event.name,
|
|
23
|
+
params: event.params ?? {},
|
|
24
|
+
options: { providers: event.providers }
|
|
25
|
+
});
|
|
26
|
+
const { email, phone_number: phone, external_id } = tiktokUserData(event.identity ?? {});
|
|
27
|
+
const response = await fetchImpl("https://business-api.tiktok.com/open_api/v1.3/event/track/", {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "Access-Token": accessToken, "Content-Type": "application/json" },
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
event_source: "web",
|
|
32
|
+
event_source_id: pixelId,
|
|
33
|
+
test_event_code: options.testEventCode,
|
|
34
|
+
data: [
|
|
35
|
+
{
|
|
36
|
+
event: name,
|
|
37
|
+
event_time: Math.floor(Date.now() / 1e3),
|
|
38
|
+
event_id: event.id,
|
|
39
|
+
user: {
|
|
40
|
+
email: email && isValidEmail(email) ? await sha256(email) : void 0,
|
|
41
|
+
phone: phone && isValidPhoneE164(phone) ? await sha256(phone) : void 0,
|
|
42
|
+
external_id: await sha256(external_id),
|
|
43
|
+
ip: request?.ip,
|
|
44
|
+
user_agent: request?.userAgent,
|
|
45
|
+
ttp: request?.cookies._ttp,
|
|
46
|
+
ttclid: request?.cookies.ttclid
|
|
47
|
+
},
|
|
48
|
+
properties: params,
|
|
49
|
+
page: { url: event.url }
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
const result = await response.json().catch(() => ({}));
|
|
55
|
+
if (!response.ok || result.code !== 0) {
|
|
56
|
+
throw new Error(`TikTok Events API ${response.ok ? result.code : response.status}: ${result.message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { tiktokEvents };
|
|
63
|
+
//# sourceMappingURL=tiktok.js.map
|
|
64
|
+
//# sourceMappingURL=tiktok.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/tiktok.ts"],"names":[],"mappings":"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","file":"tiktok.js","sourcesContent":["import { tiktokUserData, toTikTokEvent } from '../core/tiktok-events'\nimport { isValidEmail, isValidPhoneE164 } from '../core/validation'\nimport type { ServerDestination } from './handler'\nimport { sha256 } from './http'\n\n/**\n * TikTok Events API. Receives browser events too; TikTok dedupes them by event ID.\n * Page views stay with the pixel. Off (`undefined`) while `pixelId` or `accessToken` is unset.\n */\nexport function tiktokEvents(options: {\n pixelId: string | undefined\n accessToken: string | undefined\n testEventCode?: string\n fetchImpl?: typeof fetch\n}): ServerDestination<true> | undefined {\n const pixelId = options.pixelId?.trim()\n const accessToken = options.accessToken?.trim()\n if (!pixelId || !accessToken) {\n if (pixelId || accessToken) console.warn('[consentlayer] tiktokEvents is off: set both pixelId and accessToken.')\n return undefined\n }\n const fetchImpl = options.fetchImpl ?? globalThis.fetch\n return {\n id: 'tiktok',\n requiredConsent: 'marketing',\n forwardBrowserEvents: true,\n async send(event, request) {\n // ttq.page() takes no event ID, so a server copy of a page view could not be deduplicated.\n if (event.name === 'page_view') return\n const { name, params } = toTikTokEvent({\n id: event.id ?? '',\n name: event.name,\n params: event.params ?? {},\n options: { providers: event.providers },\n })\n const { email, phone_number: phone, external_id } = tiktokUserData(event.identity ?? {})\n const response = await fetchImpl('https://business-api.tiktok.com/open_api/v1.3/event/track/', {\n method: 'POST',\n headers: { 'Access-Token': accessToken, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n event_source: 'web',\n event_source_id: pixelId,\n test_event_code: options.testEventCode,\n data: [\n {\n event: name,\n event_time: Math.floor(Date.now() / 1000),\n event_id: event.id,\n user: {\n email: email && isValidEmail(email) ? await sha256(email) : undefined,\n phone: phone && isValidPhoneE164(phone) ? await sha256(phone) : undefined,\n external_id: await sha256(external_id),\n ip: request?.ip,\n user_agent: request?.userAgent,\n ttp: request?.cookies._ttp,\n ttclid: request?.cookies.ttclid,\n },\n properties: params,\n page: { url: event.url },\n },\n ],\n }),\n })\n const result = (await response.json().catch(() => ({}))) as { code?: number; message?: string }\n if (!response.ok || result.code !== 0) {\n throw new Error(`TikTok Events API ${response.ok ? result.code : response.status}: ${result.message}`)\n }\n },\n }\n}\n"]}
|
package/dist/solid.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { JSX, ComponentProps, Accessor } from 'solid-js';
|
|
2
|
+
import { b as ConsentControllerOptions, C as ConsentControllerState, a as ConsentControllerActions } from './consent-controller-DyHPNfyT.js';
|
|
3
|
+
import { m as Analytics, n as PageViewPayload, l as ErrorContext, p as ConsentState, q as ConsentApi } from './types-BV_AFZKQ.js';
|
|
4
|
+
|
|
5
|
+
/** Props for `<AnalyticsProvider>`. */
|
|
6
|
+
type AnalyticsProviderProps = ConsentControllerOptions & {
|
|
7
|
+
analytics: Analytics;
|
|
8
|
+
children?: JSX.Element;
|
|
9
|
+
/** Changes on every navigation; a page view is sent once per key. `null` holds page views until the route is known. */
|
|
10
|
+
routeKey?: string | null;
|
|
11
|
+
page?: PageViewPayload;
|
|
12
|
+
};
|
|
13
|
+
/** What `useConsentController()` returns: the UI state as reactive properties, and the actions. */
|
|
14
|
+
type ConsentController = Readonly<ConsentControllerState> & ConsentControllerActions;
|
|
15
|
+
/** Boots analytics, sends page views on navigation and provides the state for your consent UI. */
|
|
16
|
+
declare function AnalyticsProvider(props: AnalyticsProviderProps): JSX.Element;
|
|
17
|
+
/** The instance from `<AnalyticsProvider>`, else the one `createAnalytics()` made. */
|
|
18
|
+
declare function useAnalytics(): Analytics;
|
|
19
|
+
/** Current consent as an accessor, `null` before a choice. */
|
|
20
|
+
declare function useConsent(): {
|
|
21
|
+
consent: Accessor<ConsentState | null>;
|
|
22
|
+
engine: ConsentApi;
|
|
23
|
+
setConsent: (consent: ConsentState) => Promise<void>;
|
|
24
|
+
update: (partial: Partial<ConsentState>) => Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
/** State and actions for building your own consent UI. */
|
|
27
|
+
declare function useConsentController(): ConsentController;
|
|
28
|
+
/** Props for `<ErrorBoundary>`. */
|
|
29
|
+
type ErrorBoundaryProps = {
|
|
30
|
+
children?: JSX.Element;
|
|
31
|
+
fallback: JSX.Element | ((error: unknown, reset: () => void) => JSX.Element);
|
|
32
|
+
/** Defaults to the instance from `<AnalyticsProvider>`, else the one `createAnalytics()` made. */
|
|
33
|
+
analytics?: Analytics;
|
|
34
|
+
tags?: ErrorContext['tags'];
|
|
35
|
+
};
|
|
36
|
+
/** Renders `fallback` when a child throws, and sends the error through `captureError`. */
|
|
37
|
+
declare function ErrorBoundary(props: ErrorBoundaryProps): JSX.Element;
|
|
38
|
+
/**
|
|
39
|
+
* Meta's `<noscript>` PageView pixel. It fires only for visitors without
|
|
40
|
+
* JavaScript, who never see the consent banner, so it tracks them without
|
|
41
|
+
* consent. Render it only where that is lawful for you. Renders nothing without
|
|
42
|
+
* `enabled` or while `pixelId` is unset. Other attributes go to the `<img>`.
|
|
43
|
+
*/
|
|
44
|
+
declare function MetaPixelNoscript(props: {
|
|
45
|
+
pixelId: string | undefined;
|
|
46
|
+
enabled?: boolean;
|
|
47
|
+
} & ComponentProps<'img'>): JSX.Element;
|
|
48
|
+
/**
|
|
49
|
+
* Google Tag Manager's `<noscript>` iframe. It loads the container only for
|
|
50
|
+
* visitors without JavaScript, who never see the consent banner, so its tags
|
|
51
|
+
* run without consent. Render it only where that is lawful for you. Renders
|
|
52
|
+
* nothing without `enabled` or while `containerId` is unset. Other attributes go to the `<iframe>`.
|
|
53
|
+
*/
|
|
54
|
+
declare function GtmNoscript(props: {
|
|
55
|
+
containerId: string | undefined;
|
|
56
|
+
enabled?: boolean;
|
|
57
|
+
} & ComponentProps<'iframe'>): JSX.Element;
|
|
58
|
+
|
|
59
|
+
export { AnalyticsProvider, type AnalyticsProviderProps, type ConsentController, ErrorBoundary, type ErrorBoundaryProps, GtmNoscript, MetaPixelNoscript, useAnalytics, useConsent, useConsentController };
|
package/dist/solid.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"solid.js"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
import { AnalyticsProviderProps } from './solid.js';
|
|
3
|
+
export { ConsentController, ErrorBoundary, ErrorBoundaryProps, GtmNoscript, MetaPixelNoscript, useAnalytics, useConsent, useConsentController } from './solid.js';
|
|
4
|
+
import './consent-controller-DyHPNfyT.js';
|
|
5
|
+
import './types-BV_AFZKQ.js';
|
|
6
|
+
|
|
7
|
+
/** Props for `<AnalyticsProvider>` from `consentlayer/solidstart`: the route comes from Solid Router. */
|
|
8
|
+
type SolidStartAnalyticsProviderProps = Omit<AnalyticsProviderProps, 'routeKey'>;
|
|
9
|
+
/**
|
|
10
|
+
* `<AnalyticsProvider>` for SolidStart and Solid Router: sends a page view on
|
|
11
|
+
* every navigation to a new path or query string. Render it in the router's
|
|
12
|
+
* `root`.
|
|
13
|
+
*/
|
|
14
|
+
declare function AnalyticsProvider(props: SolidStartAnalyticsProviderProps): JSX.Element;
|
|
15
|
+
|
|
16
|
+
export { AnalyticsProvider, type SolidStartAnalyticsProviderProps };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AnalyticsProvider } from './chunk-RZ4TYRNY.js';
|
|
2
|
+
export { ErrorBoundary, GtmNoscript, MetaPixelNoscript, useAnalytics, useConsent, useConsentController } from './chunk-RZ4TYRNY.js';
|
|
3
|
+
import { useLocation, useIsRouting } from '@solidjs/router';
|
|
4
|
+
import { createComponent, mergeProps } from 'solid-js';
|
|
5
|
+
|
|
6
|
+
function AnalyticsProvider2(props) {
|
|
7
|
+
const location = useLocation();
|
|
8
|
+
const isRouting = useIsRouting();
|
|
9
|
+
return createComponent(
|
|
10
|
+
AnalyticsProvider,
|
|
11
|
+
mergeProps(props, {
|
|
12
|
+
// The router updates `location` before the browser URL, which it sets when routing ends.
|
|
13
|
+
get routeKey() {
|
|
14
|
+
return isRouting() ? null : location.pathname + location.search;
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { AnalyticsProvider2 as AnalyticsProvider };
|
|
21
|
+
//# sourceMappingURL=solidstart.js.map
|
|
22
|
+
//# sourceMappingURL=solidstart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/solidstart.ts"],"names":["AnalyticsProvider"],"mappings":";;;;;AAuBO,SAASA,mBAAkB,KAAA,EAAsD;AACtF,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,EAAA,OAAO,eAAA;AAAA,IACL,iBAAA;AAAA,IACA,WAAW,KAAA,EAAO;AAAA;AAAA,MAEhB,IAAI,QAAA,GAAW;AACb,QAAA,OAAO,SAAA,EAAU,GAAI,IAAA,GAAO,QAAA,CAAS,WAAW,QAAA,CAAS,MAAA;AAAA,MAC3D;AAAA,KACD;AAAA,GACH;AACF","file":"solidstart.js","sourcesContent":["import { useIsRouting, useLocation } from '@solidjs/router'\nimport { createComponent, mergeProps, type JSX } from 'solid-js'\nimport { AnalyticsProvider as SolidAnalyticsProvider, type AnalyticsProviderProps } from './solid'\n\nexport {\n ErrorBoundary,\n GtmNoscript,\n MetaPixelNoscript,\n useAnalytics,\n useConsent,\n useConsentController,\n type ConsentController,\n type ErrorBoundaryProps,\n} from './solid'\n\n/** Props for `<AnalyticsProvider>` from `consentlayer/solidstart`: the route comes from Solid Router. */\nexport type SolidStartAnalyticsProviderProps = Omit<AnalyticsProviderProps, 'routeKey'>\n\n/**\n * `<AnalyticsProvider>` for SolidStart and Solid Router: sends a page view on\n * every navigation to a new path or query string. Render it in the router's\n * `root`.\n */\nexport function AnalyticsProvider(props: SolidStartAnalyticsProviderProps): JSX.Element {\n const location = useLocation()\n const isRouting = useIsRouting()\n return createComponent(\n SolidAnalyticsProvider,\n mergeProps(props, {\n // The router updates `location` before the browser URL, which it sets when routing ends.\n get routeKey() {\n return isRouting() ? null : location.pathname + location.search\n },\n }),\n )\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLIframeAttributes } from 'svelte/elements'
|
|
3
|
+
|
|
4
|
+
let { containerId, enabled = false, ...rest }: { containerId: string | undefined; enabled?: boolean } & HTMLIframeAttributes = $props()
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
{#if enabled && containerId}
|
|
8
|
+
<noscript>
|
|
9
|
+
<iframe
|
|
10
|
+
title="Google Tag Manager"
|
|
11
|
+
height="0"
|
|
12
|
+
width="0"
|
|
13
|
+
style="display:none;visibility:hidden"
|
|
14
|
+
{...rest}
|
|
15
|
+
src="https://www.googletagmanager.com/ns.html?id={encodeURIComponent(containerId)}"
|
|
16
|
+
></iframe>
|
|
17
|
+
</noscript>
|
|
18
|
+
{/if}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLImgAttributes } from 'svelte/elements'
|
|
3
|
+
|
|
4
|
+
let { pixelId, enabled = false, ...rest }: { pixelId: string | undefined; enabled?: boolean } & HTMLImgAttributes = $props()
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
{#if enabled && pixelId}
|
|
8
|
+
<noscript>
|
|
9
|
+
<img
|
|
10
|
+
height="1"
|
|
11
|
+
width="1"
|
|
12
|
+
style="display:none"
|
|
13
|
+
{...rest}
|
|
14
|
+
alt=""
|
|
15
|
+
src="https://www.facebook.com/tr?id={encodeURIComponent(pixelId)}&ev=PageView&noscript=1"
|
|
16
|
+
/>
|
|
17
|
+
</noscript>
|
|
18
|
+
{/if}
|
package/dist/svelte.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Component } from 'svelte';
|
|
2
|
+
import { C as ConsentControllerState, a as ConsentControllerActions, b as ConsentControllerOptions } from './consent-controller-DyHPNfyT.js';
|
|
3
|
+
import { m as Analytics, n as PageViewPayload, p as ConsentState, q as ConsentApi } from './types-BV_AFZKQ.js';
|
|
4
|
+
import { HTMLIframeAttributes, HTMLImgAttributes } from 'svelte/elements';
|
|
5
|
+
|
|
6
|
+
/** The part of SvelteKit's `afterNavigate` ConsentLayer uses. */
|
|
7
|
+
type AfterNavigateLike = (callback: (navigation: {
|
|
8
|
+
to: {
|
|
9
|
+
url: URL;
|
|
10
|
+
} | null;
|
|
11
|
+
}) => void) => void;
|
|
12
|
+
/** Options for `consentLayer()`. */
|
|
13
|
+
type ConsentLayerOptions = ConsentControllerOptions & {
|
|
14
|
+
analytics: Analytics;
|
|
15
|
+
/** SvelteKit's `afterNavigate`; sends a page view on every navigation. Without it, one page view is sent for the page load. */
|
|
16
|
+
afterNavigate?: AfterNavigateLike;
|
|
17
|
+
page?: PageViewPayload;
|
|
18
|
+
};
|
|
19
|
+
/** What `useConsentController()` returns: the UI state as reactive properties, and the actions. */
|
|
20
|
+
type ConsentController = Readonly<ConsentControllerState> & ConsentControllerActions;
|
|
21
|
+
/**
|
|
22
|
+
* Meta's `<noscript>` PageView pixel. It fires only for visitors without
|
|
23
|
+
* JavaScript, who never see the consent banner, so it tracks them without
|
|
24
|
+
* consent. Render it only where that is lawful for you. Renders nothing without
|
|
25
|
+
* `enabled` or while `pixelId` is unset. Other attributes go to the `<img>`.
|
|
26
|
+
*/
|
|
27
|
+
declare const MetaPixelNoscript: Component<{
|
|
28
|
+
pixelId: string | undefined;
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
} & HTMLImgAttributes>;
|
|
31
|
+
/**
|
|
32
|
+
* Google Tag Manager's `<noscript>` iframe. It loads the container only for
|
|
33
|
+
* visitors without JavaScript, who never see the consent banner, so its tags
|
|
34
|
+
* run without consent. Render it only where that is lawful for you. Renders
|
|
35
|
+
* nothing without `enabled` or while `containerId` is unset. Other attributes go to the `<iframe>`.
|
|
36
|
+
*/
|
|
37
|
+
declare const GtmNoscript: Component<{
|
|
38
|
+
containerId: string | undefined;
|
|
39
|
+
enabled?: boolean;
|
|
40
|
+
} & HTMLIframeAttributes>;
|
|
41
|
+
/** Boots analytics, sends page views on navigation and provides the state for your consent UI. Call it in your root component. */
|
|
42
|
+
declare function consentLayer({ analytics, afterNavigate, page, ...options }: ConsentLayerOptions): ConsentController;
|
|
43
|
+
/** The instance from `consentLayer()`, else the one `createAnalytics()` made. */
|
|
44
|
+
declare function useAnalytics(): Analytics;
|
|
45
|
+
/** Current consent as a reactive property, `null` before a choice. */
|
|
46
|
+
declare function useConsent(): {
|
|
47
|
+
readonly consent: ConsentState | null;
|
|
48
|
+
engine: ConsentApi;
|
|
49
|
+
setConsent: (consent: ConsentState) => Promise<void>;
|
|
50
|
+
update: (partial: Partial<ConsentState>) => Promise<void>;
|
|
51
|
+
};
|
|
52
|
+
/** State and actions for building your own consent UI. */
|
|
53
|
+
declare function useConsentController(): ConsentController;
|
|
54
|
+
|
|
55
|
+
export { type AfterNavigateLike, type ConsentController, type ConsentLayerOptions, GtmNoscript, MetaPixelNoscript, consentLayer, useAnalytics, useConsent, useConsentController };
|
package/dist/svelte.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"svelte.js"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { m as Analytics } from './types-BV_AFZKQ.js';
|
|
2
|
+
import { ConsentLayerOptions, ConsentController } from './svelte.js';
|
|
3
|
+
export { GtmNoscript, MetaPixelNoscript, useAnalytics, useConsent, useConsentController } from './svelte.js';
|
|
4
|
+
import 'svelte';
|
|
5
|
+
import './consent-controller-DyHPNfyT.js';
|
|
6
|
+
import 'svelte/elements';
|
|
7
|
+
|
|
8
|
+
/** Options for `consentLayer()` from `consentlayer/sveltekit`. */
|
|
9
|
+
type SvelteKitConsentLayerOptions = Omit<ConsentLayerOptions, 'afterNavigate'>;
|
|
10
|
+
/** Boots analytics, sends a page view on every SvelteKit navigation and provides the state for your consent UI. Call it in your root layout. */
|
|
11
|
+
declare function consentLayer(options: SvelteKitConsentLayerOptions): ConsentController;
|
|
12
|
+
/** A client `handleError` hook that sends the errors your code throws (`kind: 'unknown'`) through `captureError`. */
|
|
13
|
+
declare function handleErrorWith(analytics: Analytics): (input: {
|
|
14
|
+
kind: string;
|
|
15
|
+
error: unknown;
|
|
16
|
+
}) => void;
|
|
17
|
+
|
|
18
|
+
export { ConsentController, type SvelteKitConsentLayerOptions, consentLayer, handleErrorWith };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { consentLayer } from './chunk-TYOQYV45.js';
|
|
2
|
+
export { GtmNoscript, MetaPixelNoscript, useAnalytics, useConsent, useConsentController } from './chunk-TYOQYV45.js';
|
|
3
|
+
import { afterNavigate } from '$app/navigation';
|
|
4
|
+
|
|
5
|
+
function consentLayer2(options) {
|
|
6
|
+
return consentLayer({ ...options, afterNavigate });
|
|
7
|
+
}
|
|
8
|
+
function handleErrorWith(analytics) {
|
|
9
|
+
return ({ kind, error }) => {
|
|
10
|
+
if (kind === "unknown") void analytics.captureError(error, { source: "boundary" });
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { consentLayer2 as consentLayer, handleErrorWith };
|
|
15
|
+
//# sourceMappingURL=sveltekit.js.map
|
|
16
|
+
//# sourceMappingURL=sveltekit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sveltekit.ts"],"names":["consentLayer"],"mappings":";;;;AAiBO,SAASA,cAAa,OAAA,EAA0D;AACrF,EAAA,OAAO,YAAA,CAAmB,EAAE,GAAG,OAAA,EAAS,eAAe,CAAA;AACzD;AAGO,SAAS,gBAAgB,SAAA,EAAyE;AACvG,EAAA,OAAO,CAAC,EAAE,IAAA,EAAM,KAAA,EAAM,KAAM;AAC1B,IAAA,IAAI,IAAA,KAAS,WAAW,KAAK,SAAA,CAAU,aAAa,KAAA,EAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,CAAA;AAAA,EACnF,CAAA;AACF","file":"sveltekit.js","sourcesContent":["import { afterNavigate } from '$app/navigation'\nimport type { Analytics } from './core/types'\nimport { consentLayer as svelteConsentLayer, type ConsentController, type ConsentLayerOptions } from './svelte'\n\nexport {\n GtmNoscript,\n MetaPixelNoscript,\n useAnalytics,\n useConsent,\n useConsentController,\n type ConsentController,\n} from './svelte'\n\n/** Options for `consentLayer()` from `consentlayer/sveltekit`. */\nexport type SvelteKitConsentLayerOptions = Omit<ConsentLayerOptions, 'afterNavigate'>\n\n/** Boots analytics, sends a page view on every SvelteKit navigation and provides the state for your consent UI. Call it in your root layout. */\nexport function consentLayer(options: SvelteKitConsentLayerOptions): ConsentController {\n return svelteConsentLayer({ ...options, afterNavigate })\n}\n\n/** A client `handleError` hook that sends the errors your code throws (`kind: 'unknown'`) through `captureError`. */\nexport function handleErrorWith(analytics: Analytics): (input: { kind: string; error: unknown }) => void {\n return ({ kind, error }) => {\n if (kind === 'unknown') void analytics.captureError(error, { source: 'boundary' })\n }\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { f as AnalyticsPlugin } from './types-BV_AFZKQ.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Runs a plugin through the manager in a DOM test environment (jsdom, happy-dom)
|
|
5
|
+
* and returns the contract problems found; `[]` means it passed.
|
|
6
|
+
* Checks that nothing loads, sends or sets cookies before consent
|
|
7
|
+
* (scripts are allowed with `loadBeforeConsent`), and that no hook throws.
|
|
8
|
+
*/
|
|
9
|
+
declare function checkPlugin(plugin: AnalyticsPlugin): Promise<string[]>;
|
|
10
|
+
|
|
11
|
+
export { checkPlugin };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createAnalyticsManager } from './chunk-LEAQJXJU.js';
|
|
2
|
+
|
|
3
|
+
// src/testing.ts
|
|
4
|
+
var denied = { preferences: false, analytics: false, marketing: false };
|
|
5
|
+
var granted = { preferences: true, analytics: true, marketing: true };
|
|
6
|
+
async function checkPlugin(plugin) {
|
|
7
|
+
const problems = [];
|
|
8
|
+
const scripts = () => document.querySelectorAll("script[src]").length;
|
|
9
|
+
const before = { scripts: scripts(), cookies: document.cookie };
|
|
10
|
+
const realFetch = globalThis.fetch;
|
|
11
|
+
let requests = 0;
|
|
12
|
+
globalThis.fetch = (() => {
|
|
13
|
+
requests += 1;
|
|
14
|
+
return Promise.resolve(new Response(null, { status: 204 }));
|
|
15
|
+
});
|
|
16
|
+
const manager = createAnalyticsManager({
|
|
17
|
+
providers: [plugin],
|
|
18
|
+
consent: denied,
|
|
19
|
+
onError: (error) => problems.push(`${error.message}: ${String(error.cause)}`)
|
|
20
|
+
});
|
|
21
|
+
const exercise = async () => {
|
|
22
|
+
await manager.page();
|
|
23
|
+
await manager.track("sign_up", { method: "test" });
|
|
24
|
+
await manager.identify("contract_user", { email: "contract@example.com" });
|
|
25
|
+
await manager.group("contract_group");
|
|
26
|
+
};
|
|
27
|
+
try {
|
|
28
|
+
await manager.boot();
|
|
29
|
+
await exercise();
|
|
30
|
+
if (plugin.requiredConsent !== "none") {
|
|
31
|
+
if (!plugin.loadBeforeConsent && scripts() > before.scripts) {
|
|
32
|
+
problems.push("loaded a script before consent");
|
|
33
|
+
}
|
|
34
|
+
if (requests) problems.push("sent a request before consent");
|
|
35
|
+
if (document.cookie !== before.cookies) problems.push("set a cookie before consent");
|
|
36
|
+
}
|
|
37
|
+
await manager.updateConsent(granted);
|
|
38
|
+
await exercise();
|
|
39
|
+
await manager.reset();
|
|
40
|
+
await manager.updateConsent(denied);
|
|
41
|
+
} finally {
|
|
42
|
+
manager.destroy();
|
|
43
|
+
globalThis.fetch = realFetch;
|
|
44
|
+
}
|
|
45
|
+
return problems;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { checkPlugin };
|
|
49
|
+
//# sourceMappingURL=testing.js.map
|
|
50
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"names":[],"mappings":";;;AAGA,IAAM,SAAuB,EAAE,WAAA,EAAa,OAAO,SAAA,EAAW,KAAA,EAAO,WAAW,KAAA,EAAM;AACtF,IAAM,UAAwB,EAAE,WAAA,EAAa,MAAM,SAAA,EAAW,IAAA,EAAM,WAAW,IAAA,EAAK;AAQpF,eAAsB,YAAY,MAAA,EAA4C;AAC5E,EAAA,MAAM,WAAqB,EAAC;AAC5B,EAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,gBAAA,CAAiB,aAAa,CAAA,CAAE,MAAA;AAC/D,EAAA,MAAM,SAAS,EAAE,OAAA,EAAS,SAAQ,EAAG,OAAA,EAAS,SAAS,MAAA,EAAO;AAC9D,EAAA,MAAM,YAAY,UAAA,CAAW,KAAA;AAC7B,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,UAAA,CAAW,SAAS,MAAM;AACxB,IAAA,QAAA,IAAY,CAAA;AACZ,IAAA,OAAO,OAAA,CAAQ,QAAQ,IAAI,QAAA,CAAS,MAAM,EAAE,MAAA,EAAQ,GAAA,EAAK,CAAC,CAAA;AAAA,EAC5D,CAAA,CAAA;AAEA,EAAA,MAAM,UAAU,sBAAA,CAAuB;AAAA,IACrC,SAAA,EAAW,CAAC,MAAM,CAAA;AAAA,IAClB,OAAA,EAAS,MAAA;AAAA,IACT,OAAA,EAAS,CAAC,KAAA,KAAU,QAAA,CAAS,IAAA,CAAK,CAAA,EAAG,KAAA,CAAM,OAAO,CAAA,EAAA,EAAK,MAAA,CAAO,KAAA,CAAM,KAAK,CAAC,CAAA,CAAE;AAAA,GAC7E,CAAA;AACD,EAAA,MAAM,WAAW,YAAY;AAC3B,IAAA,MAAM,QAAQ,IAAA,EAAK;AACnB,IAAA,MAAM,QAAQ,KAAA,CAAM,SAAA,EAAW,EAAE,MAAA,EAAQ,QAAQ,CAAA;AACjD,IAAA,MAAM,QAAQ,QAAA,CAAS,eAAA,EAAiB,EAAE,KAAA,EAAO,wBAAwB,CAAA;AACzE,IAAA,MAAM,OAAA,CAAQ,MAAM,gBAAgB,CAAA;AAAA,EACtC,CAAA;AACA,EAAA,IAAI;AACF,IAAA,MAAM,QAAQ,IAAA,EAAK;AACnB,IAAA,MAAM,QAAA,EAAS;AACf,IAAA,IAAI,MAAA,CAAO,oBAAoB,MAAA,EAAQ;AACrC,MAAA,IAAI,CAAC,MAAA,CAAO,iBAAA,IAAqB,OAAA,EAAQ,GAAI,OAAO,OAAA,EAAS;AAC3D,QAAA,QAAA,CAAS,KAAK,gCAAgC,CAAA;AAAA,MAChD;AACA,MAAA,IAAI,QAAA,EAAU,QAAA,CAAS,IAAA,CAAK,+BAA+B,CAAA;AAC3D,MAAA,IAAI,SAAS,MAAA,KAAW,MAAA,CAAO,OAAA,EAAS,QAAA,CAAS,KAAK,6BAA6B,CAAA;AAAA,IACrF;AACA,IAAA,MAAM,OAAA,CAAQ,cAAc,OAAO,CAAA;AACnC,IAAA,MAAM,QAAA,EAAS;AACf,IAAA,MAAM,QAAQ,KAAA,EAAM;AACpB,IAAA,MAAM,OAAA,CAAQ,cAAc,MAAM,CAAA;AAAA,EACpC,CAAA,SAAE;AACA,IAAA,OAAA,CAAQ,OAAA,EAAQ;AAChB,IAAA,UAAA,CAAW,KAAA,GAAQ,SAAA;AAAA,EACrB;AACA,EAAA,OAAO,QAAA;AACT","file":"testing.js","sourcesContent":["import { createAnalyticsManager } from './core/analytics-manager'\nimport type { AnalyticsPlugin, ConsentState } from './core/types'\n\nconst denied: ConsentState = { preferences: false, analytics: false, marketing: false }\nconst granted: ConsentState = { preferences: true, analytics: true, marketing: true }\n\n/**\n * Runs a plugin through the manager in a DOM test environment (jsdom, happy-dom)\n * and returns the contract problems found; `[]` means it passed.\n * Checks that nothing loads, sends or sets cookies before consent\n * (scripts are allowed with `loadBeforeConsent`), and that no hook throws.\n */\nexport async function checkPlugin(plugin: AnalyticsPlugin): Promise<string[]> {\n const problems: string[] = []\n const scripts = () => document.querySelectorAll('script[src]').length\n const before = { scripts: scripts(), cookies: document.cookie }\n const realFetch = globalThis.fetch\n let requests = 0\n globalThis.fetch = (() => {\n requests += 1\n return Promise.resolve(new Response(null, { status: 204 }))\n }) as typeof fetch\n\n const manager = createAnalyticsManager({\n providers: [plugin],\n consent: denied,\n onError: (error) => problems.push(`${error.message}: ${String(error.cause)}`),\n })\n const exercise = async () => {\n await manager.page()\n await manager.track('sign_up', { method: 'test' })\n await manager.identify('contract_user', { email: 'contract@example.com' })\n await manager.group('contract_group')\n }\n try {\n await manager.boot()\n await exercise()\n if (plugin.requiredConsent !== 'none') {\n if (!plugin.loadBeforeConsent && scripts() > before.scripts) {\n problems.push('loaded a script before consent')\n }\n if (requests) problems.push('sent a request before consent')\n if (document.cookie !== before.cookies) problems.push('set a cookie before consent')\n }\n await manager.updateConsent(granted)\n await exercise()\n await manager.reset()\n await manager.updateConsent(denied)\n } finally {\n manager.destroy()\n globalThis.fetch = realFetch\n }\n return problems\n}\n"]}
|