@shware/analytics 3.6.3 → 3.6.4

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.
@@ -26,9 +26,8 @@ module.exports = __toCommonJS(fbsdk_exports);
26
26
  var import_react_native_fbsdk_next = require("react-native-fbsdk-next");
27
27
  var import_fbq = require("../track/fbq.cjs");
28
28
  async function sendFBEvent(name, properties) {
29
- const { logEvent } = import_react_native_fbsdk_next.AppEventsLogger;
30
29
  const [_, fbEventName, fbEventProperties] = (0, import_fbq.mapFBEvent)(name, properties);
31
- logEvent(fbEventName, fbEventProperties);
30
+ import_react_native_fbsdk_next.AppEventsLogger.logEvent(fbEventName, fbEventProperties);
32
31
  }
33
32
  // Annotate the CommonJS export names for ESM import in node:
34
33
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/native/fbsdk.ts"],"sourcesContent":["import { AppEventsLogger, type Params } from 'react-native-fbsdk-next';\nimport { mapFBEvent } from '../track/fbq';\nimport type { EventName, TrackName, TrackProperties } from '../track/types';\n\n/**\n * ref: https://developers.facebook.com/docs/app-events/guides/maximize-in-app-ad-revenue/\n */\nexport async function sendFBEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>\n) {\n const { logEvent } = AppEventsLogger;\n const [_, fbEventName, fbEventProperties] = mapFBEvent(name, properties);\n logEvent(fbEventName, fbEventProperties as Params);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAA6C;AAC7C,iBAA2B;AAM3B,eAAsB,YACpB,MACA,YACA;AACA,QAAM,EAAE,SAAS,IAAI;AACrB,QAAM,CAAC,GAAG,aAAa,iBAAiB,QAAI,uBAAW,MAAM,UAAU;AACvE,WAAS,aAAa,iBAA2B;AACnD;","names":[]}
1
+ {"version":3,"sources":["../../src/native/fbsdk.ts"],"sourcesContent":["import { AppEventsLogger, type Params } from 'react-native-fbsdk-next';\nimport { mapFBEvent } from '../track/fbq';\nimport type { EventName, TrackName, TrackProperties } from '../track/types';\n\n/**\n * ref: https://developers.facebook.com/docs/app-events/guides/maximize-in-app-ad-revenue/\n */\nexport async function sendFBEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>\n) {\n const [_, fbEventName, fbEventProperties] = mapFBEvent(name, properties);\n AppEventsLogger.logEvent(fbEventName, fbEventProperties as Params);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAA6C;AAC7C,iBAA2B;AAM3B,eAAsB,YACpB,MACA,YACA;AACA,QAAM,CAAC,GAAG,aAAa,iBAAiB,QAAI,uBAAW,MAAM,UAAU;AACvE,iDAAgB,SAAS,aAAa,iBAA2B;AACnE;","names":[]}
@@ -2,9 +2,8 @@
2
2
  import { AppEventsLogger } from "react-native-fbsdk-next";
3
3
  import { mapFBEvent } from "../track/fbq.mjs";
4
4
  async function sendFBEvent(name, properties) {
5
- const { logEvent } = AppEventsLogger;
6
5
  const [_, fbEventName, fbEventProperties] = mapFBEvent(name, properties);
7
- logEvent(fbEventName, fbEventProperties);
6
+ AppEventsLogger.logEvent(fbEventName, fbEventProperties);
8
7
  }
9
8
  export {
10
9
  sendFBEvent
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/native/fbsdk.ts"],"sourcesContent":["import { AppEventsLogger, type Params } from 'react-native-fbsdk-next';\nimport { mapFBEvent } from '../track/fbq';\nimport type { EventName, TrackName, TrackProperties } from '../track/types';\n\n/**\n * ref: https://developers.facebook.com/docs/app-events/guides/maximize-in-app-ad-revenue/\n */\nexport async function sendFBEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>\n) {\n const { logEvent } = AppEventsLogger;\n const [_, fbEventName, fbEventProperties] = mapFBEvent(name, properties);\n logEvent(fbEventName, fbEventProperties as Params);\n}\n"],"mappings":";AAAA,SAAS,uBAAoC;AAC7C,SAAS,kBAAkB;AAM3B,eAAsB,YACpB,MACA,YACA;AACA,QAAM,EAAE,SAAS,IAAI;AACrB,QAAM,CAAC,GAAG,aAAa,iBAAiB,IAAI,WAAW,MAAM,UAAU;AACvE,WAAS,aAAa,iBAA2B;AACnD;","names":[]}
1
+ {"version":3,"sources":["../../src/native/fbsdk.ts"],"sourcesContent":["import { AppEventsLogger, type Params } from 'react-native-fbsdk-next';\nimport { mapFBEvent } from '../track/fbq';\nimport type { EventName, TrackName, TrackProperties } from '../track/types';\n\n/**\n * ref: https://developers.facebook.com/docs/app-events/guides/maximize-in-app-ad-revenue/\n */\nexport async function sendFBEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>\n) {\n const [_, fbEventName, fbEventProperties] = mapFBEvent(name, properties);\n AppEventsLogger.logEvent(fbEventName, fbEventProperties as Params);\n}\n"],"mappings":";AAAA,SAAS,uBAAoC;AAC7C,SAAS,kBAAkB;AAM3B,eAAsB,YACpB,MACA,YACA;AACA,QAAM,CAAC,GAAG,aAAa,iBAAiB,IAAI,WAAW,MAAM,UAAU;AACvE,kBAAgB,SAAS,aAAa,iBAA2B;AACnE;","names":[]}
@@ -23,7 +23,7 @@ __export(linkedin_conversions_api_exports, {
23
23
  sendEvents: () => sendEvents
24
24
  });
25
25
  module.exports = __toCommonJS(linkedin_conversions_api_exports);
26
- var import_crypto = require("crypto");
26
+ var import_node_crypto = require("crypto");
27
27
  var import_utils = require("@shware/utils");
28
28
  var import_ignored_events = require("../third-parties/ignored-events.cjs");
29
29
  var import_field = require("../utils/field.cjs");
@@ -42,7 +42,7 @@ async function sendEvents(accessToken, config, events, data = {}) {
42
42
  if (email)
43
43
  userIds.push({
44
44
  idType: "SHA256_EMAIL",
45
- idValue: (0, import_crypto.createHash)("sha256").update(email).digest("hex")
45
+ idValue: (0, import_node_crypto.createHash)("sha256").update(email).digest("hex")
46
46
  });
47
47
  }
48
48
  const dto = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/linkedin-conversions-api.ts"],"sourcesContent":["/**\n * Conversions API Payload Builder: https://www.linkedin.com/developers/payload-builder\n * https://learn.microsoft.com/en-us/linkedin/marketing/conversions/conversions-overview?view=li-lms-2025-09\n */\nimport { createHash } from 'crypto';\nimport { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\ntype UserIdType =\n | 'SHA256_EMAIL'\n | 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID'\n | 'ACXIOM_ID'\n | 'ORACLE_MOAT_ID';\n\nexport interface CreateLinkedinEventDTO {\n /**\n * For any conversion that you want to send through multiple methods, such as Insight Tag and\n * Conversions API, you must create a conversion rule for each data source (browser and server).\n * Then, you can implement a logic to pick up the eventId from the browser and send it with the\n * corresponding event from your server. If we receive an Insight Tag event and a Conversions API\n * event from the same account with the same eventId, we discard the Conversions API event and\n * count only the Insight Tag event in campaign reporting.\n */\n eventId?: string;\n\n /**\n * Replace <id> with the conversion ID extracted when creating the conversion rule\n * (e.g. urn:lla:llaPartnerConversion:<id>).\n */\n conversion: `urn:lla:llaPartnerConversion:${number}`;\n\n /** Epoch timestamp in milliseconds at which the conversion event happened. */\n conversionHappenedAt: number;\n conversionValue: { currencyCode: string; amount: string };\n user: {\n userIds: { idType: UserIdType; idValue: string }[];\n userInfo?: {\n firstName?: string;\n lastName?: string;\n companyName?: string;\n countryCode?: string;\n title?: string;\n };\n\n /**\n * The maximum supported size of the list is 1 at the moment. If the list contains multiple\n * values, only the first value will be used.\n */\n externalIds?: [string, ...string[]];\n\n /**\n * This is generated when users submit the Linkedin Lead-gen form\n * (e.g. urn:li:leadGenFormResponse:<id>).\n */\n lead?: `urn:li:leadGenFormResponse:${string}`;\n };\n}\n\nexport interface CreateMultipleLinkedinEventsDTO {\n elements: CreateLinkedinEventDTO[];\n}\n\nexport type LinkedinConversionConfig = Record<Lowercase<string>, number>;\n\nexport async function sendEvents(\n accessToken: string,\n config: LinkedinConversionConfig,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {}\n) {\n const eventNames = Object.keys(config);\n const address = getFirst(data.address);\n const userIds: { idType: UserIdType; idValue: string }[] = [];\n const externalIds: [string, ...string[]] | undefined = data.user_id ? [data.user_id] : undefined;\n const userInfo =\n address && address.first_name && address.last_name\n ? {\n firstName: address.first_name,\n lastName: address.last_name,\n countryCode: address.country,\n }\n : undefined;\n\n if (data.email) {\n const email = getFirst(data.email);\n if (email)\n userIds.push({\n idType: 'SHA256_EMAIL',\n idValue: createHash('sha256').update(email).digest('hex'),\n });\n }\n\n const dto: CreateMultipleLinkedinEventsDTO = {\n elements: events\n .filter((event) => eventNames.includes(event.name) && !IGNORED_EVENTS.includes(event.name))\n .map((event) => ({\n eventId: event.id,\n conversion: `urn:lla:llaPartnerConversion:${config[event.name]}`,\n conversionHappenedAt: Date.now(),\n conversionValue: {\n currencyCode: event.properties?.currency?.toUpperCase() ?? 'USD',\n amount: event.properties?.value?.toString() ?? '0',\n },\n user: {\n userIds: event.tags.li_fat_id\n ? [\n {\n idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID',\n idValue: event.tags.li_fat_id,\n },\n ...userIds,\n ]\n : userIds,\n userInfo,\n externalIds,\n },\n })),\n };\n\n if (dto.elements.length === 0) return;\n try {\n const response = await fetch('https://api.linkedin.com/rest/conversionEvents', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'LinkedIn-Version': '202509',\n 'X-Restli-Protocol-Version': '2.0.0',\n 'X-RestLi-Method': 'BATCH_CREATE',\n },\n body: JSON.stringify(dto),\n });\n\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send LinkedIn conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send LinkedIn conversion, network error:', error);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,oBAA2B;AAC3B,mBAAsB;AACtB,4BAA+B;AAE/B,mBAAyB;AA0DzB,eAAsB,WACpB,aACA,QAEA,QACA,OAAyB,CAAC,GAC1B;AACA,QAAM,aAAa,OAAO,KAAK,MAAM;AACrC,QAAM,cAAU,uBAAS,KAAK,OAAO;AACrC,QAAM,UAAqD,CAAC;AAC5D,QAAM,cAAiD,KAAK,UAAU,CAAC,KAAK,OAAO,IAAI;AACvF,QAAM,WACJ,WAAW,QAAQ,cAAc,QAAQ,YACrC;AAAA,IACE,WAAW,QAAQ;AAAA,IACnB,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ;AAAA,EACvB,IACA;AAEN,MAAI,KAAK,OAAO;AACd,UAAM,YAAQ,uBAAS,KAAK,KAAK;AACjC,QAAI;AACF,cAAQ,KAAK;AAAA,QACX,QAAQ;AAAA,QACR,aAAS,0BAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AAAA,MAC1D,CAAC;AAAA,EACL;AAEA,QAAM,MAAuC;AAAA,IAC3C,UAAU,OACP,OAAO,CAAC,UAAU,WAAW,SAAS,MAAM,IAAI,KAAK,CAAC,qCAAe,SAAS,MAAM,IAAI,CAAC,EACzF,IAAI,CAAC,UAAO;AAlGnB;AAkGuB;AAAA,QACf,SAAS,MAAM;AAAA,QACf,YAAY,gCAAgC,OAAO,MAAM,IAAI,CAAC;AAAA,QAC9D,sBAAsB,KAAK,IAAI;AAAA,QAC/B,iBAAiB;AAAA,UACf,gBAAc,iBAAM,eAAN,mBAAkB,aAAlB,mBAA4B,kBAAiB;AAAA,UAC3D,UAAQ,iBAAM,eAAN,mBAAkB,UAAlB,mBAAyB,eAAc;AAAA,QACjD;AAAA,QACA,MAAM;AAAA,UACJ,SAAS,MAAM,KAAK,YAChB;AAAA,YACE;AAAA,cACE,QAAQ;AAAA,cACR,SAAS,MAAM,KAAK;AAAA,YACtB;AAAA,YACA,GAAG;AAAA,UACL,IACA;AAAA,UACJ;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,KAAE;AAAA,EACN;AAEA,MAAI,IAAI,SAAS,WAAW,EAAG;AAC/B,MAAI;AACF,UAAM,WAAW,UAAM,oBAAM,kDAAkD;AAAA,MAC7E,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,WAAW;AAAA,QACpC,gBAAgB;AAAA,QAChB,oBAAoB;AAAA,QACpB,6BAA6B;AAAA,QAC7B,mBAAmB;AAAA,MACrB;AAAA,MACA,MAAM,KAAK,UAAU,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,+CAA+C,MAAM,WAAW,OAAO,EAAE;AAAA,EACzF,SAAS,OAAO;AACd,YAAQ,MAAM,sDAAsD,KAAK;AAAA,EAC3E;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/server/linkedin-conversions-api.ts"],"sourcesContent":["/**\n * Conversions API Payload Builder: https://www.linkedin.com/developers/payload-builder\n * https://learn.microsoft.com/en-us/linkedin/marketing/conversions/conversions-overview?view=li-lms-2025-09\n */\nimport { createHash } from 'node:crypto';\nimport { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\ntype UserIdType =\n | 'SHA256_EMAIL'\n | 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID'\n | 'ACXIOM_ID'\n | 'ORACLE_MOAT_ID';\n\nexport interface CreateLinkedinEventDTO {\n /**\n * For any conversion that you want to send through multiple methods, such as Insight Tag and\n * Conversions API, you must create a conversion rule for each data source (browser and server).\n * Then, you can implement a logic to pick up the eventId from the browser and send it with the\n * corresponding event from your server. If we receive an Insight Tag event and a Conversions API\n * event from the same account with the same eventId, we discard the Conversions API event and\n * count only the Insight Tag event in campaign reporting.\n */\n eventId?: string;\n\n /**\n * Replace <id> with the conversion ID extracted when creating the conversion rule\n * (e.g. urn:lla:llaPartnerConversion:<id>).\n */\n conversion: `urn:lla:llaPartnerConversion:${number}`;\n\n /** Epoch timestamp in milliseconds at which the conversion event happened. */\n conversionHappenedAt: number;\n conversionValue: { currencyCode: string; amount: string };\n user: {\n userIds: { idType: UserIdType; idValue: string }[];\n userInfo?: {\n firstName?: string;\n lastName?: string;\n companyName?: string;\n countryCode?: string;\n title?: string;\n };\n\n /**\n * The maximum supported size of the list is 1 at the moment. If the list contains multiple\n * values, only the first value will be used.\n */\n externalIds?: [string, ...string[]];\n\n /**\n * This is generated when users submit the Linkedin Lead-gen form\n * (e.g. urn:li:leadGenFormResponse:<id>).\n */\n lead?: `urn:li:leadGenFormResponse:${string}`;\n };\n}\n\nexport interface CreateMultipleLinkedinEventsDTO {\n elements: CreateLinkedinEventDTO[];\n}\n\nexport type LinkedinConversionConfig = Record<Lowercase<string>, number>;\n\nexport async function sendEvents(\n accessToken: string,\n config: LinkedinConversionConfig,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {}\n) {\n const eventNames = Object.keys(config);\n const address = getFirst(data.address);\n const userIds: { idType: UserIdType; idValue: string }[] = [];\n const externalIds: [string, ...string[]] | undefined = data.user_id ? [data.user_id] : undefined;\n const userInfo =\n address && address.first_name && address.last_name\n ? {\n firstName: address.first_name,\n lastName: address.last_name,\n countryCode: address.country,\n }\n : undefined;\n\n if (data.email) {\n const email = getFirst(data.email);\n if (email)\n userIds.push({\n idType: 'SHA256_EMAIL',\n idValue: createHash('sha256').update(email).digest('hex'),\n });\n }\n\n const dto: CreateMultipleLinkedinEventsDTO = {\n elements: events\n .filter((event) => eventNames.includes(event.name) && !IGNORED_EVENTS.includes(event.name))\n .map((event) => ({\n eventId: event.id,\n conversion: `urn:lla:llaPartnerConversion:${config[event.name]}`,\n conversionHappenedAt: Date.now(),\n conversionValue: {\n currencyCode: event.properties?.currency?.toUpperCase() ?? 'USD',\n amount: event.properties?.value?.toString() ?? '0',\n },\n user: {\n userIds: event.tags.li_fat_id\n ? [\n {\n idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID',\n idValue: event.tags.li_fat_id,\n },\n ...userIds,\n ]\n : userIds,\n userInfo,\n externalIds,\n },\n })),\n };\n\n if (dto.elements.length === 0) return;\n try {\n const response = await fetch('https://api.linkedin.com/rest/conversionEvents', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'LinkedIn-Version': '202509',\n 'X-Restli-Protocol-Version': '2.0.0',\n 'X-RestLi-Method': 'BATCH_CREATE',\n },\n body: JSON.stringify(dto),\n });\n\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send LinkedIn conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send LinkedIn conversion, network error:', error);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,yBAA2B;AAC3B,mBAAsB;AACtB,4BAA+B;AAE/B,mBAAyB;AA0DzB,eAAsB,WACpB,aACA,QAEA,QACA,OAAyB,CAAC,GAC1B;AACA,QAAM,aAAa,OAAO,KAAK,MAAM;AACrC,QAAM,cAAU,uBAAS,KAAK,OAAO;AACrC,QAAM,UAAqD,CAAC;AAC5D,QAAM,cAAiD,KAAK,UAAU,CAAC,KAAK,OAAO,IAAI;AACvF,QAAM,WACJ,WAAW,QAAQ,cAAc,QAAQ,YACrC;AAAA,IACE,WAAW,QAAQ;AAAA,IACnB,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ;AAAA,EACvB,IACA;AAEN,MAAI,KAAK,OAAO;AACd,UAAM,YAAQ,uBAAS,KAAK,KAAK;AACjC,QAAI;AACF,cAAQ,KAAK;AAAA,QACX,QAAQ;AAAA,QACR,aAAS,+BAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AAAA,MAC1D,CAAC;AAAA,EACL;AAEA,QAAM,MAAuC;AAAA,IAC3C,UAAU,OACP,OAAO,CAAC,UAAU,WAAW,SAAS,MAAM,IAAI,KAAK,CAAC,qCAAe,SAAS,MAAM,IAAI,CAAC,EACzF,IAAI,CAAC,UAAO;AAlGnB;AAkGuB;AAAA,QACf,SAAS,MAAM;AAAA,QACf,YAAY,gCAAgC,OAAO,MAAM,IAAI,CAAC;AAAA,QAC9D,sBAAsB,KAAK,IAAI;AAAA,QAC/B,iBAAiB;AAAA,UACf,gBAAc,iBAAM,eAAN,mBAAkB,aAAlB,mBAA4B,kBAAiB;AAAA,UAC3D,UAAQ,iBAAM,eAAN,mBAAkB,UAAlB,mBAAyB,eAAc;AAAA,QACjD;AAAA,QACA,MAAM;AAAA,UACJ,SAAS,MAAM,KAAK,YAChB;AAAA,YACE;AAAA,cACE,QAAQ;AAAA,cACR,SAAS,MAAM,KAAK;AAAA,YACtB;AAAA,YACA,GAAG;AAAA,UACL,IACA;AAAA,UACJ;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,KAAE;AAAA,EACN;AAEA,MAAI,IAAI,SAAS,WAAW,EAAG;AAC/B,MAAI;AACF,UAAM,WAAW,UAAM,oBAAM,kDAAkD;AAAA,MAC7E,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,WAAW;AAAA,QACpC,gBAAgB;AAAA,QAChB,oBAAoB;AAAA,QACpB,6BAA6B;AAAA,QAC7B,mBAAmB;AAAA,MACrB;AAAA,MACA,MAAM,KAAK,UAAU,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,+CAA+C,MAAM,WAAW,OAAO,EAAE;AAAA,EACzF,SAAS,OAAO;AACd,YAAQ,MAAM,sDAAsD,KAAK;AAAA,EAC3E;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/linkedin-conversions-api.ts"],"sourcesContent":["/**\n * Conversions API Payload Builder: https://www.linkedin.com/developers/payload-builder\n * https://learn.microsoft.com/en-us/linkedin/marketing/conversions/conversions-overview?view=li-lms-2025-09\n */\nimport { createHash } from 'crypto';\nimport { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\ntype UserIdType =\n | 'SHA256_EMAIL'\n | 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID'\n | 'ACXIOM_ID'\n | 'ORACLE_MOAT_ID';\n\nexport interface CreateLinkedinEventDTO {\n /**\n * For any conversion that you want to send through multiple methods, such as Insight Tag and\n * Conversions API, you must create a conversion rule for each data source (browser and server).\n * Then, you can implement a logic to pick up the eventId from the browser and send it with the\n * corresponding event from your server. If we receive an Insight Tag event and a Conversions API\n * event from the same account with the same eventId, we discard the Conversions API event and\n * count only the Insight Tag event in campaign reporting.\n */\n eventId?: string;\n\n /**\n * Replace <id> with the conversion ID extracted when creating the conversion rule\n * (e.g. urn:lla:llaPartnerConversion:<id>).\n */\n conversion: `urn:lla:llaPartnerConversion:${number}`;\n\n /** Epoch timestamp in milliseconds at which the conversion event happened. */\n conversionHappenedAt: number;\n conversionValue: { currencyCode: string; amount: string };\n user: {\n userIds: { idType: UserIdType; idValue: string }[];\n userInfo?: {\n firstName?: string;\n lastName?: string;\n companyName?: string;\n countryCode?: string;\n title?: string;\n };\n\n /**\n * The maximum supported size of the list is 1 at the moment. If the list contains multiple\n * values, only the first value will be used.\n */\n externalIds?: [string, ...string[]];\n\n /**\n * This is generated when users submit the Linkedin Lead-gen form\n * (e.g. urn:li:leadGenFormResponse:<id>).\n */\n lead?: `urn:li:leadGenFormResponse:${string}`;\n };\n}\n\nexport interface CreateMultipleLinkedinEventsDTO {\n elements: CreateLinkedinEventDTO[];\n}\n\nexport type LinkedinConversionConfig = Record<Lowercase<string>, number>;\n\nexport async function sendEvents(\n accessToken: string,\n config: LinkedinConversionConfig,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {}\n) {\n const eventNames = Object.keys(config);\n const address = getFirst(data.address);\n const userIds: { idType: UserIdType; idValue: string }[] = [];\n const externalIds: [string, ...string[]] | undefined = data.user_id ? [data.user_id] : undefined;\n const userInfo =\n address && address.first_name && address.last_name\n ? {\n firstName: address.first_name,\n lastName: address.last_name,\n countryCode: address.country,\n }\n : undefined;\n\n if (data.email) {\n const email = getFirst(data.email);\n if (email)\n userIds.push({\n idType: 'SHA256_EMAIL',\n idValue: createHash('sha256').update(email).digest('hex'),\n });\n }\n\n const dto: CreateMultipleLinkedinEventsDTO = {\n elements: events\n .filter((event) => eventNames.includes(event.name) && !IGNORED_EVENTS.includes(event.name))\n .map((event) => ({\n eventId: event.id,\n conversion: `urn:lla:llaPartnerConversion:${config[event.name]}`,\n conversionHappenedAt: Date.now(),\n conversionValue: {\n currencyCode: event.properties?.currency?.toUpperCase() ?? 'USD',\n amount: event.properties?.value?.toString() ?? '0',\n },\n user: {\n userIds: event.tags.li_fat_id\n ? [\n {\n idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID',\n idValue: event.tags.li_fat_id,\n },\n ...userIds,\n ]\n : userIds,\n userInfo,\n externalIds,\n },\n })),\n };\n\n if (dto.elements.length === 0) return;\n try {\n const response = await fetch('https://api.linkedin.com/rest/conversionEvents', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'LinkedIn-Version': '202509',\n 'X-Restli-Protocol-Version': '2.0.0',\n 'X-RestLi-Method': 'BATCH_CREATE',\n },\n body: JSON.stringify(dto),\n });\n\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send LinkedIn conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send LinkedIn conversion, network error:', error);\n }\n}\n"],"mappings":";AAIA,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AACtB,SAAS,sBAAsB;AAE/B,SAAS,gBAAgB;AA0DzB,eAAsB,WACpB,aACA,QAEA,QACA,OAAyB,CAAC,GAC1B;AACA,QAAM,aAAa,OAAO,KAAK,MAAM;AACrC,QAAM,UAAU,SAAS,KAAK,OAAO;AACrC,QAAM,UAAqD,CAAC;AAC5D,QAAM,cAAiD,KAAK,UAAU,CAAC,KAAK,OAAO,IAAI;AACvF,QAAM,WACJ,WAAW,QAAQ,cAAc,QAAQ,YACrC;AAAA,IACE,WAAW,QAAQ;AAAA,IACnB,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ;AAAA,EACvB,IACA;AAEN,MAAI,KAAK,OAAO;AACd,UAAM,QAAQ,SAAS,KAAK,KAAK;AACjC,QAAI;AACF,cAAQ,KAAK;AAAA,QACX,QAAQ;AAAA,QACR,SAAS,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AAAA,MAC1D,CAAC;AAAA,EACL;AAEA,QAAM,MAAuC;AAAA,IAC3C,UAAU,OACP,OAAO,CAAC,UAAU,WAAW,SAAS,MAAM,IAAI,KAAK,CAAC,eAAe,SAAS,MAAM,IAAI,CAAC,EACzF,IAAI,CAAC,UAAO;AAlGnB;AAkGuB;AAAA,QACf,SAAS,MAAM;AAAA,QACf,YAAY,gCAAgC,OAAO,MAAM,IAAI,CAAC;AAAA,QAC9D,sBAAsB,KAAK,IAAI;AAAA,QAC/B,iBAAiB;AAAA,UACf,gBAAc,iBAAM,eAAN,mBAAkB,aAAlB,mBAA4B,kBAAiB;AAAA,UAC3D,UAAQ,iBAAM,eAAN,mBAAkB,UAAlB,mBAAyB,eAAc;AAAA,QACjD;AAAA,QACA,MAAM;AAAA,UACJ,SAAS,MAAM,KAAK,YAChB;AAAA,YACE;AAAA,cACE,QAAQ;AAAA,cACR,SAAS,MAAM,KAAK;AAAA,YACtB;AAAA,YACA,GAAG;AAAA,UACL,IACA;AAAA,UACJ;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,KAAE;AAAA,EACN;AAEA,MAAI,IAAI,SAAS,WAAW,EAAG;AAC/B,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,kDAAkD;AAAA,MAC7E,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,WAAW;AAAA,QACpC,gBAAgB;AAAA,QAChB,oBAAoB;AAAA,QACpB,6BAA6B;AAAA,QAC7B,mBAAmB;AAAA,MACrB;AAAA,MACA,MAAM,KAAK,UAAU,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,+CAA+C,MAAM,WAAW,OAAO,EAAE;AAAA,EACzF,SAAS,OAAO;AACd,YAAQ,MAAM,sDAAsD,KAAK;AAAA,EAC3E;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/server/linkedin-conversions-api.ts"],"sourcesContent":["/**\n * Conversions API Payload Builder: https://www.linkedin.com/developers/payload-builder\n * https://learn.microsoft.com/en-us/linkedin/marketing/conversions/conversions-overview?view=li-lms-2025-09\n */\nimport { createHash } from 'node:crypto';\nimport { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\ntype UserIdType =\n | 'SHA256_EMAIL'\n | 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID'\n | 'ACXIOM_ID'\n | 'ORACLE_MOAT_ID';\n\nexport interface CreateLinkedinEventDTO {\n /**\n * For any conversion that you want to send through multiple methods, such as Insight Tag and\n * Conversions API, you must create a conversion rule for each data source (browser and server).\n * Then, you can implement a logic to pick up the eventId from the browser and send it with the\n * corresponding event from your server. If we receive an Insight Tag event and a Conversions API\n * event from the same account with the same eventId, we discard the Conversions API event and\n * count only the Insight Tag event in campaign reporting.\n */\n eventId?: string;\n\n /**\n * Replace <id> with the conversion ID extracted when creating the conversion rule\n * (e.g. urn:lla:llaPartnerConversion:<id>).\n */\n conversion: `urn:lla:llaPartnerConversion:${number}`;\n\n /** Epoch timestamp in milliseconds at which the conversion event happened. */\n conversionHappenedAt: number;\n conversionValue: { currencyCode: string; amount: string };\n user: {\n userIds: { idType: UserIdType; idValue: string }[];\n userInfo?: {\n firstName?: string;\n lastName?: string;\n companyName?: string;\n countryCode?: string;\n title?: string;\n };\n\n /**\n * The maximum supported size of the list is 1 at the moment. If the list contains multiple\n * values, only the first value will be used.\n */\n externalIds?: [string, ...string[]];\n\n /**\n * This is generated when users submit the Linkedin Lead-gen form\n * (e.g. urn:li:leadGenFormResponse:<id>).\n */\n lead?: `urn:li:leadGenFormResponse:${string}`;\n };\n}\n\nexport interface CreateMultipleLinkedinEventsDTO {\n elements: CreateLinkedinEventDTO[];\n}\n\nexport type LinkedinConversionConfig = Record<Lowercase<string>, number>;\n\nexport async function sendEvents(\n accessToken: string,\n config: LinkedinConversionConfig,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {}\n) {\n const eventNames = Object.keys(config);\n const address = getFirst(data.address);\n const userIds: { idType: UserIdType; idValue: string }[] = [];\n const externalIds: [string, ...string[]] | undefined = data.user_id ? [data.user_id] : undefined;\n const userInfo =\n address && address.first_name && address.last_name\n ? {\n firstName: address.first_name,\n lastName: address.last_name,\n countryCode: address.country,\n }\n : undefined;\n\n if (data.email) {\n const email = getFirst(data.email);\n if (email)\n userIds.push({\n idType: 'SHA256_EMAIL',\n idValue: createHash('sha256').update(email).digest('hex'),\n });\n }\n\n const dto: CreateMultipleLinkedinEventsDTO = {\n elements: events\n .filter((event) => eventNames.includes(event.name) && !IGNORED_EVENTS.includes(event.name))\n .map((event) => ({\n eventId: event.id,\n conversion: `urn:lla:llaPartnerConversion:${config[event.name]}`,\n conversionHappenedAt: Date.now(),\n conversionValue: {\n currencyCode: event.properties?.currency?.toUpperCase() ?? 'USD',\n amount: event.properties?.value?.toString() ?? '0',\n },\n user: {\n userIds: event.tags.li_fat_id\n ? [\n {\n idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID',\n idValue: event.tags.li_fat_id,\n },\n ...userIds,\n ]\n : userIds,\n userInfo,\n externalIds,\n },\n })),\n };\n\n if (dto.elements.length === 0) return;\n try {\n const response = await fetch('https://api.linkedin.com/rest/conversionEvents', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'LinkedIn-Version': '202509',\n 'X-Restli-Protocol-Version': '2.0.0',\n 'X-RestLi-Method': 'BATCH_CREATE',\n },\n body: JSON.stringify(dto),\n });\n\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send LinkedIn conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send LinkedIn conversion, network error:', error);\n }\n}\n"],"mappings":";AAIA,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AACtB,SAAS,sBAAsB;AAE/B,SAAS,gBAAgB;AA0DzB,eAAsB,WACpB,aACA,QAEA,QACA,OAAyB,CAAC,GAC1B;AACA,QAAM,aAAa,OAAO,KAAK,MAAM;AACrC,QAAM,UAAU,SAAS,KAAK,OAAO;AACrC,QAAM,UAAqD,CAAC;AAC5D,QAAM,cAAiD,KAAK,UAAU,CAAC,KAAK,OAAO,IAAI;AACvF,QAAM,WACJ,WAAW,QAAQ,cAAc,QAAQ,YACrC;AAAA,IACE,WAAW,QAAQ;AAAA,IACnB,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ;AAAA,EACvB,IACA;AAEN,MAAI,KAAK,OAAO;AACd,UAAM,QAAQ,SAAS,KAAK,KAAK;AACjC,QAAI;AACF,cAAQ,KAAK;AAAA,QACX,QAAQ;AAAA,QACR,SAAS,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AAAA,MAC1D,CAAC;AAAA,EACL;AAEA,QAAM,MAAuC;AAAA,IAC3C,UAAU,OACP,OAAO,CAAC,UAAU,WAAW,SAAS,MAAM,IAAI,KAAK,CAAC,eAAe,SAAS,MAAM,IAAI,CAAC,EACzF,IAAI,CAAC,UAAO;AAlGnB;AAkGuB;AAAA,QACf,SAAS,MAAM;AAAA,QACf,YAAY,gCAAgC,OAAO,MAAM,IAAI,CAAC;AAAA,QAC9D,sBAAsB,KAAK,IAAI;AAAA,QAC/B,iBAAiB;AAAA,UACf,gBAAc,iBAAM,eAAN,mBAAkB,aAAlB,mBAA4B,kBAAiB;AAAA,UAC3D,UAAQ,iBAAM,eAAN,mBAAkB,UAAlB,mBAAyB,eAAc;AAAA,QACjD;AAAA,QACA,MAAM;AAAA,UACJ,SAAS,MAAM,KAAK,YAChB;AAAA,YACE;AAAA,cACE,QAAQ;AAAA,cACR,SAAS,MAAM,KAAK;AAAA,YACtB;AAAA,YACA,GAAG;AAAA,UACL,IACA;AAAA,UACJ;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,KAAE;AAAA,EACN;AAEA,MAAI,IAAI,SAAS,WAAW,EAAG;AAC/B,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,kDAAkD;AAAA,MAC7E,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,WAAW;AAAA,QACpC,gBAAgB;AAAA,QAChB,oBAAoB;AAAA,QACpB,6BAA6B;AAAA,QAC7B,mBAAmB;AAAA,MACrB;AAAA,MACA,MAAM,KAAK,UAAU,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,+CAA+C,MAAM,WAAW,OAAO,EAAE;AAAA,EACzF,SAAS,OAAO;AACd,YAAQ,MAAM,sDAAsD,KAAK;AAAA,EAC3E;AACF;","names":[]}
@@ -24,14 +24,14 @@ __export(openai_conversions_api_exports, {
24
24
  sendEvents: () => sendEvents
25
25
  });
26
26
  module.exports = __toCommonJS(openai_conversions_api_exports);
27
- var import_crypto = require("crypto");
27
+ var import_node_crypto = require("crypto");
28
28
  var import_utils = require("@shware/utils");
29
29
  var import_ignored_events = require("../third-parties/ignored-events.cjs");
30
30
  var import_oaiq = require("../track/oaiq.cjs");
31
31
  var import_field = require("../utils/field.cjs");
32
32
  var ENDPOINT = "https://bzr.openai.com/v1/events";
33
33
  function sha256(value) {
34
- return (0, import_crypto.createHash)("sha256").update(value).digest("hex");
34
+ return (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
35
35
  }
36
36
  function mapActionSource(source) {
37
37
  switch (source) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/openai-conversions-api.ts"],"sourcesContent":["/**\n * OpenAI Conversions API\n * https://developers.openai.com/ads/conversions-api\n * https://developers.openai.com/ads/supported-events\n */\nimport { createHash } from 'crypto';\nimport { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { type EventData, NON_AD_EVENTS, mapOAIEvent } from '../track/oaiq';\nimport type { TrackEvent, TrackTags, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\nconst ENDPOINT = 'https://bzr.openai.com/v1/events';\n\ntype ActionSource =\n | 'web'\n | 'mobile_app'\n | 'offline'\n | 'physical_store'\n | 'phone_call'\n | 'email'\n | 'other';\n\n/**\n * User/identity fields. Email and external id must be sent as lowercase 64-char SHA-256 hex\n * strings; geographic, IP, and user-agent fields are sent as raw values.\n */\nexport interface OpenAIUser {\n email_sha256?: string;\n external_id_sha256?: string;\n /** Two-letter ISO 3166-1 country code (e.g. \"US\"). */\n country?: string;\n city?: string;\n zip_code?: string;\n ip_address?: string;\n user_agent?: string;\n}\n\nexport interface OpenAIEvent {\n /** Unique event id; combined with `type` for deduplication against pixel events. */\n id: string;\n /** Standard event name or `custom`. */\n type: string;\n /** Required when `type` is `custom`. */\n custom_event_name?: string;\n /** Event timestamp in ms; must be within 7 days and no more than 10 minutes in the future. */\n timestamp_ms: number;\n /** Required for `action_source: \"web\"`. */\n source_url?: string;\n action_source?: ActionSource;\n /** OpenAI-provided privacy-preserving identifier. */\n oppref?: string;\n /** When true, opts the event out of personalization. */\n opt_out?: boolean;\n user?: OpenAIUser;\n data: EventData;\n}\n\nexport interface CreateOpenAIEventsDTO {\n /** When true, validates the events without persisting them. */\n validate_only?: boolean;\n events: OpenAIEvent[];\n}\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value).digest('hex');\n}\n\nfunction mapActionSource(source: TrackTags['source']): ActionSource | undefined {\n switch (source) {\n case 'web':\n return 'web';\n case 'app':\n return 'mobile_app';\n case 'offline':\n return 'offline';\n default:\n return undefined;\n }\n}\n\nfunction getUser(data: UserProvidedData): OpenAIUser | undefined {\n const email = getFirst(data.email)?.trim().toLowerCase();\n const address = getFirst(data.address);\n\n const user: OpenAIUser = {\n email_sha256: email ? sha256(email) : undefined,\n external_id_sha256: data.user_id ? sha256(data.user_id) : undefined,\n country: address?.country?.trim().toUpperCase(),\n city: address?.city?.trim().toLowerCase(),\n zip_code: address?.postal_code,\n ip_address: data.ip_address,\n user_agent: data.user_agent,\n };\n\n return Object.values(user).some((value) => value !== undefined) ? user : undefined;\n}\n\nexport function getServerEvent(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData\n): OpenAIEvent {\n const { type, data: eventData } = mapOAIEvent(event.name, event.properties);\n\n return {\n id: event.tags.idempotency_key ?? event.id.toString(),\n type,\n // For custom events the original track name is the OpenAI custom_event_name; this matches\n // the browser pixel so the two deduplicate. Standard events omit it.\n custom_event_name: type === 'custom' ? event.name : undefined,\n timestamp_ms: Date.now(),\n source_url: event.tags.source_url,\n action_source: mapActionSource(event.tags.source),\n user: getUser(data),\n data: eventData,\n };\n}\n\nexport async function sendEvents(\n apiKey: string,\n pixelId: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {},\n validateOnly = false\n) {\n const dto: CreateOpenAIEventsDTO = {\n validate_only: validateOnly,\n events: events\n .filter((event) => !IGNORED_EVENTS.includes(event.name))\n .filter((event) => !NON_AD_EVENTS.includes(event.name))\n .map((event) => getServerEvent(event, data)),\n };\n\n if (dto.events.length === 0) return;\n\n try {\n const response = await fetch(`${ENDPOINT}?pid=${pixelId}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(dto),\n });\n\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send OpenAI conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send OpenAI conversion, network error:', error);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,oBAA2B;AAC3B,mBAAsB;AACtB,4BAA+B;AAC/B,kBAA2D;AAE3D,mBAAyB;AAEzB,IAAM,WAAW;AAoDjB,SAAS,OAAO,OAAuB;AACrC,aAAO,0BAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxD;AAEA,SAAS,gBAAgB,QAAuD;AAC9E,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,QAAQ,MAAgD;AAjFjE;AAkFE,QAAM,SAAQ,gCAAS,KAAK,KAAK,MAAnB,mBAAsB,OAAO;AAC3C,QAAM,cAAU,uBAAS,KAAK,OAAO;AAErC,QAAM,OAAmB;AAAA,IACvB,cAAc,QAAQ,OAAO,KAAK,IAAI;AAAA,IACtC,oBAAoB,KAAK,UAAU,OAAO,KAAK,OAAO,IAAI;AAAA,IAC1D,UAAS,wCAAS,YAAT,mBAAkB,OAAO;AAAA,IAClC,OAAM,wCAAS,SAAT,mBAAe,OAAO;AAAA,IAC5B,UAAU,mCAAS;AAAA,IACnB,YAAY,KAAK;AAAA,IACjB,YAAY,KAAK;AAAA,EACnB;AAEA,SAAO,OAAO,OAAO,IAAI,EAAE,KAAK,CAAC,UAAU,UAAU,MAAS,IAAI,OAAO;AAC3E;AAEO,SAAS,eAEd,OACA,MACa;AACb,QAAM,EAAE,MAAM,MAAM,UAAU,QAAI,yBAAY,MAAM,MAAM,MAAM,UAAU;AAE1E,SAAO;AAAA,IACL,IAAI,MAAM,KAAK,mBAAmB,MAAM,GAAG,SAAS;AAAA,IACpD;AAAA;AAAA;AAAA,IAGA,mBAAmB,SAAS,WAAW,MAAM,OAAO;AAAA,IACpD,cAAc,KAAK,IAAI;AAAA,IACvB,YAAY,MAAM,KAAK;AAAA,IACvB,eAAe,gBAAgB,MAAM,KAAK,MAAM;AAAA,IAChD,MAAM,QAAQ,IAAI;AAAA,IAClB,MAAM;AAAA,EACR;AACF;AAEA,eAAsB,WACpB,QACA,SAEA,QACA,OAAyB,CAAC,GAC1B,eAAe,OACf;AACA,QAAM,MAA6B;AAAA,IACjC,eAAe;AAAA,IACf,QAAQ,OACL,OAAO,CAAC,UAAU,CAAC,qCAAe,SAAS,MAAM,IAAI,CAAC,EACtD,OAAO,CAAC,UAAU,CAAC,0BAAc,SAAS,MAAM,IAAI,CAAC,EACrD,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AAAA,EAC/C;AAEA,MAAI,IAAI,OAAO,WAAW,EAAG;AAE7B,MAAI;AACF,UAAM,WAAW,UAAM,oBAAM,GAAG,QAAQ,QAAQ,OAAO,IAAI;AAAA,MACzD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,QAAQ;AAAA,QACR,eAAe,UAAU,MAAM;AAAA,MACjC;AAAA,MACA,MAAM,KAAK,UAAU,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,6CAA6C,MAAM,WAAW,OAAO,EAAE;AAAA,EACvF,SAAS,OAAO;AACd,YAAQ,MAAM,oDAAoD,KAAK;AAAA,EACzE;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/server/openai-conversions-api.ts"],"sourcesContent":["/**\n * OpenAI Conversions API\n * https://developers.openai.com/ads/conversions-api\n * https://developers.openai.com/ads/supported-events\n */\nimport { createHash } from 'node:crypto';\nimport { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { type EventData, NON_AD_EVENTS, mapOAIEvent } from '../track/oaiq';\nimport type { TrackEvent, TrackTags, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\nconst ENDPOINT = 'https://bzr.openai.com/v1/events';\n\ntype ActionSource =\n | 'web'\n | 'mobile_app'\n | 'offline'\n | 'physical_store'\n | 'phone_call'\n | 'email'\n | 'other';\n\n/**\n * User/identity fields. Email and external id must be sent as lowercase 64-char SHA-256 hex\n * strings; geographic, IP, and user-agent fields are sent as raw values.\n */\nexport interface OpenAIUser {\n email_sha256?: string;\n external_id_sha256?: string;\n /** Two-letter ISO 3166-1 country code (e.g. \"US\"). */\n country?: string;\n city?: string;\n zip_code?: string;\n ip_address?: string;\n user_agent?: string;\n}\n\nexport interface OpenAIEvent {\n /** Unique event id; combined with `type` for deduplication against pixel events. */\n id: string;\n /** Standard event name or `custom`. */\n type: string;\n /** Required when `type` is `custom`. */\n custom_event_name?: string;\n /** Event timestamp in ms; must be within 7 days and no more than 10 minutes in the future. */\n timestamp_ms: number;\n /** Required for `action_source: \"web\"`. */\n source_url?: string;\n action_source?: ActionSource;\n /** OpenAI-provided privacy-preserving identifier. */\n oppref?: string;\n /** When true, opts the event out of personalization. */\n opt_out?: boolean;\n user?: OpenAIUser;\n data: EventData;\n}\n\nexport interface CreateOpenAIEventsDTO {\n /** When true, validates the events without persisting them. */\n validate_only?: boolean;\n events: OpenAIEvent[];\n}\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value).digest('hex');\n}\n\nfunction mapActionSource(source: TrackTags['source']): ActionSource | undefined {\n switch (source) {\n case 'web':\n return 'web';\n case 'app':\n return 'mobile_app';\n case 'offline':\n return 'offline';\n default:\n return undefined;\n }\n}\n\nfunction getUser(data: UserProvidedData): OpenAIUser | undefined {\n const email = getFirst(data.email)?.trim().toLowerCase();\n const address = getFirst(data.address);\n\n const user: OpenAIUser = {\n email_sha256: email ? sha256(email) : undefined,\n external_id_sha256: data.user_id ? sha256(data.user_id) : undefined,\n country: address?.country?.trim().toUpperCase(),\n city: address?.city?.trim().toLowerCase(),\n zip_code: address?.postal_code,\n ip_address: data.ip_address,\n user_agent: data.user_agent,\n };\n\n return Object.values(user).some((value) => value !== undefined) ? user : undefined;\n}\n\nexport function getServerEvent(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData\n): OpenAIEvent {\n const { type, data: eventData } = mapOAIEvent(event.name, event.properties);\n\n return {\n id: event.tags.idempotency_key ?? event.id.toString(),\n type,\n // For custom events the original track name is the OpenAI custom_event_name; this matches\n // the browser pixel so the two deduplicate. Standard events omit it.\n custom_event_name: type === 'custom' ? event.name : undefined,\n timestamp_ms: Date.now(),\n source_url: event.tags.source_url,\n action_source: mapActionSource(event.tags.source),\n user: getUser(data),\n data: eventData,\n };\n}\n\nexport async function sendEvents(\n apiKey: string,\n pixelId: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {},\n validateOnly = false\n) {\n const dto: CreateOpenAIEventsDTO = {\n validate_only: validateOnly,\n events: events\n .filter((event) => !IGNORED_EVENTS.includes(event.name))\n .filter((event) => !NON_AD_EVENTS.includes(event.name))\n .map((event) => getServerEvent(event, data)),\n };\n\n if (dto.events.length === 0) return;\n\n try {\n const response = await fetch(`${ENDPOINT}?pid=${pixelId}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(dto),\n });\n\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send OpenAI conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send OpenAI conversion, network error:', error);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,yBAA2B;AAC3B,mBAAsB;AACtB,4BAA+B;AAC/B,kBAA2D;AAE3D,mBAAyB;AAEzB,IAAM,WAAW;AAoDjB,SAAS,OAAO,OAAuB;AACrC,aAAO,+BAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxD;AAEA,SAAS,gBAAgB,QAAuD;AAC9E,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,QAAQ,MAAgD;AAjFjE;AAkFE,QAAM,SAAQ,gCAAS,KAAK,KAAK,MAAnB,mBAAsB,OAAO;AAC3C,QAAM,cAAU,uBAAS,KAAK,OAAO;AAErC,QAAM,OAAmB;AAAA,IACvB,cAAc,QAAQ,OAAO,KAAK,IAAI;AAAA,IACtC,oBAAoB,KAAK,UAAU,OAAO,KAAK,OAAO,IAAI;AAAA,IAC1D,UAAS,wCAAS,YAAT,mBAAkB,OAAO;AAAA,IAClC,OAAM,wCAAS,SAAT,mBAAe,OAAO;AAAA,IAC5B,UAAU,mCAAS;AAAA,IACnB,YAAY,KAAK;AAAA,IACjB,YAAY,KAAK;AAAA,EACnB;AAEA,SAAO,OAAO,OAAO,IAAI,EAAE,KAAK,CAAC,UAAU,UAAU,MAAS,IAAI,OAAO;AAC3E;AAEO,SAAS,eAEd,OACA,MACa;AACb,QAAM,EAAE,MAAM,MAAM,UAAU,QAAI,yBAAY,MAAM,MAAM,MAAM,UAAU;AAE1E,SAAO;AAAA,IACL,IAAI,MAAM,KAAK,mBAAmB,MAAM,GAAG,SAAS;AAAA,IACpD;AAAA;AAAA;AAAA,IAGA,mBAAmB,SAAS,WAAW,MAAM,OAAO;AAAA,IACpD,cAAc,KAAK,IAAI;AAAA,IACvB,YAAY,MAAM,KAAK;AAAA,IACvB,eAAe,gBAAgB,MAAM,KAAK,MAAM;AAAA,IAChD,MAAM,QAAQ,IAAI;AAAA,IAClB,MAAM;AAAA,EACR;AACF;AAEA,eAAsB,WACpB,QACA,SAEA,QACA,OAAyB,CAAC,GAC1B,eAAe,OACf;AACA,QAAM,MAA6B;AAAA,IACjC,eAAe;AAAA,IACf,QAAQ,OACL,OAAO,CAAC,UAAU,CAAC,qCAAe,SAAS,MAAM,IAAI,CAAC,EACtD,OAAO,CAAC,UAAU,CAAC,0BAAc,SAAS,MAAM,IAAI,CAAC,EACrD,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AAAA,EAC/C;AAEA,MAAI,IAAI,OAAO,WAAW,EAAG;AAE7B,MAAI;AACF,UAAM,WAAW,UAAM,oBAAM,GAAG,QAAQ,QAAQ,OAAO,IAAI;AAAA,MACzD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,QAAQ;AAAA,QACR,eAAe,UAAU,MAAM;AAAA,MACjC;AAAA,MACA,MAAM,KAAK,UAAU,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,6CAA6C,MAAM,WAAW,OAAO,EAAE;AAAA,EACvF,SAAS,OAAO;AACd,YAAQ,MAAM,oDAAoD,KAAK;AAAA,EACzE;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/openai-conversions-api.ts"],"sourcesContent":["/**\n * OpenAI Conversions API\n * https://developers.openai.com/ads/conversions-api\n * https://developers.openai.com/ads/supported-events\n */\nimport { createHash } from 'crypto';\nimport { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { type EventData, NON_AD_EVENTS, mapOAIEvent } from '../track/oaiq';\nimport type { TrackEvent, TrackTags, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\nconst ENDPOINT = 'https://bzr.openai.com/v1/events';\n\ntype ActionSource =\n | 'web'\n | 'mobile_app'\n | 'offline'\n | 'physical_store'\n | 'phone_call'\n | 'email'\n | 'other';\n\n/**\n * User/identity fields. Email and external id must be sent as lowercase 64-char SHA-256 hex\n * strings; geographic, IP, and user-agent fields are sent as raw values.\n */\nexport interface OpenAIUser {\n email_sha256?: string;\n external_id_sha256?: string;\n /** Two-letter ISO 3166-1 country code (e.g. \"US\"). */\n country?: string;\n city?: string;\n zip_code?: string;\n ip_address?: string;\n user_agent?: string;\n}\n\nexport interface OpenAIEvent {\n /** Unique event id; combined with `type` for deduplication against pixel events. */\n id: string;\n /** Standard event name or `custom`. */\n type: string;\n /** Required when `type` is `custom`. */\n custom_event_name?: string;\n /** Event timestamp in ms; must be within 7 days and no more than 10 minutes in the future. */\n timestamp_ms: number;\n /** Required for `action_source: \"web\"`. */\n source_url?: string;\n action_source?: ActionSource;\n /** OpenAI-provided privacy-preserving identifier. */\n oppref?: string;\n /** When true, opts the event out of personalization. */\n opt_out?: boolean;\n user?: OpenAIUser;\n data: EventData;\n}\n\nexport interface CreateOpenAIEventsDTO {\n /** When true, validates the events without persisting them. */\n validate_only?: boolean;\n events: OpenAIEvent[];\n}\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value).digest('hex');\n}\n\nfunction mapActionSource(source: TrackTags['source']): ActionSource | undefined {\n switch (source) {\n case 'web':\n return 'web';\n case 'app':\n return 'mobile_app';\n case 'offline':\n return 'offline';\n default:\n return undefined;\n }\n}\n\nfunction getUser(data: UserProvidedData): OpenAIUser | undefined {\n const email = getFirst(data.email)?.trim().toLowerCase();\n const address = getFirst(data.address);\n\n const user: OpenAIUser = {\n email_sha256: email ? sha256(email) : undefined,\n external_id_sha256: data.user_id ? sha256(data.user_id) : undefined,\n country: address?.country?.trim().toUpperCase(),\n city: address?.city?.trim().toLowerCase(),\n zip_code: address?.postal_code,\n ip_address: data.ip_address,\n user_agent: data.user_agent,\n };\n\n return Object.values(user).some((value) => value !== undefined) ? user : undefined;\n}\n\nexport function getServerEvent(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData\n): OpenAIEvent {\n const { type, data: eventData } = mapOAIEvent(event.name, event.properties);\n\n return {\n id: event.tags.idempotency_key ?? event.id.toString(),\n type,\n // For custom events the original track name is the OpenAI custom_event_name; this matches\n // the browser pixel so the two deduplicate. Standard events omit it.\n custom_event_name: type === 'custom' ? event.name : undefined,\n timestamp_ms: Date.now(),\n source_url: event.tags.source_url,\n action_source: mapActionSource(event.tags.source),\n user: getUser(data),\n data: eventData,\n };\n}\n\nexport async function sendEvents(\n apiKey: string,\n pixelId: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {},\n validateOnly = false\n) {\n const dto: CreateOpenAIEventsDTO = {\n validate_only: validateOnly,\n events: events\n .filter((event) => !IGNORED_EVENTS.includes(event.name))\n .filter((event) => !NON_AD_EVENTS.includes(event.name))\n .map((event) => getServerEvent(event, data)),\n };\n\n if (dto.events.length === 0) return;\n\n try {\n const response = await fetch(`${ENDPOINT}?pid=${pixelId}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(dto),\n });\n\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send OpenAI conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send OpenAI conversion, network error:', error);\n }\n}\n"],"mappings":";AAKA,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AACtB,SAAS,sBAAsB;AAC/B,SAAyB,eAAe,mBAAmB;AAE3D,SAAS,gBAAgB;AAEzB,IAAM,WAAW;AAoDjB,SAAS,OAAO,OAAuB;AACrC,SAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxD;AAEA,SAAS,gBAAgB,QAAuD;AAC9E,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,QAAQ,MAAgD;AAjFjE;AAkFE,QAAM,SAAQ,cAAS,KAAK,KAAK,MAAnB,mBAAsB,OAAO;AAC3C,QAAM,UAAU,SAAS,KAAK,OAAO;AAErC,QAAM,OAAmB;AAAA,IACvB,cAAc,QAAQ,OAAO,KAAK,IAAI;AAAA,IACtC,oBAAoB,KAAK,UAAU,OAAO,KAAK,OAAO,IAAI;AAAA,IAC1D,UAAS,wCAAS,YAAT,mBAAkB,OAAO;AAAA,IAClC,OAAM,wCAAS,SAAT,mBAAe,OAAO;AAAA,IAC5B,UAAU,mCAAS;AAAA,IACnB,YAAY,KAAK;AAAA,IACjB,YAAY,KAAK;AAAA,EACnB;AAEA,SAAO,OAAO,OAAO,IAAI,EAAE,KAAK,CAAC,UAAU,UAAU,MAAS,IAAI,OAAO;AAC3E;AAEO,SAAS,eAEd,OACA,MACa;AACb,QAAM,EAAE,MAAM,MAAM,UAAU,IAAI,YAAY,MAAM,MAAM,MAAM,UAAU;AAE1E,SAAO;AAAA,IACL,IAAI,MAAM,KAAK,mBAAmB,MAAM,GAAG,SAAS;AAAA,IACpD;AAAA;AAAA;AAAA,IAGA,mBAAmB,SAAS,WAAW,MAAM,OAAO;AAAA,IACpD,cAAc,KAAK,IAAI;AAAA,IACvB,YAAY,MAAM,KAAK;AAAA,IACvB,eAAe,gBAAgB,MAAM,KAAK,MAAM;AAAA,IAChD,MAAM,QAAQ,IAAI;AAAA,IAClB,MAAM;AAAA,EACR;AACF;AAEA,eAAsB,WACpB,QACA,SAEA,QACA,OAAyB,CAAC,GAC1B,eAAe,OACf;AACA,QAAM,MAA6B;AAAA,IACjC,eAAe;AAAA,IACf,QAAQ,OACL,OAAO,CAAC,UAAU,CAAC,eAAe,SAAS,MAAM,IAAI,CAAC,EACtD,OAAO,CAAC,UAAU,CAAC,cAAc,SAAS,MAAM,IAAI,CAAC,EACrD,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AAAA,EAC/C;AAEA,MAAI,IAAI,OAAO,WAAW,EAAG;AAE7B,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,QAAQ,OAAO,IAAI;AAAA,MACzD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,QAAQ;AAAA,QACR,eAAe,UAAU,MAAM;AAAA,MACjC;AAAA,MACA,MAAM,KAAK,UAAU,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,6CAA6C,MAAM,WAAW,OAAO,EAAE;AAAA,EACvF,SAAS,OAAO;AACd,YAAQ,MAAM,oDAAoD,KAAK;AAAA,EACzE;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/server/openai-conversions-api.ts"],"sourcesContent":["/**\n * OpenAI Conversions API\n * https://developers.openai.com/ads/conversions-api\n * https://developers.openai.com/ads/supported-events\n */\nimport { createHash } from 'node:crypto';\nimport { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { type EventData, NON_AD_EVENTS, mapOAIEvent } from '../track/oaiq';\nimport type { TrackEvent, TrackTags, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\nconst ENDPOINT = 'https://bzr.openai.com/v1/events';\n\ntype ActionSource =\n | 'web'\n | 'mobile_app'\n | 'offline'\n | 'physical_store'\n | 'phone_call'\n | 'email'\n | 'other';\n\n/**\n * User/identity fields. Email and external id must be sent as lowercase 64-char SHA-256 hex\n * strings; geographic, IP, and user-agent fields are sent as raw values.\n */\nexport interface OpenAIUser {\n email_sha256?: string;\n external_id_sha256?: string;\n /** Two-letter ISO 3166-1 country code (e.g. \"US\"). */\n country?: string;\n city?: string;\n zip_code?: string;\n ip_address?: string;\n user_agent?: string;\n}\n\nexport interface OpenAIEvent {\n /** Unique event id; combined with `type` for deduplication against pixel events. */\n id: string;\n /** Standard event name or `custom`. */\n type: string;\n /** Required when `type` is `custom`. */\n custom_event_name?: string;\n /** Event timestamp in ms; must be within 7 days and no more than 10 minutes in the future. */\n timestamp_ms: number;\n /** Required for `action_source: \"web\"`. */\n source_url?: string;\n action_source?: ActionSource;\n /** OpenAI-provided privacy-preserving identifier. */\n oppref?: string;\n /** When true, opts the event out of personalization. */\n opt_out?: boolean;\n user?: OpenAIUser;\n data: EventData;\n}\n\nexport interface CreateOpenAIEventsDTO {\n /** When true, validates the events without persisting them. */\n validate_only?: boolean;\n events: OpenAIEvent[];\n}\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value).digest('hex');\n}\n\nfunction mapActionSource(source: TrackTags['source']): ActionSource | undefined {\n switch (source) {\n case 'web':\n return 'web';\n case 'app':\n return 'mobile_app';\n case 'offline':\n return 'offline';\n default:\n return undefined;\n }\n}\n\nfunction getUser(data: UserProvidedData): OpenAIUser | undefined {\n const email = getFirst(data.email)?.trim().toLowerCase();\n const address = getFirst(data.address);\n\n const user: OpenAIUser = {\n email_sha256: email ? sha256(email) : undefined,\n external_id_sha256: data.user_id ? sha256(data.user_id) : undefined,\n country: address?.country?.trim().toUpperCase(),\n city: address?.city?.trim().toLowerCase(),\n zip_code: address?.postal_code,\n ip_address: data.ip_address,\n user_agent: data.user_agent,\n };\n\n return Object.values(user).some((value) => value !== undefined) ? user : undefined;\n}\n\nexport function getServerEvent(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData\n): OpenAIEvent {\n const { type, data: eventData } = mapOAIEvent(event.name, event.properties);\n\n return {\n id: event.tags.idempotency_key ?? event.id.toString(),\n type,\n // For custom events the original track name is the OpenAI custom_event_name; this matches\n // the browser pixel so the two deduplicate. Standard events omit it.\n custom_event_name: type === 'custom' ? event.name : undefined,\n timestamp_ms: Date.now(),\n source_url: event.tags.source_url,\n action_source: mapActionSource(event.tags.source),\n user: getUser(data),\n data: eventData,\n };\n}\n\nexport async function sendEvents(\n apiKey: string,\n pixelId: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {},\n validateOnly = false\n) {\n const dto: CreateOpenAIEventsDTO = {\n validate_only: validateOnly,\n events: events\n .filter((event) => !IGNORED_EVENTS.includes(event.name))\n .filter((event) => !NON_AD_EVENTS.includes(event.name))\n .map((event) => getServerEvent(event, data)),\n };\n\n if (dto.events.length === 0) return;\n\n try {\n const response = await fetch(`${ENDPOINT}?pid=${pixelId}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(dto),\n });\n\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send OpenAI conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send OpenAI conversion, network error:', error);\n }\n}\n"],"mappings":";AAKA,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AACtB,SAAS,sBAAsB;AAC/B,SAAyB,eAAe,mBAAmB;AAE3D,SAAS,gBAAgB;AAEzB,IAAM,WAAW;AAoDjB,SAAS,OAAO,OAAuB;AACrC,SAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxD;AAEA,SAAS,gBAAgB,QAAuD;AAC9E,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,QAAQ,MAAgD;AAjFjE;AAkFE,QAAM,SAAQ,cAAS,KAAK,KAAK,MAAnB,mBAAsB,OAAO;AAC3C,QAAM,UAAU,SAAS,KAAK,OAAO;AAErC,QAAM,OAAmB;AAAA,IACvB,cAAc,QAAQ,OAAO,KAAK,IAAI;AAAA,IACtC,oBAAoB,KAAK,UAAU,OAAO,KAAK,OAAO,IAAI;AAAA,IAC1D,UAAS,wCAAS,YAAT,mBAAkB,OAAO;AAAA,IAClC,OAAM,wCAAS,SAAT,mBAAe,OAAO;AAAA,IAC5B,UAAU,mCAAS;AAAA,IACnB,YAAY,KAAK;AAAA,IACjB,YAAY,KAAK;AAAA,EACnB;AAEA,SAAO,OAAO,OAAO,IAAI,EAAE,KAAK,CAAC,UAAU,UAAU,MAAS,IAAI,OAAO;AAC3E;AAEO,SAAS,eAEd,OACA,MACa;AACb,QAAM,EAAE,MAAM,MAAM,UAAU,IAAI,YAAY,MAAM,MAAM,MAAM,UAAU;AAE1E,SAAO;AAAA,IACL,IAAI,MAAM,KAAK,mBAAmB,MAAM,GAAG,SAAS;AAAA,IACpD;AAAA;AAAA;AAAA,IAGA,mBAAmB,SAAS,WAAW,MAAM,OAAO;AAAA,IACpD,cAAc,KAAK,IAAI;AAAA,IACvB,YAAY,MAAM,KAAK;AAAA,IACvB,eAAe,gBAAgB,MAAM,KAAK,MAAM;AAAA,IAChD,MAAM,QAAQ,IAAI;AAAA,IAClB,MAAM;AAAA,EACR;AACF;AAEA,eAAsB,WACpB,QACA,SAEA,QACA,OAAyB,CAAC,GAC1B,eAAe,OACf;AACA,QAAM,MAA6B;AAAA,IACjC,eAAe;AAAA,IACf,QAAQ,OACL,OAAO,CAAC,UAAU,CAAC,eAAe,SAAS,MAAM,IAAI,CAAC,EACtD,OAAO,CAAC,UAAU,CAAC,cAAc,SAAS,MAAM,IAAI,CAAC,EACrD,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AAAA,EAC/C;AAEA,MAAI,IAAI,OAAO,WAAW,EAAG;AAE7B,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,QAAQ,OAAO,IAAI;AAAA,MACzD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,QAAQ;AAAA,QACR,eAAe,UAAU,MAAM;AAAA,MACjC;AAAA,MACA,MAAM,KAAK,UAAU,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,6CAA6C,MAAM,WAAW,OAAO,EAAE;AAAA,EACvF,SAAS,OAAO;AACd,YAAQ,MAAM,oDAAoD,KAAK;AAAA,EACzE;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/reddit-conversions-api.ts"],"sourcesContent":["import { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { type ServerStandardEvent, mapRDTEvent, mapServerStandardEvent } from '../track/rdt';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\n/**\n * https://ads-api.reddit.com/docs/v3/operations/Post%20Conversion%20Events\n * https://business.reddithelp.com/s/article/map-a-catalog-to-a-signal-source\n */\nexport interface RedditEvent {\n /** Match keys: Share user identifiers to match conversions to a Reddit ad engagement. */\n click_id?: string;\n\n /** Unix epoch timestamp in milliseconds, event_at can't be older than seven days. */\n event_at: number;\n\n action_source: 'WEBSITE' | 'APP' | string;\n\n type: {\n tracking_type: ServerStandardEvent | 'CUSTOM';\n custom_event_name?: string;\n };\n\n /**\n * Event metadata\n * Share as much additional information about your conversion event as you'd like. If you're\n * using the Conversions API with the pixel, conversion_id is required for deduplication.\n */\n metadata?: {\n conversion_id?: string;\n currency?: string; // ISO 4217 3-letter currency code\n item_count?: number;\n value?: number;\n products?: { id: string; name?: string; category?: string }[];\n };\n\n user?: {\n email?: string;\n external_id?: string;\n ip_address?: string;\n phone_number?: string;\n user_agent?: string;\n\n /** The Identifier for Advertisers (IDFA) of the user's Apple device. */\n idfa?: string;\n\n /** The Android Advertising ID (AAID) of the user's Android device. */\n aaid?: string;\n /**\n * The value from the first-party Pixel _rdt_uuid cookie on your domain. Note that it is in\n * the {timestamp}.{uuid} format. You may use the full value or just the UUID portion.\n * Example: 1684189007728.7c73f2ae-a433-4d7b-9838-f467da98f48e\n */\n uuid?: string;\n\n screen_dimensions?: { width: number; height: number };\n\n /**\n * A structure of data processing options to specify the processing type for the event\n * https://business.reddithelp.com/s/article/Limited-Data-Use\n */\n data_processing_options?: {\n country: string;\n region: string;\n modes: string[] | ['LDU'];\n };\n };\n}\n\nexport interface CreateRedditEventDTO {\n data: { test_id?: string; events: RedditEvent[] };\n}\n\nexport function getServerEvent(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData\n): RedditEvent {\n const { id, name, properties, tags } = event;\n const [type, params] = mapRDTEvent(name, properties, id);\n\n return {\n click_id: tags.rdt_cid,\n event_at: Date.now(),\n action_source: tags.source === 'web' ? 'WEBSITE' : tags.source === 'app' ? 'APP' : 'UNKNOWN',\n type: {\n tracking_type: type === 'Custom' ? 'CUSTOM' : mapServerStandardEvent(type),\n custom_event_name: type === 'Custom' ? params.customEventName : undefined,\n },\n metadata: {\n conversion_id: id,\n currency:\n 'currency' in params && typeof params.currency === 'string'\n ? params.currency.toUpperCase()\n : undefined,\n item_count:\n 'itemCount' in params && typeof params.itemCount === 'number'\n ? params.itemCount\n : undefined,\n value: 'value' in params && typeof params.value === 'number' ? params.value : undefined,\n products:\n 'products' in params && Array.isArray(params.products) && params.products.length > 0\n ? params.products\n : undefined,\n },\n user: {\n email: getFirst(data.email),\n external_id: data.user_id,\n ip_address: data.ip_address,\n phone_number: getFirst(data.phone_number),\n user_agent: data.user_agent,\n idfa: tags.platform === 'ios' ? tags.advertising_id : undefined,\n aaid: tags.platform === 'android' ? tags.advertising_id : undefined,\n uuid: tags.rdt_uuid,\n screen_dimensions:\n tags.screen_width && tags.screen_height\n ? { width: tags.screen_width, height: tags.screen_height }\n : undefined,\n },\n };\n}\n\nexport async function sendEvents(\n accessToken: string,\n pixelId: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {},\n testId?: string\n) {\n const dto: CreateRedditEventDTO = {\n data: {\n test_id: testId,\n events: events\n .filter((event) => !IGNORED_EVENTS.includes(event.name))\n .map((event) => getServerEvent(event, data)),\n },\n };\n\n if (dto.data.events.length === 0) return;\n\n try {\n const response = await fetch(\n `https://ads-api.reddit.com/api/v3/pixels/${pixelId}/conversion_events`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify(dto),\n }\n );\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send Reddit conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send Reddit conversion, network error:', error);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAsB;AACtB,4BAA+B;AAC/B,iBAA8E;AAE9E,mBAAyB;AAsElB,SAAS,eAEd,OACA,MACa;AACb,QAAM,EAAE,IAAI,MAAM,YAAY,KAAK,IAAI;AACvC,QAAM,CAAC,MAAM,MAAM,QAAI,wBAAY,MAAM,YAAY,EAAE;AAEvD,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,UAAU,KAAK,IAAI;AAAA,IACnB,eAAe,KAAK,WAAW,QAAQ,YAAY,KAAK,WAAW,QAAQ,QAAQ;AAAA,IACnF,MAAM;AAAA,MACJ,eAAe,SAAS,WAAW,eAAW,mCAAuB,IAAI;AAAA,MACzE,mBAAmB,SAAS,WAAW,OAAO,kBAAkB;AAAA,IAClE;AAAA,IACA,UAAU;AAAA,MACR,eAAe;AAAA,MACf,UACE,cAAc,UAAU,OAAO,OAAO,aAAa,WAC/C,OAAO,SAAS,YAAY,IAC5B;AAAA,MACN,YACE,eAAe,UAAU,OAAO,OAAO,cAAc,WACjD,OAAO,YACP;AAAA,MACN,OAAO,WAAW,UAAU,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,MAC9E,UACE,cAAc,UAAU,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,SAAS,IAC/E,OAAO,WACP;AAAA,IACR;AAAA,IACA,MAAM;AAAA,MACJ,WAAO,uBAAS,KAAK,KAAK;AAAA,MAC1B,aAAa,KAAK;AAAA,MAClB,YAAY,KAAK;AAAA,MACjB,kBAAc,uBAAS,KAAK,YAAY;AAAA,MACxC,YAAY,KAAK;AAAA,MACjB,MAAM,KAAK,aAAa,QAAQ,KAAK,iBAAiB;AAAA,MACtD,MAAM,KAAK,aAAa,YAAY,KAAK,iBAAiB;AAAA,MAC1D,MAAM,KAAK;AAAA,MACX,mBACE,KAAK,gBAAgB,KAAK,gBACtB,EAAE,OAAO,KAAK,cAAc,QAAQ,KAAK,cAAc,IACvD;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,WACpB,aACA,SAEA,QACA,OAAyB,CAAC,GAC1B,QACA;AACA,QAAM,MAA4B;AAAA,IAChC,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,QAAQ,OACL,OAAO,CAAC,UAAU,CAAC,qCAAe,SAAS,MAAM,IAAI,CAAC,EACtD,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,IAAI,KAAK,OAAO,WAAW,EAAG;AAElC,MAAI;AACF,UAAM,WAAW,UAAM;AAAA,MACrB,4CAA4C,OAAO;AAAA,MACnD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,QAAQ;AAAA,UACR,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU,GAAG;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,6CAA6C,MAAM,WAAW,OAAO,EAAE;AAAA,EACvF,SAAS,OAAO;AACd,YAAQ,MAAM,oDAAoD,KAAK;AAAA,EACzE;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/server/reddit-conversions-api.ts"],"sourcesContent":["import { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { type ServerStandardEvent, mapRDTEvent, mapServerStandardEvent } from '../track/rdt';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\n/**\n * https://ads-api.reddit.com/docs/v3/operations/Post%20Conversion%20Events\n * https://business.reddithelp.com/s/article/map-a-catalog-to-a-signal-source\n */\nexport interface RedditEvent {\n /** Match keys: Share user identifiers to match conversions to a Reddit ad engagement. */\n click_id?: string;\n\n /** Unix epoch timestamp in milliseconds, event_at can't be older than seven days. */\n event_at: number;\n\n action_source: 'WEBSITE' | 'APP' | (string & {});\n\n type: {\n tracking_type: ServerStandardEvent | 'CUSTOM';\n custom_event_name?: string;\n };\n\n /**\n * Event metadata\n * Share as much additional information about your conversion event as you'd like. If you're\n * using the Conversions API with the pixel, conversion_id is required for deduplication.\n */\n metadata?: {\n conversion_id?: string;\n currency?: string; // ISO 4217 3-letter currency code\n item_count?: number;\n value?: number;\n products?: { id: string; name?: string; category?: string }[];\n };\n\n user?: {\n email?: string;\n external_id?: string;\n ip_address?: string;\n phone_number?: string;\n user_agent?: string;\n\n /** The Identifier for Advertisers (IDFA) of the user's Apple device. */\n idfa?: string;\n\n /** The Android Advertising ID (AAID) of the user's Android device. */\n aaid?: string;\n /**\n * The value from the first-party Pixel _rdt_uuid cookie on your domain. Note that it is in\n * the {timestamp}.{uuid} format. You may use the full value or just the UUID portion.\n * Example: 1684189007728.7c73f2ae-a433-4d7b-9838-f467da98f48e\n */\n uuid?: string;\n\n screen_dimensions?: { width: number; height: number };\n\n /**\n * A structure of data processing options to specify the processing type for the event\n * https://business.reddithelp.com/s/article/Limited-Data-Use\n */\n data_processing_options?: {\n country: string;\n region: string;\n modes: string[] | ['LDU'];\n };\n };\n}\n\nexport interface CreateRedditEventDTO {\n data: { test_id?: string; events: RedditEvent[] };\n}\n\nexport function getServerEvent(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData\n): RedditEvent {\n const { id, name, properties, tags } = event;\n const [type, params] = mapRDTEvent(name, properties, id);\n\n return {\n click_id: tags.rdt_cid,\n event_at: Date.now(),\n action_source: tags.source === 'web' ? 'WEBSITE' : tags.source === 'app' ? 'APP' : 'UNKNOWN',\n type: {\n tracking_type: type === 'Custom' ? 'CUSTOM' : mapServerStandardEvent(type),\n custom_event_name: type === 'Custom' ? params.customEventName : undefined,\n },\n metadata: {\n conversion_id: id,\n currency:\n 'currency' in params && typeof params.currency === 'string'\n ? params.currency.toUpperCase()\n : undefined,\n item_count:\n 'itemCount' in params && typeof params.itemCount === 'number'\n ? params.itemCount\n : undefined,\n value: 'value' in params && typeof params.value === 'number' ? params.value : undefined,\n products:\n 'products' in params && Array.isArray(params.products) && params.products.length > 0\n ? params.products\n : undefined,\n },\n user: {\n email: getFirst(data.email),\n external_id: data.user_id,\n ip_address: data.ip_address,\n phone_number: getFirst(data.phone_number),\n user_agent: data.user_agent,\n idfa: tags.platform === 'ios' ? tags.advertising_id : undefined,\n aaid: tags.platform === 'android' ? tags.advertising_id : undefined,\n uuid: tags.rdt_uuid,\n screen_dimensions:\n tags.screen_width && tags.screen_height\n ? { width: tags.screen_width, height: tags.screen_height }\n : undefined,\n },\n };\n}\n\nexport async function sendEvents(\n accessToken: string,\n pixelId: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {},\n testId?: string\n) {\n const dto: CreateRedditEventDTO = {\n data: {\n test_id: testId,\n events: events\n .filter((event) => !IGNORED_EVENTS.includes(event.name))\n .map((event) => getServerEvent(event, data)),\n },\n };\n\n if (dto.data.events.length === 0) return;\n\n try {\n const response = await fetch(\n `https://ads-api.reddit.com/api/v3/pixels/${pixelId}/conversion_events`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify(dto),\n }\n );\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send Reddit conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send Reddit conversion, network error:', error);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAsB;AACtB,4BAA+B;AAC/B,iBAA8E;AAE9E,mBAAyB;AAsElB,SAAS,eAEd,OACA,MACa;AACb,QAAM,EAAE,IAAI,MAAM,YAAY,KAAK,IAAI;AACvC,QAAM,CAAC,MAAM,MAAM,QAAI,wBAAY,MAAM,YAAY,EAAE;AAEvD,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,UAAU,KAAK,IAAI;AAAA,IACnB,eAAe,KAAK,WAAW,QAAQ,YAAY,KAAK,WAAW,QAAQ,QAAQ;AAAA,IACnF,MAAM;AAAA,MACJ,eAAe,SAAS,WAAW,eAAW,mCAAuB,IAAI;AAAA,MACzE,mBAAmB,SAAS,WAAW,OAAO,kBAAkB;AAAA,IAClE;AAAA,IACA,UAAU;AAAA,MACR,eAAe;AAAA,MACf,UACE,cAAc,UAAU,OAAO,OAAO,aAAa,WAC/C,OAAO,SAAS,YAAY,IAC5B;AAAA,MACN,YACE,eAAe,UAAU,OAAO,OAAO,cAAc,WACjD,OAAO,YACP;AAAA,MACN,OAAO,WAAW,UAAU,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,MAC9E,UACE,cAAc,UAAU,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,SAAS,IAC/E,OAAO,WACP;AAAA,IACR;AAAA,IACA,MAAM;AAAA,MACJ,WAAO,uBAAS,KAAK,KAAK;AAAA,MAC1B,aAAa,KAAK;AAAA,MAClB,YAAY,KAAK;AAAA,MACjB,kBAAc,uBAAS,KAAK,YAAY;AAAA,MACxC,YAAY,KAAK;AAAA,MACjB,MAAM,KAAK,aAAa,QAAQ,KAAK,iBAAiB;AAAA,MACtD,MAAM,KAAK,aAAa,YAAY,KAAK,iBAAiB;AAAA,MAC1D,MAAM,KAAK;AAAA,MACX,mBACE,KAAK,gBAAgB,KAAK,gBACtB,EAAE,OAAO,KAAK,cAAc,QAAQ,KAAK,cAAc,IACvD;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,WACpB,aACA,SAEA,QACA,OAAyB,CAAC,GAC1B,QACA;AACA,QAAM,MAA4B;AAAA,IAChC,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,QAAQ,OACL,OAAO,CAAC,UAAU,CAAC,qCAAe,SAAS,MAAM,IAAI,CAAC,EACtD,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,IAAI,KAAK,OAAO,WAAW,EAAG;AAElC,MAAI;AACF,UAAM,WAAW,UAAM;AAAA,MACrB,4CAA4C,OAAO;AAAA,MACnD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,QAAQ;AAAA,UACR,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU,GAAG;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,6CAA6C,MAAM,WAAW,OAAO,EAAE;AAAA,EACvF,SAAS,OAAO;AACd,YAAQ,MAAM,oDAAoD,KAAK;AAAA,EACzE;AACF;","names":[]}
@@ -11,7 +11,7 @@ interface RedditEvent {
11
11
  click_id?: string;
12
12
  /** Unix epoch timestamp in milliseconds, event_at can't be older than seven days. */
13
13
  event_at: number;
14
- action_source: 'WEBSITE' | 'APP' | string;
14
+ action_source: 'WEBSITE' | 'APP' | (string & {});
15
15
  type: {
16
16
  tracking_type: ServerStandardEvent | 'CUSTOM';
17
17
  custom_event_name?: string;
@@ -11,7 +11,7 @@ interface RedditEvent {
11
11
  click_id?: string;
12
12
  /** Unix epoch timestamp in milliseconds, event_at can't be older than seven days. */
13
13
  event_at: number;
14
- action_source: 'WEBSITE' | 'APP' | string;
14
+ action_source: 'WEBSITE' | 'APP' | (string & {});
15
15
  type: {
16
16
  tracking_type: ServerStandardEvent | 'CUSTOM';
17
17
  custom_event_name?: string;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/reddit-conversions-api.ts"],"sourcesContent":["import { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { type ServerStandardEvent, mapRDTEvent, mapServerStandardEvent } from '../track/rdt';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\n/**\n * https://ads-api.reddit.com/docs/v3/operations/Post%20Conversion%20Events\n * https://business.reddithelp.com/s/article/map-a-catalog-to-a-signal-source\n */\nexport interface RedditEvent {\n /** Match keys: Share user identifiers to match conversions to a Reddit ad engagement. */\n click_id?: string;\n\n /** Unix epoch timestamp in milliseconds, event_at can't be older than seven days. */\n event_at: number;\n\n action_source: 'WEBSITE' | 'APP' | string;\n\n type: {\n tracking_type: ServerStandardEvent | 'CUSTOM';\n custom_event_name?: string;\n };\n\n /**\n * Event metadata\n * Share as much additional information about your conversion event as you'd like. If you're\n * using the Conversions API with the pixel, conversion_id is required for deduplication.\n */\n metadata?: {\n conversion_id?: string;\n currency?: string; // ISO 4217 3-letter currency code\n item_count?: number;\n value?: number;\n products?: { id: string; name?: string; category?: string }[];\n };\n\n user?: {\n email?: string;\n external_id?: string;\n ip_address?: string;\n phone_number?: string;\n user_agent?: string;\n\n /** The Identifier for Advertisers (IDFA) of the user's Apple device. */\n idfa?: string;\n\n /** The Android Advertising ID (AAID) of the user's Android device. */\n aaid?: string;\n /**\n * The value from the first-party Pixel _rdt_uuid cookie on your domain. Note that it is in\n * the {timestamp}.{uuid} format. You may use the full value or just the UUID portion.\n * Example: 1684189007728.7c73f2ae-a433-4d7b-9838-f467da98f48e\n */\n uuid?: string;\n\n screen_dimensions?: { width: number; height: number };\n\n /**\n * A structure of data processing options to specify the processing type for the event\n * https://business.reddithelp.com/s/article/Limited-Data-Use\n */\n data_processing_options?: {\n country: string;\n region: string;\n modes: string[] | ['LDU'];\n };\n };\n}\n\nexport interface CreateRedditEventDTO {\n data: { test_id?: string; events: RedditEvent[] };\n}\n\nexport function getServerEvent(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData\n): RedditEvent {\n const { id, name, properties, tags } = event;\n const [type, params] = mapRDTEvent(name, properties, id);\n\n return {\n click_id: tags.rdt_cid,\n event_at: Date.now(),\n action_source: tags.source === 'web' ? 'WEBSITE' : tags.source === 'app' ? 'APP' : 'UNKNOWN',\n type: {\n tracking_type: type === 'Custom' ? 'CUSTOM' : mapServerStandardEvent(type),\n custom_event_name: type === 'Custom' ? params.customEventName : undefined,\n },\n metadata: {\n conversion_id: id,\n currency:\n 'currency' in params && typeof params.currency === 'string'\n ? params.currency.toUpperCase()\n : undefined,\n item_count:\n 'itemCount' in params && typeof params.itemCount === 'number'\n ? params.itemCount\n : undefined,\n value: 'value' in params && typeof params.value === 'number' ? params.value : undefined,\n products:\n 'products' in params && Array.isArray(params.products) && params.products.length > 0\n ? params.products\n : undefined,\n },\n user: {\n email: getFirst(data.email),\n external_id: data.user_id,\n ip_address: data.ip_address,\n phone_number: getFirst(data.phone_number),\n user_agent: data.user_agent,\n idfa: tags.platform === 'ios' ? tags.advertising_id : undefined,\n aaid: tags.platform === 'android' ? tags.advertising_id : undefined,\n uuid: tags.rdt_uuid,\n screen_dimensions:\n tags.screen_width && tags.screen_height\n ? { width: tags.screen_width, height: tags.screen_height }\n : undefined,\n },\n };\n}\n\nexport async function sendEvents(\n accessToken: string,\n pixelId: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {},\n testId?: string\n) {\n const dto: CreateRedditEventDTO = {\n data: {\n test_id: testId,\n events: events\n .filter((event) => !IGNORED_EVENTS.includes(event.name))\n .map((event) => getServerEvent(event, data)),\n },\n };\n\n if (dto.data.events.length === 0) return;\n\n try {\n const response = await fetch(\n `https://ads-api.reddit.com/api/v3/pixels/${pixelId}/conversion_events`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify(dto),\n }\n );\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send Reddit conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send Reddit conversion, network error:', error);\n }\n}\n"],"mappings":";AAAA,SAAS,aAAa;AACtB,SAAS,sBAAsB;AAC/B,SAAmC,aAAa,8BAA8B;AAE9E,SAAS,gBAAgB;AAsElB,SAAS,eAEd,OACA,MACa;AACb,QAAM,EAAE,IAAI,MAAM,YAAY,KAAK,IAAI;AACvC,QAAM,CAAC,MAAM,MAAM,IAAI,YAAY,MAAM,YAAY,EAAE;AAEvD,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,UAAU,KAAK,IAAI;AAAA,IACnB,eAAe,KAAK,WAAW,QAAQ,YAAY,KAAK,WAAW,QAAQ,QAAQ;AAAA,IACnF,MAAM;AAAA,MACJ,eAAe,SAAS,WAAW,WAAW,uBAAuB,IAAI;AAAA,MACzE,mBAAmB,SAAS,WAAW,OAAO,kBAAkB;AAAA,IAClE;AAAA,IACA,UAAU;AAAA,MACR,eAAe;AAAA,MACf,UACE,cAAc,UAAU,OAAO,OAAO,aAAa,WAC/C,OAAO,SAAS,YAAY,IAC5B;AAAA,MACN,YACE,eAAe,UAAU,OAAO,OAAO,cAAc,WACjD,OAAO,YACP;AAAA,MACN,OAAO,WAAW,UAAU,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,MAC9E,UACE,cAAc,UAAU,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,SAAS,IAC/E,OAAO,WACP;AAAA,IACR;AAAA,IACA,MAAM;AAAA,MACJ,OAAO,SAAS,KAAK,KAAK;AAAA,MAC1B,aAAa,KAAK;AAAA,MAClB,YAAY,KAAK;AAAA,MACjB,cAAc,SAAS,KAAK,YAAY;AAAA,MACxC,YAAY,KAAK;AAAA,MACjB,MAAM,KAAK,aAAa,QAAQ,KAAK,iBAAiB;AAAA,MACtD,MAAM,KAAK,aAAa,YAAY,KAAK,iBAAiB;AAAA,MAC1D,MAAM,KAAK;AAAA,MACX,mBACE,KAAK,gBAAgB,KAAK,gBACtB,EAAE,OAAO,KAAK,cAAc,QAAQ,KAAK,cAAc,IACvD;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,WACpB,aACA,SAEA,QACA,OAAyB,CAAC,GAC1B,QACA;AACA,QAAM,MAA4B;AAAA,IAChC,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,QAAQ,OACL,OAAO,CAAC,UAAU,CAAC,eAAe,SAAS,MAAM,IAAI,CAAC,EACtD,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,IAAI,KAAK,OAAO,WAAW,EAAG;AAElC,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB,4CAA4C,OAAO;AAAA,MACnD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,QAAQ;AAAA,UACR,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU,GAAG;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,6CAA6C,MAAM,WAAW,OAAO,EAAE;AAAA,EACvF,SAAS,OAAO;AACd,YAAQ,MAAM,oDAAoD,KAAK;AAAA,EACzE;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/server/reddit-conversions-api.ts"],"sourcesContent":["import { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { type ServerStandardEvent, mapRDTEvent, mapServerStandardEvent } from '../track/rdt';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\n/**\n * https://ads-api.reddit.com/docs/v3/operations/Post%20Conversion%20Events\n * https://business.reddithelp.com/s/article/map-a-catalog-to-a-signal-source\n */\nexport interface RedditEvent {\n /** Match keys: Share user identifiers to match conversions to a Reddit ad engagement. */\n click_id?: string;\n\n /** Unix epoch timestamp in milliseconds, event_at can't be older than seven days. */\n event_at: number;\n\n action_source: 'WEBSITE' | 'APP' | (string & {});\n\n type: {\n tracking_type: ServerStandardEvent | 'CUSTOM';\n custom_event_name?: string;\n };\n\n /**\n * Event metadata\n * Share as much additional information about your conversion event as you'd like. If you're\n * using the Conversions API with the pixel, conversion_id is required for deduplication.\n */\n metadata?: {\n conversion_id?: string;\n currency?: string; // ISO 4217 3-letter currency code\n item_count?: number;\n value?: number;\n products?: { id: string; name?: string; category?: string }[];\n };\n\n user?: {\n email?: string;\n external_id?: string;\n ip_address?: string;\n phone_number?: string;\n user_agent?: string;\n\n /** The Identifier for Advertisers (IDFA) of the user's Apple device. */\n idfa?: string;\n\n /** The Android Advertising ID (AAID) of the user's Android device. */\n aaid?: string;\n /**\n * The value from the first-party Pixel _rdt_uuid cookie on your domain. Note that it is in\n * the {timestamp}.{uuid} format. You may use the full value or just the UUID portion.\n * Example: 1684189007728.7c73f2ae-a433-4d7b-9838-f467da98f48e\n */\n uuid?: string;\n\n screen_dimensions?: { width: number; height: number };\n\n /**\n * A structure of data processing options to specify the processing type for the event\n * https://business.reddithelp.com/s/article/Limited-Data-Use\n */\n data_processing_options?: {\n country: string;\n region: string;\n modes: string[] | ['LDU'];\n };\n };\n}\n\nexport interface CreateRedditEventDTO {\n data: { test_id?: string; events: RedditEvent[] };\n}\n\nexport function getServerEvent(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n event: TrackEvent<any>,\n data: UserProvidedData\n): RedditEvent {\n const { id, name, properties, tags } = event;\n const [type, params] = mapRDTEvent(name, properties, id);\n\n return {\n click_id: tags.rdt_cid,\n event_at: Date.now(),\n action_source: tags.source === 'web' ? 'WEBSITE' : tags.source === 'app' ? 'APP' : 'UNKNOWN',\n type: {\n tracking_type: type === 'Custom' ? 'CUSTOM' : mapServerStandardEvent(type),\n custom_event_name: type === 'Custom' ? params.customEventName : undefined,\n },\n metadata: {\n conversion_id: id,\n currency:\n 'currency' in params && typeof params.currency === 'string'\n ? params.currency.toUpperCase()\n : undefined,\n item_count:\n 'itemCount' in params && typeof params.itemCount === 'number'\n ? params.itemCount\n : undefined,\n value: 'value' in params && typeof params.value === 'number' ? params.value : undefined,\n products:\n 'products' in params && Array.isArray(params.products) && params.products.length > 0\n ? params.products\n : undefined,\n },\n user: {\n email: getFirst(data.email),\n external_id: data.user_id,\n ip_address: data.ip_address,\n phone_number: getFirst(data.phone_number),\n user_agent: data.user_agent,\n idfa: tags.platform === 'ios' ? tags.advertising_id : undefined,\n aaid: tags.platform === 'android' ? tags.advertising_id : undefined,\n uuid: tags.rdt_uuid,\n screen_dimensions:\n tags.screen_width && tags.screen_height\n ? { width: tags.screen_width, height: tags.screen_height }\n : undefined,\n },\n };\n}\n\nexport async function sendEvents(\n accessToken: string,\n pixelId: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n events: TrackEvent<any>[],\n data: UserProvidedData = {},\n testId?: string\n) {\n const dto: CreateRedditEventDTO = {\n data: {\n test_id: testId,\n events: events\n .filter((event) => !IGNORED_EVENTS.includes(event.name))\n .map((event) => getServerEvent(event, data)),\n },\n };\n\n if (dto.data.events.length === 0) return;\n\n try {\n const response = await fetch(\n `https://ads-api.reddit.com/api/v3/pixels/${pixelId}/conversion_events`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify(dto),\n }\n );\n if (response.ok) return;\n const { status } = response;\n const message = await response.text();\n console.error(`Failed to send Reddit conversion, status: ${status}, body: ${message}`);\n } catch (error) {\n console.error('Failed to send Reddit conversion, network error:', error);\n }\n}\n"],"mappings":";AAAA,SAAS,aAAa;AACtB,SAAS,sBAAsB;AAC/B,SAAmC,aAAa,8BAA8B;AAE9E,SAAS,gBAAgB;AAsElB,SAAS,eAEd,OACA,MACa;AACb,QAAM,EAAE,IAAI,MAAM,YAAY,KAAK,IAAI;AACvC,QAAM,CAAC,MAAM,MAAM,IAAI,YAAY,MAAM,YAAY,EAAE;AAEvD,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,UAAU,KAAK,IAAI;AAAA,IACnB,eAAe,KAAK,WAAW,QAAQ,YAAY,KAAK,WAAW,QAAQ,QAAQ;AAAA,IACnF,MAAM;AAAA,MACJ,eAAe,SAAS,WAAW,WAAW,uBAAuB,IAAI;AAAA,MACzE,mBAAmB,SAAS,WAAW,OAAO,kBAAkB;AAAA,IAClE;AAAA,IACA,UAAU;AAAA,MACR,eAAe;AAAA,MACf,UACE,cAAc,UAAU,OAAO,OAAO,aAAa,WAC/C,OAAO,SAAS,YAAY,IAC5B;AAAA,MACN,YACE,eAAe,UAAU,OAAO,OAAO,cAAc,WACjD,OAAO,YACP;AAAA,MACN,OAAO,WAAW,UAAU,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,MAC9E,UACE,cAAc,UAAU,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,SAAS,IAC/E,OAAO,WACP;AAAA,IACR;AAAA,IACA,MAAM;AAAA,MACJ,OAAO,SAAS,KAAK,KAAK;AAAA,MAC1B,aAAa,KAAK;AAAA,MAClB,YAAY,KAAK;AAAA,MACjB,cAAc,SAAS,KAAK,YAAY;AAAA,MACxC,YAAY,KAAK;AAAA,MACjB,MAAM,KAAK,aAAa,QAAQ,KAAK,iBAAiB;AAAA,MACtD,MAAM,KAAK,aAAa,YAAY,KAAK,iBAAiB;AAAA,MAC1D,MAAM,KAAK;AAAA,MACX,mBACE,KAAK,gBAAgB,KAAK,gBACtB,EAAE,OAAO,KAAK,cAAc,QAAQ,KAAK,cAAc,IACvD;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,WACpB,aACA,SAEA,QACA,OAAyB,CAAC,GAC1B,QACA;AACA,QAAM,MAA4B;AAAA,IAChC,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,QAAQ,OACL,OAAO,CAAC,UAAU,CAAC,eAAe,SAAS,MAAM,IAAI,CAAC,EACtD,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,IAAI,KAAK,OAAO,WAAW,EAAG;AAElC,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB,4CAA4C,OAAO;AAAA,MACnD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,QAAQ;AAAA,UACR,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU,GAAG;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,6CAA6C,MAAM,WAAW,OAAO,EAAE;AAAA,EACvF,SAAS,OAAO;AACd,YAAQ,MAAM,oDAAoD,KAAK;AAAA,EACzE;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/track/gtag.ts"],"sourcesContent":["/** reference: https://support.google.com/analytics/answer/13316687 */\nconst reservedWebEventNames = [\n 'app_remove',\n 'app_store_refund',\n 'app_store_subscription_cancel',\n 'app_store_subscription_renew',\n 'click',\n 'error',\n 'file_download',\n 'first_open',\n 'first_visit',\n 'form_start',\n 'form_submit',\n 'in_app_purchase',\n 'page_view',\n 'scroll',\n 'session_start',\n 'user_engagement',\n 'view_complete',\n 'video_progress',\n 'video_start',\n 'view_search_results',\n] as const;\n\nconst reservedAppEventNames = [\n 'ad_activeview',\n 'ad_click',\n 'ad_exposure',\n 'ad_impression',\n 'ad_query',\n 'ad_reward',\n 'adunit_exposure',\n 'app_clear_data',\n 'app_exception',\n 'app_install',\n 'app_remove',\n 'app_store_refund',\n 'app_update',\n 'app_upgrade',\n 'dynamic_link_app_open',\n 'dynamic_link_app_update',\n 'dynamic_link_first_open',\n 'error',\n 'firebase_campaign',\n 'firebase_in_app_message_action',\n 'firebase_in_app_message_dismiss',\n 'firebase_in_app_message_impression',\n 'first_open',\n 'first_visit',\n 'in_app_purchase',\n 'notification_dismiss',\n 'notification_foreground',\n 'notification_open',\n 'notification_receive',\n 'notification_send',\n 'os_update',\n 'screen_view',\n 'session_start',\n 'user_engagement',\n] as const;\n\nexport const reservedEventNames = [...reservedWebEventNames, ...reservedAppEventNames] as const;\n\ntype ReservedWebEventNames = (typeof reservedWebEventNames)[number];\ntype ReservedAppEventNames = (typeof reservedAppEventNames)[number];\nexport type ReservedEventNames = ReservedWebEventNames | ReservedAppEventNames;\n\nexport type ReservedEventValues =\n | 'cid'\n | 'currency'\n | 'customer_id'\n | 'customerid'\n | 'dclid'\n | 'gclid'\n | 'session_id'\n | 'sessionid'\n | 'sfmc_id'\n | 'sid'\n | 'srsltid'\n | 'uid'\n | 'user_id'\n | 'userid'\n | `_${string}`\n | `firebase_${string}`\n | `ga_${string}`\n | `google_${string}`\n | `gtag.${string}`;\n\nexport type ReservedUserPropertiesNames =\n | 'cid'\n | 'customer_id'\n | 'customerid'\n | 'first_open_after_install'\n | 'first_open_time'\n | 'first_visit_time'\n | 'google_allow_ad_personalization_signals'\n | 'last_advertising_id_reset'\n | 'last_deep_link_referrer'\n | 'last_gclid'\n | 'lifetime_user_engagement'\n | 'non_personalized_ads'\n | 'session_id'\n | 'session_number'\n | 'sessionid'\n | 'sfmc_id'\n | 'sid'\n | 'uid'\n | 'user_id'\n | 'userid'\n | `_${string}`\n | `firebase_${string}`\n | `ga_${string}`\n | `google_${string}`;\n\nexport type UserPropertiesValue = string | boolean | number | null | undefined;\nexport type UserProperties = {\n [key: string]: UserPropertiesValue;\n} & {\n [key in ReservedUserPropertiesNames]?: never;\n};\n\n/**\n * reference: https://support.google.com/analytics/answer/14078702\n * You must turn on user-provided data collection in Google Analytics.\n *\n * Validate your user-provided data implementation: https://support.google.com/analytics/answer/14171683\n *\n * Usage: gtag('set', 'user_data', { email: 'abc@abc.com' })\n *\n * To standardize the hash results, before hashing one of these values, you must:\n *\n * - Remove leading and trailing whitespaces.\n * - Convert the text to lowercase.\n * - Format phone numbers according to the E164 standard.\n * - Remove all periods (.) that precede the domain name in gmail.com and googlemail.com email addresses.\n * */\ntype UserProvidedDataAddress = {\n first_name?: string;\n last_name?: string;\n street?: string;\n city?: string;\n /** User province, state, or region. Example: `Hampshire` */\n region?: string;\n postal_code?: string;\n /**\n * User country code.\n * Example: 'UK'. Use 2-letter country codes, per the ISO 3166-1 alpha-2 standard.\n */\n country?: string;\n};\n\n/**\n * To standardize the hash results, before hashing one of these values, you must:\n *\n * - Remove leading and trailing whitespaces.\n * - Convert the text to lowercase.\n * - Format phone numbers according to the E164 standard.\n * - Remove all periods (.) that precede the domain name in gmail.com and googlemail.com email addresses.\n */\nexport type UserProvidedData = {\n email?: string | string[];\n /**\n * User phone number. Must be in E.164 format, which means it must be 11 to 15 digits including a\n * plus sign (+) prefix and country code with no dashes, parentheses, or spaces.\n *\n * Example: ‘+11231234567’\n */\n phone_number?: string | string[];\n address?: UserProvidedDataAddress | UserProvidedDataAddress[];\n};\n\nexport type GaId = `G-${Uppercase<string>}`;\nexport type GtmId = `GTM-${Uppercase<string>}`;\n\nexport type CampaignMedium =\n | 'email'\n | 'organic'\n | 'cpc'\n | 'banner'\n | 'social'\n | 'referral'\n | 'affiliate'\n | 'video'\n | 'display'\n | 'sms'\n | 'push'\n | 'qr'\n | 'audio'\n | (string & {});\n\nexport type CampaignSource =\n | 'google' // don't need to set\n | 'googleads'\n | 'bing'\n | 'bingads'\n | 'metaads'\n | 'facebook'\n | 'twitter'\n | 'linkedin'\n | 'instagram'\n | 'tiktok'\n | 'youtube'\n | 'newsletter'\n | `website_${string}`\n | `affiliate_${string}`\n | (string & {});\n\nexport type Campaign = {\n id?: string;\n name?: string;\n term?: string;\n content?: string;\n medium?: CampaignMedium;\n source?: CampaignSource;\n};\n\nexport type Config = {\n allow_google_signals?: boolean;\n allow_ad_personalization_signals?: boolean;\n\n campaign_content?: string;\n campaign_id?: string;\n campaign_medium?: CampaignMedium;\n campaign_name?: string;\n campaign_source?: CampaignSource;\n campaign_term?: string;\n\n /**\n * @deprecated\n * Key Point: Use the campaign_ prefixed version of each campaign value instead of this field.\n */\n campaign?: Campaign;\n\n client_id?: string;\n content_group?: string;\n\n cookie_domain?: 'none' | 'auto' | string;\n cookie_expires?: number;\n cookie_flags?: string;\n cookie_path?: string;\n cookie_prefix?: string;\n cookie_update?: boolean;\n\n ignore_referrer?: boolean;\n language?: string;\n\n page_location?: string;\n page_referrer?: string;\n page_title?: string;\n\n send_page_view?: boolean;\n screen_resolution?: `${number}x${number}`;\n user_id?: string;\n user_properties?: UserProperties;\n};\n\nexport type Item = {\n item_id: string;\n item_name: string;\n affiliation?: 'Google Store' | (string & {});\n coupon?: string;\n discount?: number;\n index?: number;\n item_brand?: string;\n item_category?: string;\n item_category2?: string;\n item_category3?: string;\n item_category4?: string;\n item_category5?: string;\n item_list_id?: string;\n item_list_name?: string;\n item_variant?: string;\n location_id?: string;\n price?: number;\n quantity?: number;\n};\n\nexport type PromotionItem = {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n};\n\nexport type SurveyProperties = {\n id?: string;\n feature?: Lowercase<string>;\n trigger?: Lowercase<string>;\n};\n\nexport type SurveyQA = {\n q1: string;\n a1: string;\n q2?: string;\n a2?: string;\n q3?: string;\n a3?: string;\n q4?: string;\n a4?: string;\n q5?: string;\n a5?: string;\n q6?: string;\n a6?: string;\n completed?: boolean;\n};\n\nexport type NPSScore = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | number;\nexport type CASTScore = 1 | 2 | 3 | 4 | 5 | number;\nexport type CESScore = 1 | 2 | 3 | 4 | 5 | number;\n\n/** ref: https://support.google.com/analytics/answer/9216061 */\nexport type EnhancedMeasurementEvents = {\n page_view: {\n page_path: string;\n page_title: string;\n page_referrer?: string;\n page_location?: string;\n previous_page_path?: string;\n engagement_time_msec?: number;\n };\n // Scrolls: the first time a user reaches the bottom of each page\n // (i.e., when a 90% vertical depth becomes visible)\n scroll: {\n engagement_time_msec: number;\n };\n // Outbound clicks\n click: {\n link_id: string;\n link_url: string;\n link_text: string; // added\n link_domain: string;\n link_classes: string;\n outbound: boolean;\n };\n // Site search\n view_search_results: {\n search_term: string;\n };\n // Video engagement\n video_start: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n video_progress: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n video_complete: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n // File downloads:\n file_download: {\n file_extension: string;\n file_name: string;\n link_classes: string;\n link_id: string;\n link_text: string;\n link_url: string;\n };\n // Form interactions\n form_start: {\n form_id: string;\n form_name: string;\n form_destination: string;\n };\n form_submit: {\n form_id: string;\n form_name: string;\n form_destination: string;\n form_submit_text: string;\n };\n};\n\n/** ref: https://support.google.com/analytics/answer/9234069 */\nexport type AutomaticallyCollectedEvents = EnhancedMeasurementEvents & {\n first_visit: {\n page_path: string;\n page_title: string;\n page_referrer?: string;\n page_location?: string;\n };\n first_open: {\n screen_name: string;\n screen_class: string;\n };\n in_app_purchase: {\n product_id: string;\n price: number;\n value: number;\n currency: string;\n quantity: number;\n subscription?: boolean;\n free_trial?: boolean;\n introductory_price?: number;\n };\n notification_dismiss: {\n // Android only\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_foreground: {\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_type: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_open: {\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_receive: {\n // Android only\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_type: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n // Note: The 'screen_view' event is automatically sent only when using Android or iOS native SDKs.\n // If you are using React Native, you need to send this event manually; it should not be ignored.\n session_start: undefined;\n user_engagement: {\n engagement_time_msec: number;\n trigger: 'pagehide' | 'visibilitychange' | 'background';\n };\n};\n\n/**\n * ref: https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag\n * ref: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event\n * */\nexport type RecommendedEvents = {\n // Google Analytics 4 Recommended Events\n add_payment_info: {\n currency: string;\n value: number;\n coupon?: string;\n payment_type?: string;\n items: Item[];\n };\n add_shipping_info: {\n currency: string;\n value: number;\n coupon?: string;\n shipping_tier?: string;\n items: Item[];\n };\n add_to_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n add_to_wishlist: {\n currency: string;\n value: number;\n items: Item[];\n };\n begin_checkout: {\n currency: string;\n value: number;\n coupon?: string;\n items: Item[];\n source?: string; // added\n };\n close_convert_lead: {\n currency: string;\n value: number;\n };\n close_unconvert_lead: {\n currency: string;\n value: number;\n unconvert_lead_reason?: string;\n };\n disqualify_lead: {\n currency: string;\n value: number;\n disqualified_lead_reason?: string;\n };\n earn_virtual_currency: {\n virtual_currency_name?: string;\n value?: number;\n };\n generate_lead: {\n currency: string;\n value: number;\n lead_source?: string;\n };\n join_group: {\n group_id?: string;\n };\n level_end: {\n level_name?: string;\n success?: boolean;\n };\n level_start: {\n level_name?: string;\n };\n level_up: {\n level?: number;\n level_name?: string;\n character?: string;\n };\n login: {\n method?:\n | 'google'\n | 'apple'\n | 'facebook'\n | 'twitter'\n | 'linkedin'\n | 'github'\n | 'microsoft'\n | 'wechat'\n | 'onetap'\n | 'phone'\n | 'email'\n | (string & {});\n source?: string; // added\n };\n post_score: {\n score: number;\n level?: number;\n character?: string;\n };\n purchase: {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number; // Shipping cost associated with a transaction.\n tax?: number;\n items?: Item[];\n source?: string; // added\n };\n qualify_lead: {\n currency: string;\n value: number;\n };\n refund: {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number;\n tax?: number;\n items?: Item[];\n };\n remove_from_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n search: {\n search_term: string;\n };\n select_content: {\n content_type?: string;\n content_id?: string;\n };\n select_item: {\n item_list_id?: string;\n item_list_name?: string;\n items: Item[];\n };\n select_promotion: {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n items?: (Item & PromotionItem)[];\n };\n share: {\n method?: string;\n content_type?: string;\n item_id?: string;\n system_activity_type?: string; // added\n platform_post_id?: string; // added\n };\n sign_up: {\n method?: string;\n source?: string; // added\n };\n spend_virtual_currency: {\n value: number;\n virtual_currency_name: string;\n item_name?: string;\n };\n tutorial_begin: undefined;\n tutorial_complete: undefined;\n unlock_achievement: {\n achievement_id: string;\n };\n view_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n view_item: {\n currency: string;\n value: number;\n items: Item[];\n };\n view_item_list: {\n currency: string;\n item_list_id?: string;\n item_list_name?: string;\n items: Item[];\n };\n view_promotion: {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n items: (Item & PromotionItem)[];\n };\n working_lead: {\n currency: string;\n value: number;\n lead_status?: string;\n };\n\n // Firebase Analytics events, event_name 40 characters limit, 25 parameters limit\n ad_impression: {\n value?: number;\n currency?: string;\n ad_format?: string;\n ad_platform?: string;\n ad_source?: string;\n ad_unit_name?: string;\n };\n app_open: undefined;\n campaign_details: {\n source: string;\n medium: string;\n campaign: string;\n term?: string;\n content?: string;\n aclid?: string;\n cp1?: string;\n };\n screen_view: {\n screen_name?: string;\n screen_class?: string;\n previous_screen_class?: string; // added\n engagement_time_msec?: number;\n };\n};\n\n/**\n * Extra commonly used events that are NOT part of the GA standard event set.\n * Added to satisfy additional analytics needs.\n */\nexport type AddedRecommendedEvents = {\n logout: undefined;\n trial_begin: {\n currency: string;\n value: number;\n source?: string; // added\n items?: Item[]; // added, the plan(s) enrolled (first item's id maps to OpenAI plan_id)\n };\n subscribe: {\n currency: string;\n value: number;\n source?: string; // added\n items?: Item[]; // added, the plan(s) enrolled (first item's id maps to OpenAI plan_id)\n };\n // survey\n survey_shown: SurveyProperties;\n survey_sent: SurveyProperties & SurveyQA;\n survey_dismissed: SurveyProperties;\n /**\n * Net promoter score: Get an industry-recognized benchmark\n * How likely are you to recommend us to a friend?\n * */\n nps_shown: SurveyProperties;\n nps_sent: SurveyProperties & { score: NPSScore; feedback?: string };\n nps_dismissed: SurveyProperties;\n /**\n * Customer satisfaction score: Works best after a checkout or support flow\n * How satisfied are you with xxx?\n * */\n cast_shown: SurveyProperties;\n cast_sent: SurveyProperties & { score: CASTScore; feedback?: string };\n cast_dismissed: SurveyProperties;\n /**\n * Customer effort score: Works well with churn surveys\n * How easy is it to use the feature?\n * */\n ces_shown: SurveyProperties;\n ces_sent: SurveyProperties & { score: CESScore; feedback?: string };\n ces_dismissed: SurveyProperties;\n};\n\nexport type StandardEvents = AutomaticallyCollectedEvents &\n RecommendedEvents &\n AddedRecommendedEvents;\n\n/**\n * reference: https://developers.google.com/analytics/devguides/collection/ga4/reference/config\n * reference: https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag\n */\nexport interface Gtag {\n /**\n * To disable advertising features based on third-party advertising identifiers, set\n * allow_google_signals to false.\n *\n * @param event\n * @param option\n * @param allow - Whether to allow Google signals.\n * @default true\n */\n gtag(event: 'set', option: 'allow_google_signals', allow: boolean): void;\n\n /**\n * Set to false to disable advertising personalization features.\n *\n * @param event\n * @param option\n * @param allow - Whether to allow ad personalization signals.\n * @default true\n */\n gtag(event: 'set', option: 'allow_ad_personalization_signals', allow: boolean): void;\n\n /**\n * Used for A/B testing and content-targeted ads. Use campaign_content to differentiate ads or\n * links that point to the same URL.\n */\n gtag(event: 'set', option: 'campaign_content', content: string): void;\n\n /**\n * Used to identify which campaign this referral references. Use campaign_id to identify a\n * specific campaign.\n */\n gtag(event: 'set', option: 'campaign_id', id: string): void;\n\n /** Use campaign_medium to identify a medium such as email or cost-per-click. */\n gtag(event: 'set', option: 'campaign_medium', medium: CampaignMedium): void;\n\n /**\n * Used for keyword analysis. Use campaign_name to identify a specific product promotion or\n * strategic campaign.\n */\n gtag(event: 'set', option: 'campaign_name', name: string): void;\n\n /** Use campaign_source to identify a search engine, newsletter name, or other source. */\n gtag(event: 'set', option: 'campaign_source', source: CampaignSource): void;\n\n /** Used for paid search. Use campaign_term to note the keywords for this ad. */\n gtag(event: 'set', option: 'campaign_term', term: string): void;\n\n /**\n * @deprecated\n * Key Point: Use the campaign_ prefixed version of each campaign value instead of this field.\n */\n gtag(event: 'set', option: 'campaign', params: Campaign): void;\n\n /**\n * Pseudonymously identifies a browser instance. By default, this value is stored as part of the\n * first-party Analytics cookie with a two-year expiration.\n */\n gtag(event: 'set', option: 'client_id', id: string): void;\n\n /** example: gtag('set', 'content_group', '/news/sports'); */\n gtag(event: 'set', option: 'content_group', group: string): void;\n\n /**\n * Specifies the domain used to store the analytics cookie.\n * Set to 'none' to set the cookie without specifying a domain.\n * Set to 'auto' (the default value) to set the cookie to the top level domain plus one\n * subdomain (eTLD +1). For example, if cookie_domain is set to 'auto' https://example.com would\n * use example.com for the domain, and https://subdomain.example.com would also use example.com\n * for the domain.\n *\n * @param event\n * @param option\n * @param domain - The domain used to store the analytics cookie.\n * @default 'auto'\n */\n gtag(event: 'set', option: 'cookie_domain', domain: 'none' | 'auto' | string): void;\n\n /**\n * Every time a hit is sent to Google Analytics, the cookie expiration time is updated to be the\n * current time plus the value of the cookie_expires field. This means that if you use the default\n * value time of two years (63,072,000 seconds), and a user visits your site every month, their\n * cookie will never expire.\n *\n * If you set the cookie_expires time to 0 (zero) seconds, the cookie turns into a session-based\n * cookie and expires once the current browser session ends.\n *\n * Caution: If you set the cookie to expire too quickly, you will inflate your user count and\n * decrease the quality of your measurement.\n *\n * @param event\n * @param option\n * @param expires - The number of seconds until the cookie expires.\n * @default 63072000\n */\n gtag(event: 'set', option: 'cookie_expires', expires: number): void;\n\n /**\n * Appends additional flags to the cookie when set. Flags must be separated by semicolons. See\n * [write a new cookie](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#write_a_new_cookie) for some examples of flags to set.\n */\n gtag(event: 'set', option: 'cookie_flags', flags: string): void;\n\n /** Specifies the subpath used to store the analytics cookie. */\n gtag(event: 'set', option: 'cookie_path', path: string): void;\n\n /** Specifies a prefix to prepend to analytics cookie names. */\n gtag(event: 'set', option: 'cookie_prefix', prefix: string): void;\n\n /**\n * When cookie_update is set to true, gtag.js will update cookies on each page load. This will\n * update the cookie expiration to be set relative to the most recent visit to the site. For\n * example, if cookie expiration is set to one week, and a user visits using the same browser\n * every five days, the cookie expiration will be updated on each visit and will effectively\n * never expire.\n *\n * When set to false, cookies are not updated on each page load. This has the effect of cookie\n * expiration being relative to the first time a user visited the site.\n *\n * @param event\n * @param option\n * @param update - Whether to update the cookie on each page load.\n * @default true\n */\n gtag(event: 'set', option: 'cookie_update', update: boolean): void;\n\n /**\n * Set to true to indicate to Analytics that the referrer shouldn't be displayed as a traffic\n * source. [Learn when to use this field](https://support.google.com/analytics/answer/10327750#set-parameter)\n *\n * @param event\n * @param option\n * @param ignore - Whether to ignore the referrer.\n * @default false\n */\n gtag(event: 'set', option: 'ignore_referrer', ignore: boolean): void;\n\n /**\n * Specifies the language preference of the user. Defaults to the user's `navigator.language` value.\n *\n * @param event\n * @param option\n * @param language - The language preference of the user.\n * @default navigator.language\n */\n gtag(event: 'set', option: 'language', language: string): void;\n\n /**\n * Specifies the full URL of the page. Defaults to the user's `document.location` value.\n *\n * @param event\n * @param option\n * @param location - The full URL of the page. Character limit 1000\n * @default document.location\n */\n gtag(event: 'set', option: 'page_location', location: string): void;\n\n /**\n * Specifies which referral source brought traffic to a page. This value is also used to compute\n * the traffic source. The format of this value is a URL. Defaults to the user's `document.referrer`\n * value.\n *\n * @param event\n * @param option\n * @param referrer - The referral source. Character limit 420\n * @default document.referrer\n */\n gtag(event: 'set', option: 'page_referrer', referrer: string): void;\n\n /**\n * The title of the page or document. Defaults to the user's `document.title` value.\n *\n * @param event\n * @param option\n * @param title - The title of the page or document. Character limit 300\n * @default document.title\n */\n gtag(event: 'set', option: 'page_title', title: string): void;\n\n /**\n * Set to false to prevent the default snippet from sending a page_view.\n *\n * @param event\n * @param option\n * @param send - Whether to send a page_view.\n * @default true\n */\n gtag(event: 'set', option: 'send_page_view', send: boolean): void;\n\n /**\n * Specifies the resolution of the screen. Should be two positive integers separated by an x. For\n * example, for an 800px by 600px screen, the value would be 800x600. Calculated from the user's\n * window.screen value.\n *\n * @param event\n * @param option\n * @param resolution - The resolution of the screen.\n * @default window.screen\n */\n gtag(event: 'set', option: 'screen_resolution', resolution: `${number}x${number}`): void;\n\n /**\n * Specifies a known identifier for a user provided by the site owner/library user. It must not\n * itself be PII (personally identifiable information). The value should never be persisted in\n * Google Analytics cookies or other Analytics-provided storage.\n *\n * @param event\n * @param option\n * @param userId - The user ID. Character limit 256\n */\n gtag(event: 'set', option: 'user_id', userId: string): void;\n\n /**\n * User properties are attributes that can be used to describe segments of your user base, such\n * as language preference or geographic location. Up to 25 additional user properties can be set\n * per project.\n *\n * @param event\n * @param option\n * @param properties\n */\n gtag(event: 'set', option: 'user_properties', properties: UserProperties): void;\n\n gtag(event: 'set', option: 'user_data', data: UserProvidedData): void;\n\n /**\n * gtag('config', ...) Set for a single stream\n * gtag('set', ...) Set globally\n */\n gtag(event: 'config', gaId: GaId, config?: Config): void;\n\n gtag<T extends string>(\n event: 'event',\n eventName: T extends keyof StandardEvents ? T : string,\n eventParams?: T extends keyof StandardEvents\n ? StandardEvents[T]\n : Record<string, string | number | boolean | null | undefined>\n ): void;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,qBAAqB,CAAC,GAAG,uBAAuB,GAAG,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../src/track/gtag.ts"],"sourcesContent":["/** reference: https://support.google.com/analytics/answer/13316687 */\nconst reservedWebEventNames = [\n 'app_remove',\n 'app_store_refund',\n 'app_store_subscription_cancel',\n 'app_store_subscription_renew',\n 'click',\n 'error',\n 'file_download',\n 'first_open',\n 'first_visit',\n 'form_start',\n 'form_submit',\n 'in_app_purchase',\n 'page_view',\n 'scroll',\n 'session_start',\n 'user_engagement',\n 'view_complete',\n 'video_progress',\n 'video_start',\n 'view_search_results',\n] as const;\n\nconst reservedAppEventNames = [\n 'ad_activeview',\n 'ad_click',\n 'ad_exposure',\n 'ad_impression',\n 'ad_query',\n 'ad_reward',\n 'adunit_exposure',\n 'app_clear_data',\n 'app_exception',\n 'app_install',\n 'app_remove',\n 'app_store_refund',\n 'app_update',\n 'app_upgrade',\n 'dynamic_link_app_open',\n 'dynamic_link_app_update',\n 'dynamic_link_first_open',\n 'error',\n 'firebase_campaign',\n 'firebase_in_app_message_action',\n 'firebase_in_app_message_dismiss',\n 'firebase_in_app_message_impression',\n 'first_open',\n 'first_visit',\n 'in_app_purchase',\n 'notification_dismiss',\n 'notification_foreground',\n 'notification_open',\n 'notification_receive',\n 'notification_send',\n 'os_update',\n 'screen_view',\n 'session_start',\n 'user_engagement',\n] as const;\n\nexport const reservedEventNames = [...reservedWebEventNames, ...reservedAppEventNames] as const;\n\ntype ReservedWebEventNames = (typeof reservedWebEventNames)[number];\ntype ReservedAppEventNames = (typeof reservedAppEventNames)[number];\nexport type ReservedEventNames = ReservedWebEventNames | ReservedAppEventNames;\n\nexport type ReservedEventValues =\n | 'cid'\n | 'currency'\n | 'customer_id'\n | 'customerid'\n | 'dclid'\n | 'gclid'\n | 'session_id'\n | 'sessionid'\n | 'sfmc_id'\n | 'sid'\n | 'srsltid'\n | 'uid'\n | 'user_id'\n | 'userid'\n | `_${string}`\n | `firebase_${string}`\n | `ga_${string}`\n | `google_${string}`\n | `gtag.${string}`;\n\nexport type ReservedUserPropertiesNames =\n | 'cid'\n | 'customer_id'\n | 'customerid'\n | 'first_open_after_install'\n | 'first_open_time'\n | 'first_visit_time'\n | 'google_allow_ad_personalization_signals'\n | 'last_advertising_id_reset'\n | 'last_deep_link_referrer'\n | 'last_gclid'\n | 'lifetime_user_engagement'\n | 'non_personalized_ads'\n | 'session_id'\n | 'session_number'\n | 'sessionid'\n | 'sfmc_id'\n | 'sid'\n | 'uid'\n | 'user_id'\n | 'userid'\n | `_${string}`\n | `firebase_${string}`\n | `ga_${string}`\n | `google_${string}`;\n\nexport type UserPropertiesValue = string | boolean | number | null | undefined;\nexport type UserProperties = {\n [key: string]: UserPropertiesValue;\n} & {\n [key in ReservedUserPropertiesNames]?: never;\n};\n\n/**\n * reference: https://support.google.com/analytics/answer/14078702\n * You must turn on user-provided data collection in Google Analytics.\n *\n * Validate your user-provided data implementation: https://support.google.com/analytics/answer/14171683\n *\n * Usage: gtag('set', 'user_data', { email: 'abc@abc.com' })\n *\n * To standardize the hash results, before hashing one of these values, you must:\n *\n * - Remove leading and trailing whitespaces.\n * - Convert the text to lowercase.\n * - Format phone numbers according to the E164 standard.\n * - Remove all periods (.) that precede the domain name in gmail.com and googlemail.com email addresses.\n * */\ntype UserProvidedDataAddress = {\n first_name?: string;\n last_name?: string;\n street?: string;\n city?: string;\n /** User province, state, or region. Example: `Hampshire` */\n region?: string;\n postal_code?: string;\n /**\n * User country code.\n * Example: 'UK'. Use 2-letter country codes, per the ISO 3166-1 alpha-2 standard.\n */\n country?: string;\n};\n\n/**\n * To standardize the hash results, before hashing one of these values, you must:\n *\n * - Remove leading and trailing whitespaces.\n * - Convert the text to lowercase.\n * - Format phone numbers according to the E164 standard.\n * - Remove all periods (.) that precede the domain name in gmail.com and googlemail.com email addresses.\n */\nexport type UserProvidedData = {\n email?: string | string[];\n /**\n * User phone number. Must be in E.164 format, which means it must be 11 to 15 digits including a\n * plus sign (+) prefix and country code with no dashes, parentheses, or spaces.\n *\n * Example: ‘+11231234567’\n */\n phone_number?: string | string[];\n address?: UserProvidedDataAddress | UserProvidedDataAddress[];\n};\n\nexport type GaId = `G-${Uppercase<string>}`;\nexport type GtmId = `GTM-${Uppercase<string>}`;\n\nexport type CampaignMedium =\n | 'email'\n | 'organic'\n | 'cpc'\n | 'banner'\n | 'social'\n | 'referral'\n | 'affiliate'\n | 'video'\n | 'display'\n | 'sms'\n | 'push'\n | 'qr'\n | 'audio'\n | (string & {});\n\nexport type CampaignSource =\n | 'google' // don't need to set\n | 'googleads'\n | 'bing'\n | 'bingads'\n | 'metaads'\n | 'facebook'\n | 'twitter'\n | 'linkedin'\n | 'instagram'\n | 'tiktok'\n | 'youtube'\n | 'newsletter'\n | `website_${string}`\n | `affiliate_${string}`\n | (string & {});\n\nexport type Campaign = {\n id?: string;\n name?: string;\n term?: string;\n content?: string;\n medium?: CampaignMedium;\n source?: CampaignSource;\n};\n\nexport type Config = {\n allow_google_signals?: boolean;\n allow_ad_personalization_signals?: boolean;\n\n campaign_content?: string;\n campaign_id?: string;\n campaign_medium?: CampaignMedium;\n campaign_name?: string;\n campaign_source?: CampaignSource;\n campaign_term?: string;\n\n /**\n * @deprecated\n * Key Point: Use the campaign_ prefixed version of each campaign value instead of this field.\n */\n campaign?: Campaign;\n\n client_id?: string;\n content_group?: string;\n\n cookie_domain?: 'none' | 'auto' | (string & {});\n cookie_expires?: number;\n cookie_flags?: string;\n cookie_path?: string;\n cookie_prefix?: string;\n cookie_update?: boolean;\n\n ignore_referrer?: boolean;\n language?: string;\n\n page_location?: string;\n page_referrer?: string;\n page_title?: string;\n\n send_page_view?: boolean;\n screen_resolution?: `${number}x${number}`;\n user_id?: string;\n user_properties?: UserProperties;\n};\n\nexport type Item = {\n item_id: string;\n item_name: string;\n affiliation?: 'Google Store' | (string & {});\n coupon?: string;\n discount?: number;\n index?: number;\n item_brand?: string;\n item_category?: string;\n item_category2?: string;\n item_category3?: string;\n item_category4?: string;\n item_category5?: string;\n item_list_id?: string;\n item_list_name?: string;\n item_variant?: string;\n location_id?: string;\n price?: number;\n quantity?: number;\n};\n\nexport type PromotionItem = {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n};\n\nexport type SurveyProperties = {\n id?: string;\n feature?: Lowercase<string>;\n trigger?: Lowercase<string>;\n};\n\nexport type SurveyQA = {\n q1: string;\n a1: string;\n q2?: string;\n a2?: string;\n q3?: string;\n a3?: string;\n q4?: string;\n a4?: string;\n q5?: string;\n a5?: string;\n q6?: string;\n a6?: string;\n completed?: boolean;\n};\n\nexport type NPSScore = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | (number & {});\nexport type CASTScore = 1 | 2 | 3 | 4 | 5 | (number & {});\nexport type CESScore = 1 | 2 | 3 | 4 | 5 | (number & {});\n\n/** ref: https://support.google.com/analytics/answer/9216061 */\nexport type EnhancedMeasurementEvents = {\n page_view: {\n page_path: string;\n page_title: string;\n page_referrer?: string;\n page_location?: string;\n previous_page_path?: string;\n engagement_time_msec?: number;\n };\n // Scrolls: the first time a user reaches the bottom of each page\n // (i.e., when a 90% vertical depth becomes visible)\n scroll: {\n engagement_time_msec: number;\n };\n // Outbound clicks\n click: {\n link_id: string;\n link_url: string;\n link_text: string; // added\n link_domain: string;\n link_classes: string;\n outbound: boolean;\n };\n // Site search\n view_search_results: {\n search_term: string;\n };\n // Video engagement\n video_start: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n video_progress: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n video_complete: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n // File downloads:\n file_download: {\n file_extension: string;\n file_name: string;\n link_classes: string;\n link_id: string;\n link_text: string;\n link_url: string;\n };\n // Form interactions\n form_start: {\n form_id: string;\n form_name: string;\n form_destination: string;\n };\n form_submit: {\n form_id: string;\n form_name: string;\n form_destination: string;\n form_submit_text: string;\n };\n};\n\n/** ref: https://support.google.com/analytics/answer/9234069 */\nexport type AutomaticallyCollectedEvents = EnhancedMeasurementEvents & {\n first_visit: {\n page_path: string;\n page_title: string;\n page_referrer?: string;\n page_location?: string;\n };\n first_open: {\n screen_name: string;\n screen_class: string;\n };\n in_app_purchase: {\n product_id: string;\n price: number;\n value: number;\n currency: string;\n quantity: number;\n subscription?: boolean;\n free_trial?: boolean;\n introductory_price?: number;\n };\n notification_dismiss: {\n // Android only\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_foreground: {\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_type: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_open: {\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_receive: {\n // Android only\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_type: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n // Note: The 'screen_view' event is automatically sent only when using Android or iOS native SDKs.\n // If you are using React Native, you need to send this event manually; it should not be ignored.\n session_start: undefined;\n user_engagement: {\n engagement_time_msec: number;\n trigger: 'pagehide' | 'visibilitychange' | 'background';\n };\n};\n\n/**\n * ref: https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag\n * ref: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event\n * */\nexport type RecommendedEvents = {\n // Google Analytics 4 Recommended Events\n add_payment_info: {\n currency: string;\n value: number;\n coupon?: string;\n payment_type?: string;\n items: Item[];\n };\n add_shipping_info: {\n currency: string;\n value: number;\n coupon?: string;\n shipping_tier?: string;\n items: Item[];\n };\n add_to_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n add_to_wishlist: {\n currency: string;\n value: number;\n items: Item[];\n };\n begin_checkout: {\n currency: string;\n value: number;\n coupon?: string;\n items: Item[];\n source?: string; // added\n };\n close_convert_lead: {\n currency: string;\n value: number;\n };\n close_unconvert_lead: {\n currency: string;\n value: number;\n unconvert_lead_reason?: string;\n };\n disqualify_lead: {\n currency: string;\n value: number;\n disqualified_lead_reason?: string;\n };\n earn_virtual_currency: {\n virtual_currency_name?: string;\n value?: number;\n };\n generate_lead: {\n currency: string;\n value: number;\n lead_source?: string;\n };\n join_group: {\n group_id?: string;\n };\n level_end: {\n level_name?: string;\n success?: boolean;\n };\n level_start: {\n level_name?: string;\n };\n level_up: {\n level?: number;\n level_name?: string;\n character?: string;\n };\n login: {\n method?:\n | 'google'\n | 'apple'\n | 'facebook'\n | 'twitter'\n | 'linkedin'\n | 'github'\n | 'microsoft'\n | 'wechat'\n | 'onetap'\n | 'phone'\n | 'email'\n | (string & {});\n source?: string; // added\n };\n post_score: {\n score: number;\n level?: number;\n character?: string;\n };\n purchase: {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number; // Shipping cost associated with a transaction.\n tax?: number;\n items?: Item[];\n source?: string; // added\n };\n qualify_lead: {\n currency: string;\n value: number;\n };\n refund: {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number;\n tax?: number;\n items?: Item[];\n };\n remove_from_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n search: {\n search_term: string;\n };\n select_content: {\n content_type?: string;\n content_id?: string;\n };\n select_item: {\n item_list_id?: string;\n item_list_name?: string;\n items: Item[];\n };\n select_promotion: {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n items?: (Item & PromotionItem)[];\n };\n share: {\n method?: string;\n content_type?: string;\n item_id?: string;\n system_activity_type?: string; // added\n platform_post_id?: string; // added\n };\n sign_up: {\n method?: string;\n source?: string; // added\n };\n spend_virtual_currency: {\n value: number;\n virtual_currency_name: string;\n item_name?: string;\n };\n tutorial_begin: undefined;\n tutorial_complete: undefined;\n unlock_achievement: {\n achievement_id: string;\n };\n view_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n view_item: {\n currency: string;\n value: number;\n items: Item[];\n };\n view_item_list: {\n currency: string;\n item_list_id?: string;\n item_list_name?: string;\n items: Item[];\n };\n view_promotion: {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n items: (Item & PromotionItem)[];\n };\n working_lead: {\n currency: string;\n value: number;\n lead_status?: string;\n };\n\n // Firebase Analytics events, event_name 40 characters limit, 25 parameters limit\n ad_impression: {\n value?: number;\n currency?: string;\n ad_format?: string;\n ad_platform?: string;\n ad_source?: string;\n ad_unit_name?: string;\n };\n app_open: undefined;\n campaign_details: {\n source: string;\n medium: string;\n campaign: string;\n term?: string;\n content?: string;\n aclid?: string;\n cp1?: string;\n };\n screen_view: {\n screen_name?: string;\n screen_class?: string;\n previous_screen_class?: string; // added\n engagement_time_msec?: number;\n };\n};\n\n/**\n * Extra commonly used events that are NOT part of the GA standard event set.\n * Added to satisfy additional analytics needs.\n */\nexport type AddedRecommendedEvents = {\n logout: undefined;\n trial_begin: {\n currency: string;\n value: number;\n source?: string; // added\n items?: Item[]; // added, the plan(s) enrolled (first item's id maps to OpenAI plan_id)\n };\n subscribe: {\n currency: string;\n value: number;\n source?: string; // added\n items?: Item[]; // added, the plan(s) enrolled (first item's id maps to OpenAI plan_id)\n };\n // survey\n survey_shown: SurveyProperties;\n survey_sent: SurveyProperties & SurveyQA;\n survey_dismissed: SurveyProperties;\n /**\n * Net promoter score: Get an industry-recognized benchmark\n * How likely are you to recommend us to a friend?\n * */\n nps_shown: SurveyProperties;\n nps_sent: SurveyProperties & { score: NPSScore; feedback?: string };\n nps_dismissed: SurveyProperties;\n /**\n * Customer satisfaction score: Works best after a checkout or support flow\n * How satisfied are you with xxx?\n * */\n cast_shown: SurveyProperties;\n cast_sent: SurveyProperties & { score: CASTScore; feedback?: string };\n cast_dismissed: SurveyProperties;\n /**\n * Customer effort score: Works well with churn surveys\n * How easy is it to use the feature?\n * */\n ces_shown: SurveyProperties;\n ces_sent: SurveyProperties & { score: CESScore; feedback?: string };\n ces_dismissed: SurveyProperties;\n};\n\nexport type StandardEvents = AutomaticallyCollectedEvents &\n RecommendedEvents &\n AddedRecommendedEvents;\n\n/**\n * reference: https://developers.google.com/analytics/devguides/collection/ga4/reference/config\n * reference: https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag\n */\nexport interface Gtag {\n /**\n * To disable advertising features based on third-party advertising identifiers, set\n * allow_google_signals to false.\n *\n * @param event\n * @param option\n * @param allow - Whether to allow Google signals.\n * @default true\n */\n gtag(event: 'set', option: 'allow_google_signals', allow: boolean): void;\n\n /**\n * Set to false to disable advertising personalization features.\n *\n * @param event\n * @param option\n * @param allow - Whether to allow ad personalization signals.\n * @default true\n */\n gtag(event: 'set', option: 'allow_ad_personalization_signals', allow: boolean): void;\n\n /**\n * Used for A/B testing and content-targeted ads. Use campaign_content to differentiate ads or\n * links that point to the same URL.\n */\n gtag(event: 'set', option: 'campaign_content', content: string): void;\n\n /**\n * Used to identify which campaign this referral references. Use campaign_id to identify a\n * specific campaign.\n */\n gtag(event: 'set', option: 'campaign_id', id: string): void;\n\n /** Use campaign_medium to identify a medium such as email or cost-per-click. */\n gtag(event: 'set', option: 'campaign_medium', medium: CampaignMedium): void;\n\n /**\n * Used for keyword analysis. Use campaign_name to identify a specific product promotion or\n * strategic campaign.\n */\n gtag(event: 'set', option: 'campaign_name', name: string): void;\n\n /** Use campaign_source to identify a search engine, newsletter name, or other source. */\n gtag(event: 'set', option: 'campaign_source', source: CampaignSource): void;\n\n /** Used for paid search. Use campaign_term to note the keywords for this ad. */\n gtag(event: 'set', option: 'campaign_term', term: string): void;\n\n /**\n * @deprecated\n * Key Point: Use the campaign_ prefixed version of each campaign value instead of this field.\n */\n gtag(event: 'set', option: 'campaign', params: Campaign): void;\n\n /**\n * Pseudonymously identifies a browser instance. By default, this value is stored as part of the\n * first-party Analytics cookie with a two-year expiration.\n */\n gtag(event: 'set', option: 'client_id', id: string): void;\n\n /** example: gtag('set', 'content_group', '/news/sports'); */\n gtag(event: 'set', option: 'content_group', group: string): void;\n\n /**\n * Specifies the domain used to store the analytics cookie.\n * Set to 'none' to set the cookie without specifying a domain.\n * Set to 'auto' (the default value) to set the cookie to the top level domain plus one\n * subdomain (eTLD +1). For example, if cookie_domain is set to 'auto' https://example.com would\n * use example.com for the domain, and https://subdomain.example.com would also use example.com\n * for the domain.\n *\n * @param event\n * @param option\n * @param domain - The domain used to store the analytics cookie.\n * @default 'auto'\n */\n gtag(event: 'set', option: 'cookie_domain', domain: 'none' | 'auto' | (string & {})): void;\n\n /**\n * Every time a hit is sent to Google Analytics, the cookie expiration time is updated to be the\n * current time plus the value of the cookie_expires field. This means that if you use the default\n * value time of two years (63,072,000 seconds), and a user visits your site every month, their\n * cookie will never expire.\n *\n * If you set the cookie_expires time to 0 (zero) seconds, the cookie turns into a session-based\n * cookie and expires once the current browser session ends.\n *\n * Caution: If you set the cookie to expire too quickly, you will inflate your user count and\n * decrease the quality of your measurement.\n *\n * @param event\n * @param option\n * @param expires - The number of seconds until the cookie expires.\n * @default 63072000\n */\n gtag(event: 'set', option: 'cookie_expires', expires: number): void;\n\n /**\n * Appends additional flags to the cookie when set. Flags must be separated by semicolons. See\n * [write a new cookie](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#write_a_new_cookie) for some examples of flags to set.\n */\n gtag(event: 'set', option: 'cookie_flags', flags: string): void;\n\n /** Specifies the subpath used to store the analytics cookie. */\n gtag(event: 'set', option: 'cookie_path', path: string): void;\n\n /** Specifies a prefix to prepend to analytics cookie names. */\n gtag(event: 'set', option: 'cookie_prefix', prefix: string): void;\n\n /**\n * When cookie_update is set to true, gtag.js will update cookies on each page load. This will\n * update the cookie expiration to be set relative to the most recent visit to the site. For\n * example, if cookie expiration is set to one week, and a user visits using the same browser\n * every five days, the cookie expiration will be updated on each visit and will effectively\n * never expire.\n *\n * When set to false, cookies are not updated on each page load. This has the effect of cookie\n * expiration being relative to the first time a user visited the site.\n *\n * @param event\n * @param option\n * @param update - Whether to update the cookie on each page load.\n * @default true\n */\n gtag(event: 'set', option: 'cookie_update', update: boolean): void;\n\n /**\n * Set to true to indicate to Analytics that the referrer shouldn't be displayed as a traffic\n * source. [Learn when to use this field](https://support.google.com/analytics/answer/10327750#set-parameter)\n *\n * @param event\n * @param option\n * @param ignore - Whether to ignore the referrer.\n * @default false\n */\n gtag(event: 'set', option: 'ignore_referrer', ignore: boolean): void;\n\n /**\n * Specifies the language preference of the user. Defaults to the user's `navigator.language` value.\n *\n * @param event\n * @param option\n * @param language - The language preference of the user.\n * @default navigator.language\n */\n gtag(event: 'set', option: 'language', language: string): void;\n\n /**\n * Specifies the full URL of the page. Defaults to the user's `document.location` value.\n *\n * @param event\n * @param option\n * @param location - The full URL of the page. Character limit 1000\n * @default document.location\n */\n gtag(event: 'set', option: 'page_location', location: string): void;\n\n /**\n * Specifies which referral source brought traffic to a page. This value is also used to compute\n * the traffic source. The format of this value is a URL. Defaults to the user's `document.referrer`\n * value.\n *\n * @param event\n * @param option\n * @param referrer - The referral source. Character limit 420\n * @default document.referrer\n */\n gtag(event: 'set', option: 'page_referrer', referrer: string): void;\n\n /**\n * The title of the page or document. Defaults to the user's `document.title` value.\n *\n * @param event\n * @param option\n * @param title - The title of the page or document. Character limit 300\n * @default document.title\n */\n gtag(event: 'set', option: 'page_title', title: string): void;\n\n /**\n * Set to false to prevent the default snippet from sending a page_view.\n *\n * @param event\n * @param option\n * @param send - Whether to send a page_view.\n * @default true\n */\n gtag(event: 'set', option: 'send_page_view', send: boolean): void;\n\n /**\n * Specifies the resolution of the screen. Should be two positive integers separated by an x. For\n * example, for an 800px by 600px screen, the value would be 800x600. Calculated from the user's\n * window.screen value.\n *\n * @param event\n * @param option\n * @param resolution - The resolution of the screen.\n * @default window.screen\n */\n gtag(event: 'set', option: 'screen_resolution', resolution: `${number}x${number}`): void;\n\n /**\n * Specifies a known identifier for a user provided by the site owner/library user. It must not\n * itself be PII (personally identifiable information). The value should never be persisted in\n * Google Analytics cookies or other Analytics-provided storage.\n *\n * @param event\n * @param option\n * @param userId - The user ID. Character limit 256\n */\n gtag(event: 'set', option: 'user_id', userId: string): void;\n\n /**\n * User properties are attributes that can be used to describe segments of your user base, such\n * as language preference or geographic location. Up to 25 additional user properties can be set\n * per project.\n *\n * @param event\n * @param option\n * @param properties\n */\n gtag(event: 'set', option: 'user_properties', properties: UserProperties): void;\n\n gtag(event: 'set', option: 'user_data', data: UserProvidedData): void;\n\n /**\n * gtag('config', ...) Set for a single stream\n * gtag('set', ...) Set globally\n */\n gtag(event: 'config', gaId: GaId, config?: Config): void;\n\n gtag<T extends string>(\n event: 'event',\n eventName: T extends keyof StandardEvents ? T : string,\n eventParams?: T extends keyof StandardEvents\n ? StandardEvents[T]\n : Record<string, string | number | boolean | null | undefined>\n ): void;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,qBAAqB,CAAC,GAAG,uBAAuB,GAAG,qBAAqB;","names":[]}
@@ -89,7 +89,7 @@ type Config = {
89
89
  campaign?: Campaign;
90
90
  client_id?: string;
91
91
  content_group?: string;
92
- cookie_domain?: 'none' | 'auto' | string;
92
+ cookie_domain?: 'none' | 'auto' | (string & {});
93
93
  cookie_expires?: number;
94
94
  cookie_flags?: string;
95
95
  cookie_path?: string;
@@ -151,9 +151,9 @@ type SurveyQA = {
151
151
  a6?: string;
152
152
  completed?: boolean;
153
153
  };
154
- type NPSScore = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | number;
155
- type CASTScore = 1 | 2 | 3 | 4 | 5 | number;
156
- type CESScore = 1 | 2 | 3 | 4 | 5 | number;
154
+ type NPSScore = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | (number & {});
155
+ type CASTScore = 1 | 2 | 3 | 4 | 5 | (number & {});
156
+ type CESScore = 1 | 2 | 3 | 4 | 5 | (number & {});
157
157
  /** ref: https://support.google.com/analytics/answer/9216061 */
158
158
  type EnhancedMeasurementEvents = {
159
159
  page_view: {
@@ -618,7 +618,7 @@ interface Gtag {
618
618
  * @param domain - The domain used to store the analytics cookie.
619
619
  * @default 'auto'
620
620
  */
621
- gtag(event: 'set', option: 'cookie_domain', domain: 'none' | 'auto' | string): void;
621
+ gtag(event: 'set', option: 'cookie_domain', domain: 'none' | 'auto' | (string & {})): void;
622
622
  /**
623
623
  * Every time a hit is sent to Google Analytics, the cookie expiration time is updated to be the
624
624
  * current time plus the value of the cookie_expires field. This means that if you use the default
@@ -89,7 +89,7 @@ type Config = {
89
89
  campaign?: Campaign;
90
90
  client_id?: string;
91
91
  content_group?: string;
92
- cookie_domain?: 'none' | 'auto' | string;
92
+ cookie_domain?: 'none' | 'auto' | (string & {});
93
93
  cookie_expires?: number;
94
94
  cookie_flags?: string;
95
95
  cookie_path?: string;
@@ -151,9 +151,9 @@ type SurveyQA = {
151
151
  a6?: string;
152
152
  completed?: boolean;
153
153
  };
154
- type NPSScore = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | number;
155
- type CASTScore = 1 | 2 | 3 | 4 | 5 | number;
156
- type CESScore = 1 | 2 | 3 | 4 | 5 | number;
154
+ type NPSScore = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | (number & {});
155
+ type CASTScore = 1 | 2 | 3 | 4 | 5 | (number & {});
156
+ type CESScore = 1 | 2 | 3 | 4 | 5 | (number & {});
157
157
  /** ref: https://support.google.com/analytics/answer/9216061 */
158
158
  type EnhancedMeasurementEvents = {
159
159
  page_view: {
@@ -618,7 +618,7 @@ interface Gtag {
618
618
  * @param domain - The domain used to store the analytics cookie.
619
619
  * @default 'auto'
620
620
  */
621
- gtag(event: 'set', option: 'cookie_domain', domain: 'none' | 'auto' | string): void;
621
+ gtag(event: 'set', option: 'cookie_domain', domain: 'none' | 'auto' | (string & {})): void;
622
622
  /**
623
623
  * Every time a hit is sent to Google Analytics, the cookie expiration time is updated to be the
624
624
  * current time plus the value of the cookie_expires field. This means that if you use the default
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/track/gtag.ts"],"sourcesContent":["/** reference: https://support.google.com/analytics/answer/13316687 */\nconst reservedWebEventNames = [\n 'app_remove',\n 'app_store_refund',\n 'app_store_subscription_cancel',\n 'app_store_subscription_renew',\n 'click',\n 'error',\n 'file_download',\n 'first_open',\n 'first_visit',\n 'form_start',\n 'form_submit',\n 'in_app_purchase',\n 'page_view',\n 'scroll',\n 'session_start',\n 'user_engagement',\n 'view_complete',\n 'video_progress',\n 'video_start',\n 'view_search_results',\n] as const;\n\nconst reservedAppEventNames = [\n 'ad_activeview',\n 'ad_click',\n 'ad_exposure',\n 'ad_impression',\n 'ad_query',\n 'ad_reward',\n 'adunit_exposure',\n 'app_clear_data',\n 'app_exception',\n 'app_install',\n 'app_remove',\n 'app_store_refund',\n 'app_update',\n 'app_upgrade',\n 'dynamic_link_app_open',\n 'dynamic_link_app_update',\n 'dynamic_link_first_open',\n 'error',\n 'firebase_campaign',\n 'firebase_in_app_message_action',\n 'firebase_in_app_message_dismiss',\n 'firebase_in_app_message_impression',\n 'first_open',\n 'first_visit',\n 'in_app_purchase',\n 'notification_dismiss',\n 'notification_foreground',\n 'notification_open',\n 'notification_receive',\n 'notification_send',\n 'os_update',\n 'screen_view',\n 'session_start',\n 'user_engagement',\n] as const;\n\nexport const reservedEventNames = [...reservedWebEventNames, ...reservedAppEventNames] as const;\n\ntype ReservedWebEventNames = (typeof reservedWebEventNames)[number];\ntype ReservedAppEventNames = (typeof reservedAppEventNames)[number];\nexport type ReservedEventNames = ReservedWebEventNames | ReservedAppEventNames;\n\nexport type ReservedEventValues =\n | 'cid'\n | 'currency'\n | 'customer_id'\n | 'customerid'\n | 'dclid'\n | 'gclid'\n | 'session_id'\n | 'sessionid'\n | 'sfmc_id'\n | 'sid'\n | 'srsltid'\n | 'uid'\n | 'user_id'\n | 'userid'\n | `_${string}`\n | `firebase_${string}`\n | `ga_${string}`\n | `google_${string}`\n | `gtag.${string}`;\n\nexport type ReservedUserPropertiesNames =\n | 'cid'\n | 'customer_id'\n | 'customerid'\n | 'first_open_after_install'\n | 'first_open_time'\n | 'first_visit_time'\n | 'google_allow_ad_personalization_signals'\n | 'last_advertising_id_reset'\n | 'last_deep_link_referrer'\n | 'last_gclid'\n | 'lifetime_user_engagement'\n | 'non_personalized_ads'\n | 'session_id'\n | 'session_number'\n | 'sessionid'\n | 'sfmc_id'\n | 'sid'\n | 'uid'\n | 'user_id'\n | 'userid'\n | `_${string}`\n | `firebase_${string}`\n | `ga_${string}`\n | `google_${string}`;\n\nexport type UserPropertiesValue = string | boolean | number | null | undefined;\nexport type UserProperties = {\n [key: string]: UserPropertiesValue;\n} & {\n [key in ReservedUserPropertiesNames]?: never;\n};\n\n/**\n * reference: https://support.google.com/analytics/answer/14078702\n * You must turn on user-provided data collection in Google Analytics.\n *\n * Validate your user-provided data implementation: https://support.google.com/analytics/answer/14171683\n *\n * Usage: gtag('set', 'user_data', { email: 'abc@abc.com' })\n *\n * To standardize the hash results, before hashing one of these values, you must:\n *\n * - Remove leading and trailing whitespaces.\n * - Convert the text to lowercase.\n * - Format phone numbers according to the E164 standard.\n * - Remove all periods (.) that precede the domain name in gmail.com and googlemail.com email addresses.\n * */\ntype UserProvidedDataAddress = {\n first_name?: string;\n last_name?: string;\n street?: string;\n city?: string;\n /** User province, state, or region. Example: `Hampshire` */\n region?: string;\n postal_code?: string;\n /**\n * User country code.\n * Example: 'UK'. Use 2-letter country codes, per the ISO 3166-1 alpha-2 standard.\n */\n country?: string;\n};\n\n/**\n * To standardize the hash results, before hashing one of these values, you must:\n *\n * - Remove leading and trailing whitespaces.\n * - Convert the text to lowercase.\n * - Format phone numbers according to the E164 standard.\n * - Remove all periods (.) that precede the domain name in gmail.com and googlemail.com email addresses.\n */\nexport type UserProvidedData = {\n email?: string | string[];\n /**\n * User phone number. Must be in E.164 format, which means it must be 11 to 15 digits including a\n * plus sign (+) prefix and country code with no dashes, parentheses, or spaces.\n *\n * Example: ‘+11231234567’\n */\n phone_number?: string | string[];\n address?: UserProvidedDataAddress | UserProvidedDataAddress[];\n};\n\nexport type GaId = `G-${Uppercase<string>}`;\nexport type GtmId = `GTM-${Uppercase<string>}`;\n\nexport type CampaignMedium =\n | 'email'\n | 'organic'\n | 'cpc'\n | 'banner'\n | 'social'\n | 'referral'\n | 'affiliate'\n | 'video'\n | 'display'\n | 'sms'\n | 'push'\n | 'qr'\n | 'audio'\n | (string & {});\n\nexport type CampaignSource =\n | 'google' // don't need to set\n | 'googleads'\n | 'bing'\n | 'bingads'\n | 'metaads'\n | 'facebook'\n | 'twitter'\n | 'linkedin'\n | 'instagram'\n | 'tiktok'\n | 'youtube'\n | 'newsletter'\n | `website_${string}`\n | `affiliate_${string}`\n | (string & {});\n\nexport type Campaign = {\n id?: string;\n name?: string;\n term?: string;\n content?: string;\n medium?: CampaignMedium;\n source?: CampaignSource;\n};\n\nexport type Config = {\n allow_google_signals?: boolean;\n allow_ad_personalization_signals?: boolean;\n\n campaign_content?: string;\n campaign_id?: string;\n campaign_medium?: CampaignMedium;\n campaign_name?: string;\n campaign_source?: CampaignSource;\n campaign_term?: string;\n\n /**\n * @deprecated\n * Key Point: Use the campaign_ prefixed version of each campaign value instead of this field.\n */\n campaign?: Campaign;\n\n client_id?: string;\n content_group?: string;\n\n cookie_domain?: 'none' | 'auto' | string;\n cookie_expires?: number;\n cookie_flags?: string;\n cookie_path?: string;\n cookie_prefix?: string;\n cookie_update?: boolean;\n\n ignore_referrer?: boolean;\n language?: string;\n\n page_location?: string;\n page_referrer?: string;\n page_title?: string;\n\n send_page_view?: boolean;\n screen_resolution?: `${number}x${number}`;\n user_id?: string;\n user_properties?: UserProperties;\n};\n\nexport type Item = {\n item_id: string;\n item_name: string;\n affiliation?: 'Google Store' | (string & {});\n coupon?: string;\n discount?: number;\n index?: number;\n item_brand?: string;\n item_category?: string;\n item_category2?: string;\n item_category3?: string;\n item_category4?: string;\n item_category5?: string;\n item_list_id?: string;\n item_list_name?: string;\n item_variant?: string;\n location_id?: string;\n price?: number;\n quantity?: number;\n};\n\nexport type PromotionItem = {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n};\n\nexport type SurveyProperties = {\n id?: string;\n feature?: Lowercase<string>;\n trigger?: Lowercase<string>;\n};\n\nexport type SurveyQA = {\n q1: string;\n a1: string;\n q2?: string;\n a2?: string;\n q3?: string;\n a3?: string;\n q4?: string;\n a4?: string;\n q5?: string;\n a5?: string;\n q6?: string;\n a6?: string;\n completed?: boolean;\n};\n\nexport type NPSScore = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | number;\nexport type CASTScore = 1 | 2 | 3 | 4 | 5 | number;\nexport type CESScore = 1 | 2 | 3 | 4 | 5 | number;\n\n/** ref: https://support.google.com/analytics/answer/9216061 */\nexport type EnhancedMeasurementEvents = {\n page_view: {\n page_path: string;\n page_title: string;\n page_referrer?: string;\n page_location?: string;\n previous_page_path?: string;\n engagement_time_msec?: number;\n };\n // Scrolls: the first time a user reaches the bottom of each page\n // (i.e., when a 90% vertical depth becomes visible)\n scroll: {\n engagement_time_msec: number;\n };\n // Outbound clicks\n click: {\n link_id: string;\n link_url: string;\n link_text: string; // added\n link_domain: string;\n link_classes: string;\n outbound: boolean;\n };\n // Site search\n view_search_results: {\n search_term: string;\n };\n // Video engagement\n video_start: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n video_progress: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n video_complete: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n // File downloads:\n file_download: {\n file_extension: string;\n file_name: string;\n link_classes: string;\n link_id: string;\n link_text: string;\n link_url: string;\n };\n // Form interactions\n form_start: {\n form_id: string;\n form_name: string;\n form_destination: string;\n };\n form_submit: {\n form_id: string;\n form_name: string;\n form_destination: string;\n form_submit_text: string;\n };\n};\n\n/** ref: https://support.google.com/analytics/answer/9234069 */\nexport type AutomaticallyCollectedEvents = EnhancedMeasurementEvents & {\n first_visit: {\n page_path: string;\n page_title: string;\n page_referrer?: string;\n page_location?: string;\n };\n first_open: {\n screen_name: string;\n screen_class: string;\n };\n in_app_purchase: {\n product_id: string;\n price: number;\n value: number;\n currency: string;\n quantity: number;\n subscription?: boolean;\n free_trial?: boolean;\n introductory_price?: number;\n };\n notification_dismiss: {\n // Android only\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_foreground: {\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_type: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_open: {\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_receive: {\n // Android only\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_type: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n // Note: The 'screen_view' event is automatically sent only when using Android or iOS native SDKs.\n // If you are using React Native, you need to send this event manually; it should not be ignored.\n session_start: undefined;\n user_engagement: {\n engagement_time_msec: number;\n trigger: 'pagehide' | 'visibilitychange' | 'background';\n };\n};\n\n/**\n * ref: https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag\n * ref: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event\n * */\nexport type RecommendedEvents = {\n // Google Analytics 4 Recommended Events\n add_payment_info: {\n currency: string;\n value: number;\n coupon?: string;\n payment_type?: string;\n items: Item[];\n };\n add_shipping_info: {\n currency: string;\n value: number;\n coupon?: string;\n shipping_tier?: string;\n items: Item[];\n };\n add_to_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n add_to_wishlist: {\n currency: string;\n value: number;\n items: Item[];\n };\n begin_checkout: {\n currency: string;\n value: number;\n coupon?: string;\n items: Item[];\n source?: string; // added\n };\n close_convert_lead: {\n currency: string;\n value: number;\n };\n close_unconvert_lead: {\n currency: string;\n value: number;\n unconvert_lead_reason?: string;\n };\n disqualify_lead: {\n currency: string;\n value: number;\n disqualified_lead_reason?: string;\n };\n earn_virtual_currency: {\n virtual_currency_name?: string;\n value?: number;\n };\n generate_lead: {\n currency: string;\n value: number;\n lead_source?: string;\n };\n join_group: {\n group_id?: string;\n };\n level_end: {\n level_name?: string;\n success?: boolean;\n };\n level_start: {\n level_name?: string;\n };\n level_up: {\n level?: number;\n level_name?: string;\n character?: string;\n };\n login: {\n method?:\n | 'google'\n | 'apple'\n | 'facebook'\n | 'twitter'\n | 'linkedin'\n | 'github'\n | 'microsoft'\n | 'wechat'\n | 'onetap'\n | 'phone'\n | 'email'\n | (string & {});\n source?: string; // added\n };\n post_score: {\n score: number;\n level?: number;\n character?: string;\n };\n purchase: {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number; // Shipping cost associated with a transaction.\n tax?: number;\n items?: Item[];\n source?: string; // added\n };\n qualify_lead: {\n currency: string;\n value: number;\n };\n refund: {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number;\n tax?: number;\n items?: Item[];\n };\n remove_from_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n search: {\n search_term: string;\n };\n select_content: {\n content_type?: string;\n content_id?: string;\n };\n select_item: {\n item_list_id?: string;\n item_list_name?: string;\n items: Item[];\n };\n select_promotion: {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n items?: (Item & PromotionItem)[];\n };\n share: {\n method?: string;\n content_type?: string;\n item_id?: string;\n system_activity_type?: string; // added\n platform_post_id?: string; // added\n };\n sign_up: {\n method?: string;\n source?: string; // added\n };\n spend_virtual_currency: {\n value: number;\n virtual_currency_name: string;\n item_name?: string;\n };\n tutorial_begin: undefined;\n tutorial_complete: undefined;\n unlock_achievement: {\n achievement_id: string;\n };\n view_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n view_item: {\n currency: string;\n value: number;\n items: Item[];\n };\n view_item_list: {\n currency: string;\n item_list_id?: string;\n item_list_name?: string;\n items: Item[];\n };\n view_promotion: {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n items: (Item & PromotionItem)[];\n };\n working_lead: {\n currency: string;\n value: number;\n lead_status?: string;\n };\n\n // Firebase Analytics events, event_name 40 characters limit, 25 parameters limit\n ad_impression: {\n value?: number;\n currency?: string;\n ad_format?: string;\n ad_platform?: string;\n ad_source?: string;\n ad_unit_name?: string;\n };\n app_open: undefined;\n campaign_details: {\n source: string;\n medium: string;\n campaign: string;\n term?: string;\n content?: string;\n aclid?: string;\n cp1?: string;\n };\n screen_view: {\n screen_name?: string;\n screen_class?: string;\n previous_screen_class?: string; // added\n engagement_time_msec?: number;\n };\n};\n\n/**\n * Extra commonly used events that are NOT part of the GA standard event set.\n * Added to satisfy additional analytics needs.\n */\nexport type AddedRecommendedEvents = {\n logout: undefined;\n trial_begin: {\n currency: string;\n value: number;\n source?: string; // added\n items?: Item[]; // added, the plan(s) enrolled (first item's id maps to OpenAI plan_id)\n };\n subscribe: {\n currency: string;\n value: number;\n source?: string; // added\n items?: Item[]; // added, the plan(s) enrolled (first item's id maps to OpenAI plan_id)\n };\n // survey\n survey_shown: SurveyProperties;\n survey_sent: SurveyProperties & SurveyQA;\n survey_dismissed: SurveyProperties;\n /**\n * Net promoter score: Get an industry-recognized benchmark\n * How likely are you to recommend us to a friend?\n * */\n nps_shown: SurveyProperties;\n nps_sent: SurveyProperties & { score: NPSScore; feedback?: string };\n nps_dismissed: SurveyProperties;\n /**\n * Customer satisfaction score: Works best after a checkout or support flow\n * How satisfied are you with xxx?\n * */\n cast_shown: SurveyProperties;\n cast_sent: SurveyProperties & { score: CASTScore; feedback?: string };\n cast_dismissed: SurveyProperties;\n /**\n * Customer effort score: Works well with churn surveys\n * How easy is it to use the feature?\n * */\n ces_shown: SurveyProperties;\n ces_sent: SurveyProperties & { score: CESScore; feedback?: string };\n ces_dismissed: SurveyProperties;\n};\n\nexport type StandardEvents = AutomaticallyCollectedEvents &\n RecommendedEvents &\n AddedRecommendedEvents;\n\n/**\n * reference: https://developers.google.com/analytics/devguides/collection/ga4/reference/config\n * reference: https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag\n */\nexport interface Gtag {\n /**\n * To disable advertising features based on third-party advertising identifiers, set\n * allow_google_signals to false.\n *\n * @param event\n * @param option\n * @param allow - Whether to allow Google signals.\n * @default true\n */\n gtag(event: 'set', option: 'allow_google_signals', allow: boolean): void;\n\n /**\n * Set to false to disable advertising personalization features.\n *\n * @param event\n * @param option\n * @param allow - Whether to allow ad personalization signals.\n * @default true\n */\n gtag(event: 'set', option: 'allow_ad_personalization_signals', allow: boolean): void;\n\n /**\n * Used for A/B testing and content-targeted ads. Use campaign_content to differentiate ads or\n * links that point to the same URL.\n */\n gtag(event: 'set', option: 'campaign_content', content: string): void;\n\n /**\n * Used to identify which campaign this referral references. Use campaign_id to identify a\n * specific campaign.\n */\n gtag(event: 'set', option: 'campaign_id', id: string): void;\n\n /** Use campaign_medium to identify a medium such as email or cost-per-click. */\n gtag(event: 'set', option: 'campaign_medium', medium: CampaignMedium): void;\n\n /**\n * Used for keyword analysis. Use campaign_name to identify a specific product promotion or\n * strategic campaign.\n */\n gtag(event: 'set', option: 'campaign_name', name: string): void;\n\n /** Use campaign_source to identify a search engine, newsletter name, or other source. */\n gtag(event: 'set', option: 'campaign_source', source: CampaignSource): void;\n\n /** Used for paid search. Use campaign_term to note the keywords for this ad. */\n gtag(event: 'set', option: 'campaign_term', term: string): void;\n\n /**\n * @deprecated\n * Key Point: Use the campaign_ prefixed version of each campaign value instead of this field.\n */\n gtag(event: 'set', option: 'campaign', params: Campaign): void;\n\n /**\n * Pseudonymously identifies a browser instance. By default, this value is stored as part of the\n * first-party Analytics cookie with a two-year expiration.\n */\n gtag(event: 'set', option: 'client_id', id: string): void;\n\n /** example: gtag('set', 'content_group', '/news/sports'); */\n gtag(event: 'set', option: 'content_group', group: string): void;\n\n /**\n * Specifies the domain used to store the analytics cookie.\n * Set to 'none' to set the cookie without specifying a domain.\n * Set to 'auto' (the default value) to set the cookie to the top level domain plus one\n * subdomain (eTLD +1). For example, if cookie_domain is set to 'auto' https://example.com would\n * use example.com for the domain, and https://subdomain.example.com would also use example.com\n * for the domain.\n *\n * @param event\n * @param option\n * @param domain - The domain used to store the analytics cookie.\n * @default 'auto'\n */\n gtag(event: 'set', option: 'cookie_domain', domain: 'none' | 'auto' | string): void;\n\n /**\n * Every time a hit is sent to Google Analytics, the cookie expiration time is updated to be the\n * current time plus the value of the cookie_expires field. This means that if you use the default\n * value time of two years (63,072,000 seconds), and a user visits your site every month, their\n * cookie will never expire.\n *\n * If you set the cookie_expires time to 0 (zero) seconds, the cookie turns into a session-based\n * cookie and expires once the current browser session ends.\n *\n * Caution: If you set the cookie to expire too quickly, you will inflate your user count and\n * decrease the quality of your measurement.\n *\n * @param event\n * @param option\n * @param expires - The number of seconds until the cookie expires.\n * @default 63072000\n */\n gtag(event: 'set', option: 'cookie_expires', expires: number): void;\n\n /**\n * Appends additional flags to the cookie when set. Flags must be separated by semicolons. See\n * [write a new cookie](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#write_a_new_cookie) for some examples of flags to set.\n */\n gtag(event: 'set', option: 'cookie_flags', flags: string): void;\n\n /** Specifies the subpath used to store the analytics cookie. */\n gtag(event: 'set', option: 'cookie_path', path: string): void;\n\n /** Specifies a prefix to prepend to analytics cookie names. */\n gtag(event: 'set', option: 'cookie_prefix', prefix: string): void;\n\n /**\n * When cookie_update is set to true, gtag.js will update cookies on each page load. This will\n * update the cookie expiration to be set relative to the most recent visit to the site. For\n * example, if cookie expiration is set to one week, and a user visits using the same browser\n * every five days, the cookie expiration will be updated on each visit and will effectively\n * never expire.\n *\n * When set to false, cookies are not updated on each page load. This has the effect of cookie\n * expiration being relative to the first time a user visited the site.\n *\n * @param event\n * @param option\n * @param update - Whether to update the cookie on each page load.\n * @default true\n */\n gtag(event: 'set', option: 'cookie_update', update: boolean): void;\n\n /**\n * Set to true to indicate to Analytics that the referrer shouldn't be displayed as a traffic\n * source. [Learn when to use this field](https://support.google.com/analytics/answer/10327750#set-parameter)\n *\n * @param event\n * @param option\n * @param ignore - Whether to ignore the referrer.\n * @default false\n */\n gtag(event: 'set', option: 'ignore_referrer', ignore: boolean): void;\n\n /**\n * Specifies the language preference of the user. Defaults to the user's `navigator.language` value.\n *\n * @param event\n * @param option\n * @param language - The language preference of the user.\n * @default navigator.language\n */\n gtag(event: 'set', option: 'language', language: string): void;\n\n /**\n * Specifies the full URL of the page. Defaults to the user's `document.location` value.\n *\n * @param event\n * @param option\n * @param location - The full URL of the page. Character limit 1000\n * @default document.location\n */\n gtag(event: 'set', option: 'page_location', location: string): void;\n\n /**\n * Specifies which referral source brought traffic to a page. This value is also used to compute\n * the traffic source. The format of this value is a URL. Defaults to the user's `document.referrer`\n * value.\n *\n * @param event\n * @param option\n * @param referrer - The referral source. Character limit 420\n * @default document.referrer\n */\n gtag(event: 'set', option: 'page_referrer', referrer: string): void;\n\n /**\n * The title of the page or document. Defaults to the user's `document.title` value.\n *\n * @param event\n * @param option\n * @param title - The title of the page or document. Character limit 300\n * @default document.title\n */\n gtag(event: 'set', option: 'page_title', title: string): void;\n\n /**\n * Set to false to prevent the default snippet from sending a page_view.\n *\n * @param event\n * @param option\n * @param send - Whether to send a page_view.\n * @default true\n */\n gtag(event: 'set', option: 'send_page_view', send: boolean): void;\n\n /**\n * Specifies the resolution of the screen. Should be two positive integers separated by an x. For\n * example, for an 800px by 600px screen, the value would be 800x600. Calculated from the user's\n * window.screen value.\n *\n * @param event\n * @param option\n * @param resolution - The resolution of the screen.\n * @default window.screen\n */\n gtag(event: 'set', option: 'screen_resolution', resolution: `${number}x${number}`): void;\n\n /**\n * Specifies a known identifier for a user provided by the site owner/library user. It must not\n * itself be PII (personally identifiable information). The value should never be persisted in\n * Google Analytics cookies or other Analytics-provided storage.\n *\n * @param event\n * @param option\n * @param userId - The user ID. Character limit 256\n */\n gtag(event: 'set', option: 'user_id', userId: string): void;\n\n /**\n * User properties are attributes that can be used to describe segments of your user base, such\n * as language preference or geographic location. Up to 25 additional user properties can be set\n * per project.\n *\n * @param event\n * @param option\n * @param properties\n */\n gtag(event: 'set', option: 'user_properties', properties: UserProperties): void;\n\n gtag(event: 'set', option: 'user_data', data: UserProvidedData): void;\n\n /**\n * gtag('config', ...) Set for a single stream\n * gtag('set', ...) Set globally\n */\n gtag(event: 'config', gaId: GaId, config?: Config): void;\n\n gtag<T extends string>(\n event: 'event',\n eventName: T extends keyof StandardEvents ? T : string,\n eventParams?: T extends keyof StandardEvents\n ? StandardEvents[T]\n : Record<string, string | number | boolean | null | undefined>\n ): void;\n}\n"],"mappings":";AACA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,qBAAqB,CAAC,GAAG,uBAAuB,GAAG,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../src/track/gtag.ts"],"sourcesContent":["/** reference: https://support.google.com/analytics/answer/13316687 */\nconst reservedWebEventNames = [\n 'app_remove',\n 'app_store_refund',\n 'app_store_subscription_cancel',\n 'app_store_subscription_renew',\n 'click',\n 'error',\n 'file_download',\n 'first_open',\n 'first_visit',\n 'form_start',\n 'form_submit',\n 'in_app_purchase',\n 'page_view',\n 'scroll',\n 'session_start',\n 'user_engagement',\n 'view_complete',\n 'video_progress',\n 'video_start',\n 'view_search_results',\n] as const;\n\nconst reservedAppEventNames = [\n 'ad_activeview',\n 'ad_click',\n 'ad_exposure',\n 'ad_impression',\n 'ad_query',\n 'ad_reward',\n 'adunit_exposure',\n 'app_clear_data',\n 'app_exception',\n 'app_install',\n 'app_remove',\n 'app_store_refund',\n 'app_update',\n 'app_upgrade',\n 'dynamic_link_app_open',\n 'dynamic_link_app_update',\n 'dynamic_link_first_open',\n 'error',\n 'firebase_campaign',\n 'firebase_in_app_message_action',\n 'firebase_in_app_message_dismiss',\n 'firebase_in_app_message_impression',\n 'first_open',\n 'first_visit',\n 'in_app_purchase',\n 'notification_dismiss',\n 'notification_foreground',\n 'notification_open',\n 'notification_receive',\n 'notification_send',\n 'os_update',\n 'screen_view',\n 'session_start',\n 'user_engagement',\n] as const;\n\nexport const reservedEventNames = [...reservedWebEventNames, ...reservedAppEventNames] as const;\n\ntype ReservedWebEventNames = (typeof reservedWebEventNames)[number];\ntype ReservedAppEventNames = (typeof reservedAppEventNames)[number];\nexport type ReservedEventNames = ReservedWebEventNames | ReservedAppEventNames;\n\nexport type ReservedEventValues =\n | 'cid'\n | 'currency'\n | 'customer_id'\n | 'customerid'\n | 'dclid'\n | 'gclid'\n | 'session_id'\n | 'sessionid'\n | 'sfmc_id'\n | 'sid'\n | 'srsltid'\n | 'uid'\n | 'user_id'\n | 'userid'\n | `_${string}`\n | `firebase_${string}`\n | `ga_${string}`\n | `google_${string}`\n | `gtag.${string}`;\n\nexport type ReservedUserPropertiesNames =\n | 'cid'\n | 'customer_id'\n | 'customerid'\n | 'first_open_after_install'\n | 'first_open_time'\n | 'first_visit_time'\n | 'google_allow_ad_personalization_signals'\n | 'last_advertising_id_reset'\n | 'last_deep_link_referrer'\n | 'last_gclid'\n | 'lifetime_user_engagement'\n | 'non_personalized_ads'\n | 'session_id'\n | 'session_number'\n | 'sessionid'\n | 'sfmc_id'\n | 'sid'\n | 'uid'\n | 'user_id'\n | 'userid'\n | `_${string}`\n | `firebase_${string}`\n | `ga_${string}`\n | `google_${string}`;\n\nexport type UserPropertiesValue = string | boolean | number | null | undefined;\nexport type UserProperties = {\n [key: string]: UserPropertiesValue;\n} & {\n [key in ReservedUserPropertiesNames]?: never;\n};\n\n/**\n * reference: https://support.google.com/analytics/answer/14078702\n * You must turn on user-provided data collection in Google Analytics.\n *\n * Validate your user-provided data implementation: https://support.google.com/analytics/answer/14171683\n *\n * Usage: gtag('set', 'user_data', { email: 'abc@abc.com' })\n *\n * To standardize the hash results, before hashing one of these values, you must:\n *\n * - Remove leading and trailing whitespaces.\n * - Convert the text to lowercase.\n * - Format phone numbers according to the E164 standard.\n * - Remove all periods (.) that precede the domain name in gmail.com and googlemail.com email addresses.\n * */\ntype UserProvidedDataAddress = {\n first_name?: string;\n last_name?: string;\n street?: string;\n city?: string;\n /** User province, state, or region. Example: `Hampshire` */\n region?: string;\n postal_code?: string;\n /**\n * User country code.\n * Example: 'UK'. Use 2-letter country codes, per the ISO 3166-1 alpha-2 standard.\n */\n country?: string;\n};\n\n/**\n * To standardize the hash results, before hashing one of these values, you must:\n *\n * - Remove leading and trailing whitespaces.\n * - Convert the text to lowercase.\n * - Format phone numbers according to the E164 standard.\n * - Remove all periods (.) that precede the domain name in gmail.com and googlemail.com email addresses.\n */\nexport type UserProvidedData = {\n email?: string | string[];\n /**\n * User phone number. Must be in E.164 format, which means it must be 11 to 15 digits including a\n * plus sign (+) prefix and country code with no dashes, parentheses, or spaces.\n *\n * Example: ‘+11231234567’\n */\n phone_number?: string | string[];\n address?: UserProvidedDataAddress | UserProvidedDataAddress[];\n};\n\nexport type GaId = `G-${Uppercase<string>}`;\nexport type GtmId = `GTM-${Uppercase<string>}`;\n\nexport type CampaignMedium =\n | 'email'\n | 'organic'\n | 'cpc'\n | 'banner'\n | 'social'\n | 'referral'\n | 'affiliate'\n | 'video'\n | 'display'\n | 'sms'\n | 'push'\n | 'qr'\n | 'audio'\n | (string & {});\n\nexport type CampaignSource =\n | 'google' // don't need to set\n | 'googleads'\n | 'bing'\n | 'bingads'\n | 'metaads'\n | 'facebook'\n | 'twitter'\n | 'linkedin'\n | 'instagram'\n | 'tiktok'\n | 'youtube'\n | 'newsletter'\n | `website_${string}`\n | `affiliate_${string}`\n | (string & {});\n\nexport type Campaign = {\n id?: string;\n name?: string;\n term?: string;\n content?: string;\n medium?: CampaignMedium;\n source?: CampaignSource;\n};\n\nexport type Config = {\n allow_google_signals?: boolean;\n allow_ad_personalization_signals?: boolean;\n\n campaign_content?: string;\n campaign_id?: string;\n campaign_medium?: CampaignMedium;\n campaign_name?: string;\n campaign_source?: CampaignSource;\n campaign_term?: string;\n\n /**\n * @deprecated\n * Key Point: Use the campaign_ prefixed version of each campaign value instead of this field.\n */\n campaign?: Campaign;\n\n client_id?: string;\n content_group?: string;\n\n cookie_domain?: 'none' | 'auto' | (string & {});\n cookie_expires?: number;\n cookie_flags?: string;\n cookie_path?: string;\n cookie_prefix?: string;\n cookie_update?: boolean;\n\n ignore_referrer?: boolean;\n language?: string;\n\n page_location?: string;\n page_referrer?: string;\n page_title?: string;\n\n send_page_view?: boolean;\n screen_resolution?: `${number}x${number}`;\n user_id?: string;\n user_properties?: UserProperties;\n};\n\nexport type Item = {\n item_id: string;\n item_name: string;\n affiliation?: 'Google Store' | (string & {});\n coupon?: string;\n discount?: number;\n index?: number;\n item_brand?: string;\n item_category?: string;\n item_category2?: string;\n item_category3?: string;\n item_category4?: string;\n item_category5?: string;\n item_list_id?: string;\n item_list_name?: string;\n item_variant?: string;\n location_id?: string;\n price?: number;\n quantity?: number;\n};\n\nexport type PromotionItem = {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n};\n\nexport type SurveyProperties = {\n id?: string;\n feature?: Lowercase<string>;\n trigger?: Lowercase<string>;\n};\n\nexport type SurveyQA = {\n q1: string;\n a1: string;\n q2?: string;\n a2?: string;\n q3?: string;\n a3?: string;\n q4?: string;\n a4?: string;\n q5?: string;\n a5?: string;\n q6?: string;\n a6?: string;\n completed?: boolean;\n};\n\nexport type NPSScore = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | (number & {});\nexport type CASTScore = 1 | 2 | 3 | 4 | 5 | (number & {});\nexport type CESScore = 1 | 2 | 3 | 4 | 5 | (number & {});\n\n/** ref: https://support.google.com/analytics/answer/9216061 */\nexport type EnhancedMeasurementEvents = {\n page_view: {\n page_path: string;\n page_title: string;\n page_referrer?: string;\n page_location?: string;\n previous_page_path?: string;\n engagement_time_msec?: number;\n };\n // Scrolls: the first time a user reaches the bottom of each page\n // (i.e., when a 90% vertical depth becomes visible)\n scroll: {\n engagement_time_msec: number;\n };\n // Outbound clicks\n click: {\n link_id: string;\n link_url: string;\n link_text: string; // added\n link_domain: string;\n link_classes: string;\n outbound: boolean;\n };\n // Site search\n view_search_results: {\n search_term: string;\n };\n // Video engagement\n video_start: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n video_progress: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n video_complete: {\n video_current_time: number;\n video_duration: number;\n video_percent: number;\n video_provider: 'youtube' | 'vimeo' | 'html5' | (string & {});\n video_title: string;\n video_url: string;\n visible: boolean;\n };\n // File downloads:\n file_download: {\n file_extension: string;\n file_name: string;\n link_classes: string;\n link_id: string;\n link_text: string;\n link_url: string;\n };\n // Form interactions\n form_start: {\n form_id: string;\n form_name: string;\n form_destination: string;\n };\n form_submit: {\n form_id: string;\n form_name: string;\n form_destination: string;\n form_submit_text: string;\n };\n};\n\n/** ref: https://support.google.com/analytics/answer/9234069 */\nexport type AutomaticallyCollectedEvents = EnhancedMeasurementEvents & {\n first_visit: {\n page_path: string;\n page_title: string;\n page_referrer?: string;\n page_location?: string;\n };\n first_open: {\n screen_name: string;\n screen_class: string;\n };\n in_app_purchase: {\n product_id: string;\n price: number;\n value: number;\n currency: string;\n quantity: number;\n subscription?: boolean;\n free_trial?: boolean;\n introductory_price?: number;\n };\n notification_dismiss: {\n // Android only\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_foreground: {\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_type: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_open: {\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n notification_receive: {\n // Android only\n topic: string;\n label: string;\n message_id: string;\n message_name: string;\n message_type: string;\n message_time: number;\n message_device_time: number;\n message_channel: string;\n };\n // Note: The 'screen_view' event is automatically sent only when using Android or iOS native SDKs.\n // If you are using React Native, you need to send this event manually; it should not be ignored.\n session_start: undefined;\n user_engagement: {\n engagement_time_msec: number;\n trigger: 'pagehide' | 'visibilitychange' | 'background';\n };\n};\n\n/**\n * ref: https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag\n * ref: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event\n * */\nexport type RecommendedEvents = {\n // Google Analytics 4 Recommended Events\n add_payment_info: {\n currency: string;\n value: number;\n coupon?: string;\n payment_type?: string;\n items: Item[];\n };\n add_shipping_info: {\n currency: string;\n value: number;\n coupon?: string;\n shipping_tier?: string;\n items: Item[];\n };\n add_to_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n add_to_wishlist: {\n currency: string;\n value: number;\n items: Item[];\n };\n begin_checkout: {\n currency: string;\n value: number;\n coupon?: string;\n items: Item[];\n source?: string; // added\n };\n close_convert_lead: {\n currency: string;\n value: number;\n };\n close_unconvert_lead: {\n currency: string;\n value: number;\n unconvert_lead_reason?: string;\n };\n disqualify_lead: {\n currency: string;\n value: number;\n disqualified_lead_reason?: string;\n };\n earn_virtual_currency: {\n virtual_currency_name?: string;\n value?: number;\n };\n generate_lead: {\n currency: string;\n value: number;\n lead_source?: string;\n };\n join_group: {\n group_id?: string;\n };\n level_end: {\n level_name?: string;\n success?: boolean;\n };\n level_start: {\n level_name?: string;\n };\n level_up: {\n level?: number;\n level_name?: string;\n character?: string;\n };\n login: {\n method?:\n | 'google'\n | 'apple'\n | 'facebook'\n | 'twitter'\n | 'linkedin'\n | 'github'\n | 'microsoft'\n | 'wechat'\n | 'onetap'\n | 'phone'\n | 'email'\n | (string & {});\n source?: string; // added\n };\n post_score: {\n score: number;\n level?: number;\n character?: string;\n };\n purchase: {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number; // Shipping cost associated with a transaction.\n tax?: number;\n items?: Item[];\n source?: string; // added\n };\n qualify_lead: {\n currency: string;\n value: number;\n };\n refund: {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number;\n tax?: number;\n items?: Item[];\n };\n remove_from_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n search: {\n search_term: string;\n };\n select_content: {\n content_type?: string;\n content_id?: string;\n };\n select_item: {\n item_list_id?: string;\n item_list_name?: string;\n items: Item[];\n };\n select_promotion: {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n items?: (Item & PromotionItem)[];\n };\n share: {\n method?: string;\n content_type?: string;\n item_id?: string;\n system_activity_type?: string; // added\n platform_post_id?: string; // added\n };\n sign_up: {\n method?: string;\n source?: string; // added\n };\n spend_virtual_currency: {\n value: number;\n virtual_currency_name: string;\n item_name?: string;\n };\n tutorial_begin: undefined;\n tutorial_complete: undefined;\n unlock_achievement: {\n achievement_id: string;\n };\n view_cart: {\n currency: string;\n value: number;\n items: Item[];\n };\n view_item: {\n currency: string;\n value: number;\n items: Item[];\n };\n view_item_list: {\n currency: string;\n item_list_id?: string;\n item_list_name?: string;\n items: Item[];\n };\n view_promotion: {\n creative_name?: string;\n creative_slot?: string;\n promotion_id?: string;\n promotion_name?: string;\n items: (Item & PromotionItem)[];\n };\n working_lead: {\n currency: string;\n value: number;\n lead_status?: string;\n };\n\n // Firebase Analytics events, event_name 40 characters limit, 25 parameters limit\n ad_impression: {\n value?: number;\n currency?: string;\n ad_format?: string;\n ad_platform?: string;\n ad_source?: string;\n ad_unit_name?: string;\n };\n app_open: undefined;\n campaign_details: {\n source: string;\n medium: string;\n campaign: string;\n term?: string;\n content?: string;\n aclid?: string;\n cp1?: string;\n };\n screen_view: {\n screen_name?: string;\n screen_class?: string;\n previous_screen_class?: string; // added\n engagement_time_msec?: number;\n };\n};\n\n/**\n * Extra commonly used events that are NOT part of the GA standard event set.\n * Added to satisfy additional analytics needs.\n */\nexport type AddedRecommendedEvents = {\n logout: undefined;\n trial_begin: {\n currency: string;\n value: number;\n source?: string; // added\n items?: Item[]; // added, the plan(s) enrolled (first item's id maps to OpenAI plan_id)\n };\n subscribe: {\n currency: string;\n value: number;\n source?: string; // added\n items?: Item[]; // added, the plan(s) enrolled (first item's id maps to OpenAI plan_id)\n };\n // survey\n survey_shown: SurveyProperties;\n survey_sent: SurveyProperties & SurveyQA;\n survey_dismissed: SurveyProperties;\n /**\n * Net promoter score: Get an industry-recognized benchmark\n * How likely are you to recommend us to a friend?\n * */\n nps_shown: SurveyProperties;\n nps_sent: SurveyProperties & { score: NPSScore; feedback?: string };\n nps_dismissed: SurveyProperties;\n /**\n * Customer satisfaction score: Works best after a checkout or support flow\n * How satisfied are you with xxx?\n * */\n cast_shown: SurveyProperties;\n cast_sent: SurveyProperties & { score: CASTScore; feedback?: string };\n cast_dismissed: SurveyProperties;\n /**\n * Customer effort score: Works well with churn surveys\n * How easy is it to use the feature?\n * */\n ces_shown: SurveyProperties;\n ces_sent: SurveyProperties & { score: CESScore; feedback?: string };\n ces_dismissed: SurveyProperties;\n};\n\nexport type StandardEvents = AutomaticallyCollectedEvents &\n RecommendedEvents &\n AddedRecommendedEvents;\n\n/**\n * reference: https://developers.google.com/analytics/devguides/collection/ga4/reference/config\n * reference: https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag\n */\nexport interface Gtag {\n /**\n * To disable advertising features based on third-party advertising identifiers, set\n * allow_google_signals to false.\n *\n * @param event\n * @param option\n * @param allow - Whether to allow Google signals.\n * @default true\n */\n gtag(event: 'set', option: 'allow_google_signals', allow: boolean): void;\n\n /**\n * Set to false to disable advertising personalization features.\n *\n * @param event\n * @param option\n * @param allow - Whether to allow ad personalization signals.\n * @default true\n */\n gtag(event: 'set', option: 'allow_ad_personalization_signals', allow: boolean): void;\n\n /**\n * Used for A/B testing and content-targeted ads. Use campaign_content to differentiate ads or\n * links that point to the same URL.\n */\n gtag(event: 'set', option: 'campaign_content', content: string): void;\n\n /**\n * Used to identify which campaign this referral references. Use campaign_id to identify a\n * specific campaign.\n */\n gtag(event: 'set', option: 'campaign_id', id: string): void;\n\n /** Use campaign_medium to identify a medium such as email or cost-per-click. */\n gtag(event: 'set', option: 'campaign_medium', medium: CampaignMedium): void;\n\n /**\n * Used for keyword analysis. Use campaign_name to identify a specific product promotion or\n * strategic campaign.\n */\n gtag(event: 'set', option: 'campaign_name', name: string): void;\n\n /** Use campaign_source to identify a search engine, newsletter name, or other source. */\n gtag(event: 'set', option: 'campaign_source', source: CampaignSource): void;\n\n /** Used for paid search. Use campaign_term to note the keywords for this ad. */\n gtag(event: 'set', option: 'campaign_term', term: string): void;\n\n /**\n * @deprecated\n * Key Point: Use the campaign_ prefixed version of each campaign value instead of this field.\n */\n gtag(event: 'set', option: 'campaign', params: Campaign): void;\n\n /**\n * Pseudonymously identifies a browser instance. By default, this value is stored as part of the\n * first-party Analytics cookie with a two-year expiration.\n */\n gtag(event: 'set', option: 'client_id', id: string): void;\n\n /** example: gtag('set', 'content_group', '/news/sports'); */\n gtag(event: 'set', option: 'content_group', group: string): void;\n\n /**\n * Specifies the domain used to store the analytics cookie.\n * Set to 'none' to set the cookie without specifying a domain.\n * Set to 'auto' (the default value) to set the cookie to the top level domain plus one\n * subdomain (eTLD +1). For example, if cookie_domain is set to 'auto' https://example.com would\n * use example.com for the domain, and https://subdomain.example.com would also use example.com\n * for the domain.\n *\n * @param event\n * @param option\n * @param domain - The domain used to store the analytics cookie.\n * @default 'auto'\n */\n gtag(event: 'set', option: 'cookie_domain', domain: 'none' | 'auto' | (string & {})): void;\n\n /**\n * Every time a hit is sent to Google Analytics, the cookie expiration time is updated to be the\n * current time plus the value of the cookie_expires field. This means that if you use the default\n * value time of two years (63,072,000 seconds), and a user visits your site every month, their\n * cookie will never expire.\n *\n * If you set the cookie_expires time to 0 (zero) seconds, the cookie turns into a session-based\n * cookie and expires once the current browser session ends.\n *\n * Caution: If you set the cookie to expire too quickly, you will inflate your user count and\n * decrease the quality of your measurement.\n *\n * @param event\n * @param option\n * @param expires - The number of seconds until the cookie expires.\n * @default 63072000\n */\n gtag(event: 'set', option: 'cookie_expires', expires: number): void;\n\n /**\n * Appends additional flags to the cookie when set. Flags must be separated by semicolons. See\n * [write a new cookie](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#write_a_new_cookie) for some examples of flags to set.\n */\n gtag(event: 'set', option: 'cookie_flags', flags: string): void;\n\n /** Specifies the subpath used to store the analytics cookie. */\n gtag(event: 'set', option: 'cookie_path', path: string): void;\n\n /** Specifies a prefix to prepend to analytics cookie names. */\n gtag(event: 'set', option: 'cookie_prefix', prefix: string): void;\n\n /**\n * When cookie_update is set to true, gtag.js will update cookies on each page load. This will\n * update the cookie expiration to be set relative to the most recent visit to the site. For\n * example, if cookie expiration is set to one week, and a user visits using the same browser\n * every five days, the cookie expiration will be updated on each visit and will effectively\n * never expire.\n *\n * When set to false, cookies are not updated on each page load. This has the effect of cookie\n * expiration being relative to the first time a user visited the site.\n *\n * @param event\n * @param option\n * @param update - Whether to update the cookie on each page load.\n * @default true\n */\n gtag(event: 'set', option: 'cookie_update', update: boolean): void;\n\n /**\n * Set to true to indicate to Analytics that the referrer shouldn't be displayed as a traffic\n * source. [Learn when to use this field](https://support.google.com/analytics/answer/10327750#set-parameter)\n *\n * @param event\n * @param option\n * @param ignore - Whether to ignore the referrer.\n * @default false\n */\n gtag(event: 'set', option: 'ignore_referrer', ignore: boolean): void;\n\n /**\n * Specifies the language preference of the user. Defaults to the user's `navigator.language` value.\n *\n * @param event\n * @param option\n * @param language - The language preference of the user.\n * @default navigator.language\n */\n gtag(event: 'set', option: 'language', language: string): void;\n\n /**\n * Specifies the full URL of the page. Defaults to the user's `document.location` value.\n *\n * @param event\n * @param option\n * @param location - The full URL of the page. Character limit 1000\n * @default document.location\n */\n gtag(event: 'set', option: 'page_location', location: string): void;\n\n /**\n * Specifies which referral source brought traffic to a page. This value is also used to compute\n * the traffic source. The format of this value is a URL. Defaults to the user's `document.referrer`\n * value.\n *\n * @param event\n * @param option\n * @param referrer - The referral source. Character limit 420\n * @default document.referrer\n */\n gtag(event: 'set', option: 'page_referrer', referrer: string): void;\n\n /**\n * The title of the page or document. Defaults to the user's `document.title` value.\n *\n * @param event\n * @param option\n * @param title - The title of the page or document. Character limit 300\n * @default document.title\n */\n gtag(event: 'set', option: 'page_title', title: string): void;\n\n /**\n * Set to false to prevent the default snippet from sending a page_view.\n *\n * @param event\n * @param option\n * @param send - Whether to send a page_view.\n * @default true\n */\n gtag(event: 'set', option: 'send_page_view', send: boolean): void;\n\n /**\n * Specifies the resolution of the screen. Should be two positive integers separated by an x. For\n * example, for an 800px by 600px screen, the value would be 800x600. Calculated from the user's\n * window.screen value.\n *\n * @param event\n * @param option\n * @param resolution - The resolution of the screen.\n * @default window.screen\n */\n gtag(event: 'set', option: 'screen_resolution', resolution: `${number}x${number}`): void;\n\n /**\n * Specifies a known identifier for a user provided by the site owner/library user. It must not\n * itself be PII (personally identifiable information). The value should never be persisted in\n * Google Analytics cookies or other Analytics-provided storage.\n *\n * @param event\n * @param option\n * @param userId - The user ID. Character limit 256\n */\n gtag(event: 'set', option: 'user_id', userId: string): void;\n\n /**\n * User properties are attributes that can be used to describe segments of your user base, such\n * as language preference or geographic location. Up to 25 additional user properties can be set\n * per project.\n *\n * @param event\n * @param option\n * @param properties\n */\n gtag(event: 'set', option: 'user_properties', properties: UserProperties): void;\n\n gtag(event: 'set', option: 'user_data', data: UserProvidedData): void;\n\n /**\n * gtag('config', ...) Set for a single stream\n * gtag('set', ...) Set globally\n */\n gtag(event: 'config', gaId: GaId, config?: Config): void;\n\n gtag<T extends string>(\n event: 'event',\n eventName: T extends keyof StandardEvents ? T : string,\n eventParams?: T extends keyof StandardEvents\n ? StandardEvents[T]\n : Record<string, string | number | boolean | null | undefined>\n ): void;\n}\n"],"mappings":";AACA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,qBAAqB,CAAC,GAAG,uBAAuB,GAAG,qBAAqB;","names":[]}
@@ -103,7 +103,7 @@ function track(name, properties, options = defaultOptions) {
103
103
  if (list.length >= batch) {
104
104
  const copy = [...list];
105
105
  list.length = 0;
106
- sendEvents(copy);
106
+ void sendEvents(copy);
107
107
  return;
108
108
  }
109
109
  if (timer) clearTimeout(timer);
@@ -111,7 +111,7 @@ function track(name, properties, options = defaultOptions) {
111
111
  timer = null;
112
112
  const copy = [...list];
113
113
  list.length = 0;
114
- sendEvents(copy);
114
+ void sendEvents(copy);
115
115
  }, delay);
116
116
  }
117
117
  async function trackAsync(name, properties, options = defaultOptions) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/track/index.ts"],"sourcesContent":["import { TokenBucket, fetch } from '@shware/utils';\nimport type { CreateTrackEventDTO } from '../schema/index';\nimport { cache, config } from '../setup/index';\nimport { session } from '../setup/session';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { getVisitor } from '../visitor/index';\nimport type { EventName, TrackEventResponse, TrackName, TrackProperties } from './types';\n\nexport interface TrackOptions {\n enableThirdPartyTracking?: boolean;\n onSucceed?: (response?: TrackEventResponse[number]) => void;\n onError?: (error: unknown) => void;\n}\n\nconst defaultOptions: TrackOptions = { enableThirdPartyTracking: true };\nconst tokenBucket = new TokenBucket({ rate: 1, capacity: 20, requested: 2 });\n\ntype Item = {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n name: TrackName<any>;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n properties: TrackProperties<any>;\n timestamp: string;\n options: TrackOptions;\n};\n\nasync function sendEvents(events: Item[]) {\n try {\n if (events.length === 0) return;\n\n if (session.isExpired()) {\n session.refresh();\n events.unshift({\n name: 'session_start',\n properties: {},\n options: { enableThirdPartyTracking: false },\n timestamp: new Date().toISOString(),\n });\n } else {\n session.updateLastActiveTime();\n }\n\n await tokenBucket.removeTokens();\n\n const tags = await config.getTags();\n const visitor_id = (await getVisitor()).id;\n\n const dto: CreateTrackEventDTO = events.map((event) => ({\n name: event.name,\n properties: event.properties,\n tags,\n visitor_id,\n session_id: session.getId(),\n platform: config.platform,\n environment: config.environment,\n timestamp: event.timestamp,\n }));\n\n const response = await fetch(`${config.endpoint}/events`, {\n method: 'POST',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(dto),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send track event: ${response.status} ${await response.text()}`);\n }\n\n const data = (await response.json()) as TrackEventResponse;\n\n let index = 0;\n while (events.length > 0) {\n const event = events.shift();\n if (!event) {\n index++;\n continue;\n }\n const { options, name, properties } = event;\n const eventId = data[index].id;\n options.onSucceed?.({ id: eventId });\n index++;\n if (\n !config.thirdPartyTrackers ||\n !options.enableThirdPartyTracking ||\n IGNORED_EVENTS.includes(name)\n ) {\n continue;\n }\n config.thirdPartyTrackers.forEach((tracker) => tracker(name, properties, eventId));\n }\n } catch (e: unknown) {\n if (e instanceof Error) console.log(e.message);\n events.forEach((event) => event.options.onError?.(e));\n }\n}\n\nconst batch = 10;\nconst delay = 2000;\nconst list: Item[] = [];\nlet timer: ReturnType<typeof setTimeout> | null = null;\n\nexport function track<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n options: TrackOptions = defaultOptions\n) {\n list.push({ name, properties, options, timestamp: new Date().toISOString() });\n if (list.length >= batch) {\n const copy = [...list];\n list.length = 0;\n sendEvents(copy);\n return;\n }\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n const copy = [...list];\n list.length = 0;\n sendEvents(copy);\n }, delay);\n}\n\nexport async function trackAsync<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n options: TrackOptions = defaultOptions\n) {\n await sendEvents([{ name, properties, options, timestamp: new Date().toISOString() }]);\n}\n\nexport function sendBeacon<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>\n) {\n if (!cache.tags || !cache.visitor) return;\n session.updateLastActiveTime();\n\n const dto: CreateTrackEventDTO = [\n {\n name,\n properties,\n tags: cache.tags,\n visitor_id: cache.visitor.id,\n session_id: session.getId(),\n platform: config.platform,\n environment: config.environment,\n timestamp: new Date().toISOString(),\n },\n ];\n const blob = new Blob([JSON.stringify(dto)], { type: 'application/json' });\n const success = navigator.sendBeacon(`${config.endpoint}/events`, blob);\n if (success) return;\n console.warn('Failed to send beacon', name, properties);\n}\n"],"mappings":"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","names":["_a"]}
1
+ {"version":3,"sources":["../../src/track/index.ts"],"sourcesContent":["import { TokenBucket, fetch } from '@shware/utils';\nimport type { CreateTrackEventDTO } from '../schema/index';\nimport { cache, config } from '../setup/index';\nimport { session } from '../setup/session';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { getVisitor } from '../visitor/index';\nimport type { EventName, TrackEventResponse, TrackName, TrackProperties } from './types';\n\nexport interface TrackOptions {\n enableThirdPartyTracking?: boolean;\n onSucceed?: (response?: TrackEventResponse[number]) => void;\n onError?: (error: unknown) => void;\n}\n\nconst defaultOptions: TrackOptions = { enableThirdPartyTracking: true };\nconst tokenBucket = new TokenBucket({ rate: 1, capacity: 20, requested: 2 });\n\ntype Item = {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n name: TrackName<any>;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n properties: TrackProperties<any>;\n timestamp: string;\n options: TrackOptions;\n};\n\nasync function sendEvents(events: Item[]) {\n try {\n if (events.length === 0) return;\n\n if (session.isExpired()) {\n session.refresh();\n events.unshift({\n name: 'session_start',\n properties: {},\n options: { enableThirdPartyTracking: false },\n timestamp: new Date().toISOString(),\n });\n } else {\n session.updateLastActiveTime();\n }\n\n await tokenBucket.removeTokens();\n\n const tags = await config.getTags();\n const visitor_id = (await getVisitor()).id;\n\n const dto: CreateTrackEventDTO = events.map((event) => ({\n name: event.name,\n properties: event.properties,\n tags,\n visitor_id,\n session_id: session.getId(),\n platform: config.platform,\n environment: config.environment,\n timestamp: event.timestamp,\n }));\n\n const response = await fetch(`${config.endpoint}/events`, {\n method: 'POST',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(dto),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send track event: ${response.status} ${await response.text()}`);\n }\n\n const data = (await response.json()) as TrackEventResponse;\n\n let index = 0;\n while (events.length > 0) {\n const event = events.shift();\n if (!event) {\n index++;\n continue;\n }\n const { options, name, properties } = event;\n const eventId = data[index].id;\n options.onSucceed?.({ id: eventId });\n index++;\n if (\n !config.thirdPartyTrackers ||\n !options.enableThirdPartyTracking ||\n IGNORED_EVENTS.includes(name)\n ) {\n continue;\n }\n config.thirdPartyTrackers.forEach((tracker) => tracker(name, properties, eventId));\n }\n } catch (e: unknown) {\n if (e instanceof Error) console.log(e.message);\n events.forEach((event) => event.options.onError?.(e));\n }\n}\n\nconst batch = 10;\nconst delay = 2000;\nconst list: Item[] = [];\nlet timer: ReturnType<typeof setTimeout> | null = null;\n\nexport function track<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n options: TrackOptions = defaultOptions\n) {\n list.push({ name, properties, options, timestamp: new Date().toISOString() });\n if (list.length >= batch) {\n const copy = [...list];\n list.length = 0;\n void sendEvents(copy);\n return;\n }\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n const copy = [...list];\n list.length = 0;\n void sendEvents(copy);\n }, delay);\n}\n\nexport async function trackAsync<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n options: TrackOptions = defaultOptions\n) {\n await sendEvents([{ name, properties, options, timestamp: new Date().toISOString() }]);\n}\n\nexport function sendBeacon<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>\n) {\n if (!cache.tags || !cache.visitor) return;\n session.updateLastActiveTime();\n\n const dto: CreateTrackEventDTO = [\n {\n name,\n properties,\n tags: cache.tags,\n visitor_id: cache.visitor.id,\n session_id: session.getId(),\n platform: config.platform,\n environment: config.environment,\n timestamp: new Date().toISOString(),\n },\n ];\n const blob = new Blob([JSON.stringify(dto)], { type: 'application/json' });\n const success = navigator.sendBeacon(`${config.endpoint}/events`, blob);\n if (success) return;\n console.warn('Failed to send beacon', name, properties);\n}\n"],"mappings":"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","names":["_a"]}
@@ -77,7 +77,7 @@ function track(name, properties, options = defaultOptions) {
77
77
  if (list.length >= batch) {
78
78
  const copy = [...list];
79
79
  list.length = 0;
80
- sendEvents(copy);
80
+ void sendEvents(copy);
81
81
  return;
82
82
  }
83
83
  if (timer) clearTimeout(timer);
@@ -85,7 +85,7 @@ function track(name, properties, options = defaultOptions) {
85
85
  timer = null;
86
86
  const copy = [...list];
87
87
  list.length = 0;
88
- sendEvents(copy);
88
+ void sendEvents(copy);
89
89
  }, delay);
90
90
  }
91
91
  async function trackAsync(name, properties, options = defaultOptions) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/track/index.ts"],"sourcesContent":["import { TokenBucket, fetch } from '@shware/utils';\nimport type { CreateTrackEventDTO } from '../schema/index';\nimport { cache, config } from '../setup/index';\nimport { session } from '../setup/session';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { getVisitor } from '../visitor/index';\nimport type { EventName, TrackEventResponse, TrackName, TrackProperties } from './types';\n\nexport interface TrackOptions {\n enableThirdPartyTracking?: boolean;\n onSucceed?: (response?: TrackEventResponse[number]) => void;\n onError?: (error: unknown) => void;\n}\n\nconst defaultOptions: TrackOptions = { enableThirdPartyTracking: true };\nconst tokenBucket = new TokenBucket({ rate: 1, capacity: 20, requested: 2 });\n\ntype Item = {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n name: TrackName<any>;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n properties: TrackProperties<any>;\n timestamp: string;\n options: TrackOptions;\n};\n\nasync function sendEvents(events: Item[]) {\n try {\n if (events.length === 0) return;\n\n if (session.isExpired()) {\n session.refresh();\n events.unshift({\n name: 'session_start',\n properties: {},\n options: { enableThirdPartyTracking: false },\n timestamp: new Date().toISOString(),\n });\n } else {\n session.updateLastActiveTime();\n }\n\n await tokenBucket.removeTokens();\n\n const tags = await config.getTags();\n const visitor_id = (await getVisitor()).id;\n\n const dto: CreateTrackEventDTO = events.map((event) => ({\n name: event.name,\n properties: event.properties,\n tags,\n visitor_id,\n session_id: session.getId(),\n platform: config.platform,\n environment: config.environment,\n timestamp: event.timestamp,\n }));\n\n const response = await fetch(`${config.endpoint}/events`, {\n method: 'POST',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(dto),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send track event: ${response.status} ${await response.text()}`);\n }\n\n const data = (await response.json()) as TrackEventResponse;\n\n let index = 0;\n while (events.length > 0) {\n const event = events.shift();\n if (!event) {\n index++;\n continue;\n }\n const { options, name, properties } = event;\n const eventId = data[index].id;\n options.onSucceed?.({ id: eventId });\n index++;\n if (\n !config.thirdPartyTrackers ||\n !options.enableThirdPartyTracking ||\n IGNORED_EVENTS.includes(name)\n ) {\n continue;\n }\n config.thirdPartyTrackers.forEach((tracker) => tracker(name, properties, eventId));\n }\n } catch (e: unknown) {\n if (e instanceof Error) console.log(e.message);\n events.forEach((event) => event.options.onError?.(e));\n }\n}\n\nconst batch = 10;\nconst delay = 2000;\nconst list: Item[] = [];\nlet timer: ReturnType<typeof setTimeout> | null = null;\n\nexport function track<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n options: TrackOptions = defaultOptions\n) {\n list.push({ name, properties, options, timestamp: new Date().toISOString() });\n if (list.length >= batch) {\n const copy = [...list];\n list.length = 0;\n sendEvents(copy);\n return;\n }\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n const copy = [...list];\n list.length = 0;\n sendEvents(copy);\n }, delay);\n}\n\nexport async function trackAsync<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n options: TrackOptions = defaultOptions\n) {\n await sendEvents([{ name, properties, options, timestamp: new Date().toISOString() }]);\n}\n\nexport function sendBeacon<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>\n) {\n if (!cache.tags || !cache.visitor) return;\n session.updateLastActiveTime();\n\n const dto: CreateTrackEventDTO = [\n {\n name,\n properties,\n tags: cache.tags,\n visitor_id: cache.visitor.id,\n session_id: session.getId(),\n platform: config.platform,\n environment: config.environment,\n timestamp: new Date().toISOString(),\n },\n ];\n const blob = new Blob([JSON.stringify(dto)], { type: 'application/json' });\n const success = navigator.sendBeacon(`${config.endpoint}/events`, blob);\n if (success) return;\n console.warn('Failed to send beacon', name, properties);\n}\n"],"mappings":"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","names":["_a"]}
1
+ {"version":3,"sources":["../../src/track/index.ts"],"sourcesContent":["import { TokenBucket, fetch } from '@shware/utils';\nimport type { CreateTrackEventDTO } from '../schema/index';\nimport { cache, config } from '../setup/index';\nimport { session } from '../setup/session';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { getVisitor } from '../visitor/index';\nimport type { EventName, TrackEventResponse, TrackName, TrackProperties } from './types';\n\nexport interface TrackOptions {\n enableThirdPartyTracking?: boolean;\n onSucceed?: (response?: TrackEventResponse[number]) => void;\n onError?: (error: unknown) => void;\n}\n\nconst defaultOptions: TrackOptions = { enableThirdPartyTracking: true };\nconst tokenBucket = new TokenBucket({ rate: 1, capacity: 20, requested: 2 });\n\ntype Item = {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n name: TrackName<any>;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n properties: TrackProperties<any>;\n timestamp: string;\n options: TrackOptions;\n};\n\nasync function sendEvents(events: Item[]) {\n try {\n if (events.length === 0) return;\n\n if (session.isExpired()) {\n session.refresh();\n events.unshift({\n name: 'session_start',\n properties: {},\n options: { enableThirdPartyTracking: false },\n timestamp: new Date().toISOString(),\n });\n } else {\n session.updateLastActiveTime();\n }\n\n await tokenBucket.removeTokens();\n\n const tags = await config.getTags();\n const visitor_id = (await getVisitor()).id;\n\n const dto: CreateTrackEventDTO = events.map((event) => ({\n name: event.name,\n properties: event.properties,\n tags,\n visitor_id,\n session_id: session.getId(),\n platform: config.platform,\n environment: config.environment,\n timestamp: event.timestamp,\n }));\n\n const response = await fetch(`${config.endpoint}/events`, {\n method: 'POST',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(dto),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send track event: ${response.status} ${await response.text()}`);\n }\n\n const data = (await response.json()) as TrackEventResponse;\n\n let index = 0;\n while (events.length > 0) {\n const event = events.shift();\n if (!event) {\n index++;\n continue;\n }\n const { options, name, properties } = event;\n const eventId = data[index].id;\n options.onSucceed?.({ id: eventId });\n index++;\n if (\n !config.thirdPartyTrackers ||\n !options.enableThirdPartyTracking ||\n IGNORED_EVENTS.includes(name)\n ) {\n continue;\n }\n config.thirdPartyTrackers.forEach((tracker) => tracker(name, properties, eventId));\n }\n } catch (e: unknown) {\n if (e instanceof Error) console.log(e.message);\n events.forEach((event) => event.options.onError?.(e));\n }\n}\n\nconst batch = 10;\nconst delay = 2000;\nconst list: Item[] = [];\nlet timer: ReturnType<typeof setTimeout> | null = null;\n\nexport function track<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n options: TrackOptions = defaultOptions\n) {\n list.push({ name, properties, options, timestamp: new Date().toISOString() });\n if (list.length >= batch) {\n const copy = [...list];\n list.length = 0;\n void sendEvents(copy);\n return;\n }\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n const copy = [...list];\n list.length = 0;\n void sendEvents(copy);\n }, delay);\n}\n\nexport async function trackAsync<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n options: TrackOptions = defaultOptions\n) {\n await sendEvents([{ name, properties, options, timestamp: new Date().toISOString() }]);\n}\n\nexport function sendBeacon<T extends EventName = EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>\n) {\n if (!cache.tags || !cache.visitor) return;\n session.updateLastActiveTime();\n\n const dto: CreateTrackEventDTO = [\n {\n name,\n properties,\n tags: cache.tags,\n visitor_id: cache.visitor.id,\n session_id: session.getId(),\n platform: config.platform,\n environment: config.environment,\n timestamp: new Date().toISOString(),\n },\n ];\n const blob = new Blob([JSON.stringify(dto)], { type: 'application/json' });\n const success = navigator.sendBeacon(`${config.endpoint}/events`, blob);\n if (success) return;\n console.warn('Failed to send beacon', name, properties);\n}\n"],"mappings":"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","names":["_a"]}
@@ -106,7 +106,7 @@ function mapServerStandardEvent(name) {
106
106
  if (name === "Purchase") return "PURCHASE";
107
107
  if (name === "Lead") return "LEAD";
108
108
  if (name === "SignUp") return "SIGN_UP";
109
- throw new Error(`Unsupported standard event: ${name}`);
109
+ throw new Error(`Unsupported standard event: ${String(name)}`);
110
110
  }
111
111
  // Annotate the CommonJS export names for ESM import in node:
112
112
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/track/rdt.ts"],"sourcesContent":["import type { Item } from './gtag';\nimport type { EventName, TrackName, TrackProperties } from './types';\n\n/** https://business.reddithelp.com/s/article/about-event-metadata */\nexport type Product = {\n /**\n * (Required) Product ID: Either the SKU or GTIN, which represents the variant ID, not the\n * parent ID. If there are no variants, pass the assigned ID for that item.\n */\n id: string;\n\n /** (Optional) Product Category: The group the product belongs to. */\n name?: string;\n\n /** (Optional) Product Name: The title of the product. */\n category?: string;\n};\n\n/**\n * https://business.reddithelp.com/s/article/supported-conversion-events#supported-conversion-events\n * https://business.reddithelp.com/s/article/about-event-metadata\n * https://business.reddithelp.com/s/article/map-a-catalog-to-a-signal-source\n */\nexport type StandardEvents = {\n PageVisit: {\n conversionId?: string;\n products?: Product[];\n };\n ViewContent: {\n conversionId?: string;\n products?: Product[];\n };\n Search: {\n conversionId?: string;\n products?: Product[];\n };\n AddToCart: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n AddToWishlist: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n Purchase: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n Lead: {\n value?: number;\n currency?: string;\n conversionId?: string;\n products?: Product[];\n };\n SignUp: {\n value?: number;\n currency?: string;\n conversionId?: string;\n products?: Product[];\n };\n};\n\nexport type PixelId = `a2_${string}`;\n\nexport type MatchKeys = {\n email?: string;\n phoneNumber?: string;\n externalId?: string;\n idfa?: string;\n aaid?: string;\n};\n\nexport type CustomEventParams<T extends string = string> = {\n customEventName: T;\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n};\n\nexport interface RDT {\n rdt(event: 'init', pixelId: PixelId, matchKeys?: MatchKeys): void;\n\n rdt<T extends keyof StandardEvents>(event: 'track', type: T, params?: StandardEvents[T]): void;\n\n rdt(event: 'track', type: 'Custom', params: CustomEventParams): void;\n}\n\ntype Mapped<F extends keyof StandardEvents> = [F, StandardEvents[F]];\ntype Missed<F extends string> = ['Custom', CustomEventParams<F>];\n\nexport function mapItems(items?: Item[]): undefined | Product[] {\n if (!items || items.length === 0) return undefined;\n return items.map((item) => ({\n id: item.item_id,\n name: item.item_name,\n category: item.item_category,\n }));\n}\n\nexport function mapRDTEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n): Mapped<keyof StandardEvents> | Missed<TrackName<T>> {\n // standard events\n if (name === 'page_view') {\n return ['PageVisit', { conversionId: eventId }];\n } else if (name === 'view_item') {\n return ['ViewContent', { conversionId: eventId }];\n } else if (name === 'search') {\n return ['Search', { conversionId: eventId }];\n } else if (name === 'add_to_cart') {\n const p = properties as TrackProperties<'add_to_cart'> | undefined;\n return [\n 'AddToCart',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'add_to_wishlist') {\n const p = properties as TrackProperties<'add_to_wishlist'> | undefined;\n return [\n 'AddToWishlist',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'purchase') {\n const p = properties as TrackProperties<'purchase'> | undefined;\n return [\n 'Purchase',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'generate_lead') {\n const p = properties as TrackProperties<'generate_lead'> | undefined;\n return [\n 'Lead',\n { conversionId: eventId, value: p?.value, currency: p?.currency.toUpperCase() },\n ];\n } else if (name === 'sign_up' || name === 'login') {\n return ['SignUp', { conversionId: eventId }];\n } else {\n // custom event\n return [\n 'Custom',\n {\n customEventName: name,\n conversionId: eventId,\n value:\n properties && 'value' in properties && typeof properties.value === 'number'\n ? properties.value\n : undefined,\n currency:\n properties && 'currency' in properties && typeof properties.currency === 'string'\n ? properties.currency.toUpperCase()\n : undefined,\n },\n ];\n }\n}\n\nexport type ServerStandardEvent =\n | 'PAGE_VISIT'\n | 'VIEW_CONTENT'\n | 'SEARCH'\n | 'ADD_TO_CART'\n | 'ADD_TO_WISHLIST'\n | 'PURCHASE'\n | 'LEAD'\n | 'SIGN_UP';\n\nexport function mapServerStandardEvent(name: keyof StandardEvents): ServerStandardEvent {\n if (name === 'PageVisit') return 'PAGE_VISIT';\n if (name === 'ViewContent') return 'VIEW_CONTENT';\n if (name === 'Search') return 'SEARCH';\n if (name === 'AddToCart') return 'ADD_TO_CART';\n if (name === 'AddToWishlist') return 'ADD_TO_WISHLIST';\n if (name === 'Purchase') return 'PURCHASE';\n if (name === 'Lead') return 'LEAD';\n if (name === 'SignUp') return 'SIGN_UP';\n throw new Error(`Unsupported standard event: ${name}`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqGO,SAAS,SAAS,OAAuC;AAC9D,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,SAAO,MAAM,IAAI,CAAC,UAAU;AAAA,IAC1B,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,IACX,UAAU,KAAK;AAAA,EACjB,EAAE;AACJ;AAEO,SAAS,YACd,MACA,YACA,SACqD;AAlHvD;AAoHE,MAAI,SAAS,aAAa;AACxB,WAAO,CAAC,aAAa,EAAE,cAAc,QAAQ,CAAC;AAAA,EAChD,WAAW,SAAS,aAAa;AAC/B,WAAO,CAAC,eAAe,EAAE,cAAc,QAAQ,CAAC;AAAA,EAClD,WAAW,SAAS,UAAU;AAC5B,WAAO,CAAC,UAAU,EAAE,cAAc,QAAQ,CAAC;AAAA,EAC7C,WAAW,SAAS,eAAe;AACjC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,mBAAmB;AACrC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,YAAY;AAC9B,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,iBAAiB;AACnC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA,EAAE,cAAc,SAAS,OAAO,uBAAG,OAAO,UAAU,uBAAG,SAAS,cAAc;AAAA,IAChF;AAAA,EACF,WAAW,SAAS,aAAa,SAAS,SAAS;AACjD,WAAO,CAAC,UAAU,EAAE,cAAc,QAAQ,CAAC;AAAA,EAC7C,OAAO;AAEL,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,iBAAiB;AAAA,QACjB,cAAc;AAAA,QACd,OACE,cAAc,WAAW,cAAc,OAAO,WAAW,UAAU,WAC/D,WAAW,QACX;AAAA,QACN,UACE,cAAc,cAAc,cAAc,OAAO,WAAW,aAAa,WACrE,WAAW,SAAS,YAAY,IAChC;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAYO,SAAS,uBAAuB,MAAiD;AACtF,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,SAAS,cAAe,QAAO;AACnC,MAAI,SAAS,SAAU,QAAO;AAC9B,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,SAAS,gBAAiB,QAAO;AACrC,MAAI,SAAS,WAAY,QAAO;AAChC,MAAI,SAAS,OAAQ,QAAO;AAC5B,MAAI,SAAS,SAAU,QAAO;AAC9B,QAAM,IAAI,MAAM,+BAA+B,IAAI,EAAE;AACvD;","names":[]}
1
+ {"version":3,"sources":["../../src/track/rdt.ts"],"sourcesContent":["import type { Item } from './gtag';\nimport type { EventName, TrackName, TrackProperties } from './types';\n\n/** https://business.reddithelp.com/s/article/about-event-metadata */\nexport type Product = {\n /**\n * (Required) Product ID: Either the SKU or GTIN, which represents the variant ID, not the\n * parent ID. If there are no variants, pass the assigned ID for that item.\n */\n id: string;\n\n /** (Optional) Product Category: The group the product belongs to. */\n name?: string;\n\n /** (Optional) Product Name: The title of the product. */\n category?: string;\n};\n\n/**\n * https://business.reddithelp.com/s/article/supported-conversion-events#supported-conversion-events\n * https://business.reddithelp.com/s/article/about-event-metadata\n * https://business.reddithelp.com/s/article/map-a-catalog-to-a-signal-source\n */\nexport type StandardEvents = {\n PageVisit: {\n conversionId?: string;\n products?: Product[];\n };\n ViewContent: {\n conversionId?: string;\n products?: Product[];\n };\n Search: {\n conversionId?: string;\n products?: Product[];\n };\n AddToCart: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n AddToWishlist: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n Purchase: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n Lead: {\n value?: number;\n currency?: string;\n conversionId?: string;\n products?: Product[];\n };\n SignUp: {\n value?: number;\n currency?: string;\n conversionId?: string;\n products?: Product[];\n };\n};\n\nexport type PixelId = `a2_${string}`;\n\nexport type MatchKeys = {\n email?: string;\n phoneNumber?: string;\n externalId?: string;\n idfa?: string;\n aaid?: string;\n};\n\nexport type CustomEventParams<T extends string = string> = {\n customEventName: T;\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n};\n\nexport interface RDT {\n rdt(event: 'init', pixelId: PixelId, matchKeys?: MatchKeys): void;\n\n rdt<T extends keyof StandardEvents>(event: 'track', type: T, params?: StandardEvents[T]): void;\n\n rdt(event: 'track', type: 'Custom', params: CustomEventParams): void;\n}\n\ntype Mapped<F extends keyof StandardEvents> = [F, StandardEvents[F]];\ntype Missed<F extends string> = ['Custom', CustomEventParams<F>];\n\nexport function mapItems(items?: Item[]): undefined | Product[] {\n if (!items || items.length === 0) return undefined;\n return items.map((item) => ({\n id: item.item_id,\n name: item.item_name,\n category: item.item_category,\n }));\n}\n\nexport function mapRDTEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n): Mapped<keyof StandardEvents> | Missed<TrackName<T>> {\n // standard events\n if (name === 'page_view') {\n return ['PageVisit', { conversionId: eventId }];\n } else if (name === 'view_item') {\n return ['ViewContent', { conversionId: eventId }];\n } else if (name === 'search') {\n return ['Search', { conversionId: eventId }];\n } else if (name === 'add_to_cart') {\n const p = properties as TrackProperties<'add_to_cart'> | undefined;\n return [\n 'AddToCart',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'add_to_wishlist') {\n const p = properties as TrackProperties<'add_to_wishlist'> | undefined;\n return [\n 'AddToWishlist',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'purchase') {\n const p = properties as TrackProperties<'purchase'> | undefined;\n return [\n 'Purchase',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'generate_lead') {\n const p = properties as TrackProperties<'generate_lead'> | undefined;\n return [\n 'Lead',\n { conversionId: eventId, value: p?.value, currency: p?.currency.toUpperCase() },\n ];\n } else if (name === 'sign_up' || name === 'login') {\n return ['SignUp', { conversionId: eventId }];\n } else {\n // custom event\n return [\n 'Custom',\n {\n customEventName: name,\n conversionId: eventId,\n value:\n properties && 'value' in properties && typeof properties.value === 'number'\n ? properties.value\n : undefined,\n currency:\n properties && 'currency' in properties && typeof properties.currency === 'string'\n ? properties.currency.toUpperCase()\n : undefined,\n },\n ];\n }\n}\n\nexport type ServerStandardEvent =\n | 'PAGE_VISIT'\n | 'VIEW_CONTENT'\n | 'SEARCH'\n | 'ADD_TO_CART'\n | 'ADD_TO_WISHLIST'\n | 'PURCHASE'\n | 'LEAD'\n | 'SIGN_UP';\n\nexport function mapServerStandardEvent(name: keyof StandardEvents): ServerStandardEvent {\n if (name === 'PageVisit') return 'PAGE_VISIT';\n if (name === 'ViewContent') return 'VIEW_CONTENT';\n if (name === 'Search') return 'SEARCH';\n if (name === 'AddToCart') return 'ADD_TO_CART';\n if (name === 'AddToWishlist') return 'ADD_TO_WISHLIST';\n if (name === 'Purchase') return 'PURCHASE';\n if (name === 'Lead') return 'LEAD';\n if (name === 'SignUp') return 'SIGN_UP';\n throw new Error(`Unsupported standard event: ${String(name)}`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqGO,SAAS,SAAS,OAAuC;AAC9D,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,SAAO,MAAM,IAAI,CAAC,UAAU;AAAA,IAC1B,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,IACX,UAAU,KAAK;AAAA,EACjB,EAAE;AACJ;AAEO,SAAS,YACd,MACA,YACA,SACqD;AAlHvD;AAoHE,MAAI,SAAS,aAAa;AACxB,WAAO,CAAC,aAAa,EAAE,cAAc,QAAQ,CAAC;AAAA,EAChD,WAAW,SAAS,aAAa;AAC/B,WAAO,CAAC,eAAe,EAAE,cAAc,QAAQ,CAAC;AAAA,EAClD,WAAW,SAAS,UAAU;AAC5B,WAAO,CAAC,UAAU,EAAE,cAAc,QAAQ,CAAC;AAAA,EAC7C,WAAW,SAAS,eAAe;AACjC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,mBAAmB;AACrC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,YAAY;AAC9B,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,iBAAiB;AACnC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA,EAAE,cAAc,SAAS,OAAO,uBAAG,OAAO,UAAU,uBAAG,SAAS,cAAc;AAAA,IAChF;AAAA,EACF,WAAW,SAAS,aAAa,SAAS,SAAS;AACjD,WAAO,CAAC,UAAU,EAAE,cAAc,QAAQ,CAAC;AAAA,EAC7C,OAAO;AAEL,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,iBAAiB;AAAA,QACjB,cAAc;AAAA,QACd,OACE,cAAc,WAAW,cAAc,OAAO,WAAW,UAAU,WAC/D,WAAW,QACX;AAAA,QACN,UACE,cAAc,cAAc,cAAc,OAAO,WAAW,aAAa,WACrE,WAAW,SAAS,YAAY,IAChC;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAYO,SAAS,uBAAuB,MAAiD;AACtF,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,SAAS,cAAe,QAAO;AACnC,MAAI,SAAS,SAAU,QAAO;AAC9B,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,SAAS,gBAAiB,QAAO;AACrC,MAAI,SAAS,WAAY,QAAO;AAChC,MAAI,SAAS,OAAQ,QAAO;AAC5B,MAAI,SAAS,SAAU,QAAO;AAC9B,QAAM,IAAI,MAAM,+BAA+B,OAAO,IAAI,CAAC,EAAE;AAC/D;","names":[]}
@@ -80,7 +80,7 @@ function mapServerStandardEvent(name) {
80
80
  if (name === "Purchase") return "PURCHASE";
81
81
  if (name === "Lead") return "LEAD";
82
82
  if (name === "SignUp") return "SIGN_UP";
83
- throw new Error(`Unsupported standard event: ${name}`);
83
+ throw new Error(`Unsupported standard event: ${String(name)}`);
84
84
  }
85
85
  export {
86
86
  mapItems,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/track/rdt.ts"],"sourcesContent":["import type { Item } from './gtag';\nimport type { EventName, TrackName, TrackProperties } from './types';\n\n/** https://business.reddithelp.com/s/article/about-event-metadata */\nexport type Product = {\n /**\n * (Required) Product ID: Either the SKU or GTIN, which represents the variant ID, not the\n * parent ID. If there are no variants, pass the assigned ID for that item.\n */\n id: string;\n\n /** (Optional) Product Category: The group the product belongs to. */\n name?: string;\n\n /** (Optional) Product Name: The title of the product. */\n category?: string;\n};\n\n/**\n * https://business.reddithelp.com/s/article/supported-conversion-events#supported-conversion-events\n * https://business.reddithelp.com/s/article/about-event-metadata\n * https://business.reddithelp.com/s/article/map-a-catalog-to-a-signal-source\n */\nexport type StandardEvents = {\n PageVisit: {\n conversionId?: string;\n products?: Product[];\n };\n ViewContent: {\n conversionId?: string;\n products?: Product[];\n };\n Search: {\n conversionId?: string;\n products?: Product[];\n };\n AddToCart: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n AddToWishlist: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n Purchase: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n Lead: {\n value?: number;\n currency?: string;\n conversionId?: string;\n products?: Product[];\n };\n SignUp: {\n value?: number;\n currency?: string;\n conversionId?: string;\n products?: Product[];\n };\n};\n\nexport type PixelId = `a2_${string}`;\n\nexport type MatchKeys = {\n email?: string;\n phoneNumber?: string;\n externalId?: string;\n idfa?: string;\n aaid?: string;\n};\n\nexport type CustomEventParams<T extends string = string> = {\n customEventName: T;\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n};\n\nexport interface RDT {\n rdt(event: 'init', pixelId: PixelId, matchKeys?: MatchKeys): void;\n\n rdt<T extends keyof StandardEvents>(event: 'track', type: T, params?: StandardEvents[T]): void;\n\n rdt(event: 'track', type: 'Custom', params: CustomEventParams): void;\n}\n\ntype Mapped<F extends keyof StandardEvents> = [F, StandardEvents[F]];\ntype Missed<F extends string> = ['Custom', CustomEventParams<F>];\n\nexport function mapItems(items?: Item[]): undefined | Product[] {\n if (!items || items.length === 0) return undefined;\n return items.map((item) => ({\n id: item.item_id,\n name: item.item_name,\n category: item.item_category,\n }));\n}\n\nexport function mapRDTEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n): Mapped<keyof StandardEvents> | Missed<TrackName<T>> {\n // standard events\n if (name === 'page_view') {\n return ['PageVisit', { conversionId: eventId }];\n } else if (name === 'view_item') {\n return ['ViewContent', { conversionId: eventId }];\n } else if (name === 'search') {\n return ['Search', { conversionId: eventId }];\n } else if (name === 'add_to_cart') {\n const p = properties as TrackProperties<'add_to_cart'> | undefined;\n return [\n 'AddToCart',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'add_to_wishlist') {\n const p = properties as TrackProperties<'add_to_wishlist'> | undefined;\n return [\n 'AddToWishlist',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'purchase') {\n const p = properties as TrackProperties<'purchase'> | undefined;\n return [\n 'Purchase',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'generate_lead') {\n const p = properties as TrackProperties<'generate_lead'> | undefined;\n return [\n 'Lead',\n { conversionId: eventId, value: p?.value, currency: p?.currency.toUpperCase() },\n ];\n } else if (name === 'sign_up' || name === 'login') {\n return ['SignUp', { conversionId: eventId }];\n } else {\n // custom event\n return [\n 'Custom',\n {\n customEventName: name,\n conversionId: eventId,\n value:\n properties && 'value' in properties && typeof properties.value === 'number'\n ? properties.value\n : undefined,\n currency:\n properties && 'currency' in properties && typeof properties.currency === 'string'\n ? properties.currency.toUpperCase()\n : undefined,\n },\n ];\n }\n}\n\nexport type ServerStandardEvent =\n | 'PAGE_VISIT'\n | 'VIEW_CONTENT'\n | 'SEARCH'\n | 'ADD_TO_CART'\n | 'ADD_TO_WISHLIST'\n | 'PURCHASE'\n | 'LEAD'\n | 'SIGN_UP';\n\nexport function mapServerStandardEvent(name: keyof StandardEvents): ServerStandardEvent {\n if (name === 'PageVisit') return 'PAGE_VISIT';\n if (name === 'ViewContent') return 'VIEW_CONTENT';\n if (name === 'Search') return 'SEARCH';\n if (name === 'AddToCart') return 'ADD_TO_CART';\n if (name === 'AddToWishlist') return 'ADD_TO_WISHLIST';\n if (name === 'Purchase') return 'PURCHASE';\n if (name === 'Lead') return 'LEAD';\n if (name === 'SignUp') return 'SIGN_UP';\n throw new Error(`Unsupported standard event: ${name}`);\n}\n"],"mappings":";AAqGO,SAAS,SAAS,OAAuC;AAC9D,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,SAAO,MAAM,IAAI,CAAC,UAAU;AAAA,IAC1B,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,IACX,UAAU,KAAK;AAAA,EACjB,EAAE;AACJ;AAEO,SAAS,YACd,MACA,YACA,SACqD;AAlHvD;AAoHE,MAAI,SAAS,aAAa;AACxB,WAAO,CAAC,aAAa,EAAE,cAAc,QAAQ,CAAC;AAAA,EAChD,WAAW,SAAS,aAAa;AAC/B,WAAO,CAAC,eAAe,EAAE,cAAc,QAAQ,CAAC;AAAA,EAClD,WAAW,SAAS,UAAU;AAC5B,WAAO,CAAC,UAAU,EAAE,cAAc,QAAQ,CAAC;AAAA,EAC7C,WAAW,SAAS,eAAe;AACjC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,mBAAmB;AACrC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,YAAY;AAC9B,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,iBAAiB;AACnC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA,EAAE,cAAc,SAAS,OAAO,uBAAG,OAAO,UAAU,uBAAG,SAAS,cAAc;AAAA,IAChF;AAAA,EACF,WAAW,SAAS,aAAa,SAAS,SAAS;AACjD,WAAO,CAAC,UAAU,EAAE,cAAc,QAAQ,CAAC;AAAA,EAC7C,OAAO;AAEL,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,iBAAiB;AAAA,QACjB,cAAc;AAAA,QACd,OACE,cAAc,WAAW,cAAc,OAAO,WAAW,UAAU,WAC/D,WAAW,QACX;AAAA,QACN,UACE,cAAc,cAAc,cAAc,OAAO,WAAW,aAAa,WACrE,WAAW,SAAS,YAAY,IAChC;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAYO,SAAS,uBAAuB,MAAiD;AACtF,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,SAAS,cAAe,QAAO;AACnC,MAAI,SAAS,SAAU,QAAO;AAC9B,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,SAAS,gBAAiB,QAAO;AACrC,MAAI,SAAS,WAAY,QAAO;AAChC,MAAI,SAAS,OAAQ,QAAO;AAC5B,MAAI,SAAS,SAAU,QAAO;AAC9B,QAAM,IAAI,MAAM,+BAA+B,IAAI,EAAE;AACvD;","names":[]}
1
+ {"version":3,"sources":["../../src/track/rdt.ts"],"sourcesContent":["import type { Item } from './gtag';\nimport type { EventName, TrackName, TrackProperties } from './types';\n\n/** https://business.reddithelp.com/s/article/about-event-metadata */\nexport type Product = {\n /**\n * (Required) Product ID: Either the SKU or GTIN, which represents the variant ID, not the\n * parent ID. If there are no variants, pass the assigned ID for that item.\n */\n id: string;\n\n /** (Optional) Product Category: The group the product belongs to. */\n name?: string;\n\n /** (Optional) Product Name: The title of the product. */\n category?: string;\n};\n\n/**\n * https://business.reddithelp.com/s/article/supported-conversion-events#supported-conversion-events\n * https://business.reddithelp.com/s/article/about-event-metadata\n * https://business.reddithelp.com/s/article/map-a-catalog-to-a-signal-source\n */\nexport type StandardEvents = {\n PageVisit: {\n conversionId?: string;\n products?: Product[];\n };\n ViewContent: {\n conversionId?: string;\n products?: Product[];\n };\n Search: {\n conversionId?: string;\n products?: Product[];\n };\n AddToCart: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n AddToWishlist: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n Purchase: {\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n };\n Lead: {\n value?: number;\n currency?: string;\n conversionId?: string;\n products?: Product[];\n };\n SignUp: {\n value?: number;\n currency?: string;\n conversionId?: string;\n products?: Product[];\n };\n};\n\nexport type PixelId = `a2_${string}`;\n\nexport type MatchKeys = {\n email?: string;\n phoneNumber?: string;\n externalId?: string;\n idfa?: string;\n aaid?: string;\n};\n\nexport type CustomEventParams<T extends string = string> = {\n customEventName: T;\n value?: number;\n currency?: string;\n itemCount?: number;\n conversionId?: string;\n products?: Product[];\n};\n\nexport interface RDT {\n rdt(event: 'init', pixelId: PixelId, matchKeys?: MatchKeys): void;\n\n rdt<T extends keyof StandardEvents>(event: 'track', type: T, params?: StandardEvents[T]): void;\n\n rdt(event: 'track', type: 'Custom', params: CustomEventParams): void;\n}\n\ntype Mapped<F extends keyof StandardEvents> = [F, StandardEvents[F]];\ntype Missed<F extends string> = ['Custom', CustomEventParams<F>];\n\nexport function mapItems(items?: Item[]): undefined | Product[] {\n if (!items || items.length === 0) return undefined;\n return items.map((item) => ({\n id: item.item_id,\n name: item.item_name,\n category: item.item_category,\n }));\n}\n\nexport function mapRDTEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n): Mapped<keyof StandardEvents> | Missed<TrackName<T>> {\n // standard events\n if (name === 'page_view') {\n return ['PageVisit', { conversionId: eventId }];\n } else if (name === 'view_item') {\n return ['ViewContent', { conversionId: eventId }];\n } else if (name === 'search') {\n return ['Search', { conversionId: eventId }];\n } else if (name === 'add_to_cart') {\n const p = properties as TrackProperties<'add_to_cart'> | undefined;\n return [\n 'AddToCart',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'add_to_wishlist') {\n const p = properties as TrackProperties<'add_to_wishlist'> | undefined;\n return [\n 'AddToWishlist',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'purchase') {\n const p = properties as TrackProperties<'purchase'> | undefined;\n return [\n 'Purchase',\n {\n conversionId: eventId,\n value: p?.value,\n currency: p?.currency.toUpperCase(),\n itemCount: p?.items?.reduce((acc, i) => acc + (i.quantity ?? 1), 0),\n products: mapItems(p?.items),\n },\n ];\n } else if (name === 'generate_lead') {\n const p = properties as TrackProperties<'generate_lead'> | undefined;\n return [\n 'Lead',\n { conversionId: eventId, value: p?.value, currency: p?.currency.toUpperCase() },\n ];\n } else if (name === 'sign_up' || name === 'login') {\n return ['SignUp', { conversionId: eventId }];\n } else {\n // custom event\n return [\n 'Custom',\n {\n customEventName: name,\n conversionId: eventId,\n value:\n properties && 'value' in properties && typeof properties.value === 'number'\n ? properties.value\n : undefined,\n currency:\n properties && 'currency' in properties && typeof properties.currency === 'string'\n ? properties.currency.toUpperCase()\n : undefined,\n },\n ];\n }\n}\n\nexport type ServerStandardEvent =\n | 'PAGE_VISIT'\n | 'VIEW_CONTENT'\n | 'SEARCH'\n | 'ADD_TO_CART'\n | 'ADD_TO_WISHLIST'\n | 'PURCHASE'\n | 'LEAD'\n | 'SIGN_UP';\n\nexport function mapServerStandardEvent(name: keyof StandardEvents): ServerStandardEvent {\n if (name === 'PageVisit') return 'PAGE_VISIT';\n if (name === 'ViewContent') return 'VIEW_CONTENT';\n if (name === 'Search') return 'SEARCH';\n if (name === 'AddToCart') return 'ADD_TO_CART';\n if (name === 'AddToWishlist') return 'ADD_TO_WISHLIST';\n if (name === 'Purchase') return 'PURCHASE';\n if (name === 'Lead') return 'LEAD';\n if (name === 'SignUp') return 'SIGN_UP';\n throw new Error(`Unsupported standard event: ${String(name)}`);\n}\n"],"mappings":";AAqGO,SAAS,SAAS,OAAuC;AAC9D,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,SAAO,MAAM,IAAI,CAAC,UAAU;AAAA,IAC1B,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,IACX,UAAU,KAAK;AAAA,EACjB,EAAE;AACJ;AAEO,SAAS,YACd,MACA,YACA,SACqD;AAlHvD;AAoHE,MAAI,SAAS,aAAa;AACxB,WAAO,CAAC,aAAa,EAAE,cAAc,QAAQ,CAAC;AAAA,EAChD,WAAW,SAAS,aAAa;AAC/B,WAAO,CAAC,eAAe,EAAE,cAAc,QAAQ,CAAC;AAAA,EAClD,WAAW,SAAS,UAAU;AAC5B,WAAO,CAAC,UAAU,EAAE,cAAc,QAAQ,CAAC;AAAA,EAC7C,WAAW,SAAS,eAAe;AACjC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,mBAAmB;AACrC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,YAAY;AAC9B,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,cAAc;AAAA,QACd,OAAO,uBAAG;AAAA,QACV,UAAU,uBAAG,SAAS;AAAA,QACtB,YAAW,4BAAG,UAAH,mBAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,YAAY,IAAI;AAAA,QACjE,UAAU,SAAS,uBAAG,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,WAAW,SAAS,iBAAiB;AACnC,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,MACA,EAAE,cAAc,SAAS,OAAO,uBAAG,OAAO,UAAU,uBAAG,SAAS,cAAc;AAAA,IAChF;AAAA,EACF,WAAW,SAAS,aAAa,SAAS,SAAS;AACjD,WAAO,CAAC,UAAU,EAAE,cAAc,QAAQ,CAAC;AAAA,EAC7C,OAAO;AAEL,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,iBAAiB;AAAA,QACjB,cAAc;AAAA,QACd,OACE,cAAc,WAAW,cAAc,OAAO,WAAW,UAAU,WAC/D,WAAW,QACX;AAAA,QACN,UACE,cAAc,cAAc,cAAc,OAAO,WAAW,aAAa,WACrE,WAAW,SAAS,YAAY,IAChC;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAYO,SAAS,uBAAuB,MAAiD;AACtF,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,SAAS,cAAe,QAAO;AACnC,MAAI,SAAS,SAAU,QAAO;AAC9B,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,SAAS,gBAAiB,QAAO;AACrC,MAAI,SAAS,WAAY,QAAO;AAChC,MAAI,SAAS,OAAQ,QAAO;AAC5B,MAAI,SAAS,SAAU,QAAO;AAC9B,QAAM,IAAI,MAAM,+BAA+B,OAAO,IAAI,CAAC,EAAE;AAC/D;","names":[]}
@@ -79,7 +79,7 @@ async function getTags() {
79
79
  time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,
80
80
  environment: process.env.NODE_ENV === "development" ? "development" : "production",
81
81
  source: "web",
82
- source_url: window.location.origin + window.location.pathname,
82
+ source_url: window.location.href,
83
83
  page_referrer: document.referrer || void 0,
84
84
  // Meta Ads
85
85
  fbc: parsed._fbc ?? import_utils.expiringStorage.getItem(import_storage.keys.fbc) ?? void 0,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/web/index.ts"],"sourcesContent":["import { expiringStorage } from '@shware/utils';\nimport Bowser from 'bowser';\nimport { parseCookie } from 'cookie';\nimport { v4 as uuidv4 } from 'uuid';\nimport { keys } from '../constants/storage';\nimport { getLink } from '../link/index';\nimport { type Storage, cache, config } from '../setup/index';\nimport type { TrackTags } from '../track/types';\n\nexport function getDeviceId() {\n const cached = localStorage.getItem(keys.device_id);\n if (cached) return cached;\n const id = crypto?.randomUUID ? crypto.randomUUID() : uuidv4();\n localStorage.setItem(keys.device_id, id);\n return id;\n}\n\nexport async function getTags() {\n const parser = Bowser.getParser(window.navigator.userAgent);\n const params = new URLSearchParams(window.location.search);\n const os = parser.getOS();\n const browser = parser.getBrowser();\n const platform = parser.getPlatform();\n const parsed = parseCookie(document.cookie);\n\n const linkId = params.get('s');\n const link = linkId ? await getLink(linkId) : null;\n\n const tags: TrackTags = {\n os: `${os.name} ${os.version}`,\n os_name: os.name,\n os_version: os.version,\n browser: `${browser.name} ${browser.version}`,\n browser_name: browser.name,\n browser_version: browser.version,\n platform: 'web',\n device: platform.model,\n device_id: getDeviceId(),\n device_type: platform.type,\n device_vendor: platform.vendor,\n device_pixel_ratio: window.devicePixelRatio,\n screen_width: window.screen.width,\n screen_height: window.screen.height,\n screen_resolution: `${window.screen.width}x${window.screen.height}`,\n release: config.release,\n language: navigator.language,\n time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n environment: process.env.NODE_ENV === 'development' ? 'development' : 'production',\n source: 'web',\n source_url: window.location.origin + window.location.pathname,\n page_referrer: document.referrer || undefined,\n // Meta Ads\n fbc: parsed._fbc ?? expiringStorage.getItem<string>(keys.fbc) ?? undefined,\n fbp: parsed._fbp,\n fbclid: params.get('fbclid') ?? expiringStorage.getItem<string>(keys.fbclid) ?? undefined,\n ad_id: params.get('ad_id') ?? undefined,\n ad_name: params.get('ad_name') ?? undefined,\n adset_id: params.get('adset_id') ?? undefined,\n adset_name: params.get('adset_name') ?? undefined,\n campaign_id: params.get('campaign_id') ?? undefined,\n campaign_name: params.get('campaign_name') ?? undefined,\n placement: params.get('placement') ?? undefined,\n site_source_name: params.get('site_source_name') ?? undefined,\n // Google Ads\n gclid: params.get('gclid') ?? undefined,\n gclsrc: params.get('gclsrc') ?? undefined,\n gad_source: params.get('gad_source') ?? undefined,\n gad_campaignid: params.get('gad_campaignid') ?? undefined,\n // Reddit Ads\n rdt_cid:\n params.get('rdt_cid') ??\n parsed._rdt_cid ??\n expiringStorage.getItem<string>(keys.rdt_cid) ??\n undefined,\n rdt_uuid: parsed._rdt_uuid,\n // LinkedIn Ads: get click id from url params or first-party cookie\n li_fat_id: params.get('li_fat_id') ?? parsed.li_fat_id ?? undefined,\n // click ids\n dclid: params.get('dclid') ?? undefined,\n ko_click_id: params.get('ko_click_id') ?? undefined,\n msclkid: params.get('msclkid') ?? undefined,\n sccid: params.get('sccid') ?? undefined,\n ttclid: params.get('ttclid') ?? undefined,\n twclid: params.get('twclid') ?? undefined,\n wbraid: params.get('wbraid') ?? undefined,\n yclid: params.get('yclid') ?? undefined,\n // utm params\n utm_source: link?.utm_source ?? params.get('utm_source') ?? undefined,\n utm_medium: link?.utm_medium ?? params.get('utm_medium') ?? undefined,\n utm_campaign: link?.utm_campaign ?? params.get('utm_campaign') ?? undefined,\n utm_term: link?.utm_term ?? params.get('utm_term') ?? undefined,\n utm_content: link?.utm_content ?? params.get('utm_content') ?? undefined,\n utm_id: link?.utm_id ?? params.get('utm_id') ?? undefined,\n utm_source_platform:\n link?.utm_source_platform ?? params.get('utm_source_platform') ?? undefined,\n utm_creative_format:\n link?.utm_creative_format ?? params.get('utm_creative_format') ?? undefined,\n utm_marketing_tactic:\n link?.utm_marketing_tactic ?? params.get('utm_marketing_tactic') ?? undefined,\n };\n\n cache.tags = tags;\n return tags;\n}\n\nconst map = new Map<string, string>();\n\nexport const storage: Storage = {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n console.error('localStorage is not available');\n return map.get(key) ?? null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n console.error('localStorage is not available');\n map.set(key, value);\n }\n },\n};\n"],"mappings":"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","names":["uuidv4","Bowser"]}
1
+ {"version":3,"sources":["../../src/web/index.ts"],"sourcesContent":["import { expiringStorage } from '@shware/utils';\nimport Bowser from 'bowser';\nimport { parseCookie } from 'cookie';\nimport { v4 as uuidv4 } from 'uuid';\nimport { keys } from '../constants/storage';\nimport { getLink } from '../link/index';\nimport { type Storage, cache, config } from '../setup/index';\nimport type { TrackTags } from '../track/types';\n\nexport function getDeviceId() {\n const cached = localStorage.getItem(keys.device_id);\n if (cached) return cached;\n const id = crypto?.randomUUID ? crypto.randomUUID() : uuidv4();\n localStorage.setItem(keys.device_id, id);\n return id;\n}\n\nexport async function getTags() {\n const parser = Bowser.getParser(window.navigator.userAgent);\n const params = new URLSearchParams(window.location.search);\n const os = parser.getOS();\n const browser = parser.getBrowser();\n const platform = parser.getPlatform();\n const parsed = parseCookie(document.cookie);\n\n const linkId = params.get('s');\n const link = linkId ? await getLink(linkId) : null;\n\n const tags: TrackTags = {\n os: `${os.name} ${os.version}`,\n os_name: os.name,\n os_version: os.version,\n browser: `${browser.name} ${browser.version}`,\n browser_name: browser.name,\n browser_version: browser.version,\n platform: 'web',\n device: platform.model,\n device_id: getDeviceId(),\n device_type: platform.type,\n device_vendor: platform.vendor,\n device_pixel_ratio: window.devicePixelRatio,\n screen_width: window.screen.width,\n screen_height: window.screen.height,\n screen_resolution: `${window.screen.width}x${window.screen.height}`,\n release: config.release,\n language: navigator.language,\n time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n environment: process.env.NODE_ENV === 'development' ? 'development' : 'production',\n source: 'web',\n source_url: window.location.href,\n page_referrer: document.referrer || undefined,\n // Meta Ads\n fbc: parsed._fbc ?? expiringStorage.getItem<string>(keys.fbc) ?? undefined,\n fbp: parsed._fbp,\n fbclid: params.get('fbclid') ?? expiringStorage.getItem<string>(keys.fbclid) ?? undefined,\n ad_id: params.get('ad_id') ?? undefined,\n ad_name: params.get('ad_name') ?? undefined,\n adset_id: params.get('adset_id') ?? undefined,\n adset_name: params.get('adset_name') ?? undefined,\n campaign_id: params.get('campaign_id') ?? undefined,\n campaign_name: params.get('campaign_name') ?? undefined,\n placement: params.get('placement') ?? undefined,\n site_source_name: params.get('site_source_name') ?? undefined,\n // Google Ads\n gclid: params.get('gclid') ?? undefined,\n gclsrc: params.get('gclsrc') ?? undefined,\n gad_source: params.get('gad_source') ?? undefined,\n gad_campaignid: params.get('gad_campaignid') ?? undefined,\n // Reddit Ads\n rdt_cid:\n params.get('rdt_cid') ??\n parsed._rdt_cid ??\n expiringStorage.getItem<string>(keys.rdt_cid) ??\n undefined,\n rdt_uuid: parsed._rdt_uuid,\n // LinkedIn Ads: get click id from url params or first-party cookie\n li_fat_id: params.get('li_fat_id') ?? parsed.li_fat_id ?? undefined,\n // click ids\n dclid: params.get('dclid') ?? undefined,\n ko_click_id: params.get('ko_click_id') ?? undefined,\n msclkid: params.get('msclkid') ?? undefined,\n sccid: params.get('sccid') ?? undefined,\n ttclid: params.get('ttclid') ?? undefined,\n twclid: params.get('twclid') ?? undefined,\n wbraid: params.get('wbraid') ?? undefined,\n yclid: params.get('yclid') ?? undefined,\n // utm params\n utm_source: link?.utm_source ?? params.get('utm_source') ?? undefined,\n utm_medium: link?.utm_medium ?? params.get('utm_medium') ?? undefined,\n utm_campaign: link?.utm_campaign ?? params.get('utm_campaign') ?? undefined,\n utm_term: link?.utm_term ?? params.get('utm_term') ?? undefined,\n utm_content: link?.utm_content ?? params.get('utm_content') ?? undefined,\n utm_id: link?.utm_id ?? params.get('utm_id') ?? undefined,\n utm_source_platform:\n link?.utm_source_platform ?? params.get('utm_source_platform') ?? undefined,\n utm_creative_format:\n link?.utm_creative_format ?? params.get('utm_creative_format') ?? undefined,\n utm_marketing_tactic:\n link?.utm_marketing_tactic ?? params.get('utm_marketing_tactic') ?? undefined,\n };\n\n cache.tags = tags;\n return tags;\n}\n\nconst map = new Map<string, string>();\n\nexport const storage: Storage = {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n console.error('localStorage is not available');\n return map.get(key) ?? null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n console.error('localStorage is not available');\n map.set(key, value);\n }\n },\n};\n"],"mappings":"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","names":["uuidv4","Bowser"]}
@@ -43,7 +43,7 @@ async function getTags() {
43
43
  time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,
44
44
  environment: process.env.NODE_ENV === "development" ? "development" : "production",
45
45
  source: "web",
46
- source_url: window.location.origin + window.location.pathname,
46
+ source_url: window.location.href,
47
47
  page_referrer: document.referrer || void 0,
48
48
  // Meta Ads
49
49
  fbc: parsed._fbc ?? expiringStorage.getItem(keys.fbc) ?? void 0,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/web/index.ts"],"sourcesContent":["import { expiringStorage } from '@shware/utils';\nimport Bowser from 'bowser';\nimport { parseCookie } from 'cookie';\nimport { v4 as uuidv4 } from 'uuid';\nimport { keys } from '../constants/storage';\nimport { getLink } from '../link/index';\nimport { type Storage, cache, config } from '../setup/index';\nimport type { TrackTags } from '../track/types';\n\nexport function getDeviceId() {\n const cached = localStorage.getItem(keys.device_id);\n if (cached) return cached;\n const id = crypto?.randomUUID ? crypto.randomUUID() : uuidv4();\n localStorage.setItem(keys.device_id, id);\n return id;\n}\n\nexport async function getTags() {\n const parser = Bowser.getParser(window.navigator.userAgent);\n const params = new URLSearchParams(window.location.search);\n const os = parser.getOS();\n const browser = parser.getBrowser();\n const platform = parser.getPlatform();\n const parsed = parseCookie(document.cookie);\n\n const linkId = params.get('s');\n const link = linkId ? await getLink(linkId) : null;\n\n const tags: TrackTags = {\n os: `${os.name} ${os.version}`,\n os_name: os.name,\n os_version: os.version,\n browser: `${browser.name} ${browser.version}`,\n browser_name: browser.name,\n browser_version: browser.version,\n platform: 'web',\n device: platform.model,\n device_id: getDeviceId(),\n device_type: platform.type,\n device_vendor: platform.vendor,\n device_pixel_ratio: window.devicePixelRatio,\n screen_width: window.screen.width,\n screen_height: window.screen.height,\n screen_resolution: `${window.screen.width}x${window.screen.height}`,\n release: config.release,\n language: navigator.language,\n time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n environment: process.env.NODE_ENV === 'development' ? 'development' : 'production',\n source: 'web',\n source_url: window.location.origin + window.location.pathname,\n page_referrer: document.referrer || undefined,\n // Meta Ads\n fbc: parsed._fbc ?? expiringStorage.getItem<string>(keys.fbc) ?? undefined,\n fbp: parsed._fbp,\n fbclid: params.get('fbclid') ?? expiringStorage.getItem<string>(keys.fbclid) ?? undefined,\n ad_id: params.get('ad_id') ?? undefined,\n ad_name: params.get('ad_name') ?? undefined,\n adset_id: params.get('adset_id') ?? undefined,\n adset_name: params.get('adset_name') ?? undefined,\n campaign_id: params.get('campaign_id') ?? undefined,\n campaign_name: params.get('campaign_name') ?? undefined,\n placement: params.get('placement') ?? undefined,\n site_source_name: params.get('site_source_name') ?? undefined,\n // Google Ads\n gclid: params.get('gclid') ?? undefined,\n gclsrc: params.get('gclsrc') ?? undefined,\n gad_source: params.get('gad_source') ?? undefined,\n gad_campaignid: params.get('gad_campaignid') ?? undefined,\n // Reddit Ads\n rdt_cid:\n params.get('rdt_cid') ??\n parsed._rdt_cid ??\n expiringStorage.getItem<string>(keys.rdt_cid) ??\n undefined,\n rdt_uuid: parsed._rdt_uuid,\n // LinkedIn Ads: get click id from url params or first-party cookie\n li_fat_id: params.get('li_fat_id') ?? parsed.li_fat_id ?? undefined,\n // click ids\n dclid: params.get('dclid') ?? undefined,\n ko_click_id: params.get('ko_click_id') ?? undefined,\n msclkid: params.get('msclkid') ?? undefined,\n sccid: params.get('sccid') ?? undefined,\n ttclid: params.get('ttclid') ?? undefined,\n twclid: params.get('twclid') ?? undefined,\n wbraid: params.get('wbraid') ?? undefined,\n yclid: params.get('yclid') ?? undefined,\n // utm params\n utm_source: link?.utm_source ?? params.get('utm_source') ?? undefined,\n utm_medium: link?.utm_medium ?? params.get('utm_medium') ?? undefined,\n utm_campaign: link?.utm_campaign ?? params.get('utm_campaign') ?? undefined,\n utm_term: link?.utm_term ?? params.get('utm_term') ?? undefined,\n utm_content: link?.utm_content ?? params.get('utm_content') ?? undefined,\n utm_id: link?.utm_id ?? params.get('utm_id') ?? undefined,\n utm_source_platform:\n link?.utm_source_platform ?? params.get('utm_source_platform') ?? undefined,\n utm_creative_format:\n link?.utm_creative_format ?? params.get('utm_creative_format') ?? undefined,\n utm_marketing_tactic:\n link?.utm_marketing_tactic ?? params.get('utm_marketing_tactic') ?? undefined,\n };\n\n cache.tags = tags;\n return tags;\n}\n\nconst map = new Map<string, string>();\n\nexport const storage: Storage = {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n console.error('localStorage is not available');\n return map.get(key) ?? null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n console.error('localStorage is not available');\n map.set(key, value);\n }\n },\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/web/index.ts"],"sourcesContent":["import { expiringStorage } from '@shware/utils';\nimport Bowser from 'bowser';\nimport { parseCookie } from 'cookie';\nimport { v4 as uuidv4 } from 'uuid';\nimport { keys } from '../constants/storage';\nimport { getLink } from '../link/index';\nimport { type Storage, cache, config } from '../setup/index';\nimport type { TrackTags } from '../track/types';\n\nexport function getDeviceId() {\n const cached = localStorage.getItem(keys.device_id);\n if (cached) return cached;\n const id = crypto?.randomUUID ? crypto.randomUUID() : uuidv4();\n localStorage.setItem(keys.device_id, id);\n return id;\n}\n\nexport async function getTags() {\n const parser = Bowser.getParser(window.navigator.userAgent);\n const params = new URLSearchParams(window.location.search);\n const os = parser.getOS();\n const browser = parser.getBrowser();\n const platform = parser.getPlatform();\n const parsed = parseCookie(document.cookie);\n\n const linkId = params.get('s');\n const link = linkId ? await getLink(linkId) : null;\n\n const tags: TrackTags = {\n os: `${os.name} ${os.version}`,\n os_name: os.name,\n os_version: os.version,\n browser: `${browser.name} ${browser.version}`,\n browser_name: browser.name,\n browser_version: browser.version,\n platform: 'web',\n device: platform.model,\n device_id: getDeviceId(),\n device_type: platform.type,\n device_vendor: platform.vendor,\n device_pixel_ratio: window.devicePixelRatio,\n screen_width: window.screen.width,\n screen_height: window.screen.height,\n screen_resolution: `${window.screen.width}x${window.screen.height}`,\n release: config.release,\n language: navigator.language,\n time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n environment: process.env.NODE_ENV === 'development' ? 'development' : 'production',\n source: 'web',\n source_url: window.location.href,\n page_referrer: document.referrer || undefined,\n // Meta Ads\n fbc: parsed._fbc ?? expiringStorage.getItem<string>(keys.fbc) ?? undefined,\n fbp: parsed._fbp,\n fbclid: params.get('fbclid') ?? expiringStorage.getItem<string>(keys.fbclid) ?? undefined,\n ad_id: params.get('ad_id') ?? undefined,\n ad_name: params.get('ad_name') ?? undefined,\n adset_id: params.get('adset_id') ?? undefined,\n adset_name: params.get('adset_name') ?? undefined,\n campaign_id: params.get('campaign_id') ?? undefined,\n campaign_name: params.get('campaign_name') ?? undefined,\n placement: params.get('placement') ?? undefined,\n site_source_name: params.get('site_source_name') ?? undefined,\n // Google Ads\n gclid: params.get('gclid') ?? undefined,\n gclsrc: params.get('gclsrc') ?? undefined,\n gad_source: params.get('gad_source') ?? undefined,\n gad_campaignid: params.get('gad_campaignid') ?? undefined,\n // Reddit Ads\n rdt_cid:\n params.get('rdt_cid') ??\n parsed._rdt_cid ??\n expiringStorage.getItem<string>(keys.rdt_cid) ??\n undefined,\n rdt_uuid: parsed._rdt_uuid,\n // LinkedIn Ads: get click id from url params or first-party cookie\n li_fat_id: params.get('li_fat_id') ?? parsed.li_fat_id ?? undefined,\n // click ids\n dclid: params.get('dclid') ?? undefined,\n ko_click_id: params.get('ko_click_id') ?? undefined,\n msclkid: params.get('msclkid') ?? undefined,\n sccid: params.get('sccid') ?? undefined,\n ttclid: params.get('ttclid') ?? undefined,\n twclid: params.get('twclid') ?? undefined,\n wbraid: params.get('wbraid') ?? undefined,\n yclid: params.get('yclid') ?? undefined,\n // utm params\n utm_source: link?.utm_source ?? params.get('utm_source') ?? undefined,\n utm_medium: link?.utm_medium ?? params.get('utm_medium') ?? undefined,\n utm_campaign: link?.utm_campaign ?? params.get('utm_campaign') ?? undefined,\n utm_term: link?.utm_term ?? params.get('utm_term') ?? undefined,\n utm_content: link?.utm_content ?? params.get('utm_content') ?? undefined,\n utm_id: link?.utm_id ?? params.get('utm_id') ?? undefined,\n utm_source_platform:\n link?.utm_source_platform ?? params.get('utm_source_platform') ?? undefined,\n utm_creative_format:\n link?.utm_creative_format ?? params.get('utm_creative_format') ?? undefined,\n utm_marketing_tactic:\n link?.utm_marketing_tactic ?? params.get('utm_marketing_tactic') ?? undefined,\n };\n\n cache.tags = tags;\n return tags;\n}\n\nconst map = new Map<string, string>();\n\nexport const storage: Storage = {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n console.error('localStorage is not available');\n return map.get(key) ?? null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n console.error('localStorage is not available');\n map.set(key, value);\n }\n },\n};\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shware/analytics",
3
- "version": "3.6.3",
3
+ "version": "3.6.4",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -76,7 +76,7 @@
76
76
  "uuid": "^14.0.1",
77
77
  "web-vitals": "^5.3.0",
78
78
  "zod": "^4.4.3",
79
- "@shware/utils": "^1.4.4"
79
+ "@shware/utils": "^1.4.5"
80
80
  },
81
81
  "devDependencies": {
82
82
  "@types/facebook-nodejs-business-sdk": "^24",
@@ -162,7 +162,7 @@
162
162
  "dev": "tsc --watch",
163
163
  "check-types": "tsc --noEmit",
164
164
  "test": "vitest run",
165
- "lint": "oxlint . --fix",
165
+ "lint": "oxlint --type-aware . --fix",
166
166
  "build": "tsup",
167
167
  "build:tsup": "tsup",
168
168
  "build:unbuild": "unbuild"