@posthog/browser-common 0.2.4 → 0.3.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/README.md CHANGED
@@ -56,14 +56,15 @@ What an extension is given in `setup` — the adapter shared by extensions on th
56
56
 
57
57
  - **identity and session**: `distinctId`, `anonymousId`, `groups`, `session`
58
58
  - **events**: `capture(...)`, `registerDynamicEventProperties(...)`, `onEvent(...)`
59
- - **server config**: `getRemoteConfig()` and `onRemoteConfig(...)`
59
+ - **server config**: `onRemoteConfig(...)`
60
60
  - **transport**: `projectToken`, `sendRequest(path, init?)`
61
61
  - **storage and logging**: `kv`, `logger`
62
62
 
63
63
  Identity, session, and the public project token are always-ready synchronous
64
64
  reads. Operations that may perform I/O, including `capture`, `sendRequest`,
65
- `kv`, and `getRemoteConfig`, are awaitable. Extensions that want a named log
66
- prefix can create a child with `client.logger.createLogger('[myExtension]')`.
65
+ and `kv`, are awaitable. `onRemoteConfig` immediately replays the latest known
66
+ success or failure and then reports subsequent outcomes. Extensions that want a
67
+ named log prefix can create a child with `client.logger.createLogger('[myExtension]')`.
67
68
 
68
69
  ### Host runtime
69
70
 
package/dist/client.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { Properties } from '@posthog/types';
3
3
  import type { Disposable } from './disposable';
4
4
  import type { KeyValueStore } from './persistence';
5
5
  import type { Listener } from './pubsub';
6
- import type { RemoteConfig } from './types/remote-config';
6
+ import type { RemoteConfigResult } from './types/remote-config';
7
7
  /** Recursively marks object properties as readonly while preserving callable values. */
8
8
  export type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends object ? {
9
9
  readonly [K in keyof T]: DeepReadonly<T[K]>;
@@ -87,10 +87,8 @@ export interface Client {
87
87
  registerDynamicEventProperties(producer: () => Record<string, unknown>): Disposable;
88
88
  /** Fires for every captured event through a deeply readonly view. */
89
89
  readonly onEvent: Listener<CapturedEventInfo>;
90
- /** Resolves with the current remote config, awaiting the first outcome when necessary. */
91
- getRemoteConfig(): Promise<DeepReadonly<RemoteConfig> | undefined>;
92
- /** Fires when server-provided config arrives or changes successfully. */
93
- readonly onRemoteConfig: Listener<DeepReadonly<RemoteConfig>>;
90
+ /** Replays the latest remote-config outcome on subscription and fires for subsequent outcomes. */
91
+ readonly onRemoteConfig: Listener<DeepReadonly<RemoteConfigResult>>;
94
92
  /** Public project token used to authenticate endpoint-specific requests. */
95
93
  readonly projectToken: string;
96
94
  /** Sends a request through the host SDK's transport. */
package/dist/config.js CHANGED
@@ -26,7 +26,7 @@ __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
27
  default: ()=>__WEBPACK_DEFAULT_EXPORT__
28
28
  });
29
- const packageVersion = "0.2.4";
29
+ const packageVersion = "0.3.0";
30
30
  const Config = {
31
31
  DEBUG: false,
32
32
  LIB_VERSION: packageVersion,
package/dist/config.mjs CHANGED
@@ -1,4 +1,4 @@
1
- const packageVersion = "0.2.4";
1
+ const packageVersion = "0.3.0";
2
2
  const Config = {
3
3
  DEBUG: false,
4
4
  LIB_VERSION: packageVersion,
package/dist/pubsub.d.ts CHANGED
@@ -2,8 +2,9 @@ import { type Disposable } from './disposable';
2
2
  /**
3
3
  * Call it with a handler to start listening; dispose the returned
4
4
  * {@link Disposable} to stop. There is one `Listener` per event type, so every
5
- * event carries its own payload type. The handler is called synchronously for
6
- * each future payload, and the returned disposable unregisters it.
5
+ * event carries its own payload type. The handler is called synchronously, and
6
+ * stateful listeners may replay their current value during registration when
7
+ * documented. The returned disposable unregisters it from future payloads.
7
8
  */
8
9
  export type Listener<T> = (handler: (payload: T) => void) => Disposable;
9
10
  /**
@@ -178,3 +178,10 @@ export interface RemoteConfig {
178
178
  /** Conversations widget configuration. */
179
179
  conversations?: boolean | ConversationsRemoteConfig;
180
180
  }
181
+ /** The latest outcome of loading remote configuration from PostHog. */
182
+ export type RemoteConfigResult = {
183
+ ok: true;
184
+ config: RemoteConfig;
185
+ } | {
186
+ ok: false;
187
+ };
@@ -37,15 +37,26 @@ function getNativeImplementation(name, assignableWindow) {
37
37
  let impl = assignableWindow[name];
38
38
  if ((0, core_namespaceObject.isNativeFunction)(impl) && !(0, external_type_utils_js_namespaceObject.isAngularZonePresent)()) return cachedImplementations[name] = impl.bind(assignableWindow);
39
39
  const document = assignableWindow.document;
40
- if (document && (0, core_namespaceObject.isFunction)(document.createElement)) try {
41
- const sandbox = document.createElement('iframe');
42
- sandbox.hidden = true;
43
- document.head.appendChild(sandbox);
44
- const contentWindow = sandbox.contentWindow;
45
- if (contentWindow && contentWindow[name]) impl = contentWindow[name];
46
- document.head.removeChild(sandbox);
47
- } catch (e) {
48
- external_logger_js_namespaceObject.logger.warn(`Could not create sandbox iframe for ${name} check, bailing to assignableWindow.${name}: `, e);
40
+ if (document && (0, core_namespaceObject.isFunction)(document.createElement)) {
41
+ let sandbox;
42
+ let keepSandboxAttached = false;
43
+ try {
44
+ sandbox = document.createElement('iframe');
45
+ sandbox.hidden = true;
46
+ document.head.appendChild(sandbox);
47
+ const contentWindow = sandbox.contentWindow;
48
+ if (contentWindow && contentWindow[name]) {
49
+ impl = contentWindow[name];
50
+ if ('MutationObserver' === name && (0, core_namespaceObject.isWebKit)(assignableWindow.navigator?.userAgent ?? '')) {
51
+ sandbox.classList.add('rr-block', 'ph-no-capture');
52
+ keepSandboxAttached = true;
53
+ }
54
+ }
55
+ } catch (e) {
56
+ external_logger_js_namespaceObject.logger.warn(`Could not create sandbox iframe for ${name} check, bailing to assignableWindow.${name}: `, e);
57
+ } finally{
58
+ if (!keepSandboxAttached && sandbox?.parentNode) sandbox.parentNode.removeChild(sandbox);
59
+ }
49
60
  }
50
61
  if (!impl || !(0, core_namespaceObject.isFunction)(impl)) return impl;
51
62
  return cachedImplementations[name] = impl.bind(assignableWindow);
@@ -1,4 +1,4 @@
1
- import { isFunction, isNativeFunction } from "@posthog/core";
1
+ import { isFunction, isNativeFunction, isWebKit } from "@posthog/core";
2
2
  import { logger } from "./logger.mjs";
3
3
  import { isAngularZonePresent } from "./type-utils.mjs";
4
4
  const cachedImplementations = {};
@@ -8,15 +8,26 @@ function getNativeImplementation(name, assignableWindow) {
8
8
  let impl = assignableWindow[name];
9
9
  if (isNativeFunction(impl) && !isAngularZonePresent()) return cachedImplementations[name] = impl.bind(assignableWindow);
10
10
  const document = assignableWindow.document;
11
- if (document && isFunction(document.createElement)) try {
12
- const sandbox = document.createElement('iframe');
13
- sandbox.hidden = true;
14
- document.head.appendChild(sandbox);
15
- const contentWindow = sandbox.contentWindow;
16
- if (contentWindow && contentWindow[name]) impl = contentWindow[name];
17
- document.head.removeChild(sandbox);
18
- } catch (e) {
19
- logger.warn(`Could not create sandbox iframe for ${name} check, bailing to assignableWindow.${name}: `, e);
11
+ if (document && isFunction(document.createElement)) {
12
+ let sandbox;
13
+ let keepSandboxAttached = false;
14
+ try {
15
+ sandbox = document.createElement('iframe');
16
+ sandbox.hidden = true;
17
+ document.head.appendChild(sandbox);
18
+ const contentWindow = sandbox.contentWindow;
19
+ if (contentWindow && contentWindow[name]) {
20
+ impl = contentWindow[name];
21
+ if ('MutationObserver' === name && isWebKit(assignableWindow.navigator?.userAgent ?? '')) {
22
+ sandbox.classList.add('rr-block', 'ph-no-capture');
23
+ keepSandboxAttached = true;
24
+ }
25
+ }
26
+ } catch (e) {
27
+ logger.warn(`Could not create sandbox iframe for ${name} check, bailing to assignableWindow.${name}: `, e);
28
+ } finally{
29
+ if (!keepSandboxAttached && sandbox?.parentNode) sandbox.parentNode.removeChild(sandbox);
30
+ }
20
31
  }
21
32
  if (!impl || !isFunction(impl)) return impl;
22
33
  return cachedImplementations[name] = impl.bind(assignableWindow);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/browser-common",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Internal shared browser utilities and extension primitives for PostHog Browser SDKs",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -65,13 +65,14 @@
65
65
  }
66
66
  },
67
67
  "dependencies": {
68
- "@posthog/core": "^1.45.1",
69
- "@posthog/types": "^1.398.0"
68
+ "@posthog/core": "^1.46.0",
69
+ "@posthog/types": "^1.399.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@rslib/core": "0.10.6",
73
73
  "@types/jest": "^29.5.14",
74
74
  "jest": "29.7.0",
75
+ "jest-environment-jsdom": "^29.7.0",
75
76
  "ts-jest": "29.4.11",
76
77
  "typescript": "5.8.2",
77
78
  "@posthog-tooling/tsconfig-base": "1.1.1"