posthog-node 5.14.0 → 5.15.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.
Files changed (46) hide show
  1. package/dist/client.d.ts +44 -0
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +23 -3
  4. package/dist/client.mjs +24 -4
  5. package/dist/entrypoints/index.edge.d.ts +1 -0
  6. package/dist/entrypoints/index.edge.d.ts.map +1 -1
  7. package/dist/entrypoints/index.edge.js +1 -0
  8. package/dist/entrypoints/index.edge.mjs +1 -0
  9. package/dist/entrypoints/index.node.d.ts +2 -0
  10. package/dist/entrypoints/index.node.d.ts.map +1 -1
  11. package/dist/entrypoints/index.node.js +7 -0
  12. package/dist/entrypoints/index.node.mjs +4 -0
  13. package/dist/extensions/context/context.d.ts +8 -0
  14. package/dist/extensions/context/context.d.ts.map +1 -0
  15. package/dist/extensions/context/context.js +60 -0
  16. package/dist/extensions/context/context.mjs +26 -0
  17. package/dist/extensions/context/types.d.ts +18 -0
  18. package/dist/extensions/context/types.d.ts.map +1 -0
  19. package/dist/extensions/context/types.js +18 -0
  20. package/dist/extensions/context/types.mjs +0 -0
  21. package/dist/extensions/error-tracking/autocapture.js +2 -2
  22. package/dist/extensions/error-tracking/autocapture.mjs +2 -2
  23. package/dist/extensions/express.d.ts +3 -3
  24. package/dist/extensions/express.d.ts.map +1 -1
  25. package/dist/extensions/express.js +21 -7
  26. package/dist/extensions/express.mjs +21 -7
  27. package/dist/extensions/feature-flags/feature-flags.d.ts +0 -1
  28. package/dist/extensions/feature-flags/feature-flags.d.ts.map +1 -1
  29. package/dist/extensions/feature-flags/feature-flags.js +6 -13
  30. package/dist/extensions/feature-flags/feature-flags.mjs +6 -13
  31. package/dist/types.d.ts +23 -8
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/version.d.ts +1 -1
  34. package/dist/version.js +1 -1
  35. package/dist/version.mjs +1 -1
  36. package/package.json +4 -3
  37. package/src/client.ts +86 -6
  38. package/src/entrypoints/index.edge.ts +4 -0
  39. package/src/entrypoints/index.node.ts +5 -0
  40. package/src/extensions/context/context.ts +33 -0
  41. package/src/extensions/context/types.ts +19 -0
  42. package/src/extensions/error-tracking/autocapture.ts +2 -2
  43. package/src/extensions/express.ts +28 -12
  44. package/src/extensions/feature-flags/feature-flags.ts +10 -15
  45. package/src/types.ts +21 -4
  46. package/src/version.ts +1 -1
@@ -68,7 +68,6 @@ class FeatureFlagsPoller {
68
68
  this.debugMode = false;
69
69
  this.shouldBeginExponentialBackoff = false;
70
70
  this.backOffCount = 0;
71
- this.hasAttemptedCacheLoad = false;
72
71
  this.pollingInterval = pollingInterval;
73
72
  this.personalApiKey = personalApiKey;
74
73
  this.featureFlags = [];
@@ -296,15 +295,11 @@ class FeatureFlagsPoller {
296
295
  }
297
296
  }
298
297
  async loadFeatureFlags(forceReload = false) {
299
- if (this.cacheProvider && !this.hasAttemptedCacheLoad) {
300
- this.hasAttemptedCacheLoad = true;
301
- await this.loadFromCache('Loaded flags from cache');
302
- }
303
- if (this.loadingPromise) return this.loadingPromise;
304
- if (!this.loadedSuccessfullyOnce || forceReload) {
305
- this.loadingPromise = this._loadFeatureFlags();
306
- await this.loadingPromise;
307
- }
298
+ if (this.loadedSuccessfullyOnce && !forceReload) return;
299
+ if (!this.loadingPromise) this.loadingPromise = this._loadFeatureFlags().catch((err)=>this.logMsgIfDebug(()=>console.debug(`[FEATURE FLAGS] Failed to load feature flags: ${err}`))).finally(()=>{
300
+ this.loadingPromise = void 0;
301
+ });
302
+ return this.loadingPromise;
308
303
  }
309
304
  isLocalEvaluationReady() {
310
305
  return (this.loadedSuccessfullyOnce ?? false) && (this.featureFlags?.length ?? 0) > 0;
@@ -318,7 +313,7 @@ class FeatureFlagsPoller {
318
313
  clearTimeout(this.poller);
319
314
  this.poller = void 0;
320
315
  }
321
- this.poller = setTimeout(()=>this._loadFeatureFlags(), this.getPollingInterval());
316
+ this.poller = setTimeout(()=>this.loadFeatureFlags(true), this.getPollingInterval());
322
317
  try {
323
318
  let shouldFetch = true;
324
319
  if (this.cacheProvider) try {
@@ -378,8 +373,6 @@ class FeatureFlagsPoller {
378
373
  }
379
374
  } catch (err) {
380
375
  if (err instanceof ClientError) this.onError?.(err);
381
- } finally{
382
- this.loadingPromise = void 0;
383
376
  }
384
377
  }
385
378
  getPersonalApiKeyRequestOptions(method = 'GET') {
@@ -35,7 +35,6 @@ class FeatureFlagsPoller {
35
35
  this.debugMode = false;
36
36
  this.shouldBeginExponentialBackoff = false;
37
37
  this.backOffCount = 0;
38
- this.hasAttemptedCacheLoad = false;
39
38
  this.pollingInterval = pollingInterval;
40
39
  this.personalApiKey = personalApiKey;
41
40
  this.featureFlags = [];
@@ -263,15 +262,11 @@ class FeatureFlagsPoller {
263
262
  }
264
263
  }
265
264
  async loadFeatureFlags(forceReload = false) {
266
- if (this.cacheProvider && !this.hasAttemptedCacheLoad) {
267
- this.hasAttemptedCacheLoad = true;
268
- await this.loadFromCache('Loaded flags from cache');
269
- }
270
- if (this.loadingPromise) return this.loadingPromise;
271
- if (!this.loadedSuccessfullyOnce || forceReload) {
272
- this.loadingPromise = this._loadFeatureFlags();
273
- await this.loadingPromise;
274
- }
265
+ if (this.loadedSuccessfullyOnce && !forceReload) return;
266
+ if (!this.loadingPromise) this.loadingPromise = this._loadFeatureFlags().catch((err)=>this.logMsgIfDebug(()=>console.debug(`[FEATURE FLAGS] Failed to load feature flags: ${err}`))).finally(()=>{
267
+ this.loadingPromise = void 0;
268
+ });
269
+ return this.loadingPromise;
275
270
  }
276
271
  isLocalEvaluationReady() {
277
272
  return (this.loadedSuccessfullyOnce ?? false) && (this.featureFlags?.length ?? 0) > 0;
@@ -285,7 +280,7 @@ class FeatureFlagsPoller {
285
280
  clearTimeout(this.poller);
286
281
  this.poller = void 0;
287
282
  }
288
- this.poller = setTimeout(()=>this._loadFeatureFlags(), this.getPollingInterval());
283
+ this.poller = setTimeout(()=>this.loadFeatureFlags(true), this.getPollingInterval());
289
284
  try {
290
285
  let shouldFetch = true;
291
286
  if (this.cacheProvider) try {
@@ -345,8 +340,6 @@ class FeatureFlagsPoller {
345
340
  }
346
341
  } catch (err) {
347
342
  if (err instanceof ClientError) this.onError?.(err);
348
- } finally{
349
- this.loadingPromise = void 0;
350
343
  }
351
344
  }
352
345
  getPersonalApiKeyRequestOptions(method = 'GET') {
package/dist/types.d.ts CHANGED
@@ -1,30 +1,32 @@
1
1
  import type { PostHogCoreOptions, FeatureFlagValue, JsonType, PostHogFetchOptions, PostHogFetchResponse } from '@posthog/core';
2
+ import { ContextData, ContextOptions } from './extensions/context/types';
2
3
  import type { FlagDefinitionCacheProvider } from './extensions/feature-flags/cache';
3
- export interface IdentifyMessage {
4
+ export type IdentifyMessage = {
4
5
  distinctId: string;
5
6
  properties?: Record<string | number, any>;
6
7
  disableGeoip?: boolean;
7
- }
8
- export interface SendFeatureFlagsOptions {
8
+ };
9
+ export type SendFeatureFlagsOptions = {
9
10
  onlyEvaluateLocally?: boolean;
10
11
  personProperties?: Record<string, any>;
11
12
  groupProperties?: Record<string, Record<string, any>>;
12
13
  flagKeys?: string[];
13
- }
14
- export interface EventMessage extends IdentifyMessage {
14
+ };
15
+ export type EventMessage = Omit<IdentifyMessage, 'distinctId'> & {
16
+ distinctId?: string;
15
17
  event: string;
16
18
  groups?: Record<string, string | number>;
17
19
  sendFeatureFlags?: boolean | SendFeatureFlagsOptions;
18
20
  timestamp?: Date;
19
21
  uuid?: string;
20
- }
21
- export interface GroupIdentifyMessage {
22
+ };
23
+ export type GroupIdentifyMessage = {
22
24
  groupType: string;
23
25
  groupKey: string;
24
26
  properties?: Record<string | number, any>;
25
27
  distinctId?: string;
26
28
  disableGeoip?: boolean;
27
- }
29
+ };
28
30
  export type PropertyGroup = {
29
31
  type: 'AND' | 'OR';
30
32
  values: PropertyGroup[] | FlagProperty[];
@@ -294,6 +296,19 @@ export interface IPostHog {
294
296
  * already polled automatically at a regular interval.
295
297
  */
296
298
  reloadFeatureFlags(): Promise<void>;
299
+ /**
300
+ * @description Run a function with specific context that will be applied to all events captured within that context.
301
+ * @param data Context data to apply (sessionId, distinctId, properties, enableExceptionAutocapture)
302
+ * @param fn Function to run with the context
303
+ * @param options Context options (fresh)
304
+ * @returns The return value of the function
305
+ */
306
+ withContext<T>(data: Partial<ContextData>, fn: () => T, options?: ContextOptions): T;
307
+ /**
308
+ * @description Get the current context data.
309
+ * @returns The current context data, or undefined if no context is set
310
+ */
311
+ getContext(): ContextData | undefined;
297
312
  /**
298
313
  * @description Flushes the events still in the queue and clears the feature flags poller to allow for
299
314
  * a clean shutdown.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAExE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAA;AAEnF,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAA;IACzC,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;IACrD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,GAAG;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;IACxC,gBAAgB,CAAC,EAAE,OAAO,GAAG,uBAAuB,CAAA;IACpD,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAA;IACzC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,KAAK,GAAG,IAAI,CAAA;IAClB,MAAM,EAAE,aAAa,EAAE,GAAG,YAAY,EAAE,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,iBAAiB,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAAA;AAE/E,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE,YAAY,EAAE,CAAA;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,KAAK,YAAY,GAAG,IAAI,CAAA;AAE9E,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG;IAChD,WAAW,CAAC,EAAE,QAAQ,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,0BAA0B,CAAC,EAAE,OAAO,CAAA;IAEpC,2BAA2B,CAAC,EAAE,MAAM,CAAA;IAEpC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAGpF,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,2BAA2B,CAAC,EAAE,2BAA2B,CAAA;IACzD;;;;OAIG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAAA;IAC3C;;;;;;;;;OASG;IACH,sBAAsB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1C;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,MAAM,EAAE,CAAA;IACpC;;;;;;;;;;;;;;;;OAgBG;IACH,+BAA+B,CAAC,EAAE,OAAO,CAAA;CAC1C,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE;QACR,4BAA4B,CAAC,EAAE,MAAM,CAAA;QACrC,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAA;QAC/B,YAAY,CAAC,EAAE;YACb,QAAQ,EAAE;gBACR,GAAG,EAAE,MAAM,CAAA;gBACX,kBAAkB,EAAE,MAAM,CAAA;aAC3B,EAAE,CAAA;SACJ,CAAA;QACD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAClC,CAAA;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,kBAAkB,EAAE,IAAI,GAAG,MAAM,CAAA;IACjC,4BAA4B,EAAE,OAAO,CAAA;IACrC,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB,CAAA;AAED,MAAM,WAAW,QAAQ;IACvB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,YAAY,GAAG,IAAI,CAAA;IAExF;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE1G;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,eAAe,GAAG,IAAI,CAAA;IAE3D;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE7E;;;;;;;;;;OAUG;IACH,KAAK,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAExD;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE1E;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CACd,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QACzC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;QACxD,mBAAmB,CAAC,EAAE,OAAO,CAAA;QAC7B,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAChC,GACA,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAA;IAE/B;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CACZ,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QACzC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;QACxD,mBAAmB,CAAC,EAAE,OAAO,CAAA;QAC7B,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAChC,GACA,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAA;IAExC;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,qBAAqB,CACnB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,gBAAgB,EAC7B,OAAO,CAAC,EAAE;QACR,mBAAmB,CAAC,EAAE,OAAO,CAAA;KAC9B,GACA,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAA;IAEhC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,oBAAoB,GAAG,IAAI,CAAA;IAE9E;;;OAGG;IACH,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAEnC;;;;;;OAMG;IACH,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,CAAC,CAAA;IAEpF;;;OAGG;IACH,UAAU,IAAI,WAAW,GAAG,SAAS,CAAA;IAErC;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAE1C;;;;OAIG;IACH,2BAA2B,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAEjE;;;OAGG;IACH,sBAAsB,IAAI,OAAO,CAAA;CAClC"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const version = "5.14.0";
1
+ export declare const version = "5.15.0";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -26,7 +26,7 @@ __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
27
  version: ()=>version
28
28
  });
29
- const version = '5.14.0';
29
+ const version = '5.15.0';
30
30
  exports.version = __webpack_exports__.version;
31
31
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
32
32
  "version"
package/dist/version.mjs CHANGED
@@ -1,2 +1,2 @@
1
- const version = '5.14.0';
1
+ const version = '5.15.0';
2
2
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "posthog-node",
3
- "version": "5.14.0",
3
+ "version": "5.15.0",
4
4
  "description": "PostHog Node.js integration",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,10 +28,11 @@
28
28
  "@posthog/core": "1.6.0"
29
29
  },
30
30
  "devDependencies": {
31
+ "@rslib/core": "0.10.6",
32
+ "@types/express": "^5.0.5",
33
+ "@types/jest": "^29.5.14",
31
34
  "@types/node": "^20.0.0",
32
35
  "jest": "29.7.0",
33
- "@types/jest": "^29.5.14",
34
- "@rslib/core": "0.10.6",
35
36
  "@posthog-tooling/tsconfig-base": "1.1.0"
36
37
  },
37
38
  "keywords": [
package/src/client.ts CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  PostHogFetchResponse,
9
9
  PostHogFlagsAndPayloadsResponse,
10
10
  PostHogPersistedProperty,
11
- Logger,
12
11
  PostHogCaptureOptions,
13
12
  isPlainObject,
14
13
  isBlockedUA,
@@ -30,6 +29,8 @@ import {
30
29
  import ErrorTracking from './extensions/error-tracking'
31
30
  import { safeSetTimeout, PostHogEventProperties } from '@posthog/core'
32
31
  import { PostHogMemoryStorage } from './storage-memory'
32
+ import { uuidv7 } from '@posthog/core'
33
+ import { ContextData, ContextOptions, IPostHogContext } from './extensions/context/types'
33
34
 
34
35
  // Standard local evaluation rate limit is 600 per minute (10 per second),
35
36
  // so the fastest a poller should ever be set is 100ms.
@@ -45,6 +46,7 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
45
46
  protected errorTracking: ErrorTracking
46
47
  private maxCacheSize: number
47
48
  public readonly options: PostHogOptions
49
+ protected readonly context?: IPostHogContext
48
50
 
49
51
  distinctIdHasSentFlagCalls: Record<string, string[]>
50
52
 
@@ -81,6 +83,7 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
81
83
  super(apiKey, options)
82
84
 
83
85
  this.options = options
86
+ this.context = this.initializeContext()
84
87
 
85
88
  this.options.featureFlagsPollingInterval =
86
89
  typeof options.featureFlagsPollingInterval === 'number'
@@ -1159,6 +1162,60 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1159
1162
  await this.featureFlagsPoller?.loadFeatureFlags(true)
1160
1163
  }
1161
1164
 
1165
+ protected abstract initializeContext(): IPostHogContext | undefined
1166
+
1167
+ /**
1168
+ * Run a function with specific context that will be applied to all events captured within that context.
1169
+ * It propagates the context to all subsequent calls down the call stack.
1170
+ * Context properties like tags and sessionId will be automatically attached to all events.
1171
+ *
1172
+ * @example
1173
+ * ```ts
1174
+ * posthog.withContext(
1175
+ * { distinctId: 'user_123' },
1176
+ * () => {
1177
+ * posthog.capture({ event: 'button clicked' })
1178
+ * },
1179
+ * { fresh: false }
1180
+ * )
1181
+ * ```
1182
+ *
1183
+ * {@label Context}
1184
+ *
1185
+ * @param data - Context data to apply (sessionId, distinctId, properties, enableExceptionAutocapture)
1186
+ * @param fn - Function to run with the context
1187
+ * @param options - Context options (fresh)
1188
+ * @returns The return value of the function
1189
+ */
1190
+ withContext<T>(data: Partial<ContextData>, fn: () => T, options?: ContextOptions): T {
1191
+ if (!this.context) {
1192
+ // Context not supported in this environment (e.g., edge runtime)
1193
+ return fn()
1194
+ }
1195
+
1196
+ return this.context.run(data, fn, options)
1197
+ }
1198
+
1199
+ /**
1200
+ * Get the current context data.
1201
+ *
1202
+ * @example
1203
+ * ```ts
1204
+ * // Get current context within a withContext block
1205
+ * posthog.withContext({ distinctId: 'user_123' }, () => {
1206
+ * const context = posthog.getContext()
1207
+ * console.log(context?.distinctId) // 'user_123'
1208
+ * })
1209
+ * ```
1210
+ *
1211
+ * {@label Context}
1212
+ *
1213
+ * @returns The current context data, or undefined if no context is set
1214
+ */
1215
+ getContext(): ContextData | undefined {
1216
+ return this.context?.get()
1217
+ }
1218
+
1162
1219
  /**
1163
1220
  * Shutdown the PostHog client gracefully.
1164
1221
  *
@@ -1439,11 +1496,29 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1439
1496
  const { distinctId, event, properties, groups, sendFeatureFlags, timestamp, disableGeoip, uuid }: EventMessage =
1440
1497
  props
1441
1498
 
1499
+ const contextData = this.context?.get()
1500
+
1501
+ let mergedDistinctId = distinctId || contextData?.distinctId
1502
+
1503
+ const mergedProperties = {
1504
+ ...(contextData?.properties || {}),
1505
+ ...(properties || {}),
1506
+ }
1507
+
1508
+ if (!mergedDistinctId) {
1509
+ mergedDistinctId = uuidv7()
1510
+ mergedProperties.$process_person_profile = false
1511
+ }
1512
+
1513
+ if (contextData?.sessionId && !mergedProperties.$session_id) {
1514
+ mergedProperties.$session_id = contextData.sessionId
1515
+ }
1516
+
1442
1517
  // Run before_send if configured
1443
1518
  const eventMessage = this._runBeforeSend({
1444
- distinctId,
1519
+ distinctId: mergedDistinctId,
1445
1520
  event,
1446
- properties,
1521
+ properties: mergedProperties,
1447
1522
  groups,
1448
1523
  sendFeatureFlags,
1449
1524
  timestamp,
@@ -1461,10 +1536,15 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1461
1536
  if (sendFeatureFlags) {
1462
1537
  // If we are sending feature flags, we evaluate them locally if the user prefers it, otherwise we fall back to remote evaluation
1463
1538
  const sendFeatureFlagsOptions = typeof sendFeatureFlags === 'object' ? sendFeatureFlags : undefined
1464
- return await this.getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions)
1539
+ return await this.getFeatureFlagsForEvent(
1540
+ eventMessage.distinctId!,
1541
+ groups,
1542
+ disableGeoip,
1543
+ sendFeatureFlagsOptions
1544
+ )
1465
1545
  }
1466
1546
 
1467
- if (event === '$feature_flag_called') {
1547
+ if (eventMessage.event === '$feature_flag_called') {
1468
1548
  // If we're capturing a $feature_flag_called event, we don't want to enrich the event with cached flags that may be out of date.
1469
1549
  return {}
1470
1550
  }
@@ -1514,7 +1594,7 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1514
1594
  }
1515
1595
 
1516
1596
  return {
1517
- distinctId: eventMessage.distinctId,
1597
+ distinctId: eventMessage.distinctId!,
1518
1598
  event: eventMessage.event,
1519
1599
  properties: eventProperties,
1520
1600
  options: {
@@ -19,4 +19,8 @@ export class PostHog extends PostHogBackendClient {
19
19
  getLibraryId(): string {
20
20
  return 'posthog-edge'
21
21
  }
22
+
23
+ protected initializeContext(): undefined {
24
+ return undefined
25
+ }
22
26
  }
@@ -6,6 +6,7 @@ import ErrorTracking from '../extensions/error-tracking'
6
6
 
7
7
  import { PostHogBackendClient } from '../client'
8
8
  import { ErrorTracking as CoreErrorTracking } from '@posthog/core'
9
+ import { PostHogContext } from '../extensions/context/context'
9
10
 
10
11
  ErrorTracking.errorPropertiesBuilder = new CoreErrorTracking.ErrorPropertiesBuilder(
11
12
  [
@@ -23,4 +24,8 @@ export class PostHog extends PostHogBackendClient {
23
24
  getLibraryId(): string {
24
25
  return 'posthog-node'
25
26
  }
27
+
28
+ protected initializeContext(): PostHogContext {
29
+ return new PostHogContext()
30
+ }
26
31
  }
@@ -0,0 +1,33 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks'
2
+ import { ContextData, ContextOptions, IPostHogContext } from './types'
3
+
4
+ export class PostHogContext implements IPostHogContext {
5
+ private storage: AsyncLocalStorage<ContextData>
6
+
7
+ constructor() {
8
+ this.storage = new AsyncLocalStorage<ContextData>()
9
+ }
10
+
11
+ get(): ContextData | undefined {
12
+ return this.storage.getStore()
13
+ }
14
+
15
+ run<T>(context: ContextData, fn: () => T, options?: ContextOptions): T {
16
+ const fresh = options?.fresh !== false
17
+
18
+ if (fresh) {
19
+ return this.storage.run(context, fn)
20
+ } else {
21
+ const currentContext = this.get() || {}
22
+ const mergedContext: ContextData = {
23
+ distinctId: context.distinctId ?? currentContext.distinctId,
24
+ sessionId: context.sessionId ?? currentContext.sessionId,
25
+ properties: {
26
+ ...(currentContext.properties || {}),
27
+ ...(context.properties || {}),
28
+ },
29
+ }
30
+ return this.storage.run(mergedContext, fn)
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,19 @@
1
+ export interface ContextData {
2
+ distinctId?: string
3
+ sessionId?: string
4
+ properties?: Record<string, any>
5
+ }
6
+
7
+ export interface ContextOptions {
8
+ /**
9
+ * If true, replaces the current context entirely.
10
+ * If false, merges with the existing context (new values override existing ones).
11
+ * @default true
12
+ */
13
+ fresh?: boolean
14
+ }
15
+
16
+ export interface IPostHogContext {
17
+ get(): ContextData | undefined
18
+ run<T>(context: ContextData, fn: () => T, options?: ContextOptions): T
19
+ }
@@ -50,13 +50,13 @@ export function addUncaughtExceptionListener(
50
50
  captureFn: (exception: Error, hint: CoreErrorTracking.EventHint) => void,
51
51
  onFatalFn: (exception: Error) => void
52
52
  ): void {
53
- global.process.on('uncaughtException', makeUncaughtExceptionHandler(captureFn, onFatalFn))
53
+ globalThis.process?.on('uncaughtException', makeUncaughtExceptionHandler(captureFn, onFatalFn))
54
54
  }
55
55
 
56
56
  export function addUnhandledRejectionListener(
57
57
  captureFn: (exception: unknown, hint: CoreErrorTracking.EventHint) => void
58
58
  ): void {
59
- global.process.on('unhandledRejection', (reason: unknown) => {
59
+ globalThis.process?.on('unhandledRejection', (reason: unknown) => {
60
60
  return captureFn(reason, {
61
61
  mechanism: {
62
62
  type: 'onunhandledrejection',
@@ -1,15 +1,14 @@
1
- import type * as http from 'node:http'
2
- import { uuidv7 } from '@posthog/core'
3
1
  import ErrorTracking from './error-tracking'
4
2
  import { PostHogBackendClient } from '../client'
5
3
  import { ErrorTracking as CoreErrorTracking } from '@posthog/core'
4
+ import type { Request, Response } from 'express'
6
5
 
7
- type ExpressMiddleware = (req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void
6
+ type ExpressMiddleware = (req: Request, res: Response, next: () => void) => void
8
7
 
9
8
  type ExpressErrorMiddleware = (
10
9
  error: MiddlewareError,
11
- req: http.IncomingMessage,
12
- res: http.ServerResponse,
10
+ req: Request,
11
+ res: Response,
13
12
  next: (error: MiddlewareError) => void
14
13
  ) => void
15
14
 
@@ -28,13 +27,30 @@ export function setupExpressErrorHandler(
28
27
  use: (middleware: ExpressMiddleware | ExpressErrorMiddleware) => unknown
29
28
  }
30
29
  ): void {
31
- app.use((error: MiddlewareError, _, __, next: (error: MiddlewareError) => void): void => {
32
- const hint: CoreErrorTracking.EventHint = { mechanism: { type: 'middleware', handled: false } }
33
- // Given stateless nature of Node SDK we capture exceptions using personless processing
34
- // when no user can be determined e.g. in the case of exception autocapture
35
- ErrorTracking.buildEventMessage(error, hint, uuidv7(), { $process_person_profile: false }).then((msg) =>
36
- _posthog.capture(msg)
30
+ app.use(posthogErrorHandler(_posthog))
31
+ }
32
+
33
+ function posthogErrorHandler(posthog: PostHogBackendClient): ExpressErrorMiddleware {
34
+ return (error: MiddlewareError, req, res, next: (error: MiddlewareError) => void): void => {
35
+ const sessionId: string | undefined = req.headers['x-posthog-session-id'] as string | undefined
36
+ const distinctId: string | undefined = req.headers['x-posthog-distinct-id'] as string | undefined
37
+ const syntheticException = new Error('Synthetic exception')
38
+ const hint: CoreErrorTracking.EventHint = { mechanism: { type: 'middleware', handled: false }, syntheticException }
39
+
40
+ posthog.addPendingPromise(
41
+ ErrorTracking.buildEventMessage(error, hint, distinctId, {
42
+ $session_id: sessionId,
43
+ $current_url: req.url,
44
+ $request_method: req.method,
45
+ $request_path: req.path,
46
+ $user_agent: req.headers['user-agent'],
47
+ $response_status_code: res.statusCode,
48
+ $ip: req.headers['x-forwarded-for'] || req?.socket?.remoteAddress,
49
+ }).then((msg) => {
50
+ posthog.capture(msg)
51
+ })
37
52
  )
53
+
38
54
  next(error)
39
- })
55
+ }
40
56
  }
@@ -77,7 +77,6 @@ class FeatureFlagsPoller {
77
77
  backOffCount: number = 0
78
78
  onLoad?: (count: number) => void
79
79
  private cacheProvider?: FlagDefinitionCacheProvider
80
- private hasAttemptedCacheLoad: boolean = false
81
80
  private loadingPromise?: Promise<void>
82
81
 
83
82
  constructor({
@@ -582,21 +581,19 @@ class FeatureFlagsPoller {
582
581
  }
583
582
 
584
583
  async loadFeatureFlags(forceReload = false): Promise<void> {
585
- // On first load, try to initialize from cache (if a cache provider is configured)
586
- if (this.cacheProvider && !this.hasAttemptedCacheLoad) {
587
- this.hasAttemptedCacheLoad = true
588
- await this.loadFromCache('Loaded flags from cache')
584
+ if (this.loadedSuccessfullyOnce && !forceReload) {
585
+ return
589
586
  }
590
587
 
591
- // If a fetch is already in progress, wait for it
592
- if (this.loadingPromise) {
593
- return this.loadingPromise
594
- }
595
-
596
- if (!this.loadedSuccessfullyOnce || forceReload) {
588
+ if (!this.loadingPromise) {
597
589
  this.loadingPromise = this._loadFeatureFlags()
598
- await this.loadingPromise
590
+ .catch((err) => this.logMsgIfDebug(() => console.debug(`[FEATURE FLAGS] Failed to load feature flags: ${err}`)))
591
+ .finally(() => {
592
+ this.loadingPromise = undefined
593
+ })
599
594
  }
595
+
596
+ return this.loadingPromise
600
597
  }
601
598
 
602
599
  /**
@@ -627,7 +624,7 @@ class FeatureFlagsPoller {
627
624
  this.poller = undefined
628
625
  }
629
626
 
630
- this.poller = setTimeout(() => this._loadFeatureFlags(), this.getPollingInterval())
627
+ this.poller = setTimeout(() => this.loadFeatureFlags(true), this.getPollingInterval())
631
628
 
632
629
  try {
633
630
  let shouldFetch = true
@@ -764,8 +761,6 @@ class FeatureFlagsPoller {
764
761
  if (err instanceof ClientError) {
765
762
  this.onError?.(err)
766
763
  }
767
- } finally {
768
- this.loadingPromise = undefined
769
764
  }
770
765
  }
771
766
 
package/src/types.ts CHANGED
@@ -5,23 +5,25 @@ import type {
5
5
  PostHogFetchOptions,
6
6
  PostHogFetchResponse,
7
7
  } from '@posthog/core'
8
+ import { ContextData, ContextOptions } from './extensions/context/types'
8
9
 
9
10
  import type { FlagDefinitionCacheProvider } from './extensions/feature-flags/cache'
10
11
 
11
- export interface IdentifyMessage {
12
+ export type IdentifyMessage = {
12
13
  distinctId: string
13
14
  properties?: Record<string | number, any>
14
15
  disableGeoip?: boolean
15
16
  }
16
17
 
17
- export interface SendFeatureFlagsOptions {
18
+ export type SendFeatureFlagsOptions = {
18
19
  onlyEvaluateLocally?: boolean
19
20
  personProperties?: Record<string, any>
20
21
  groupProperties?: Record<string, Record<string, any>>
21
22
  flagKeys?: string[]
22
23
  }
23
24
 
24
- export interface EventMessage extends IdentifyMessage {
25
+ export type EventMessage = Omit<IdentifyMessage, 'distinctId'> & {
26
+ distinctId?: string // Optional - can be provided via context
25
27
  event: string
26
28
  groups?: Record<string, string | number> // Mapping of group type to group id
27
29
  sendFeatureFlags?: boolean | SendFeatureFlagsOptions
@@ -29,7 +31,7 @@ export interface EventMessage extends IdentifyMessage {
29
31
  uuid?: string
30
32
  }
31
33
 
32
- export interface GroupIdentifyMessage {
34
+ export type GroupIdentifyMessage = {
33
35
  groupType: string
34
36
  groupKey: string // Unique identifier for the group
35
37
  properties?: Record<string | number, any>
@@ -335,6 +337,21 @@ export interface IPostHog {
335
337
  */
336
338
  reloadFeatureFlags(): Promise<void>
337
339
 
340
+ /**
341
+ * @description Run a function with specific context that will be applied to all events captured within that context.
342
+ * @param data Context data to apply (sessionId, distinctId, properties, enableExceptionAutocapture)
343
+ * @param fn Function to run with the context
344
+ * @param options Context options (fresh)
345
+ * @returns The return value of the function
346
+ */
347
+ withContext<T>(data: Partial<ContextData>, fn: () => T, options?: ContextOptions): T
348
+
349
+ /**
350
+ * @description Get the current context data.
351
+ * @returns The current context data, or undefined if no context is set
352
+ */
353
+ getContext(): ContextData | undefined
354
+
338
355
  /**
339
356
  * @description Flushes the events still in the queue and clears the feature flags poller to allow for
340
357
  * a clean shutdown.
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '5.14.0'
1
+ export const version = '5.15.0'