@webaround/openai-ads 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pixel.js ADDED
@@ -0,0 +1,305 @@
1
+ import { OpenAIAdsError } from './errors.js';
2
+ import { assertUsableEventId } from './eventId.js';
3
+ import { CUSTOM_EVENT_NAME_PATTERN, DATA_SHAPES, PIXEL_SUPPORTED, SDK_URL, isEventName } from './spec.js';
4
+ /**
5
+ * A typed, consent-aware wrapper over OpenAI's official browser SDK.
6
+ *
7
+ * It does NOT reimplement the transport. The official SDK is a script on
8
+ * OpenAI's CDN plus a global command queue, and this wrapper loads that script
9
+ * and speaks to that queue. Everything the SDK owns - batching, timestamps,
10
+ * `source_url`, capturing `oppref` into the `__oppref` cookie - is left to it.
11
+ *
12
+ * Nothing here throws at the caller. A measurement failure must never break a
13
+ * checkout or a form submission, so errors go to `onError`.
14
+ */
15
+ class OpenAIAdsPixel {
16
+ constructor() {
17
+ this.pixelIds = new Set();
18
+ this.debug = false;
19
+ this.consentGranted = true;
20
+ this.scriptInjected = false;
21
+ this.onError = () => { };
22
+ }
23
+ /**
24
+ * Configure error reporting. Optional; call before `init` to catch setup
25
+ * problems.
26
+ */
27
+ configure(options) {
28
+ if (options.onError !== undefined) {
29
+ this.onError = options.onError;
30
+ }
31
+ }
32
+ /**
33
+ * Record the visitor's consent decision.
34
+ *
35
+ * Must be called before `init` to take effect on the SDK, per its
36
+ * documentation. When consent is denied this wrapper also stops emitting
37
+ * events of its own accord - silently, because a refused consent is a normal
38
+ * outcome and not an error.
39
+ */
40
+ consent(granted) {
41
+ this.consentGranted = granted;
42
+ // Recorded before init so the SDK sees the decision first, as documented.
43
+ this.safely(() => {
44
+ // The queue is created without injecting the script, so a denial can be
45
+ // recorded on a page that never loads the SDK, and a grant is replayed
46
+ // ahead of init once the script arrives.
47
+ this.ensureQueue();
48
+ this.queue()('consent', granted);
49
+ });
50
+ }
51
+ /**
52
+ * Load the SDK and initialize a Pixel ID.
53
+ *
54
+ * Safe to call more than once, which is required rather than merely tolerated:
55
+ * the documented way to attach identity once a visitor becomes known is to
56
+ * call init again with `user`. Re-initializing the same Pixel ID with no new
57
+ * user data is skipped, which is the guard against the common mistake of
58
+ * initializing in both a root layout and a page.
59
+ *
60
+ * On a page with several pixels, call this once per Pixel ID.
61
+ */
62
+ init(config = {}) {
63
+ this.safely(() => {
64
+ if (config.debug !== undefined) {
65
+ this.debug = config.debug;
66
+ }
67
+ const pixelId = config.pixelId?.trim();
68
+ if (pixelId === undefined || pixelId === '') {
69
+ if (this.pixelIds.size === 0) {
70
+ throw new OpenAIAdsError('init requires a pixelId on the first call.');
71
+ }
72
+ }
73
+ if (!this.consentGranted) {
74
+ // Do not load a measurement SDK the visitor has refused.
75
+ this.warn('Consent is denied; the Pixel SDK was not loaded.');
76
+ return;
77
+ }
78
+ this.load();
79
+ if (pixelId !== undefined && pixelId !== '' && this.pixelIds.has(pixelId) && config.user === undefined) {
80
+ this.warn(`Pixel "${pixelId}" is already initialized and no new user data was supplied; ` +
81
+ 'skipping. Initializing twice usually means a layout and a page both call init.');
82
+ return;
83
+ }
84
+ const payload = {};
85
+ if (pixelId !== undefined && pixelId !== '') {
86
+ payload['pixelId'] = pixelId;
87
+ this.pixelIds.add(pixelId);
88
+ }
89
+ if (this.debug) {
90
+ payload['debug'] = true;
91
+ }
92
+ if (config.user !== undefined) {
93
+ payload['user'] = config.user;
94
+ }
95
+ this.queue()('init', payload);
96
+ });
97
+ }
98
+ /**
99
+ * Emit a standard event at a confirmed conversion boundary.
100
+ *
101
+ * Fire after the action has succeeded - after payment is confirmed, after the
102
+ * lead is accepted - never on a button click, unless the click genuinely is
103
+ * the conversion.
104
+ */
105
+ track(name, data, options = {}) {
106
+ this.safely(() => {
107
+ this.emit(name, data, options);
108
+ });
109
+ }
110
+ /**
111
+ * Emit a custom event.
112
+ *
113
+ * Use only where no standard event describes the action. The same name must be
114
+ * used on the Conversions API side or the two will not deduplicate.
115
+ */
116
+ trackCustom(customEventName, data, options = {}) {
117
+ this.safely(() => {
118
+ this.emit('custom', data, { ...options, customEventName });
119
+ });
120
+ }
121
+ /** Pixel IDs initialized so far. Exposed for diagnostics and tests. */
122
+ initializedPixelIds() {
123
+ return [...this.pixelIds];
124
+ }
125
+ /** Test seam. Not part of the public API. */
126
+ reset() {
127
+ this.pixelIds.clear();
128
+ this.debug = false;
129
+ this.consentGranted = true;
130
+ this.scriptInjected = false;
131
+ this.onError = () => { };
132
+ }
133
+ /**
134
+ * `name` is a string rather than an EventName on purpose.
135
+ *
136
+ * TypeScript proves the caller passed a valid one; JavaScript proves nothing,
137
+ * and this package is published for both. The check below is what a plain-JS
138
+ * caller gets instead of a compile error.
139
+ */
140
+ emit(name, data, options) {
141
+ if (!isEventName(name)) {
142
+ throw new OpenAIAdsError(`"${name}" is not a supported event name.`);
143
+ }
144
+ if (!PIXEL_SUPPORTED[name]) {
145
+ throw new OpenAIAdsError(`"${name}" is a Conversions API event and cannot be sent from the browser. ` +
146
+ 'Send it server-side.');
147
+ }
148
+ if (this.pixelIds.size === 0) {
149
+ throw new OpenAIAdsError(`init must be called before tracking "${name}".`);
150
+ }
151
+ if (!this.consentGranted) {
152
+ // Silent by design: a refused consent is an expected outcome.
153
+ return;
154
+ }
155
+ const customEventName = this.validateCustomEventName(name, options.customEventName);
156
+ const payload = this.buildData(name, data);
157
+ const sdkOptions = this.buildOptions(options, customEventName);
158
+ const target = options.pixelId?.trim();
159
+ if (target !== undefined && target !== '') {
160
+ if (!this.pixelIds.has(target)) {
161
+ throw new OpenAIAdsError(`Pixel "${target}" has not been initialized.`);
162
+ }
163
+ this.queue()('measureSingle', target, name, payload, sdkOptions);
164
+ return;
165
+ }
166
+ if (this.pixelIds.size > 1) {
167
+ // `measure` goes to every pixel initialized at the time of the call. That
168
+ // is the SDK's documented behaviour and is preserved here, but on a
169
+ // multi-pixel page it is usually not what the caller meant.
170
+ this.warn(`This event was broadcast to ${this.pixelIds.size} initialized pixels. ` +
171
+ 'Pass options.pixelId to send it to one.');
172
+ }
173
+ this.queue()('measure', name, payload, sdkOptions);
174
+ }
175
+ buildData(name, data) {
176
+ const shape = DATA_SHAPES[name];
177
+ const merged = { ...data, type: shape };
178
+ if (merged.amount !== undefined) {
179
+ if (!Number.isInteger(merged.amount)) {
180
+ const suggestion = Math.round(merged.amount * 100);
181
+ throw new OpenAIAdsError(`amount must be an integer in the currency's minor unit; got ${merged.amount}. ` +
182
+ `Did you mean ${suggestion}?`);
183
+ }
184
+ if (merged.currency === undefined) {
185
+ throw new OpenAIAdsError('currency is required when amount is present.');
186
+ }
187
+ }
188
+ if (merged.currency !== undefined && !/^[A-Za-z]{3}$/.test(merged.currency)) {
189
+ throw new OpenAIAdsError(`currency must be an ISO 4217 alpha-3 code such as "EUR"; got "${merged.currency}".`);
190
+ }
191
+ if (merged.currency !== undefined) {
192
+ merged.currency = merged.currency.toUpperCase();
193
+ }
194
+ return merged;
195
+ }
196
+ buildOptions(options, customEventName) {
197
+ const sdkOptions = {};
198
+ if (options.eventId !== undefined) {
199
+ sdkOptions['event_id'] = assertUsableEventId(options.eventId);
200
+ }
201
+ if (customEventName !== undefined) {
202
+ sdkOptions['custom_event_name'] = customEventName;
203
+ }
204
+ if (options.optOut !== undefined) {
205
+ sdkOptions['opt_out'] = options.optOut;
206
+ }
207
+ return sdkOptions;
208
+ }
209
+ validateCustomEventName(name, customEventName) {
210
+ if (name !== 'custom') {
211
+ if (customEventName !== undefined) {
212
+ throw new OpenAIAdsError(`customEventName is only valid for the "custom" event; "${name}" is a standard event.`);
213
+ }
214
+ return undefined;
215
+ }
216
+ if (customEventName === undefined || customEventName === '') {
217
+ throw new OpenAIAdsError('customEventName is required for a custom event.');
218
+ }
219
+ if (!CUSTOM_EVENT_NAME_PATTERN.test(customEventName)) {
220
+ throw new OpenAIAdsError('customEventName must be 1-64 characters of letters, digits, underscores or dashes, ' +
221
+ `starting and ending with a letter or digit; got "${customEventName}".`);
222
+ }
223
+ if (isEventName(customEventName)) {
224
+ throw new OpenAIAdsError(`customEventName must not reuse the standard event name "${customEventName}".`);
225
+ }
226
+ return customEventName;
227
+ }
228
+ /**
229
+ * Create the global command queue.
230
+ *
231
+ * This is the first half of the documented loader snippet: define the queue
232
+ * synchronously so commands issued before the script arrives are replayed in
233
+ * order. Kept separate from injecting the script so that `consent(false)` can
234
+ * be recorded on a page where the SDK is never loaded at all.
235
+ */
236
+ ensureQueue() {
237
+ if (globalThis.oaiq !== undefined) {
238
+ return;
239
+ }
240
+ const queue = function (...args) {
241
+ queue.q?.push(args);
242
+ };
243
+ queue.q = [];
244
+ globalThis.oaiq = queue;
245
+ }
246
+ /** The second half of the loader snippet: fetch the official SDK, once. */
247
+ load() {
248
+ if (typeof document === 'undefined') {
249
+ throw new OpenAIAdsError('The Pixel requires a browser document; it cannot run server-side.');
250
+ }
251
+ this.ensureQueue();
252
+ if (this.scriptInjected) {
253
+ return;
254
+ }
255
+ this.scriptInjected = true;
256
+ const script = document.createElement('script');
257
+ script.async = true;
258
+ script.src = SDK_URL;
259
+ script.addEventListener('error', () => {
260
+ this.onError(new OpenAIAdsError('The OpenAI Ads Pixel SDK failed to load; events were not delivered.'));
261
+ });
262
+ const first = document.getElementsByTagName('script')[0];
263
+ if (first?.parentNode) {
264
+ first.parentNode.insertBefore(script, first);
265
+ }
266
+ else {
267
+ (document.head ?? document.documentElement).appendChild(script);
268
+ }
269
+ }
270
+ queue() {
271
+ const queue = globalThis.oaiq;
272
+ if (queue === undefined) {
273
+ throw new OpenAIAdsError('The Pixel SDK is not loaded; call init first.');
274
+ }
275
+ return queue;
276
+ }
277
+ /**
278
+ * The reason nothing here throws at the caller.
279
+ *
280
+ * A measurement mistake on a checkout page must not take the checkout with it,
281
+ * so failures are reported and swallowed. Supply `onError` to route them into
282
+ * your own logging.
283
+ */
284
+ safely(operation) {
285
+ try {
286
+ operation();
287
+ }
288
+ catch (error) {
289
+ const wrapped = error instanceof Error ? error : new OpenAIAdsError('Unknown failure', { cause: error });
290
+ this.onError(wrapped);
291
+ if (this.debug) {
292
+ console.warn('[openai-ads]', wrapped.message);
293
+ }
294
+ }
295
+ }
296
+ warn(message) {
297
+ if (this.debug) {
298
+ console.warn('[openai-ads]', message);
299
+ }
300
+ }
301
+ }
302
+ export { OpenAIAdsPixel };
303
+ /** The shared instance. A page has one Pixel SDK, so it has one of these. */
304
+ export const OpenAIAds = new OpenAIAdsPixel();
305
+ //# sourceMappingURL=pixel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pixel.js","sourceRoot":"","sources":["../src/pixel.ts"],"names":[],"mappings":"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"}
package/dist/spec.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The events OpenAI Ads documents.
3
+ *
4
+ * Mirrors `packages/spec/events.json`. `tests/spec-parity.test.ts` asserts this
5
+ * file and the specification agree on the catalogue, the data shapes and Pixel
6
+ * support, so an upstream addition turns this package red rather than letting it
7
+ * drift from the PHP core.
8
+ */
9
+ export declare const EVENT_NAMES: readonly ["page_viewed", "contents_viewed", "items_added", "checkout_started", "order_created", "lead_created", "registration_completed", "appointment_scheduled", "subscription_created", "trial_started", "custom", "app_installed", "app_opened"];
10
+ export type EventName = (typeof EVENT_NAMES)[number];
11
+ export type DataShape = 'contents' | 'customer_action' | 'plan_enrollment' | 'custom';
12
+ export declare const DATA_SHAPES: Readonly<Record<EventName, DataShape>>;
13
+ /**
14
+ * Whether the browser Pixel can emit the event at all.
15
+ *
16
+ * `app_installed` and `app_opened` are Conversions API only. Attempting them
17
+ * here is a programming error the wrapper refuses rather than passes to the SDK,
18
+ * which would accept and silently discard them.
19
+ */
20
+ export declare const PIXEL_SUPPORTED: Readonly<Record<EventName, boolean>>;
21
+ /** Mirrors `patterns.custom_event_name` in the specification. */
22
+ export declare const CUSTOM_EVENT_NAME_PATTERN: RegExp;
23
+ /** The official SDK. Loaded from OpenAI's CDN; never bundled or vendored. */
24
+ export declare const SDK_URL = "https://bzrcdn.openai.com/sdk/oaiq.min.js";
25
+ export declare function isEventName(value: string): value is EventName;
26
+ //# sourceMappingURL=spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,WAAW,sPAcd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,QAAQ,CAAC;AAEtF,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAc9D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAchE,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,yBAAyB,QACuB,CAAC;AAE9D,6EAA6E;AAC7E,eAAO,MAAM,OAAO,8CAA8C,CAAC;AAEnE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,SAAS,CAE7D"}
package/dist/spec.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The events OpenAI Ads documents.
3
+ *
4
+ * Mirrors `packages/spec/events.json`. `tests/spec-parity.test.ts` asserts this
5
+ * file and the specification agree on the catalogue, the data shapes and Pixel
6
+ * support, so an upstream addition turns this package red rather than letting it
7
+ * drift from the PHP core.
8
+ */
9
+ export const EVENT_NAMES = [
10
+ 'page_viewed',
11
+ 'contents_viewed',
12
+ 'items_added',
13
+ 'checkout_started',
14
+ 'order_created',
15
+ 'lead_created',
16
+ 'registration_completed',
17
+ 'appointment_scheduled',
18
+ 'subscription_created',
19
+ 'trial_started',
20
+ 'custom',
21
+ 'app_installed',
22
+ 'app_opened',
23
+ ];
24
+ export const DATA_SHAPES = {
25
+ page_viewed: 'contents',
26
+ contents_viewed: 'contents',
27
+ items_added: 'contents',
28
+ checkout_started: 'contents',
29
+ order_created: 'contents',
30
+ lead_created: 'customer_action',
31
+ registration_completed: 'customer_action',
32
+ appointment_scheduled: 'customer_action',
33
+ subscription_created: 'plan_enrollment',
34
+ trial_started: 'plan_enrollment',
35
+ custom: 'custom',
36
+ app_installed: 'customer_action',
37
+ app_opened: 'customer_action',
38
+ };
39
+ /**
40
+ * Whether the browser Pixel can emit the event at all.
41
+ *
42
+ * `app_installed` and `app_opened` are Conversions API only. Attempting them
43
+ * here is a programming error the wrapper refuses rather than passes to the SDK,
44
+ * which would accept and silently discard them.
45
+ */
46
+ export const PIXEL_SUPPORTED = {
47
+ page_viewed: true,
48
+ contents_viewed: true,
49
+ items_added: true,
50
+ checkout_started: true,
51
+ order_created: true,
52
+ lead_created: true,
53
+ registration_completed: true,
54
+ appointment_scheduled: true,
55
+ subscription_created: true,
56
+ trial_started: true,
57
+ custom: true,
58
+ app_installed: false,
59
+ app_opened: false,
60
+ };
61
+ /** Mirrors `patterns.custom_event_name` in the specification. */
62
+ export const CUSTOM_EVENT_NAME_PATTERN = /^[A-Za-z0-9]$|^[A-Za-z0-9][A-Za-z0-9_-]{0,62}[A-Za-z0-9]$/;
63
+ /** The official SDK. Loaded from OpenAI's CDN; never bundled or vendored. */
64
+ export const SDK_URL = 'https://bzrcdn.openai.com/sdk/oaiq.min.js';
65
+ export function isEventName(value) {
66
+ return EVENT_NAMES.includes(value);
67
+ }
68
+ //# sourceMappingURL=spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,aAAa;IACb,iBAAiB;IACjB,aAAa;IACb,kBAAkB;IAClB,eAAe;IACf,cAAc;IACd,wBAAwB;IACxB,uBAAuB;IACvB,sBAAsB;IACtB,eAAe;IACf,QAAQ;IACR,eAAe;IACf,YAAY;CACJ,CAAC;AAMX,MAAM,CAAC,MAAM,WAAW,GAA2C;IACjE,WAAW,EAAE,UAAU;IACvB,eAAe,EAAE,UAAU;IAC3B,WAAW,EAAE,UAAU;IACvB,gBAAgB,EAAE,UAAU;IAC5B,aAAa,EAAE,UAAU;IACzB,YAAY,EAAE,iBAAiB;IAC/B,sBAAsB,EAAE,iBAAiB;IACzC,qBAAqB,EAAE,iBAAiB;IACxC,oBAAoB,EAAE,iBAAiB;IACvC,aAAa,EAAE,iBAAiB;IAChC,MAAM,EAAE,QAAQ;IAChB,aAAa,EAAE,iBAAiB;IAChC,UAAU,EAAE,iBAAiB;CAC9B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAyC;IACnE,WAAW,EAAE,IAAI;IACjB,eAAe,EAAE,IAAI;IACrB,WAAW,EAAE,IAAI;IACjB,gBAAgB,EAAE,IAAI;IACtB,aAAa,EAAE,IAAI;IACnB,YAAY,EAAE,IAAI;IAClB,sBAAsB,EAAE,IAAI;IAC5B,qBAAqB,EAAE,IAAI;IAC3B,oBAAoB,EAAE,IAAI;IAC1B,aAAa,EAAE,IAAI;IACnB,MAAM,EAAE,IAAI;IACZ,aAAa,EAAE,KAAK;IACpB,UAAU,EAAE,KAAK;CAClB,CAAC;AAEF,iEAAiE;AACjE,MAAM,CAAC,MAAM,yBAAyB,GACpC,2DAA2D,CAAC;AAE9D,6EAA6E;AAC7E,MAAM,CAAC,MAAM,OAAO,GAAG,2CAA2C,CAAC;AAEnE,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAQ,WAAiC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,107 @@
1
+ import type { EventName } from './spec.js';
2
+ /** An item inside a `contents` array. */
3
+ export interface Content {
4
+ id?: string;
5
+ name?: string;
6
+ content_type?: string;
7
+ quantity?: number;
8
+ amount?: number;
9
+ currency?: string;
10
+ }
11
+ interface BaseData {
12
+ /** Integer, in the currency's minor unit. 1299, never 12.99. */
13
+ amount?: number;
14
+ /** ISO 4217 alpha-3. Required whenever `amount` is present. */
15
+ currency?: string;
16
+ }
17
+ export interface ContentsData extends BaseData {
18
+ type: 'contents';
19
+ contents?: Content[];
20
+ }
21
+ export interface CustomerActionData extends BaseData {
22
+ type: 'customer_action';
23
+ }
24
+ export interface PlanEnrollmentData extends BaseData {
25
+ type: 'plan_enrollment';
26
+ plan_id?: string;
27
+ contents?: Content[];
28
+ }
29
+ export interface CustomData extends BaseData {
30
+ type: 'custom';
31
+ plan_id?: string;
32
+ contents?: Content[];
33
+ [key: string]: unknown;
34
+ }
35
+ export type EventData = ContentsData | CustomerActionData | PlanEnrollmentData | CustomData;
36
+ /** Maps an event name to the only data shape it accepts. */
37
+ export type DataFor<N extends EventName> = Extract<EventData, {
38
+ type: DataShapeOf<N>;
39
+ }>;
40
+ type DataShapeOf<N extends EventName> = N extends 'page_viewed' | 'contents_viewed' | 'items_added' | 'checkout_started' | 'order_created' ? 'contents' : N extends 'lead_created' | 'registration_completed' | 'appointment_scheduled' ? 'customer_action' : N extends 'subscription_created' | 'trial_started' ? 'plan_enrollment' : 'custom';
41
+ /**
42
+ * Identity for the Pixel: singular keys, scalar values.
43
+ *
44
+ * The Conversions API wants the same digests under plural keys with array
45
+ * values. Producing this shape is `hashUser()`'s job; do not hand-build it from
46
+ * a Conversions API payload.
47
+ */
48
+ export interface PixelUser {
49
+ email_sha256?: string;
50
+ phone_number_sha256?: string;
51
+ external_id_sha256?: string;
52
+ first_name_sha256?: string;
53
+ last_name_sha256?: string;
54
+ country?: string;
55
+ city?: string;
56
+ region?: string;
57
+ postal_code?: string;
58
+ }
59
+ /** Raw identity, normalized and hashed in the browser by `hashUser()`. */
60
+ export interface RawUser {
61
+ email?: string;
62
+ phone?: string;
63
+ externalId?: string;
64
+ firstName?: string;
65
+ lastName?: string;
66
+ country?: string;
67
+ city?: string;
68
+ region?: string;
69
+ postalCode?: string;
70
+ }
71
+ export interface InitConfig {
72
+ /** Required on the first call. Subsequent calls may omit it on a single-pixel page. */
73
+ pixelId?: string;
74
+ /** Turns on the SDK's own debug output and this wrapper's warnings. */
75
+ debug?: boolean;
76
+ /** Already-hashed identity. Use `hashUser()` to produce it. */
77
+ user?: PixelUser;
78
+ }
79
+ export interface TrackOptions {
80
+ /**
81
+ * Ties this browser event to its server-side twin. Both sides must use the
82
+ * same Pixel ID, event name and id, or the conversion is counted twice.
83
+ */
84
+ eventId?: string;
85
+ /** Required when the event is `custom`. */
86
+ customEventName?: string;
87
+ /** Excludes the event from personalization. NOT a consent gate. */
88
+ optOut?: boolean;
89
+ /**
90
+ * Send to one Pixel ID instead of every initialized one.
91
+ *
92
+ * Without this the SDK broadcasts to every pixel initialized at the time of
93
+ * the call, which double-counts on a page running more than one pixel.
94
+ */
95
+ pixelId?: string;
96
+ }
97
+ export interface ToolkitOptions {
98
+ /**
99
+ * Called instead of throwing. Measurement must never break the page, so every
100
+ * failure - a validation mistake included - is routed here.
101
+ *
102
+ * Defaults to a `console.warn` when `debug` is on, and to silence otherwise.
103
+ */
104
+ onError?: (error: Error) => void;
105
+ }
106
+ export {};
107
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ import type { PixelUser, RawUser } from './types.js';
2
+ export declare function normalizeEmail(value: string): string;
3
+ /**
4
+ * Remove the four documented separators, then a leading '+', then leading
5
+ * zeroes - and nothing else.
6
+ *
7
+ * Upstream documents '8-15 digits after removing a leading +, leading zeroes,
8
+ * whitespace, parentheses, periods, and hyphens'. Removing every non-digit
9
+ * instead looks equivalent and is not: '+1 (555) 123-4567 ext. 89' would become
10
+ * '1555123456789', which passes a length check and hashes to nobody. Whatever
11
+ * is left over is returned as-is so the caller can refuse it.
12
+ *
13
+ * Dialling plans are still not parsed: '+00 44 (0)20 7946 0958' becomes
14
+ * '4402079460958'.
15
+ */
16
+ export declare function normalizePhone(value: string): string;
17
+ /**
18
+ * ISO 3166-1 alpha-2, uppercased.
19
+ *
20
+ * Returns undefined for anything that is not two ASCII letters. A country name
21
+ * rather than a code is dropped by the API without an error, so sending one
22
+ * would look like matching data and be nothing of the sort.
23
+ */
24
+ export declare function normalizeCountry(value: string): string | undefined;
25
+ /**
26
+ * Trim, lowercase, cap at 128 characters - what the API does on receipt.
27
+ *
28
+ * Applied here too so the string sent is the string stored, and so the Pixel
29
+ * and the Conversions API carry the same one for the same person.
30
+ */
31
+ export declare function normalizeCityOrRegion(value: string): string;
32
+ /**
33
+ * Reduce to letters, digits, spaces and hyphens, cap at 32 characters.
34
+ *
35
+ * Disallowed characters are removed rather than refused - a stray period is a
36
+ * formatting artefact. Case is deliberately not folded: upstream states a
37
+ * lowercase rule for cities and regions and states none here.
38
+ */
39
+ export declare function normalizePostalCode(value: string): string;
40
+ export declare function normalizeExternalId(value: string): string;
41
+ /**
42
+ * Lowercase, then strip whitespace and ASCII punctuation.
43
+ *
44
+ * `toLowerCase()` is Unicode-aware, which is what the PHP side gets from
45
+ * `mb_strtolower`. A byte-wise lowercase there would leave a diacritic
46
+ * untouched and produce a digest that never matches this one.
47
+ */
48
+ export declare function normalizeName(value: string): string;
49
+ /**
50
+ * SHA-256 as lowercase hexadecimal.
51
+ *
52
+ * Web Crypto is asynchronous and only available in a secure context, so this is
53
+ * async and fails loudly on plain HTTP rather than degrading to a weaker hash or
54
+ * - far worse - sending the raw value.
55
+ */
56
+ export declare function sha256Hex(value: string): Promise<string>;
57
+ /**
58
+ * Normalize and hash raw identity into the Pixel's shape.
59
+ *
60
+ * Pass the result to `init({ user })` when the person becomes known - after a
61
+ * login, a checkout, a lead submission. Geographic values are sent unhashed, as
62
+ * documented.
63
+ *
64
+ * Values that normalize away to nothing are dropped rather than hashed: the
65
+ * digest of an empty string is a valid-looking value that matches nobody.
66
+ */
67
+ export declare function hashUser(raw: RawUser): Promise<PixelUser>;
68
+ //# sourceMappingURL=userData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userData.d.ts","sourceRoot":"","sources":["../src/userData.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAwBrD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIlE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAa9D;AAED;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CA2E/D"}