@outlit/core 1.4.3 → 1.5.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/index.d.ts CHANGED
@@ -18,29 +18,37 @@ interface BrowserTrackOptions {
18
18
  eventName: string;
19
19
  properties?: Record<string, string | number | boolean | null>;
20
20
  }
21
- interface BrowserIdentifyOptions {
21
+ interface BrowserIdentifyOptions extends CustomerAttribution {
22
22
  email?: string;
23
+ /** Your system-owned user/contact ID. */
23
24
  userId?: string;
25
+ /** User/contact traits. */
24
26
  traits?: IdentifyTraits;
27
+ /** Customer/account-level traits. */
28
+ customerTraits?: CustomerTraits;
25
29
  }
26
30
  /**
27
- * Server identity - requires at least one of fingerprint, email, or userId.
28
- * This is validated at runtime to avoid complex union types that
29
- * cause TypeScript memory issues during type checking.
31
+ * Base server-side user identity.
32
+ * Track calls may use customerId without these fields.
33
+ * Identify calls still require email or userId at runtime.
34
+ * `customerId`-only track calls are valid immediately, but they stay provisional
35
+ * until the same customer/account/workspace later appears on identify() with
36
+ * the matching customerId and an email.
30
37
  *
31
38
  * - fingerprint: Device identifier for anonymous tracking (can be linked later)
32
39
  * - email: User's email address (definitive identity, resolves immediately)
33
- * - userId: App's internal user ID
40
+ * - userId: Your system-owned user/contact ID
34
41
  */
35
42
  interface ServerIdentity {
36
43
  fingerprint?: string;
37
44
  email?: string;
45
+ /** Your system-owned user/contact ID. */
38
46
  userId?: string;
39
47
  }
40
- /**
41
- * Customer-level traits that can be nested under `customer` in identify.
42
- * These are applied to the customer/account, not the individual user.
43
- */
48
+ interface CustomerAttribution {
49
+ /** Your system-owned customer/account/workspace ID. */
50
+ customerId?: string;
51
+ }
44
52
  interface CustomerTraits {
45
53
  /** Customer's billing plan */
46
54
  plan?: string;
@@ -48,33 +56,31 @@ interface CustomerTraits {
48
56
  [key: string]: string | number | boolean | null | undefined;
49
57
  }
50
58
  /**
51
- * Traits for identify calls, supporting both user-level
52
- * and nested customer-level properties.
59
+ * Traits for identify calls.
60
+ * These are user/contact traits, not customer/account traits.
53
61
  */
54
62
  interface IdentifyTraits {
55
- /** Nested customer/account-level traits */
56
- customer?: CustomerTraits;
57
63
  /** User-level traits */
58
- [key: string]: string | number | boolean | null | CustomerTraits | undefined;
64
+ [key: string]: string | number | boolean | null | undefined;
59
65
  }
60
- interface ServerTrackOptions extends ServerIdentity {
66
+ interface ServerTrackOptions extends ServerIdentity, CustomerAttribution {
61
67
  eventName: string;
62
68
  properties?: Record<string, string | number | boolean | null>;
63
69
  timestamp?: number;
64
70
  }
65
- interface ServerIdentifyOptions extends ServerIdentity {
71
+ interface ServerIdentifyOptions extends ServerIdentity, CustomerAttribution {
66
72
  traits?: IdentifyTraits;
73
+ customerTraits?: CustomerTraits;
67
74
  }
68
75
  /**
69
76
  * Customer identity for SDK billing methods.
70
- * Domain is required as the primary identifier; additional identifiers are optional.
77
+ * Public billing calls should use `customerId`.
71
78
  */
72
- interface CustomerIdentifier {
73
- /** Required: The customer's domain (e.g., "acme.com") */
74
- domain: string;
75
- /** Optional: Your internal customer ID */
76
- customerId?: string;
77
- /** Optional: Stripe customer ID (e.g., "cus_xxx") */
79
+ interface CustomerIdentifier extends CustomerAttribution {
80
+ /**
81
+ * @deprecated Stripe customer identifier.
82
+ * Billing attribution should use `customerId` publicly.
83
+ */
78
84
  stripeCustomerId?: string;
79
85
  }
80
86
  interface BaseEvent {
@@ -99,11 +105,17 @@ interface IdentifyEvent extends BaseEvent {
99
105
  email?: string;
100
106
  userId?: string;
101
107
  fingerprint?: string;
108
+ customerId?: string;
109
+ customerTraits?: CustomerTraits;
102
110
  traits?: IdentifyTraits;
103
111
  }
104
112
  interface CustomEvent extends BaseEvent {
105
113
  type: "custom";
106
114
  eventName: string;
115
+ email?: string;
116
+ userId?: string;
117
+ fingerprint?: string;
118
+ customerId?: string;
107
119
  properties?: Record<string, string | number | boolean | null>;
108
120
  }
109
121
  interface CalendarEvent extends BaseEvent {
@@ -141,7 +153,6 @@ interface BillingEvent extends BaseEvent {
141
153
  /** Optional customer identifiers */
142
154
  customerId?: string;
143
155
  stripeCustomerId?: string;
144
- domain?: string;
145
156
  /** Optional properties for context */
146
157
  properties?: Record<string, string | number | boolean | null>;
147
158
  }
@@ -149,10 +160,34 @@ type TrackerEvent = PageviewEvent | FormEvent | IdentifyEvent | CustomEvent | Ca
149
160
  /**
150
161
  * User identity for payload-level resolution.
151
162
  * Used by browser SDK when user is logged in (via setUser).
163
+ * Customer attribution is carried separately in `customerIdentity`.
152
164
  */
153
165
  interface PayloadUserIdentity {
154
166
  email?: string;
167
+ /** Your system-owned user/contact ID. */
155
168
  userId?: string;
169
+ /** User/contact traits. */
170
+ traits?: IdentifyTraits;
171
+ /**
172
+ * @deprecated Use payload-level `customerIdentity.customerId` instead.
173
+ * Kept for one compatibility window while callers migrate.
174
+ */
175
+ customerId?: string;
176
+ /**
177
+ * @deprecated Use payload-level `customerIdentity.customerTraits` instead.
178
+ * Kept for one compatibility window while callers migrate.
179
+ */
180
+ customerTraits?: CustomerTraits;
181
+ }
182
+ /**
183
+ * Customer identity for payload-level attribution.
184
+ * Used by browser SDK to attach account/workspace context to a batch.
185
+ * `customerId`-only attribution is valid and remains provisional until a later
186
+ * identify(email, customerId) call links that external account/workspace to a resolved customer.
187
+ */
188
+ interface PayloadCustomerIdentity extends CustomerAttribution {
189
+ /** Customer/account traits. */
190
+ customerTraits?: CustomerTraits;
156
191
  }
157
192
  interface IngestPayload {
158
193
  visitorId?: string;
@@ -180,6 +215,12 @@ interface IngestPayload {
180
215
  * allowing immediate identity resolution for SPA/React apps.
181
216
  */
182
217
  userIdentity?: PayloadUserIdentity;
218
+ /**
219
+ * Customer/account identity for this batch of events.
220
+ * Used to attribute browser batches to a customer/workspace even when
221
+ * the customer fields are not present on every event.
222
+ */
223
+ customerIdentity?: PayloadCustomerIdentity;
183
224
  }
184
225
  interface IngestResponse {
185
226
  success: boolean;
@@ -193,88 +234,6 @@ declare const DEFAULT_API_HOST = "https://app.outlit.ai";
193
234
 
194
235
  declare const DEFAULT_DENIED_FORM_FIELDS: string[];
195
236
 
196
- /**
197
- * Extract UTM parameters from a URL.
198
- */
199
- declare function extractUtmParams(url: string): UtmParams | undefined;
200
- /**
201
- * Extract path from a URL.
202
- */
203
- declare function extractPathFromUrl(url: string): string;
204
- /**
205
- * Check if a field name should be denied (case-insensitive).
206
- */
207
- declare function isFieldDenied(fieldName: string, denylist: string[]): boolean;
208
- /**
209
- * Sanitize form fields by removing sensitive data.
210
- * Returns a new object with denied fields removed.
211
- */
212
- declare function sanitizeFormFields(fields: Record<string, string> | undefined, customDenylist?: string[]): Record<string, string> | undefined;
213
- /**
214
- * Validate that at least one identity field is provided.
215
- * Used by the server SDK to enforce identity requirements.
216
- *
217
- * Valid identities:
218
- * - fingerprint: Device identifier (for anonymous tracking, can be linked later)
219
- * - email: User's email (definitive identity)
220
- * - userId: App's internal user ID
221
- */
222
- declare function validateServerIdentity(fingerprint?: string, email?: string, userId?: string): void;
223
- /**
224
- * Validate that a string looks like a valid email address.
225
- */
226
- declare function isValidEmail(value: string): boolean;
227
- /**
228
- * Find an email value from form fields.
229
- *
230
- * Priority:
231
- * 1. Fields with input type="email" (if inputTypes map provided)
232
- * 2. Field names matching email patterns
233
- * 3. Any field with a value that looks like an email
234
- *
235
- * @param fields - Form field key-value pairs
236
- * @param inputTypes - Optional map of field names to input types
237
- * @returns The email value if found, undefined otherwise
238
- */
239
- declare function findEmailField(fields: Record<string, string>, inputTypes?: Map<string, string>): string | undefined;
240
- /**
241
- * Extract name fields from form data.
242
- *
243
- * Looks for:
244
- * - Full name fields (name, full_name, etc.)
245
- * - First name fields (first_name, fname, etc.)
246
- * - Last name fields (last_name, lname, etc.)
247
- *
248
- * If only first/last names are found, combines them into a full name.
249
- *
250
- * @param fields - Form field key-value pairs
251
- * @returns Object with name, firstName, and/or lastName if found
252
- */
253
- declare function findNameFields(fields: Record<string, string>): {
254
- name?: string;
255
- firstName?: string;
256
- lastName?: string;
257
- };
258
- /**
259
- * Identity extracted from a form submission.
260
- */
261
- interface ExtractedIdentity {
262
- email: string;
263
- name?: string;
264
- firstName?: string;
265
- lastName?: string;
266
- }
267
- /**
268
- * Extract identity information (email + name) from form fields.
269
- *
270
- * Returns undefined if no valid email is found (email is required for identification).
271
- *
272
- * @param fields - Form field key-value pairs
273
- * @param inputTypes - Optional map of field names to input types
274
- * @returns Extracted identity with email and optional name fields, or undefined
275
- */
276
- declare function extractIdentityFromForm(fields: Record<string, string>, inputTypes?: Map<string, string>): ExtractedIdentity | undefined;
277
-
278
237
  interface BaseEventParams {
279
238
  url: string;
280
239
  referrer?: string;
@@ -301,6 +260,8 @@ declare function buildIdentifyEvent(params: BaseEventParams & {
301
260
  userId?: string;
302
261
  fingerprint?: string;
303
262
  traits?: IdentifyTraits;
263
+ customerId?: string;
264
+ customerTraits?: CustomerTraits;
304
265
  }): IdentifyEvent;
305
266
  /**
306
267
  * Build a custom event.
@@ -308,6 +269,10 @@ declare function buildIdentifyEvent(params: BaseEventParams & {
308
269
  declare function buildCustomEvent(params: BaseEventParams & {
309
270
  eventName: string;
310
271
  properties?: Record<string, string | number | boolean | null>;
272
+ email?: string;
273
+ userId?: string;
274
+ fingerprint?: string;
275
+ customerId?: string;
311
276
  }): CustomEvent;
312
277
  /**
313
278
  * Build a calendar booking event.
@@ -348,7 +313,6 @@ declare function buildBillingEvent(params: BaseEventParams & {
348
313
  status: BillingStatus;
349
314
  customerId?: string;
350
315
  stripeCustomerId?: string;
351
- domain?: string;
352
316
  properties?: Record<string, string | number | boolean | null>;
353
317
  }): BillingEvent;
354
318
  /**
@@ -360,8 +324,11 @@ declare function buildBillingEvent(params: BaseEventParams & {
360
324
  * @param userIdentity - Optional user identity for immediate resolution (from setUser in SPA)
361
325
  * @param sessionId - Optional session ID for grouping events (browser SDK only)
362
326
  * @param fingerprint - Optional device identifier for server-side anonymous tracking
327
+ * @param customerIdentity - Optional customer identity for batch attribution. `customerId`-only
328
+ * batches are valid and remain provisional until a later identify(email, customerId) call
329
+ * links that external account/workspace to a resolved customer.
363
330
  */
364
- declare function buildIngestPayload(visitorId: string, source: SourceType, events: TrackerEvent[], userIdentity?: PayloadUserIdentity, sessionId?: string, fingerprint?: string): IngestPayload;
331
+ declare function buildIngestPayload(visitorId: string, source: SourceType, events: TrackerEvent[], userIdentity?: PayloadUserIdentity, sessionId?: string, fingerprint?: string, customerIdentity?: PayloadCustomerIdentity): IngestPayload;
365
332
  /**
366
333
  * Maximum number of events in a single batch.
367
334
  */
@@ -371,4 +338,91 @@ declare const MAX_BATCH_SIZE = 100;
371
338
  */
372
339
  declare function batchEvents(events: TrackerEvent[]): TrackerEvent[][];
373
340
 
374
- export { type BillingEvent, type BillingStatus, type BrowserIdentifyOptions, type BrowserTrackOptions, type CalendarEvent, type CalendarProvider, type CustomEvent, type CustomerIdentifier, type CustomerTraits, DEFAULT_API_HOST, DEFAULT_DENIED_FORM_FIELDS, type EngagementEvent, type EventType, type ExplicitJourneyStage, type ExtractedIdentity, type FormEvent, type IdentifyEvent, type IdentifyTraits, type IngestPayload, type IngestResponse, MAX_BATCH_SIZE, type PageviewEvent, type PayloadUserIdentity, type ServerIdentifyOptions, type ServerIdentity, type ServerTrackOptions, type SourceType, type StageEvent, type TrackerConfig, type TrackerEvent, type UtmParams, batchEvents, buildBillingEvent, buildCalendarEvent, buildCustomEvent, buildEngagementEvent, buildFormEvent, buildIdentifyEvent, buildIngestPayload, buildPageviewEvent, buildStageEvent, extractIdentityFromForm, extractPathFromUrl, extractUtmParams, findEmailField, findNameFields, isFieldDenied, isValidEmail, sanitizeFormFields, validateServerIdentity };
341
+ /**
342
+ * Extract UTM parameters from a URL.
343
+ */
344
+ declare function extractUtmParams(url: string): UtmParams | undefined;
345
+ /**
346
+ * Extract path from a URL.
347
+ */
348
+ declare function extractPathFromUrl(url: string): string;
349
+ /**
350
+ * Check if a field name should be denied (case-insensitive).
351
+ */
352
+ declare function isFieldDenied(fieldName: string, denylist: string[]): boolean;
353
+ /**
354
+ * Sanitize form fields by removing sensitive data.
355
+ * Returns a new object with denied fields removed.
356
+ */
357
+ declare function sanitizeFormFields(fields: Record<string, string> | undefined, customDenylist?: string[]): Record<string, string> | undefined;
358
+ /**
359
+ * Validate that at least one identity field is provided.
360
+ * Used by the server SDK to enforce identity requirements.
361
+ *
362
+ * Valid identities:
363
+ * - fingerprint: Device identifier (for anonymous tracking, can be linked later)
364
+ * - email: User's email (definitive identity)
365
+ * - userId: Your system-owned user/contact ID
366
+ * - customerId: Your system-owned customer/account/workspace ID
367
+ */
368
+ declare function validateServerIdentity(fingerprint?: string, email?: string, userId?: string, customerId?: string): void;
369
+ /**
370
+ * Validate that at least one customer identifier is provided for billing calls.
371
+ */
372
+ declare function validateCustomerIdentity(customerId?: string, stripeCustomerId?: string): void;
373
+ /**
374
+ * Validate that a string looks like a valid email address.
375
+ */
376
+ declare function isValidEmail(value: string): boolean;
377
+ /**
378
+ * Find an email value from form fields.
379
+ *
380
+ * Priority:
381
+ * 1. Fields with input type="email" (if inputTypes map provided)
382
+ * 2. Field names matching email patterns
383
+ * 3. Any field with a value that looks like an email
384
+ *
385
+ * @param fields - Form field key-value pairs
386
+ * @param inputTypes - Optional map of field names to input types
387
+ * @returns The email value if found, undefined otherwise
388
+ */
389
+ declare function findEmailField(fields: Record<string, string>, inputTypes?: Map<string, string>): string | undefined;
390
+ /**
391
+ * Extract name fields from form data.
392
+ *
393
+ * Looks for:
394
+ * - Full name fields (name, full_name, etc.)
395
+ * - First name fields (first_name, fname, etc.)
396
+ * - Last name fields (last_name, lname, etc.)
397
+ *
398
+ * If only first/last names are found, combines them into a full name.
399
+ *
400
+ * @param fields - Form field key-value pairs
401
+ * @returns Object with name, firstName, and/or lastName if found
402
+ */
403
+ declare function findNameFields(fields: Record<string, string>): {
404
+ name?: string;
405
+ firstName?: string;
406
+ lastName?: string;
407
+ };
408
+ /**
409
+ * Identity extracted from a form submission.
410
+ */
411
+ interface ExtractedIdentity {
412
+ email: string;
413
+ name?: string;
414
+ firstName?: string;
415
+ lastName?: string;
416
+ }
417
+ /**
418
+ * Extract identity information (email + name) from form fields.
419
+ *
420
+ * Returns undefined if no valid email is found (email is required for identification).
421
+ *
422
+ * @param fields - Form field key-value pairs
423
+ * @param inputTypes - Optional map of field names to input types
424
+ * @returns Extracted identity with email and optional name fields, or undefined
425
+ */
426
+ declare function extractIdentityFromForm(fields: Record<string, string>, inputTypes?: Map<string, string>): ExtractedIdentity | undefined;
427
+
428
+ export { type BillingEvent, type BillingStatus, type BrowserIdentifyOptions, type BrowserTrackOptions, type CalendarEvent, type CalendarProvider, type CustomEvent, type CustomerAttribution, type CustomerIdentifier, type CustomerTraits, DEFAULT_API_HOST, DEFAULT_DENIED_FORM_FIELDS, type EngagementEvent, type EventType, type ExplicitJourneyStage, type ExtractedIdentity, type FormEvent, type IdentifyEvent, type IdentifyTraits, type IngestPayload, type IngestResponse, MAX_BATCH_SIZE, type PageviewEvent, type PayloadCustomerIdentity, type PayloadUserIdentity, type ServerIdentifyOptions, type ServerIdentity, type ServerTrackOptions, type SourceType, type StageEvent, type TrackerConfig, type TrackerEvent, type UtmParams, batchEvents, buildBillingEvent, buildCalendarEvent, buildCustomEvent, buildEngagementEvent, buildFormEvent, buildIdentifyEvent, buildIngestPayload, buildPageviewEvent, buildStageEvent, extractIdentityFromForm, extractPathFromUrl, extractUtmParams, findEmailField, findNameFields, isFieldDenied, isValidEmail, sanitizeFormFields, validateCustomerIdentity, validateServerIdentity };
package/dist/index.js CHANGED
@@ -41,6 +41,7 @@ __export(src_exports, {
41
41
  isFieldDenied: () => isFieldDenied,
42
42
  isValidEmail: () => isValidEmail,
43
43
  sanitizeFormFields: () => sanitizeFormFields,
44
+ validateCustomerIdentity: () => validateCustomerIdentity,
44
45
  validateServerIdentity: () => validateServerIdentity
45
46
  });
46
47
  module.exports = __toCommonJS(src_exports);
@@ -143,13 +144,23 @@ function sanitizeFormFields(fields, customDenylist) {
143
144
  }
144
145
  return Object.keys(sanitized).length > 0 ? sanitized : void 0;
145
146
  }
146
- function validateServerIdentity(fingerprint, email, userId) {
147
+ function validateServerIdentity(fingerprint, email, userId, customerId) {
147
148
  const hasFingerprint = fingerprint && fingerprint.trim().length > 0;
148
149
  const hasEmail = email && email.trim().length > 0;
149
150
  const hasUserId = userId && userId.trim().length > 0;
150
- if (!hasFingerprint && !hasEmail && !hasUserId) {
151
+ const hasCustomerId = customerId && customerId.trim().length > 0;
152
+ if (!hasFingerprint && !hasEmail && !hasUserId && !hasCustomerId) {
151
153
  throw new Error(
152
- "Server SDK requires at least one of: fingerprint, email, or userId for all track calls. Use fingerprint for anonymous tracking that can be linked to users later via identify()."
154
+ "Server SDK requires at least one of: fingerprint, email, userId, or customerId for track calls. Use fingerprint/email/userId for user-scoped tracking or customerId for customer-only tracking."
155
+ );
156
+ }
157
+ }
158
+ function validateCustomerIdentity(customerId, stripeCustomerId) {
159
+ const hasCustomerId = customerId && customerId.trim().length > 0;
160
+ const hasStripeCustomerId = stripeCustomerId && stripeCustomerId.trim().length > 0;
161
+ if (!hasCustomerId && !hasStripeCustomerId) {
162
+ throw new Error(
163
+ "Billing methods require customerId or stripeCustomerId. Use customerId for your system-owned account ID."
153
164
  );
154
165
  }
155
166
  }
@@ -288,7 +299,17 @@ function buildFormEvent(params) {
288
299
  };
289
300
  }
290
301
  function buildIdentifyEvent(params) {
291
- const { url, referrer, timestamp, email, userId, fingerprint, traits } = params;
302
+ const {
303
+ url,
304
+ referrer,
305
+ timestamp,
306
+ email,
307
+ userId,
308
+ fingerprint,
309
+ traits,
310
+ customerId,
311
+ customerTraits
312
+ } = params;
292
313
  return {
293
314
  type: "identify",
294
315
  timestamp: timestamp ?? Date.now(),
@@ -299,11 +320,23 @@ function buildIdentifyEvent(params) {
299
320
  email,
300
321
  userId,
301
322
  fingerprint,
323
+ customerId,
324
+ customerTraits,
302
325
  traits
303
326
  };
304
327
  }
305
328
  function buildCustomEvent(params) {
306
- const { url, referrer, timestamp, eventName, properties } = params;
329
+ const {
330
+ url,
331
+ referrer,
332
+ timestamp,
333
+ eventName,
334
+ properties,
335
+ email,
336
+ userId,
337
+ fingerprint,
338
+ customerId
339
+ } = params;
307
340
  return {
308
341
  type: "custom",
309
342
  timestamp: timestamp ?? Date.now(),
@@ -312,6 +345,10 @@ function buildCustomEvent(params) {
312
345
  referrer,
313
346
  utm: extractUtmParams(url),
314
347
  eventName,
348
+ email,
349
+ userId,
350
+ fingerprint,
351
+ customerId,
315
352
  properties
316
353
  };
317
354
  }
@@ -374,7 +411,7 @@ function buildStageEvent(params) {
374
411
  };
375
412
  }
376
413
  function buildBillingEvent(params) {
377
- const { url, referrer, timestamp, status, customerId, stripeCustomerId, domain, properties } = params;
414
+ const { url, referrer, timestamp, status, customerId, stripeCustomerId, properties } = params;
378
415
  return {
379
416
  type: "billing",
380
417
  timestamp: timestamp ?? Date.now(),
@@ -385,26 +422,41 @@ function buildBillingEvent(params) {
385
422
  status,
386
423
  customerId,
387
424
  stripeCustomerId,
388
- domain,
389
425
  properties
390
426
  };
391
427
  }
392
- function buildIngestPayload(visitorId, source, events, userIdentity, sessionId, fingerprint) {
428
+ function buildIngestPayload(visitorId, source, events, userIdentity, sessionId, fingerprint, customerIdentity) {
393
429
  const payload = {
394
430
  visitorId,
395
431
  source,
396
432
  events
397
433
  };
434
+ const legacyCustomerIdentity = customerIdentity === void 0 && userIdentity && (userIdentity.customerId || userIdentity.customerTraits) ? {
435
+ ...userIdentity.customerId && { customerId: userIdentity.customerId },
436
+ ...userIdentity.customerTraits && { customerTraits: userIdentity.customerTraits }
437
+ } : void 0;
438
+ const resolvedCustomerIdentity = customerIdentity ?? legacyCustomerIdentity;
398
439
  if (fingerprint) {
399
440
  payload.fingerprint = fingerprint;
400
441
  }
401
442
  if (sessionId) {
402
443
  payload.sessionId = sessionId;
403
444
  }
404
- if (userIdentity && (userIdentity.email || userIdentity.userId)) {
445
+ if (userIdentity && (userIdentity.email || userIdentity.userId || userIdentity.traits)) {
405
446
  payload.userIdentity = {
406
447
  ...userIdentity.email && { email: userIdentity.email },
407
- ...userIdentity.userId && { userId: userIdentity.userId }
448
+ ...userIdentity.userId && { userId: userIdentity.userId },
449
+ ...userIdentity.traits && { traits: userIdentity.traits }
450
+ };
451
+ }
452
+ if (resolvedCustomerIdentity && (resolvedCustomerIdentity.customerId || resolvedCustomerIdentity.customerTraits)) {
453
+ payload.customerIdentity = {
454
+ ...resolvedCustomerIdentity.customerId && {
455
+ customerId: resolvedCustomerIdentity.customerId
456
+ },
457
+ ...resolvedCustomerIdentity.customerTraits && {
458
+ customerTraits: resolvedCustomerIdentity.customerTraits
459
+ }
408
460
  };
409
461
  }
410
462
  return payload;
@@ -440,6 +492,7 @@ function batchEvents(events) {
440
492
  isFieldDenied,
441
493
  isValidEmail,
442
494
  sanitizeFormFields,
495
+ validateCustomerIdentity,
443
496
  validateServerIdentity
444
497
  });
445
498
  //# sourceMappingURL=index.js.map