autotel-subscribers 10.0.0 → 11.0.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.
@@ -1,5 +1,5 @@
1
1
  import { EventAttributes } from 'autotel/event-subscriber';
2
- import { E as EventSubscriber, a as EventPayload } from './event-subscriber-base-CnF3V56W.cjs';
2
+ import { E as EventSubscriber, a as EventPayload } from './event-subscriber-base-uT-C_zrL.cjs';
3
3
  import { PostHog } from 'posthog-node';
4
4
 
5
5
  /**
@@ -90,6 +90,23 @@ import { PostHog } from 'posthog-node';
90
90
  * ```
91
91
  */
92
92
 
93
+ /**
94
+ * Error context for enhanced error handling
95
+ *
96
+ * Provides detailed context about the event that caused an error.
97
+ */
98
+ interface ErrorContext {
99
+ /** The error that occurred */
100
+ error: Error;
101
+ /** Event name (if applicable) */
102
+ eventName?: string;
103
+ /** Event type (event, funnel, outcome, value) */
104
+ eventType?: 'event' | 'funnel' | 'outcome' | 'value';
105
+ /** Event attributes (filtered) */
106
+ attributes?: EventAttributes;
107
+ /** Subscriber name */
108
+ subscriberName: string;
109
+ }
93
110
  interface PostHogConfig {
94
111
  /** PostHog API key (starts with phc_) - required if not providing custom client */
95
112
  apiKey?: string;
@@ -99,6 +116,39 @@ interface PostHogConfig {
99
116
  enabled?: boolean;
100
117
  /** Custom PostHog client instance (bypasses apiKey/host) */
101
118
  client?: PostHog;
119
+ /**
120
+ * Use global browser client (window.posthog)
121
+ *
122
+ * When true, uses the PostHog client already loaded on the page via script tag.
123
+ * This is useful for Next.js apps that initialize PostHog in _app.tsx.
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * // Browser - uses window.posthog
128
+ * const subscriber = new PostHogSubscriber({
129
+ * useGlobalClient: true,
130
+ * });
131
+ * ```
132
+ */
133
+ useGlobalClient?: boolean;
134
+ /**
135
+ * Serverless mode preset (AWS Lambda, Vercel Functions, Next.js API routes)
136
+ *
137
+ * When true, auto-configures for serverless environments:
138
+ * - flushAt: 1 (send immediately, don't batch)
139
+ * - flushInterval: 0 (disable interval-based flushing)
140
+ * - requestTimeout: 3000 (shorter timeout for fast responses)
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // Vercel / Next.js API route
145
+ * const subscriber = new PostHogSubscriber({
146
+ * apiKey: 'phc_...',
147
+ * serverless: true,
148
+ * });
149
+ * ```
150
+ */
151
+ serverless?: boolean;
102
152
  /** Flush batch when it reaches this size (default: 20, set to 1 for immediate send) */
103
153
  flushAt?: number;
104
154
  /** Flush interval in milliseconds (default: 10000, set to 0 to disable) */
@@ -109,8 +159,45 @@ interface PostHogConfig {
109
159
  requestTimeout?: number;
110
160
  /** Send feature flag evaluation events (default: true) */
111
161
  sendFeatureFlags?: boolean;
162
+ /**
163
+ * Automatically filter out undefined and null values from attributes
164
+ *
165
+ * When true (default), undefined and null values are removed before sending.
166
+ * This improves DX when passing objects with optional properties.
167
+ *
168
+ * @default true
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * // With filterUndefinedValues: true (default)
173
+ * subscriber.trackEvent('user.action', {
174
+ * userId: user.id,
175
+ * email: user.email, // might be undefined - will be filtered
176
+ * plan: user.subscription, // might be null - will be filtered
177
+ * });
178
+ * ```
179
+ */
180
+ filterUndefinedValues?: boolean;
112
181
  /** Error callback for debugging and monitoring */
113
182
  onError?: (error: Error) => void;
183
+ /**
184
+ * Enhanced error callback with event context
185
+ *
186
+ * Provides detailed context about the event that caused the error.
187
+ * If both onError and onErrorWithContext are provided, both are called.
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * const subscriber = new PostHogSubscriber({
192
+ * apiKey: 'phc_...',
193
+ * onErrorWithContext: (ctx) => {
194
+ * console.error(`Failed to track ${ctx.eventType}: ${ctx.eventName}`, ctx.error);
195
+ * Sentry.captureException(ctx.error, { extra: ctx });
196
+ * }
197
+ * });
198
+ * ```
199
+ */
200
+ onErrorWithContext?: (context: ErrorContext) => void;
114
201
  /** Enable debug logging (default: false) */
115
202
  debug?: boolean;
116
203
  }
@@ -146,6 +233,8 @@ declare class PostHogSubscriber extends EventSubscriber {
146
233
  private posthog;
147
234
  private config;
148
235
  private initPromise;
236
+ /** True when using browser's window.posthog (different API signature) */
237
+ private isBrowserClient;
149
238
  constructor(config: PostHogConfig);
150
239
  private initialize;
151
240
  private setupErrorHandling;
@@ -296,4 +385,4 @@ declare class PostHogSubscriber extends EventSubscriber {
296
385
  protected handleError(error: Error, payload: EventPayload): void;
297
386
  }
298
387
 
299
- export { type FeatureFlagOptions, type PersonProperties, type PostHogConfig, PostHogSubscriber };
388
+ export { type ErrorContext, type FeatureFlagOptions, type PersonProperties, type PostHogConfig, PostHogSubscriber };
package/dist/posthog.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { EventAttributes } from 'autotel/event-subscriber';
2
- import { E as EventSubscriber, a as EventPayload } from './event-subscriber-base-CnF3V56W.js';
2
+ import { E as EventSubscriber, a as EventPayload } from './event-subscriber-base-uT-C_zrL.js';
3
3
  import { PostHog } from 'posthog-node';
4
4
 
5
5
  /**
@@ -90,6 +90,23 @@ import { PostHog } from 'posthog-node';
90
90
  * ```
91
91
  */
92
92
 
93
+ /**
94
+ * Error context for enhanced error handling
95
+ *
96
+ * Provides detailed context about the event that caused an error.
97
+ */
98
+ interface ErrorContext {
99
+ /** The error that occurred */
100
+ error: Error;
101
+ /** Event name (if applicable) */
102
+ eventName?: string;
103
+ /** Event type (event, funnel, outcome, value) */
104
+ eventType?: 'event' | 'funnel' | 'outcome' | 'value';
105
+ /** Event attributes (filtered) */
106
+ attributes?: EventAttributes;
107
+ /** Subscriber name */
108
+ subscriberName: string;
109
+ }
93
110
  interface PostHogConfig {
94
111
  /** PostHog API key (starts with phc_) - required if not providing custom client */
95
112
  apiKey?: string;
@@ -99,6 +116,39 @@ interface PostHogConfig {
99
116
  enabled?: boolean;
100
117
  /** Custom PostHog client instance (bypasses apiKey/host) */
101
118
  client?: PostHog;
119
+ /**
120
+ * Use global browser client (window.posthog)
121
+ *
122
+ * When true, uses the PostHog client already loaded on the page via script tag.
123
+ * This is useful for Next.js apps that initialize PostHog in _app.tsx.
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * // Browser - uses window.posthog
128
+ * const subscriber = new PostHogSubscriber({
129
+ * useGlobalClient: true,
130
+ * });
131
+ * ```
132
+ */
133
+ useGlobalClient?: boolean;
134
+ /**
135
+ * Serverless mode preset (AWS Lambda, Vercel Functions, Next.js API routes)
136
+ *
137
+ * When true, auto-configures for serverless environments:
138
+ * - flushAt: 1 (send immediately, don't batch)
139
+ * - flushInterval: 0 (disable interval-based flushing)
140
+ * - requestTimeout: 3000 (shorter timeout for fast responses)
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // Vercel / Next.js API route
145
+ * const subscriber = new PostHogSubscriber({
146
+ * apiKey: 'phc_...',
147
+ * serverless: true,
148
+ * });
149
+ * ```
150
+ */
151
+ serverless?: boolean;
102
152
  /** Flush batch when it reaches this size (default: 20, set to 1 for immediate send) */
103
153
  flushAt?: number;
104
154
  /** Flush interval in milliseconds (default: 10000, set to 0 to disable) */
@@ -109,8 +159,45 @@ interface PostHogConfig {
109
159
  requestTimeout?: number;
110
160
  /** Send feature flag evaluation events (default: true) */
111
161
  sendFeatureFlags?: boolean;
162
+ /**
163
+ * Automatically filter out undefined and null values from attributes
164
+ *
165
+ * When true (default), undefined and null values are removed before sending.
166
+ * This improves DX when passing objects with optional properties.
167
+ *
168
+ * @default true
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * // With filterUndefinedValues: true (default)
173
+ * subscriber.trackEvent('user.action', {
174
+ * userId: user.id,
175
+ * email: user.email, // might be undefined - will be filtered
176
+ * plan: user.subscription, // might be null - will be filtered
177
+ * });
178
+ * ```
179
+ */
180
+ filterUndefinedValues?: boolean;
112
181
  /** Error callback for debugging and monitoring */
113
182
  onError?: (error: Error) => void;
183
+ /**
184
+ * Enhanced error callback with event context
185
+ *
186
+ * Provides detailed context about the event that caused the error.
187
+ * If both onError and onErrorWithContext are provided, both are called.
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * const subscriber = new PostHogSubscriber({
192
+ * apiKey: 'phc_...',
193
+ * onErrorWithContext: (ctx) => {
194
+ * console.error(`Failed to track ${ctx.eventType}: ${ctx.eventName}`, ctx.error);
195
+ * Sentry.captureException(ctx.error, { extra: ctx });
196
+ * }
197
+ * });
198
+ * ```
199
+ */
200
+ onErrorWithContext?: (context: ErrorContext) => void;
114
201
  /** Enable debug logging (default: false) */
115
202
  debug?: boolean;
116
203
  }
@@ -146,6 +233,8 @@ declare class PostHogSubscriber extends EventSubscriber {
146
233
  private posthog;
147
234
  private config;
148
235
  private initPromise;
236
+ /** True when using browser's window.posthog (different API signature) */
237
+ private isBrowserClient;
149
238
  constructor(config: PostHogConfig);
150
239
  private initialize;
151
240
  private setupErrorHandling;
@@ -296,4 +385,4 @@ declare class PostHogSubscriber extends EventSubscriber {
296
385
  protected handleError(error: Error, payload: EventPayload): void;
297
386
  }
298
387
 
299
- export { type FeatureFlagOptions, type PersonProperties, type PostHogConfig, PostHogSubscriber };
388
+ export { type ErrorContext, type FeatureFlagOptions, type PersonProperties, type PostHogConfig, PostHogSubscriber };
package/dist/posthog.js CHANGED
@@ -3682,6 +3682,35 @@ var EventSubscriber = class {
3682
3682
  payload
3683
3683
  );
3684
3684
  }
3685
+ /**
3686
+ * Filter out undefined and null values from attributes
3687
+ *
3688
+ * This improves DX by allowing callers to pass objects with optional properties
3689
+ * without having to manually filter them first.
3690
+ *
3691
+ * @param attributes - Input attributes (may contain undefined/null)
3692
+ * @returns Filtered attributes with only defined values, or undefined if empty
3693
+ *
3694
+ * @example
3695
+ * ```typescript
3696
+ * const filtered = this.filterAttributes({
3697
+ * userId: user.id,
3698
+ * email: user.email, // might be undefined
3699
+ * plan: null, // will be filtered out
3700
+ * });
3701
+ * // Result: { userId: 'abc', email: 'test@example.com' } or { userId: 'abc' }
3702
+ * ```
3703
+ */
3704
+ filterAttributes(attributes) {
3705
+ if (!attributes) return void 0;
3706
+ const filtered = {};
3707
+ for (const [key, value] of Object.entries(attributes)) {
3708
+ if (value !== void 0 && value !== null) {
3709
+ filtered[key] = value;
3710
+ }
3711
+ }
3712
+ return Object.keys(filtered).length > 0 ? filtered : void 0;
3713
+ }
3685
3714
  /**
3686
3715
  * Track an event
3687
3716
  */
@@ -3739,6 +3768,31 @@ var EventSubscriber = class {
3739
3768
  };
3740
3769
  await this.send(payload);
3741
3770
  }
3771
+ /**
3772
+ * Track funnel progression with custom step names
3773
+ *
3774
+ * Unlike trackFunnelStep which uses FunnelStatus enum values,
3775
+ * this method allows any string as the step name for flexible funnel tracking.
3776
+ *
3777
+ * @param funnelName - Name of the funnel (e.g., "checkout", "onboarding")
3778
+ * @param stepName - Custom step name (e.g., "cart_viewed", "payment_entered")
3779
+ * @param stepNumber - Optional numeric position in the funnel
3780
+ * @param attributes - Optional event attributes
3781
+ */
3782
+ async trackFunnelProgression(funnelName, stepName, stepNumber, attributes) {
3783
+ if (!this.enabled) return;
3784
+ const payload = {
3785
+ type: "funnel",
3786
+ name: `${funnelName}.${stepName}`,
3787
+ funnel: funnelName,
3788
+ step: stepName,
3789
+ stepName,
3790
+ stepNumber,
3791
+ attributes,
3792
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3793
+ };
3794
+ await this.send(payload);
3795
+ }
3742
3796
  /**
3743
3797
  * Flush pending requests and clean up
3744
3798
  *
@@ -3799,19 +3853,47 @@ var PostHogSubscriber = class extends EventSubscriber {
3799
3853
  posthog = null;
3800
3854
  config;
3801
3855
  initPromise = null;
3856
+ /** True when using browser's window.posthog (different API signature) */
3857
+ isBrowserClient = false;
3802
3858
  constructor(config) {
3803
3859
  super();
3804
- if (!config.apiKey && !config.client) {
3805
- throw new Error("PostHogSubscriber requires either apiKey or client to be provided");
3860
+ if (config.serverless) {
3861
+ config = {
3862
+ flushAt: 1,
3863
+ flushInterval: 0,
3864
+ requestTimeout: 3e3,
3865
+ ...config
3866
+ // User config overrides serverless defaults
3867
+ };
3868
+ }
3869
+ if (!config.apiKey && !config.client && !config.useGlobalClient) {
3870
+ throw new Error(
3871
+ "PostHogSubscriber requires either apiKey, client, or useGlobalClient to be provided"
3872
+ );
3806
3873
  }
3807
3874
  this.enabled = config.enabled ?? true;
3808
- this.config = config;
3875
+ this.config = {
3876
+ filterUndefinedValues: true,
3877
+ ...config
3878
+ };
3809
3879
  if (this.enabled) {
3810
3880
  this.initPromise = this.initialize();
3811
3881
  }
3812
3882
  }
3813
3883
  async initialize() {
3814
3884
  try {
3885
+ if (this.config.useGlobalClient) {
3886
+ const globalWindow = typeof globalThis === "undefined" ? void 0 : globalThis;
3887
+ if (globalWindow?.posthog) {
3888
+ this.posthog = globalWindow.posthog;
3889
+ this.isBrowserClient = true;
3890
+ this.setupErrorHandling();
3891
+ return;
3892
+ }
3893
+ throw new Error(
3894
+ "useGlobalClient enabled but window.posthog not found. Ensure PostHog script is loaded before initializing the subscriber."
3895
+ );
3896
+ }
3815
3897
  if (this.config.client) {
3816
3898
  this.posthog = this.config.client;
3817
3899
  this.setupErrorHandling();
@@ -3858,19 +3940,31 @@ var PostHogSubscriber = class extends EventSubscriber {
3858
3940
  */
3859
3941
  async sendToDestination(payload) {
3860
3942
  await this.ensureInitialized();
3861
- let properties = payload.attributes;
3943
+ const filteredAttributes = this.config.filterUndefinedValues === false ? payload.attributes : this.filterAttributes(payload.attributes);
3944
+ const properties = { ...filteredAttributes };
3862
3945
  if (payload.value !== void 0) {
3863
- properties = { ...payload.attributes, value: payload.value };
3946
+ properties.value = payload.value;
3864
3947
  }
3865
- const capturePayload = {
3866
- distinctId: this.extractDistinctId(payload.attributes),
3867
- event: payload.name,
3868
- properties
3869
- };
3870
- if (payload.attributes?.groups) {
3871
- capturePayload.groups = payload.attributes.groups;
3948
+ if (payload.stepNumber !== void 0) {
3949
+ properties.step_number = payload.stepNumber;
3950
+ }
3951
+ if (payload.stepName !== void 0) {
3952
+ properties.step_name = payload.stepName;
3953
+ }
3954
+ const distinctId = this.extractDistinctId(filteredAttributes);
3955
+ if (this.isBrowserClient) {
3956
+ this.posthog?.capture(payload.name, properties);
3957
+ } else {
3958
+ const capturePayload = {
3959
+ distinctId,
3960
+ event: payload.name,
3961
+ properties
3962
+ };
3963
+ if (filteredAttributes?.groups) {
3964
+ capturePayload.groups = filteredAttributes.groups;
3965
+ }
3966
+ this.posthog?.capture(capturePayload);
3872
3967
  }
3873
- this.posthog?.capture(capturePayload);
3874
3968
  }
3875
3969
  // Feature Flag Methods
3876
3970
  /**
@@ -4092,6 +4186,15 @@ var PostHogSubscriber = class extends EventSubscriber {
4092
4186
  */
4093
4187
  handleError(error, payload) {
4094
4188
  this.config.onError?.(error);
4189
+ if (this.config.onErrorWithContext) {
4190
+ this.config.onErrorWithContext({
4191
+ error,
4192
+ eventName: payload.name,
4193
+ eventType: payload.type,
4194
+ attributes: payload.attributes,
4195
+ subscriberName: this.name
4196
+ });
4197
+ }
4095
4198
  super.handleError(error, payload);
4096
4199
  }
4097
4200
  };