@posthog/browser-common
Advanced tools
| import type { JsonRecord, Properties } from '@posthog/types'; | ||
| import type { Disposable } from './disposable'; | ||
| import type { Extension } from './extension'; | ||
| import type { Listener } from './pubsub'; | ||
| import type { RemoteConfig } from './types/remote-config'; | ||
| import type { ExtensionToken } from './token'; | ||
| /** Recursively marks object properties as readonly while preserving callable values. */ | ||
| export type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends object ? { | ||
| readonly [K in keyof T]: DeepReadonly<T[K]>; | ||
| } : T; | ||
| /** The current session, stamped on events to tie them to a session and a browser tab. */ | ||
| export interface SessionContext { | ||
| /** The stable session identifier attached to events captured during this session. */ | ||
| readonly sessionId: string; | ||
| /** The logical browser tab/window identifier attached alongside the session id. */ | ||
| readonly windowId: string; | ||
| /** When the session started, as a Unix timestamp in milliseconds. */ | ||
| readonly sessionStartTimestamp: number; | ||
| } | ||
| /** Why a new session started (a `reset` also starts a new session). */ | ||
| export type NewSessionReason = 'initial' | 'reset' | 'idleTimeout' | 'maxLength' | 'crossTabAdoption'; | ||
| /** Details emitted when the client starts or adopts a new session. */ | ||
| export interface NewSessionInfo extends SessionContext { | ||
| /** The condition that caused this session to begin. */ | ||
| readonly reason: NewSessionReason; | ||
| } | ||
| /** A captured event, as observed by `onEvent`. */ | ||
| export interface CapturedEventInfo { | ||
| /** The finalized captured event name. */ | ||
| readonly event: string; | ||
| /** The final event properties after client defaults and dynamic properties are applied. */ | ||
| readonly properties: DeepReadonly<JsonRecord>; | ||
| } | ||
| /** Per-call capture overrides, mirroring the client's public capture options. */ | ||
| export interface CaptureOptions { | ||
| /** Override the event timestamp sent to PostHog. */ | ||
| timestamp?: Date; | ||
| /** Override the event UUID used for de-duplication. */ | ||
| uuid?: string; | ||
| /** Person properties to set, emitted as `$set`. */ | ||
| set?: Record<string, unknown>; | ||
| /** Person properties to set if unset, emitted as `$set_once`. */ | ||
| setOnce?: Record<string, unknown>; | ||
| } | ||
| /** | ||
| * The host SDK's core analytics behavior, exposed as an extension so shared | ||
| * extensions can depend on the event pipeline without depending on a concrete | ||
| * PostHog client implementation. | ||
| */ | ||
| export interface CoreExtension extends Extension { | ||
| /** The id events are currently attributed to. */ | ||
| readonly distinctId: string; | ||
| /** The anonymous device id carried across identify calls. */ | ||
| readonly anonymousId: string; | ||
| /** Active group memberships attached to events as `$groups`. */ | ||
| readonly groups: DeepReadonly<Record<string, string>>; | ||
| /** The current session, created on first read if needed. */ | ||
| readonly session: SessionContext; | ||
| /** Records an analytics event through the client's normal pipeline. */ | ||
| capture(event: string, properties?: Properties | null, options?: CaptureOptions): Promise<void>; | ||
| /** | ||
| * Registers a producer of properties merged into every captured event. | ||
| * The producer runs inline while the event is built and must be synchronous. | ||
| */ | ||
| registerDynamicEventProperties(producer: () => Record<string, unknown>): Disposable; | ||
| /** Fires for every captured event through a deeply readonly view. */ | ||
| readonly onEvent: Listener<CapturedEventInfo>; | ||
| /** Fires when a new session starts, including on reset. */ | ||
| readonly onNewSession: Listener<NewSessionInfo>; | ||
| /** | ||
| * Resolves with the current remote config, awaiting the first outcome when | ||
| * necessary. A failed outcome resolves to `undefined`; later successful | ||
| * changes are published through `onRemoteConfig`. | ||
| */ | ||
| getRemoteConfig(): Promise<DeepReadonly<RemoteConfig> | undefined>; | ||
| /** Fires through a deeply readonly view when server-provided config arrives or changes successfully. */ | ||
| readonly onRemoteConfig: Listener<DeepReadonly<RemoteConfig>>; | ||
| } | ||
| /** Capability token used to resolve the host SDK's core analytics extension. */ | ||
| export declare const CoreExtension: ExtensionToken<CoreExtension>; |
| "use strict"; | ||
| var __webpack_require__ = {}; | ||
| (()=>{ | ||
| __webpack_require__.d = (exports1, definition)=>{ | ||
| for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { | ||
| enumerable: true, | ||
| get: definition[key] | ||
| }); | ||
| }; | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop); | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.r = (exports1)=>{ | ||
| if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { | ||
| value: 'Module' | ||
| }); | ||
| Object.defineProperty(exports1, '__esModule', { | ||
| value: true | ||
| }); | ||
| }; | ||
| })(); | ||
| var __webpack_exports__ = {}; | ||
| __webpack_require__.r(__webpack_exports__); | ||
| __webpack_require__.d(__webpack_exports__, { | ||
| CoreExtension: ()=>CoreExtension | ||
| }); | ||
| const CoreExtension = 'posthog.core'; | ||
| exports.CoreExtension = __webpack_exports__.CoreExtension; | ||
| for(var __webpack_i__ in __webpack_exports__)if (-1 === [ | ||
| "CoreExtension" | ||
| ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
| Object.defineProperty(exports, '__esModule', { | ||
| value: true | ||
| }); |
| const CoreExtension = 'posthog.core'; | ||
| export { CoreExtension }; |
| import { type Logger } from '@posthog/core'; | ||
| import type { Client } from './client'; | ||
| import type { Disposable } from './disposable'; | ||
| import type { Extension } from './extension'; | ||
| import type { ExtensionToken } from './token'; | ||
| /** | ||
| * Shared lifecycle and capability registry for browser extension hosts. | ||
| * | ||
| * Hosts provide the concrete Client adapter while this runtime coordinates | ||
| * names, capability readiness, setup failures, and reverse-order teardown. | ||
| */ | ||
| export declare class ExtensionRuntime implements Disposable { | ||
| private readonly _logger; | ||
| private readonly _extensions; | ||
| private readonly _registrationOrder; | ||
| private readonly _providerReservations; | ||
| private readonly _providers; | ||
| private _disposePromise; | ||
| constructor(_logger: Logger); | ||
| /** | ||
| * Sets up an extension and publishes its capabilities once setup succeeds. | ||
| * Names and tokens remain reserved while asynchronous setup is pending. | ||
| */ | ||
| add(extension: Extension, client: Client): Promise<void>; | ||
| /** Resolves a capability only after its provider has completed setup. */ | ||
| getExtension<T>(token: ExtensionToken<T>): T | undefined; | ||
| /** Disposes every registered extension once, in reverse registration order. */ | ||
| dispose(): Promise<void>; | ||
| private _disposeAll; | ||
| private _handleSetupFailure; | ||
| private _disposeRegistration; | ||
| private _removeRegistration; | ||
| private _publishRegistration; | ||
| } |
| "use strict"; | ||
| var __webpack_require__ = {}; | ||
| (()=>{ | ||
| __webpack_require__.d = (exports1, definition)=>{ | ||
| for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { | ||
| enumerable: true, | ||
| get: definition[key] | ||
| }); | ||
| }; | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop); | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.r = (exports1)=>{ | ||
| if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { | ||
| value: 'Module' | ||
| }); | ||
| Object.defineProperty(exports1, '__esModule', { | ||
| value: true | ||
| }); | ||
| }; | ||
| })(); | ||
| var __webpack_exports__ = {}; | ||
| __webpack_require__.r(__webpack_exports__); | ||
| __webpack_require__.d(__webpack_exports__, { | ||
| ExtensionRuntime: ()=>ExtensionRuntime | ||
| }); | ||
| const core_namespaceObject = require("@posthog/core"); | ||
| class ExtensionRuntime { | ||
| constructor(_logger){ | ||
| this._logger = _logger; | ||
| this._extensions = new Map(); | ||
| this._registrationOrder = []; | ||
| this._providerReservations = new Map(); | ||
| this._providers = new Map(); | ||
| } | ||
| async add(extension, client) { | ||
| if (this._disposePromise) throw new Error('Cannot add an extension to a disposed ExtensionRuntime'); | ||
| if (this._extensions.has(extension.name)) throw new Error(`Browser extension "${extension.name}" is already registered`); | ||
| for (const token of extension.provides ?? [])if (this._providerReservations.has(token)) throw new Error(`Browser extension token "${token}" is already registered`); | ||
| const registered = { | ||
| extension, | ||
| setupPromise: Promise.resolve() | ||
| }; | ||
| this._extensions.set(extension.name, registered); | ||
| this._registrationOrder.push(registered); | ||
| for (const token of extension.provides ?? [])this._providerReservations.set(token, registered); | ||
| let setupResult; | ||
| try { | ||
| setupResult = extension.setup(client); | ||
| } catch (error) { | ||
| registered.setupPromise = this._handleSetupFailure(registered, error); | ||
| return registered.setupPromise; | ||
| } | ||
| if (setupResult && (0, core_namespaceObject.isFunction)(setupResult.then)) registered.setupPromise = setupResult.then(()=>this._publishRegistration(registered)).catch((error)=>this._handleSetupFailure(registered, error)); | ||
| else this._publishRegistration(registered); | ||
| return registered.setupPromise; | ||
| } | ||
| getExtension(token) { | ||
| return this._providers.get(token); | ||
| } | ||
| dispose() { | ||
| if (!this._disposePromise) this._disposePromise = this._disposeAll(); | ||
| return this._disposePromise; | ||
| } | ||
| async _disposeAll() { | ||
| for (const registered of this._registrationOrder.slice().reverse()){ | ||
| await registered.setupPromise; | ||
| await this._disposeRegistration(registered); | ||
| } | ||
| this._extensions.clear(); | ||
| this._registrationOrder.length = 0; | ||
| this._providerReservations.clear(); | ||
| this._providers.clear(); | ||
| } | ||
| async _handleSetupFailure(registered, error) { | ||
| this._removeRegistration(registered); | ||
| this._logger.error(`Failed to set up browser extension "${registered.extension.name}"`, error); | ||
| if (this._disposePromise) return; | ||
| await this._disposeRegistration(registered); | ||
| const index = this._registrationOrder.indexOf(registered); | ||
| if (-1 !== index) this._registrationOrder.splice(index, 1); | ||
| } | ||
| _disposeRegistration(registered) { | ||
| if (!registered.disposalPromise) registered.disposalPromise = Promise.resolve().then(()=>registered.extension.dispose()).catch((error)=>{ | ||
| this._logger.error(`Failed to dispose browser extension "${registered.extension.name}"`, error); | ||
| }); | ||
| return registered.disposalPromise; | ||
| } | ||
| _removeRegistration(registered) { | ||
| if (this._extensions.get(registered.extension.name) === registered) this._extensions.delete(registered.extension.name); | ||
| for (const token of registered.extension.provides ?? []){ | ||
| if (this._providerReservations.get(token) === registered) this._providerReservations.delete(token); | ||
| if (this._providers.get(token) === registered.extension) this._providers.delete(token); | ||
| } | ||
| } | ||
| _publishRegistration(registered) { | ||
| if (this._disposePromise || this._extensions.get(registered.extension.name) !== registered) return; | ||
| for (const token of registered.extension.provides ?? [])this._providers.set(token, registered.extension); | ||
| } | ||
| } | ||
| exports.ExtensionRuntime = __webpack_exports__.ExtensionRuntime; | ||
| for(var __webpack_i__ in __webpack_exports__)if (-1 === [ | ||
| "ExtensionRuntime" | ||
| ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
| Object.defineProperty(exports, '__esModule', { | ||
| value: true | ||
| }); |
| import { isFunction } from "@posthog/core"; | ||
| class ExtensionRuntime { | ||
| constructor(_logger){ | ||
| this._logger = _logger; | ||
| this._extensions = new Map(); | ||
| this._registrationOrder = []; | ||
| this._providerReservations = new Map(); | ||
| this._providers = new Map(); | ||
| } | ||
| async add(extension, client) { | ||
| if (this._disposePromise) throw new Error('Cannot add an extension to a disposed ExtensionRuntime'); | ||
| if (this._extensions.has(extension.name)) throw new Error(`Browser extension "${extension.name}" is already registered`); | ||
| for (const token of extension.provides ?? [])if (this._providerReservations.has(token)) throw new Error(`Browser extension token "${token}" is already registered`); | ||
| const registered = { | ||
| extension, | ||
| setupPromise: Promise.resolve() | ||
| }; | ||
| this._extensions.set(extension.name, registered); | ||
| this._registrationOrder.push(registered); | ||
| for (const token of extension.provides ?? [])this._providerReservations.set(token, registered); | ||
| let setupResult; | ||
| try { | ||
| setupResult = extension.setup(client); | ||
| } catch (error) { | ||
| registered.setupPromise = this._handleSetupFailure(registered, error); | ||
| return registered.setupPromise; | ||
| } | ||
| if (setupResult && isFunction(setupResult.then)) registered.setupPromise = setupResult.then(()=>this._publishRegistration(registered)).catch((error)=>this._handleSetupFailure(registered, error)); | ||
| else this._publishRegistration(registered); | ||
| return registered.setupPromise; | ||
| } | ||
| getExtension(token) { | ||
| return this._providers.get(token); | ||
| } | ||
| dispose() { | ||
| if (!this._disposePromise) this._disposePromise = this._disposeAll(); | ||
| return this._disposePromise; | ||
| } | ||
| async _disposeAll() { | ||
| for (const registered of this._registrationOrder.slice().reverse()){ | ||
| await registered.setupPromise; | ||
| await this._disposeRegistration(registered); | ||
| } | ||
| this._extensions.clear(); | ||
| this._registrationOrder.length = 0; | ||
| this._providerReservations.clear(); | ||
| this._providers.clear(); | ||
| } | ||
| async _handleSetupFailure(registered, error) { | ||
| this._removeRegistration(registered); | ||
| this._logger.error(`Failed to set up browser extension "${registered.extension.name}"`, error); | ||
| if (this._disposePromise) return; | ||
| await this._disposeRegistration(registered); | ||
| const index = this._registrationOrder.indexOf(registered); | ||
| if (-1 !== index) this._registrationOrder.splice(index, 1); | ||
| } | ||
| _disposeRegistration(registered) { | ||
| if (!registered.disposalPromise) registered.disposalPromise = Promise.resolve().then(()=>registered.extension.dispose()).catch((error)=>{ | ||
| this._logger.error(`Failed to dispose browser extension "${registered.extension.name}"`, error); | ||
| }); | ||
| return registered.disposalPromise; | ||
| } | ||
| _removeRegistration(registered) { | ||
| if (this._extensions.get(registered.extension.name) === registered) this._extensions.delete(registered.extension.name); | ||
| for (const token of registered.extension.provides ?? []){ | ||
| if (this._providerReservations.get(token) === registered) this._providerReservations.delete(token); | ||
| if (this._providers.get(token) === registered.extension) this._providers.delete(token); | ||
| } | ||
| } | ||
| _publishRegistration(registered) { | ||
| if (this._disposePromise || this._extensions.get(registered.extension.name) !== registered) return; | ||
| for (const token of registered.extension.provides ?? [])this._providers.set(token, registered.extension); | ||
| } | ||
| } | ||
| export { ExtensionRuntime }; |
| export declare const Compression: { | ||
| readonly GZipJS: "gzip-js"; | ||
| readonly Base64: "base64"; | ||
| }; | ||
| export type Compression = (typeof Compression)[keyof typeof Compression]; |
| "use strict"; | ||
| var __webpack_require__ = {}; | ||
| (()=>{ | ||
| __webpack_require__.d = (exports1, definition)=>{ | ||
| for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { | ||
| enumerable: true, | ||
| get: definition[key] | ||
| }); | ||
| }; | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop); | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.r = (exports1)=>{ | ||
| if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { | ||
| value: 'Module' | ||
| }); | ||
| Object.defineProperty(exports1, '__esModule', { | ||
| value: true | ||
| }); | ||
| }; | ||
| })(); | ||
| var __webpack_exports__ = {}; | ||
| __webpack_require__.r(__webpack_exports__); | ||
| __webpack_require__.d(__webpack_exports__, { | ||
| Compression: ()=>Compression | ||
| }); | ||
| const Compression = { | ||
| GZipJS: 'gzip-js', | ||
| Base64: 'base64' | ||
| }; | ||
| exports.Compression = __webpack_exports__.Compression; | ||
| for(var __webpack_i__ in __webpack_exports__)if (-1 === [ | ||
| "Compression" | ||
| ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
| Object.defineProperty(exports, '__esModule', { | ||
| value: true | ||
| }); |
| const Compression = { | ||
| GZipJS: 'gzip-js', | ||
| Base64: 'base64' | ||
| }; | ||
| export { Compression }; |
| export * from './compression'; | ||
| export * from './network-recording'; | ||
| export * from './remote-config'; | ||
| export * from './surveys'; |
| "use strict"; | ||
| var __webpack_modules__ = { | ||
| "./compression": function(module) { | ||
| module.exports = require("./compression.js"); | ||
| }, | ||
| "./network-recording": function(module) { | ||
| module.exports = require("./network-recording.js"); | ||
| }, | ||
| "./remote-config": function(module) { | ||
| module.exports = require("./remote-config.js"); | ||
| }, | ||
| "./surveys": function(module) { | ||
| module.exports = require("./surveys.js"); | ||
| } | ||
| }; | ||
| var __webpack_module_cache__ = {}; | ||
| function __webpack_require__(moduleId) { | ||
| var cachedModule = __webpack_module_cache__[moduleId]; | ||
| if (void 0 !== cachedModule) return cachedModule.exports; | ||
| var module = __webpack_module_cache__[moduleId] = { | ||
| exports: {} | ||
| }; | ||
| __webpack_modules__[moduleId](module, module.exports, __webpack_require__); | ||
| return module.exports; | ||
| } | ||
| (()=>{ | ||
| __webpack_require__.n = (module)=>{ | ||
| var getter = module && module.__esModule ? ()=>module['default'] : ()=>module; | ||
| __webpack_require__.d(getter, { | ||
| a: getter | ||
| }); | ||
| return getter; | ||
| }; | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.d = (exports1, definition)=>{ | ||
| for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { | ||
| enumerable: true, | ||
| get: definition[key] | ||
| }); | ||
| }; | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop); | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.r = (exports1)=>{ | ||
| if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { | ||
| value: 'Module' | ||
| }); | ||
| Object.defineProperty(exports1, '__esModule', { | ||
| value: true | ||
| }); | ||
| }; | ||
| })(); | ||
| var __webpack_exports__ = {}; | ||
| (()=>{ | ||
| __webpack_require__.r(__webpack_exports__); | ||
| var _compression__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./compression"); | ||
| var __WEBPACK_REEXPORT_OBJECT__ = {}; | ||
| for(var __WEBPACK_IMPORT_KEY__ in _compression__WEBPACK_IMPORTED_MODULE_0__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) { | ||
| return _compression__WEBPACK_IMPORTED_MODULE_0__[key]; | ||
| }).bind(0, __WEBPACK_IMPORT_KEY__); | ||
| __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); | ||
| var _network_recording__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./network-recording"); | ||
| var __WEBPACK_REEXPORT_OBJECT__ = {}; | ||
| for(var __WEBPACK_IMPORT_KEY__ in _network_recording__WEBPACK_IMPORTED_MODULE_1__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) { | ||
| return _network_recording__WEBPACK_IMPORTED_MODULE_1__[key]; | ||
| }).bind(0, __WEBPACK_IMPORT_KEY__); | ||
| __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); | ||
| var _remote_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("./remote-config"); | ||
| var __WEBPACK_REEXPORT_OBJECT__ = {}; | ||
| for(var __WEBPACK_IMPORT_KEY__ in _remote_config__WEBPACK_IMPORTED_MODULE_2__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) { | ||
| return _remote_config__WEBPACK_IMPORTED_MODULE_2__[key]; | ||
| }).bind(0, __WEBPACK_IMPORT_KEY__); | ||
| __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); | ||
| var _surveys__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./surveys"); | ||
| var __WEBPACK_REEXPORT_OBJECT__ = {}; | ||
| for(var __WEBPACK_IMPORT_KEY__ in _surveys__WEBPACK_IMPORTED_MODULE_3__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) { | ||
| return _surveys__WEBPACK_IMPORTED_MODULE_3__[key]; | ||
| }).bind(0, __WEBPACK_IMPORT_KEY__); | ||
| __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); | ||
| })(); | ||
| for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
| Object.defineProperty(exports, '__esModule', { | ||
| value: true | ||
| }); |
| export * from "./compression.mjs"; | ||
| export * from "./network-recording.mjs"; | ||
| export * from "./remote-config.mjs"; | ||
| export * from "./surveys.mjs"; |
| import type { CapturedNetworkRequest, InitiatorType } from '@posthog/types'; | ||
| export type NetworkRecordOptions = { | ||
| initiatorTypes?: InitiatorType[]; | ||
| maskRequestFn?: (data: CapturedNetworkRequest) => CapturedNetworkRequest | undefined; | ||
| recordHeaders?: boolean | { | ||
| request: boolean; | ||
| response: boolean; | ||
| }; | ||
| recordBody?: boolean | string[] | { | ||
| request: boolean | string[]; | ||
| response: boolean | string[]; | ||
| }; | ||
| recordInitialRequests?: boolean; | ||
| /** | ||
| * whether to record PerformanceEntry events for network requests | ||
| */ | ||
| recordPerformance?: boolean; | ||
| /** | ||
| * the PerformanceObserver will only observe these entry types | ||
| */ | ||
| performanceEntryTypeToObserve: string[]; | ||
| /** | ||
| * the maximum size of the request/response body to record | ||
| * NB this will be at most 1MB even if set larger | ||
| */ | ||
| payloadSizeLimitBytes: number; | ||
| /** | ||
| * when true, read bodies through a streaming reader that stops at payloadSizeLimitBytes | ||
| * instead of buffering the whole body and then enforcing the limit. Reads only a clone of | ||
| * the body, so it never consumes the stream the page itself reads. | ||
| * @default false | ||
| */ | ||
| streamNetworkBody?: boolean; | ||
| /** | ||
| * some domains we should never record the payload | ||
| * for example other companies session replay ingestion payloads aren't super useful but are gigantic | ||
| * if this isn't provided we use a default list | ||
| * if this is provided - we add the provided list to the default list | ||
| * i.e. we never record the payloads on the default deny list | ||
| */ | ||
| payloadHostDenyList?: string[]; | ||
| }; |
| "use strict"; | ||
| var __webpack_require__ = {}; | ||
| (()=>{ | ||
| __webpack_require__.r = (exports1)=>{ | ||
| if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { | ||
| value: 'Module' | ||
| }); | ||
| Object.defineProperty(exports1, '__esModule', { | ||
| value: true | ||
| }); | ||
| }; | ||
| })(); | ||
| var __webpack_exports__ = {}; | ||
| __webpack_require__.r(__webpack_exports__); | ||
| for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
| Object.defineProperty(exports, '__esModule', { | ||
| value: true | ||
| }); |
| import type { PerformanceCaptureConfig, SessionRecordingCanvasOptions, SessionRecordingOptions, ToolbarParams } from '@posthog/types'; | ||
| import type { Compression } from './compression'; | ||
| import type { NetworkRecordOptions } from './network-recording'; | ||
| import type { PropertyMatchType, Survey } from './surveys'; | ||
| export type FlagVariant = { | ||
| flag: string; | ||
| variant: string; | ||
| }; | ||
| export interface SessionRecordingUrlTrigger { | ||
| url: string; | ||
| matching: 'regex'; | ||
| } | ||
| /** | ||
| * V2 event trigger - always an object with name, optionally with property filters. | ||
| * The server normalizes bare event name strings to this shape before sending. | ||
| */ | ||
| export interface SessionRecordingEventTrigger { | ||
| name: string; | ||
| properties?: SessionRecordingTriggerPropertyFilter[]; | ||
| } | ||
| export interface SessionRecordingTriggerPropertyFilter { | ||
| key: string; | ||
| type: 'event' | 'person'; | ||
| operator?: 'exact' | 'is_not' | 'icontains' | 'not_icontains' | 'regex' | 'not_regex' | 'gt' | 'lt'; | ||
| value?: string | number | boolean | string[]; | ||
| } | ||
| /** | ||
| * V2 Trigger Group - represents a single trigger group with its own conditions and sample rate | ||
| */ | ||
| export interface SessionRecordingTriggerGroup { | ||
| id: string; | ||
| name: string; | ||
| sampleRate: number; | ||
| minDurationMs?: number; | ||
| conditions: { | ||
| matchType: 'any' | 'all'; | ||
| events?: SessionRecordingEventTrigger[]; | ||
| urls?: SessionRecordingUrlTrigger[]; | ||
| flag?: string | FlagVariant; | ||
| properties?: SessionRecordingTriggerPropertyFilter[]; | ||
| }; | ||
| } | ||
| export type SessionRecordingRemoteConfig = SessionRecordingCanvasOptions & { | ||
| endpoint?: string; | ||
| consoleLogRecordingEnabled?: boolean; | ||
| sampleRate?: string | null; | ||
| minimumDurationMilliseconds?: number; | ||
| linkedFlag?: string | FlagVariant | null; | ||
| networkPayloadCapture?: Pick<NetworkRecordOptions, 'recordBody' | 'recordHeaders'>; | ||
| masking?: Pick<SessionRecordingOptions, 'maskAllInputs' | 'maskTextSelector' | 'blockSelector'>; | ||
| urlTriggers?: SessionRecordingUrlTrigger[]; | ||
| scriptConfig?: { | ||
| script?: string | undefined; | ||
| }; | ||
| urlBlocklist?: SessionRecordingUrlTrigger[]; | ||
| eventTriggers?: string[]; | ||
| /** | ||
| * Controls how event, URL, sampling, and linked flag triggers are combined. | ||
| * | ||
| * `any` means that if any of the triggers match, the session will be recorded. | ||
| * `all` means that all the triggers must match for the session to be recorded. | ||
| */ | ||
| triggerMatchType?: 'any' | 'all'; | ||
| /** | ||
| * Config version - defaults to 1 (legacy). | ||
| * When version is 2, triggerGroups is used instead of individual trigger fields. | ||
| */ | ||
| version?: 1 | 2; | ||
| /** | ||
| * V2 Trigger Groups - multiple named trigger groups with their own conditions and sample rates. | ||
| * Only used when version === 2. | ||
| */ | ||
| triggerGroups?: SessionRecordingTriggerGroup[]; | ||
| }; | ||
| export interface ErrorTrackingSuppressionRule { | ||
| type: 'AND' | 'OR'; | ||
| values: ErrorTrackingSuppressionRuleValue[]; | ||
| } | ||
| export interface ErrorTrackingSuppressionRuleValue { | ||
| key: '$exception_types' | '$exception_values'; | ||
| operator: PropertyMatchType; | ||
| value: string | string[]; | ||
| type: string; | ||
| } | ||
| /** Position of the conversations widget on the screen. */ | ||
| export type WidgetPosition = 'bottom_left' | 'bottom_right' | 'top_left' | 'top_right'; | ||
| /** Remote configuration for conversations from the PostHog server. */ | ||
| export interface ConversationsRemoteConfig { | ||
| /** Whether conversations are enabled for this team. */ | ||
| enabled: boolean; | ||
| /** Whether the widget UI should be shown. */ | ||
| widgetEnabled?: boolean; | ||
| /** Public token for authenticating conversations API requests. */ | ||
| token: string; | ||
| /** Greeting text to show when the widget is first opened. */ | ||
| greetingText?: string; | ||
| /** Primary color for the widget UI. */ | ||
| color?: string; | ||
| /** Placeholder text for the message input. */ | ||
| placeholderText?: string; | ||
| /** Whether to require an email address before starting a conversation. */ | ||
| requireEmail?: boolean; | ||
| /** Whether to show the name field in the identification form. */ | ||
| collectName?: boolean; | ||
| /** Title for the identification form. */ | ||
| identificationFormTitle?: string; | ||
| /** Description for the identification form. */ | ||
| identificationFormDescription?: string; | ||
| /** Domains where the widget may be shown. */ | ||
| domains?: string[]; | ||
| /** Position of the widget on the screen. */ | ||
| widgetPosition?: WidgetPosition; | ||
| } | ||
| /** | ||
| * Remote configuration for a PostHog browser client. | ||
| * | ||
| * These settings can be configured in PostHog. Configuration set directly on | ||
| * the client takes precedence over values from the server. | ||
| */ | ||
| export interface RemoteConfig { | ||
| /** Supported compression algorithms. */ | ||
| supportedCompression: Compression[]; | ||
| /** | ||
| * If true, disables autocapture. When absent or not a boolean, the SDK | ||
| * keeps the last known server value; a visitor with no stored value keeps | ||
| * autocapture off until a response containing the field arrives. | ||
| */ | ||
| autocapture_opt_out?: boolean; | ||
| /** Performance capture configuration shared by replay and web vitals. */ | ||
| capturePerformance?: boolean | PerformanceCaptureConfig; | ||
| /** Custom endpoint configuration for analytics events. */ | ||
| analytics?: { | ||
| endpoint?: string; | ||
| }; | ||
| /** Whether `$elements_chain` is sent as a string rather than an array. */ | ||
| elementsChainAsString?: boolean; | ||
| /** Error tracking configuration. */ | ||
| errorTracking?: { | ||
| autocaptureExceptions?: boolean; | ||
| captureExtensionExceptions?: boolean; | ||
| suppressionRules?: ErrorTrackingSuppressionRule[]; | ||
| }; | ||
| /** Log capture configuration. */ | ||
| logs?: { | ||
| captureConsoleLogs?: boolean; | ||
| }; | ||
| /** Exception autocapture configuration. */ | ||
| autocaptureExceptions?: boolean | { | ||
| endpoint?: string; | ||
| }; | ||
| /** Session recording configuration. */ | ||
| sessionRecording?: SessionRecordingRemoteConfig | false; | ||
| /** Whether surveys are enabled, optionally including their definitions. */ | ||
| surveys?: boolean | Survey[]; | ||
| /** Whether product tours are enabled. */ | ||
| productTours?: boolean; | ||
| /** Parameters for the toolbar. */ | ||
| toolbarParams: ToolbarParams; | ||
| /** @deprecated Renamed to `toolbarParams`; still present on older API responses. */ | ||
| editorParams?: ToolbarParams; | ||
| /** @deprecated Moved to `toolbarParams`. */ | ||
| toolbarVersion: 'toolbar'; | ||
| /** Whether the current user is authenticated. */ | ||
| isAuthenticated: boolean; | ||
| /** Site apps available to the client. */ | ||
| siteApps: { | ||
| id: string; | ||
| url: string; | ||
| }[]; | ||
| /** Whether heatmaps are enabled. */ | ||
| heatmaps?: boolean; | ||
| /** Whether to capture only identified users by default. */ | ||
| defaultIdentifiedOnly?: boolean; | ||
| /** Whether to capture dead clicks. */ | ||
| captureDeadClicks?: boolean; | ||
| /** Whether the team has any feature flags enabled. */ | ||
| hasFeatureFlags?: boolean; | ||
| /** Conversations widget configuration. */ | ||
| conversations?: boolean | ConversationsRemoteConfig; | ||
| } |
| "use strict"; | ||
| var __webpack_require__ = {}; | ||
| (()=>{ | ||
| __webpack_require__.r = (exports1)=>{ | ||
| if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { | ||
| value: 'Module' | ||
| }); | ||
| Object.defineProperty(exports1, '__esModule', { | ||
| value: true | ||
| }); | ||
| }; | ||
| })(); | ||
| var __webpack_exports__ = {}; | ||
| __webpack_require__.r(__webpack_exports__); | ||
| for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
| Object.defineProperty(exports, '__esModule', { | ||
| value: true | ||
| }); |
| import type { SurveyAppearance as CoreSurveyAppearance, SurveyQuestionTranslation, SurveyTranslation, SurveyValidationRule } from '@posthog/core'; | ||
| export declare const SurveyEventType: { | ||
| readonly Activation: "events"; | ||
| readonly Cancellation: "cancelEvents"; | ||
| }; | ||
| export type SurveyEventType = (typeof SurveyEventType)[keyof typeof SurveyEventType]; | ||
| export declare const SurveyWidgetType: { | ||
| readonly Button: "button"; | ||
| readonly Tab: "tab"; | ||
| readonly Selector: "selector"; | ||
| }; | ||
| export type SurveyWidgetType = (typeof SurveyWidgetType)[keyof typeof SurveyWidgetType]; | ||
| export declare const SurveyPosition: { | ||
| readonly TopLeft: "top_left"; | ||
| readonly TopRight: "top_right"; | ||
| readonly TopCenter: "top_center"; | ||
| readonly MiddleLeft: "middle_left"; | ||
| readonly MiddleRight: "middle_right"; | ||
| readonly MiddleCenter: "middle_center"; | ||
| readonly Left: "left"; | ||
| readonly Center: "center"; | ||
| readonly Right: "right"; | ||
| readonly NextToTrigger: "next_to_trigger"; | ||
| }; | ||
| export type SurveyPosition = (typeof SurveyPosition)[keyof typeof SurveyPosition]; | ||
| export declare const SurveyTabPosition: { | ||
| readonly Top: "top"; | ||
| readonly Left: "left"; | ||
| readonly Right: "right"; | ||
| readonly Bottom: "bottom"; | ||
| }; | ||
| export type SurveyTabPosition = (typeof SurveyTabPosition)[keyof typeof SurveyTabPosition]; | ||
| export declare const SurveyType: { | ||
| readonly Popover: "popover"; | ||
| readonly API: "api"; | ||
| readonly Widget: "widget"; | ||
| readonly ExternalSurvey: "external_survey"; | ||
| }; | ||
| export type SurveyType = (typeof SurveyType)[keyof typeof SurveyType]; | ||
| export declare const SurveyQuestionType: { | ||
| readonly Open: "open"; | ||
| readonly MultipleChoice: "multiple_choice"; | ||
| readonly SingleChoice: "single_choice"; | ||
| readonly Rating: "rating"; | ||
| readonly Link: "link"; | ||
| }; | ||
| export type SurveyQuestionType = (typeof SurveyQuestionType)[keyof typeof SurveyQuestionType]; | ||
| export declare const SurveyQuestionBranchingType: { | ||
| readonly NextQuestion: "next_question"; | ||
| readonly End: "end"; | ||
| readonly ResponseBased: "response_based"; | ||
| readonly SpecificQuestion: "specific_question"; | ||
| }; | ||
| export type SurveyQuestionBranchingType = (typeof SurveyQuestionBranchingType)[keyof typeof SurveyQuestionBranchingType]; | ||
| export declare const SurveySchedule: { | ||
| readonly Once: "once"; | ||
| readonly Recurring: "recurring"; | ||
| readonly Always: "always"; | ||
| }; | ||
| export type SurveySchedule = (typeof SurveySchedule)[keyof typeof SurveySchedule]; | ||
| export declare const SurveyEventName: { | ||
| readonly SHOWN: "survey shown"; | ||
| readonly DISMISSED: "survey dismissed"; | ||
| readonly SENT: "survey sent"; | ||
| readonly ABANDONED: "survey abandoned"; | ||
| }; | ||
| export type SurveyEventName = (typeof SurveyEventName)[keyof typeof SurveyEventName]; | ||
| export declare const SurveyEventProperties: { | ||
| readonly SURVEY_ID: "$survey_id"; | ||
| readonly SURVEY_NAME: "$survey_name"; | ||
| readonly SURVEY_RESPONSE: "$survey_response"; | ||
| readonly SURVEY_ITERATION: "$survey_iteration"; | ||
| readonly SURVEY_ITERATION_START_DATE: "$survey_iteration_start_date"; | ||
| readonly SURVEY_PARTIALLY_COMPLETED: "$survey_partially_completed"; | ||
| readonly SURVEY_SUBMISSION_ID: "$survey_submission_id"; | ||
| readonly SURVEY_QUESTIONS: "$survey_questions"; | ||
| readonly SURVEY_COMPLETED: "$survey_completed"; | ||
| readonly PRODUCT_TOUR_ID: "$product_tour_id"; | ||
| readonly SURVEY_LAST_SEEN_DATE: "$survey_last_seen_date"; | ||
| readonly SURVEY_LANGUAGE: "$survey_language"; | ||
| }; | ||
| export type SurveyEventProperties = (typeof SurveyEventProperties)[keyof typeof SurveyEventProperties]; | ||
| export declare const DisplaySurveyType: { | ||
| readonly Popover: "popover"; | ||
| readonly Inline: "inline"; | ||
| }; | ||
| export type DisplaySurveyType = (typeof DisplaySurveyType)[keyof typeof DisplaySurveyType]; | ||
| export type PropertyMatchType = 'regex' | 'not_regex' | 'exact' | 'is_not' | 'icontains' | 'not_icontains'; | ||
| /** Extended survey operator type with numeric comparisons. */ | ||
| export type PropertyOperator = PropertyMatchType | 'gt' | 'lt'; | ||
| export type PropertyFilters = { | ||
| [propertyName: string]: { | ||
| values: string[]; | ||
| operator: PropertyOperator; | ||
| }; | ||
| }; | ||
| export interface SurveyEventWithFilters { | ||
| name: string; | ||
| propertyFilters?: PropertyFilters; | ||
| } | ||
| export type SurveyQuestionDescriptionContentType = 'html' | 'text'; | ||
| /** Browser survey appearance, including browser-only placement and rendering options. */ | ||
| export interface SurveyAppearance extends Omit<CoreSurveyAppearance, 'position' | 'widgetType'> { | ||
| /** @deprecated Not currently used. */ | ||
| descriptionTextColor?: string; | ||
| ratingButtonHoverColor?: string; | ||
| whiteLabel?: boolean; | ||
| tabPosition?: SurveyTabPosition; | ||
| fontFamily?: string; | ||
| maxWidth?: string; | ||
| zIndex?: string; | ||
| disabledButtonOpacity?: string; | ||
| boxPadding?: string; | ||
| /** @deprecated Use `inputBackground` instead. */ | ||
| inputBackgroundColor?: string; | ||
| hideCancelButton?: boolean; | ||
| disableAutofocus?: boolean; | ||
| position?: SurveyPosition; | ||
| widgetType?: SurveyWidgetType; | ||
| } | ||
| export type SurveyQuestion = BasicSurveyQuestion | LinkSurveyQuestion | RatingSurveyQuestion | MultipleSurveyQuestion; | ||
| interface SurveyQuestionBase { | ||
| question: string; | ||
| id?: string; | ||
| description?: string | null; | ||
| descriptionContentType?: SurveyQuestionDescriptionContentType; | ||
| optional?: boolean; | ||
| buttonText?: string; | ||
| branching?: NextQuestionBranching | EndBranching | ResponseBasedBranching | SpecificQuestionBranching; | ||
| validation?: SurveyValidationRule[]; | ||
| translations?: Record<string, SurveyQuestionTranslation>; | ||
| } | ||
| export interface BasicSurveyQuestion extends SurveyQuestionBase { | ||
| type: typeof SurveyQuestionType.Open; | ||
| } | ||
| export interface LinkSurveyQuestion extends SurveyQuestionBase { | ||
| type: typeof SurveyQuestionType.Link; | ||
| link?: string | null; | ||
| } | ||
| export interface RatingSurveyQuestion extends SurveyQuestionBase { | ||
| type: typeof SurveyQuestionType.Rating; | ||
| display: 'number' | 'emoji'; | ||
| scale: 2 | 3 | 5 | 7 | 10; | ||
| lowerBoundLabel: string; | ||
| upperBoundLabel: string; | ||
| skipSubmitButton?: boolean; | ||
| } | ||
| export interface MultipleSurveyQuestion extends SurveyQuestionBase { | ||
| type: typeof SurveyQuestionType.SingleChoice | typeof SurveyQuestionType.MultipleChoice; | ||
| choices: string[]; | ||
| hasOpenChoice?: boolean; | ||
| shuffleOptions?: boolean; | ||
| skipSubmitButton?: boolean; | ||
| } | ||
| interface NextQuestionBranching { | ||
| type: typeof SurveyQuestionBranchingType.NextQuestion; | ||
| } | ||
| interface EndBranching { | ||
| type: typeof SurveyQuestionBranchingType.End; | ||
| } | ||
| interface ResponseBasedBranching { | ||
| type: typeof SurveyQuestionBranchingType.ResponseBased; | ||
| responseValues: Record<string, any>; | ||
| } | ||
| interface SpecificQuestionBranching { | ||
| type: typeof SurveyQuestionBranchingType.SpecificQuestion; | ||
| index: number; | ||
| } | ||
| /** A survey definition returned as part of browser remote config. */ | ||
| export interface Survey { | ||
| id: string; | ||
| name: string; | ||
| description?: string; | ||
| type: SurveyType; | ||
| translations?: Record<string, SurveyTranslation>; | ||
| feature_flag_keys: { | ||
| key: string; | ||
| value?: string; | ||
| }[] | null; | ||
| linked_flag_key: string | null; | ||
| targeting_flag_key: string | null; | ||
| internal_targeting_flag_key: string | null; | ||
| questions: SurveyQuestion[]; | ||
| appearance: SurveyAppearance | null; | ||
| conditions: { | ||
| url?: string; | ||
| selector?: string; | ||
| seenSurveyWaitPeriodInDays?: number; | ||
| urlMatchType?: PropertyMatchType; | ||
| events: { | ||
| repeatedActivation?: boolean; | ||
| values: SurveyEventWithFilters[]; | ||
| } | null; | ||
| cancelEvents: { | ||
| values: SurveyEventWithFilters[]; | ||
| } | null; | ||
| actions: { | ||
| values: SurveyActionType[]; | ||
| } | null; | ||
| deviceTypes?: string[]; | ||
| deviceTypesMatchType?: PropertyMatchType; | ||
| linkedFlagVariant?: string; | ||
| } | null; | ||
| start_date: string | null; | ||
| end_date: string | null; | ||
| current_iteration: number | null; | ||
| current_iteration_start_date: string | null; | ||
| schedule?: SurveySchedule | null; | ||
| enable_partial_responses?: boolean | null; | ||
| } | ||
| export type SurveyWithTypeAndAppearance = Pick<Survey, 'id' | 'type' | 'appearance'>; | ||
| export interface SurveyActionType { | ||
| id: number; | ||
| name: string | null; | ||
| steps?: ActionStepType[]; | ||
| } | ||
| /** Sync with plugin-server/src/types.ts. */ | ||
| export type ActionStepStringMatching = 'contains' | 'exact' | 'regex'; | ||
| export interface ActionStepType { | ||
| event?: string | null; | ||
| selector?: string | null; | ||
| /** Pre-compiled regex pattern for matching selector against `$elements_chain`. */ | ||
| selector_regex?: string | null; | ||
| /** @deprecated Only `selector` should be used now. */ | ||
| tag_name?: string; | ||
| text?: string | null; | ||
| /** @default StringMatching.Exact */ | ||
| text_matching?: ActionStepStringMatching | null; | ||
| href?: string | null; | ||
| /** @default StringMatching.Exact */ | ||
| href_matching?: ActionStepStringMatching | null; | ||
| url?: string | null; | ||
| /** @default StringMatching.Contains */ | ||
| url_matching?: ActionStepStringMatching | null; | ||
| /** Property filters for action step matching. */ | ||
| properties?: { | ||
| key: string; | ||
| value?: string | number | boolean | (string | number | boolean)[] | null; | ||
| operator?: PropertyMatchType; | ||
| type?: string; | ||
| }[]; | ||
| } | ||
| export {}; |
| "use strict"; | ||
| var __webpack_require__ = {}; | ||
| (()=>{ | ||
| __webpack_require__.d = (exports1, definition)=>{ | ||
| for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { | ||
| enumerable: true, | ||
| get: definition[key] | ||
| }); | ||
| }; | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop); | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.r = (exports1)=>{ | ||
| if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { | ||
| value: 'Module' | ||
| }); | ||
| Object.defineProperty(exports1, '__esModule', { | ||
| value: true | ||
| }); | ||
| }; | ||
| })(); | ||
| var __webpack_exports__ = {}; | ||
| __webpack_require__.r(__webpack_exports__); | ||
| __webpack_require__.d(__webpack_exports__, { | ||
| DisplaySurveyType: ()=>DisplaySurveyType, | ||
| SurveyEventName: ()=>SurveyEventName, | ||
| SurveyEventProperties: ()=>SurveyEventProperties, | ||
| SurveyEventType: ()=>SurveyEventType, | ||
| SurveyPosition: ()=>SurveyPosition, | ||
| SurveyQuestionBranchingType: ()=>SurveyQuestionBranchingType, | ||
| SurveyQuestionType: ()=>SurveyQuestionType, | ||
| SurveySchedule: ()=>SurveySchedule, | ||
| SurveyTabPosition: ()=>SurveyTabPosition, | ||
| SurveyType: ()=>SurveyType, | ||
| SurveyWidgetType: ()=>SurveyWidgetType | ||
| }); | ||
| const SurveyEventType = { | ||
| Activation: 'events', | ||
| Cancellation: 'cancelEvents' | ||
| }; | ||
| const SurveyWidgetType = { | ||
| Button: 'button', | ||
| Tab: 'tab', | ||
| Selector: 'selector' | ||
| }; | ||
| const SurveyPosition = { | ||
| TopLeft: 'top_left', | ||
| TopRight: 'top_right', | ||
| TopCenter: 'top_center', | ||
| MiddleLeft: 'middle_left', | ||
| MiddleRight: 'middle_right', | ||
| MiddleCenter: 'middle_center', | ||
| Left: 'left', | ||
| Center: 'center', | ||
| Right: 'right', | ||
| NextToTrigger: 'next_to_trigger' | ||
| }; | ||
| const SurveyTabPosition = { | ||
| Top: 'top', | ||
| Left: 'left', | ||
| Right: 'right', | ||
| Bottom: 'bottom' | ||
| }; | ||
| const SurveyType = { | ||
| Popover: 'popover', | ||
| API: 'api', | ||
| Widget: 'widget', | ||
| ExternalSurvey: 'external_survey' | ||
| }; | ||
| const SurveyQuestionType = { | ||
| Open: 'open', | ||
| MultipleChoice: 'multiple_choice', | ||
| SingleChoice: 'single_choice', | ||
| Rating: 'rating', | ||
| Link: 'link' | ||
| }; | ||
| const SurveyQuestionBranchingType = { | ||
| NextQuestion: 'next_question', | ||
| End: 'end', | ||
| ResponseBased: 'response_based', | ||
| SpecificQuestion: 'specific_question' | ||
| }; | ||
| const SurveySchedule = { | ||
| Once: 'once', | ||
| Recurring: 'recurring', | ||
| Always: 'always' | ||
| }; | ||
| const SurveyEventName = { | ||
| SHOWN: 'survey shown', | ||
| DISMISSED: 'survey dismissed', | ||
| SENT: 'survey sent', | ||
| ABANDONED: 'survey abandoned' | ||
| }; | ||
| const SurveyEventProperties = { | ||
| SURVEY_ID: '$survey_id', | ||
| SURVEY_NAME: '$survey_name', | ||
| SURVEY_RESPONSE: '$survey_response', | ||
| SURVEY_ITERATION: '$survey_iteration', | ||
| SURVEY_ITERATION_START_DATE: '$survey_iteration_start_date', | ||
| SURVEY_PARTIALLY_COMPLETED: '$survey_partially_completed', | ||
| SURVEY_SUBMISSION_ID: '$survey_submission_id', | ||
| SURVEY_QUESTIONS: '$survey_questions', | ||
| SURVEY_COMPLETED: '$survey_completed', | ||
| PRODUCT_TOUR_ID: '$product_tour_id', | ||
| SURVEY_LAST_SEEN_DATE: '$survey_last_seen_date', | ||
| SURVEY_LANGUAGE: '$survey_language' | ||
| }; | ||
| const DisplaySurveyType = { | ||
| Popover: 'popover', | ||
| Inline: 'inline' | ||
| }; | ||
| exports.DisplaySurveyType = __webpack_exports__.DisplaySurveyType; | ||
| exports.SurveyEventName = __webpack_exports__.SurveyEventName; | ||
| exports.SurveyEventProperties = __webpack_exports__.SurveyEventProperties; | ||
| exports.SurveyEventType = __webpack_exports__.SurveyEventType; | ||
| exports.SurveyPosition = __webpack_exports__.SurveyPosition; | ||
| exports.SurveyQuestionBranchingType = __webpack_exports__.SurveyQuestionBranchingType; | ||
| exports.SurveyQuestionType = __webpack_exports__.SurveyQuestionType; | ||
| exports.SurveySchedule = __webpack_exports__.SurveySchedule; | ||
| exports.SurveyTabPosition = __webpack_exports__.SurveyTabPosition; | ||
| exports.SurveyType = __webpack_exports__.SurveyType; | ||
| exports.SurveyWidgetType = __webpack_exports__.SurveyWidgetType; | ||
| for(var __webpack_i__ in __webpack_exports__)if (-1 === [ | ||
| "DisplaySurveyType", | ||
| "SurveyEventName", | ||
| "SurveyEventProperties", | ||
| "SurveyEventType", | ||
| "SurveyPosition", | ||
| "SurveyQuestionBranchingType", | ||
| "SurveyQuestionType", | ||
| "SurveySchedule", | ||
| "SurveyTabPosition", | ||
| "SurveyType", | ||
| "SurveyWidgetType" | ||
| ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
| Object.defineProperty(exports, '__esModule', { | ||
| value: true | ||
| }); |
| const SurveyEventType = { | ||
| Activation: 'events', | ||
| Cancellation: 'cancelEvents' | ||
| }; | ||
| const SurveyWidgetType = { | ||
| Button: 'button', | ||
| Tab: 'tab', | ||
| Selector: 'selector' | ||
| }; | ||
| const SurveyPosition = { | ||
| TopLeft: 'top_left', | ||
| TopRight: 'top_right', | ||
| TopCenter: 'top_center', | ||
| MiddleLeft: 'middle_left', | ||
| MiddleRight: 'middle_right', | ||
| MiddleCenter: 'middle_center', | ||
| Left: 'left', | ||
| Center: 'center', | ||
| Right: 'right', | ||
| NextToTrigger: 'next_to_trigger' | ||
| }; | ||
| const SurveyTabPosition = { | ||
| Top: 'top', | ||
| Left: 'left', | ||
| Right: 'right', | ||
| Bottom: 'bottom' | ||
| }; | ||
| const SurveyType = { | ||
| Popover: 'popover', | ||
| API: 'api', | ||
| Widget: 'widget', | ||
| ExternalSurvey: 'external_survey' | ||
| }; | ||
| const SurveyQuestionType = { | ||
| Open: 'open', | ||
| MultipleChoice: 'multiple_choice', | ||
| SingleChoice: 'single_choice', | ||
| Rating: 'rating', | ||
| Link: 'link' | ||
| }; | ||
| const SurveyQuestionBranchingType = { | ||
| NextQuestion: 'next_question', | ||
| End: 'end', | ||
| ResponseBased: 'response_based', | ||
| SpecificQuestion: 'specific_question' | ||
| }; | ||
| const SurveySchedule = { | ||
| Once: 'once', | ||
| Recurring: 'recurring', | ||
| Always: 'always' | ||
| }; | ||
| const SurveyEventName = { | ||
| SHOWN: 'survey shown', | ||
| DISMISSED: 'survey dismissed', | ||
| SENT: 'survey sent', | ||
| ABANDONED: 'survey abandoned' | ||
| }; | ||
| const SurveyEventProperties = { | ||
| SURVEY_ID: '$survey_id', | ||
| SURVEY_NAME: '$survey_name', | ||
| SURVEY_RESPONSE: '$survey_response', | ||
| SURVEY_ITERATION: '$survey_iteration', | ||
| SURVEY_ITERATION_START_DATE: '$survey_iteration_start_date', | ||
| SURVEY_PARTIALLY_COMPLETED: '$survey_partially_completed', | ||
| SURVEY_SUBMISSION_ID: '$survey_submission_id', | ||
| SURVEY_QUESTIONS: '$survey_questions', | ||
| SURVEY_COMPLETED: '$survey_completed', | ||
| PRODUCT_TOUR_ID: '$product_tour_id', | ||
| SURVEY_LAST_SEEN_DATE: '$survey_last_seen_date', | ||
| SURVEY_LANGUAGE: '$survey_language' | ||
| }; | ||
| const DisplaySurveyType = { | ||
| Popover: 'popover', | ||
| Inline: 'inline' | ||
| }; | ||
| export { DisplaySurveyType, SurveyEventName, SurveyEventProperties, SurveyEventType, SurveyPosition, SurveyQuestionBranchingType, SurveyQuestionType, SurveySchedule, SurveyTabPosition, SurveyType, SurveyWidgetType }; |
+15
-91
| import type { Logger } from '@posthog/core'; | ||
| import type { Disposable } from './disposable'; | ||
| import type { KeyValueStore } from './persistence'; | ||
| import type { Listener } from './pubsub'; | ||
| import type { ExtensionToken } from './token'; | ||
| /** The current session, stamped on events to tie them to a session and a browser tab. */ | ||
| export interface SessionContext { | ||
| /** The stable session identifier attached to events captured during this session. */ | ||
| sessionId: string; | ||
| /** The logical browser tab/window identifier attached alongside the session id. */ | ||
| windowId: string; | ||
| /** When the session started, as a Unix timestamp in milliseconds. */ | ||
| sessionStartTimestamp: number; | ||
| } | ||
| /** Why a new session started (a `reset` also starts a new session). */ | ||
| export type NewSessionReason = 'initial' | 'reset' | 'idleTimeout' | 'maxLength' | 'crossTabAdoption'; | ||
| /** Details emitted when the client starts or adopts a new session. */ | ||
| export interface NewSessionInfo extends SessionContext { | ||
| /** The condition that caused this session to begin. */ | ||
| reason: NewSessionReason; | ||
| } | ||
| /** A captured event, as observed by `onEvent`. */ | ||
| export interface CapturedEventInfo { | ||
| /** The event name supplied to {@link Client.capture}. */ | ||
| event: string; | ||
| /** The final event properties after client defaults and dynamic properties are applied. */ | ||
| properties: Record<string, unknown>; | ||
| } | ||
| /** Per-call capture overrides, mirroring the client's public capture options. */ | ||
| export interface CaptureOptions { | ||
| /** Override the event timestamp sent to PostHog. */ | ||
| timestamp?: Date; | ||
| /** Override the event UUID used for de-duplication. */ | ||
| uuid?: string; | ||
| /** Person properties to set, emitted as `$set`. */ | ||
| set?: Record<string, unknown>; | ||
| /** Person properties to set if unset, emitted as `$set_once`. */ | ||
| setOnce?: Record<string, unknown>; | ||
| } | ||
| /** A minimal response from {@link Client.apiRequest}. */ | ||
| export interface ApiResponse { | ||
| /** Whether the request completed with a 2xx status, or was queued for best-effort unload transport. */ | ||
| ok: boolean; | ||
| /** The HTTP status code returned by the transport, or a client-defined best-effort status for unload sends. */ | ||
| status: number; | ||
| /** Parse the response body as JSON; may be unavailable for best-effort unload requests. */ | ||
| json(): Promise<unknown>; | ||
| /** Read the response body as text; may be unavailable for best-effort unload requests. */ | ||
| text(): Promise<string>; | ||
| statusCode: number; | ||
| /** The response body parsed as JSON when available. */ | ||
| json?: unknown; | ||
| /** The response body as text when available. */ | ||
| text?: string; | ||
| /** The transport error when the request failed before receiving an HTTP response. */ | ||
| error?: unknown; | ||
| } | ||
@@ -62,4 +26,4 @@ /** Options for sending a request through {@link Client.apiRequest}. */ | ||
| * most reliable fire-and-forget transport available — `sendBeacon`, fetch | ||
| * `keepalive`, or sync XHR. The response is best-effort: `.json()` may be | ||
| * unusable (e.g. `sendBeacon` only reports "queued"), so callers must not | ||
| * `keepalive`, or sync XHR. The response is best-effort: `json` may be | ||
| * unavailable (e.g. `sendBeacon` only reports "queued"), so callers must not | ||
| * depend on it. | ||
@@ -72,37 +36,12 @@ */ | ||
| /** | ||
| * Server-provided configuration, as returned by the remote config response | ||
| * (sampling rates, suppression rules, feature enablement, quotas, …). A loose | ||
| * record by design — each extension reads only the keys it owns. | ||
| */ | ||
| export type RemoteConfig = Record<string, unknown>; | ||
| /** | ||
| * The host SDK's capability surface as seen by an extension — the client an | ||
| * extension is handed in `setup`. Each SDK (v1, v2) provides it as a client | ||
| * adapter over its own internals. | ||
| * extension is handed in `setup`. A conforming host provides it as an adapter | ||
| * over its own internals. | ||
| * | ||
| * Synchronous members are always-ready in-memory reads (identity, session); | ||
| * asynchronous members do I/O or wait for something to become ready | ||
| * (`capture`, `apiRequest`, `kv`, `getRemoteConfig`). | ||
| * Host services that may do I/O are awaitable; a host can complete them | ||
| * synchronously when its underlying implementation supports that. Core | ||
| * analytics behavior is provided separately by the core extension. | ||
| */ | ||
| export interface Client { | ||
| /** The id events are currently attributed to — the anonymous id, or the identified user's id after `identify`. */ | ||
| readonly distinctId: string; | ||
| /** The anonymous device id; used before `identify` and carried on identify events as `$anon_distinct_id`. */ | ||
| readonly anonymousId: string; | ||
| /** Active group memberships (group type → group key), attached to events as `$groups`. */ | ||
| readonly groups: Record<string, string>; | ||
| /** The current session, created on first read if needed; reading does not extend or rotate it. */ | ||
| readonly session: SessionContext; | ||
| /** Records an analytics event through the client's normal pipeline. */ | ||
| capture(event: string, properties?: Record<string, unknown> | null, options?: CaptureOptions): Promise<void>; | ||
| /** | ||
| * Registers a producer of properties merged into every captured event. | ||
| * Returns a {@link Disposable} that removes it; an extension disposes it in | ||
| * its own `dispose`. May be called more than once. The producer runs inline | ||
| * during event build, so it must be cheap and synchronous; it may return | ||
| * different properties each time (e.g. the current URL), and is recomputed | ||
| * per event rather than stored. | ||
| */ | ||
| registerDynamicEventProperties(producer: () => Record<string, unknown>): Disposable; | ||
| /** | ||
| * Sends a request to a PostHog endpoint; the client owns auth, headers, and | ||
@@ -114,17 +53,2 @@ * transport (fetch / XHR / keepalive). `path` is relative to the configured | ||
| /** | ||
| * Resolves with the client's remote config once available, or `undefined` | ||
| * if the fetch failed (never rejects, never hangs). | ||
| * Re-readable: each call resolves with the current config, awaiting the | ||
| * first fetch if none has landed. `await` it in `setup` to block until | ||
| * config is known, or `.then()` it to reconfigure once it arrives; later | ||
| * changes arrive via `onRemoteConfig`. | ||
| */ | ||
| getRemoteConfig(): Promise<RemoteConfig | undefined>; | ||
| /** Fires when server-provided config arrives or changes. */ | ||
| readonly onRemoteConfig: Listener<RemoteConfig>; | ||
| /** Fires for every captured event — hot path, keep handlers cheap and synchronous. */ | ||
| readonly onEvent: Listener<CapturedEventInfo>; | ||
| /** Fires when a new session starts, including on reset (discriminate via `reason`). */ | ||
| readonly onNewSession: Listener<NewSessionInfo>; | ||
| /** | ||
| * Resolves another registered extension by a capability token it provides, or | ||
@@ -135,3 +59,3 @@ * `undefined` if nothing registered provides it (not installed, or not loaded | ||
| getExtension<T>(token: ExtensionToken<T>): T | undefined; | ||
| /** Async key-value storage scoped to this client instance and extension. */ | ||
| /** Awaitable key-value storage backed by the host client's persistence. */ | ||
| readonly kv: KeyValueStore; | ||
@@ -138,0 +62,0 @@ /** Logger that follows the host client's debug/noise policy. */ |
+1
-1
@@ -29,3 +29,3 @@ "use strict"; | ||
| }); | ||
| const packageVersion = "0.2.1"; | ||
| const packageVersion = "0.2.2"; | ||
| const Config = { | ||
@@ -32,0 +32,0 @@ DEBUG: false, |
+1
-1
@@ -1,2 +0,2 @@ | ||
| const packageVersion = "0.2.1"; | ||
| const packageVersion = "0.2.2"; | ||
| const Config = { | ||
@@ -3,0 +3,0 @@ DEBUG: false, |
@@ -14,1 +14,3 @@ /** | ||
| } | ||
| /** Invokes teardown at most once and returns its first result to every caller. */ | ||
| export declare function createDisposable(dispose: () => void | Promise<void>): Disposable; |
+31
-1
| "use strict"; | ||
| var __webpack_require__ = {}; | ||
| (()=>{ | ||
| __webpack_require__.d = (exports1, definition)=>{ | ||
| for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { | ||
| enumerable: true, | ||
| get: definition[key] | ||
| }); | ||
| }; | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop); | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.r = (exports1)=>{ | ||
@@ -15,5 +26,24 @@ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { | ||
| __webpack_require__.r(__webpack_exports__); | ||
| for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
| __webpack_require__.d(__webpack_exports__, { | ||
| createDisposable: ()=>createDisposable | ||
| }); | ||
| function createDisposable(dispose) { | ||
| let active = true; | ||
| let result; | ||
| return { | ||
| dispose: ()=>{ | ||
| if (active) { | ||
| active = false; | ||
| result = dispose(); | ||
| } | ||
| return result; | ||
| } | ||
| }; | ||
| } | ||
| exports.createDisposable = __webpack_exports__.createDisposable; | ||
| for(var __webpack_i__ in __webpack_exports__)if (-1 === [ | ||
| "createDisposable" | ||
| ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
| Object.defineProperty(exports, '__esModule', { | ||
| value: true | ||
| }); |
+5
-2
@@ -6,7 +6,10 @@ /** | ||
| export type { Extension } from './extension'; | ||
| export type { Disposable } from './disposable'; | ||
| export { CoreExtension } from './core-extension'; | ||
| export type { DeepReadonly, SessionContext, NewSessionReason, NewSessionInfo, CapturedEventInfo, CaptureOptions, } from './core-extension'; | ||
| export * from './types'; | ||
| export { createDisposable, type Disposable } from './disposable'; | ||
| export type { ExtensionToken } from './token'; | ||
| export type { Listener } from './pubsub'; | ||
| export { Publisher } from './pubsub'; | ||
| export type { Client, SessionContext, NewSessionReason, NewSessionInfo, CapturedEventInfo, CaptureOptions, ApiResponse, ApiRequestInit, RemoteConfig, } from './client'; | ||
| export type { Client, ApiResponse, ApiRequestInit } from './client'; | ||
| export type { KeyValueStore } from './persistence'; |
+60
-7
| "use strict"; | ||
| var __webpack_require__ = {}; | ||
| var __webpack_modules__ = { | ||
| "./core-extension": function(module) { | ||
| module.exports = require("./core-extension.js"); | ||
| }, | ||
| "./disposable": function(module) { | ||
| module.exports = require("./disposable.js"); | ||
| }, | ||
| "./pubsub": function(module) { | ||
| module.exports = require("./pubsub.js"); | ||
| }, | ||
| "./types": function(module) { | ||
| module.exports = require("./types/index.js"); | ||
| } | ||
| }; | ||
| var __webpack_module_cache__ = {}; | ||
| function __webpack_require__(moduleId) { | ||
| var cachedModule = __webpack_module_cache__[moduleId]; | ||
| if (void 0 !== cachedModule) return cachedModule.exports; | ||
| var module = __webpack_module_cache__[moduleId] = { | ||
| exports: {} | ||
| }; | ||
| __webpack_modules__[moduleId](module, module.exports, __webpack_require__); | ||
| return module.exports; | ||
| } | ||
| (()=>{ | ||
| __webpack_require__.n = (module)=>{ | ||
| var getter = module && module.__esModule ? ()=>module['default'] : ()=>module; | ||
| __webpack_require__.d(getter, { | ||
| a: getter | ||
| }); | ||
| return getter; | ||
| }; | ||
| })(); | ||
| (()=>{ | ||
| __webpack_require__.d = (exports1, definition)=>{ | ||
@@ -25,10 +57,31 @@ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { | ||
| var __webpack_exports__ = {}; | ||
| __webpack_require__.r(__webpack_exports__); | ||
| __webpack_require__.d(__webpack_exports__, { | ||
| Publisher: ()=>external_pubsub_js_namespaceObject.Publisher | ||
| }); | ||
| const external_pubsub_js_namespaceObject = require("./pubsub.js"); | ||
| (()=>{ | ||
| __webpack_require__.r(__webpack_exports__); | ||
| __webpack_require__.d(__webpack_exports__, { | ||
| CoreExtension: ()=>_core_extension__WEBPACK_IMPORTED_MODULE_0__.CoreExtension, | ||
| Publisher: ()=>_pubsub__WEBPACK_IMPORTED_MODULE_3__.Publisher, | ||
| createDisposable: ()=>_disposable__WEBPACK_IMPORTED_MODULE_2__.createDisposable | ||
| }); | ||
| var _core_extension__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./core-extension"); | ||
| var _types__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./types"); | ||
| var __WEBPACK_REEXPORT_OBJECT__ = {}; | ||
| for(var __WEBPACK_IMPORT_KEY__ in _types__WEBPACK_IMPORTED_MODULE_1__)if ([ | ||
| "createDisposable", | ||
| "default", | ||
| "CoreExtension", | ||
| "Publisher" | ||
| ].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) { | ||
| return _types__WEBPACK_IMPORTED_MODULE_1__[key]; | ||
| }).bind(0, __WEBPACK_IMPORT_KEY__); | ||
| __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); | ||
| var _disposable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("./disposable"); | ||
| var _pubsub__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./pubsub"); | ||
| })(); | ||
| exports.CoreExtension = __webpack_exports__.CoreExtension; | ||
| exports.Publisher = __webpack_exports__.Publisher; | ||
| exports.createDisposable = __webpack_exports__.createDisposable; | ||
| for(var __webpack_i__ in __webpack_exports__)if (-1 === [ | ||
| "Publisher" | ||
| "CoreExtension", | ||
| "Publisher", | ||
| "createDisposable" | ||
| ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; | ||
@@ -35,0 +88,0 @@ Object.defineProperty(exports, '__esModule', { |
+4
-1
@@ -0,2 +1,5 @@ | ||
| import { CoreExtension } from "./core-extension.mjs"; | ||
| import { createDisposable } from "./disposable.mjs"; | ||
| import { Publisher } from "./pubsub.mjs"; | ||
| export { Publisher }; | ||
| export * from "./types/index.mjs"; | ||
| export { CoreExtension, Publisher, createDisposable }; |
+15
-12
| /** | ||
| * Async key-value store for small extension state. Backed by whatever the | ||
| * client provides — a synchronous store resolves immediately, an asynchronous | ||
| * one (e.g. IndexedDB) does real I/O — so reads and writes are always awaited. | ||
| * Key-value store for small extension state. Implementations backed by | ||
| * synchronous persistence may return immediately, while asynchronous stores | ||
| * (for example IndexedDB) may return promises. Consumers can await either. | ||
| * | ||
| * The store is namespaced to the client instance, so keys are local to the | ||
| * extension and never collide with core SDK state. Values must be | ||
| * JSON-serializable; setting `null`/`undefined` removes the key. | ||
| * Keys map verbatim to the host client's shared persistence. In browser-v1, | ||
| * unknown keys are normally included as event properties, collisions overwrite | ||
| * host/core state, and reset clears them. Every SDK-owned key therefore needs an | ||
| * explicit event/hidden/derived exposure policy in each host, and sensitive data | ||
| * must not be stored unless its transmission is approved. Values must be | ||
| * JSON-serializable. | ||
| */ | ||
@@ -16,10 +19,10 @@ export interface KeyValueStore { | ||
| */ | ||
| get<T = unknown>(key: string): Promise<T | undefined>; | ||
| get<T = unknown>(key: string): T | undefined | Promise<T | undefined>; | ||
| /** | ||
| * Store a JSON-serializable value by key. Passing `null` or `undefined` | ||
| * removes the key instead of persisting that value. | ||
| * Forward a JSON-serializable value, including nullish values, to the host's | ||
| * native persistence. `undefined` is not portable or durable storage. | ||
| */ | ||
| set(key: string, value: unknown): Promise<void>; | ||
| /** Remove a value by key. Resolves successfully when the key is already absent. */ | ||
| remove(key: string): Promise<void>; | ||
| set(key: string, value: unknown): void | Promise<void>; | ||
| /** Remove a value by key. This is the portable deletion operation. */ | ||
| remove(key: string): void | Promise<void>; | ||
| } |
+1
-1
@@ -1,2 +0,2 @@ | ||
| import type { Disposable } from './disposable'; | ||
| import { type Disposable } from './disposable'; | ||
| /** | ||
@@ -3,0 +3,0 @@ * Call it with a handler to start listening; dispose the returned |
+6
-8
@@ -29,2 +29,3 @@ "use strict"; | ||
| }); | ||
| const external_disposable_js_namespaceObject = require("./disposable.js"); | ||
| class Publisher { | ||
@@ -51,10 +52,7 @@ publish(payload) { | ||
| this._subscriptions.push(subscription); | ||
| return { | ||
| dispose: ()=>{ | ||
| if (!subscription.isActive) return; | ||
| subscription.isActive = false; | ||
| const index = this._subscriptions.indexOf(subscription); | ||
| if (-1 !== index) this._subscriptions.splice(index, 1); | ||
| } | ||
| }; | ||
| return (0, external_disposable_js_namespaceObject.createDisposable)(()=>{ | ||
| subscription.isActive = false; | ||
| const index = this._subscriptions.indexOf(subscription); | ||
| if (-1 !== index) this._subscriptions.splice(index, 1); | ||
| }); | ||
| }; | ||
@@ -61,0 +59,0 @@ } |
+6
-8
@@ -0,1 +1,2 @@ | ||
| import { createDisposable } from "./disposable.mjs"; | ||
| class Publisher { | ||
@@ -22,10 +23,7 @@ publish(payload) { | ||
| this._subscriptions.push(subscription); | ||
| return { | ||
| dispose: ()=>{ | ||
| if (!subscription.isActive) return; | ||
| subscription.isActive = false; | ||
| const index = this._subscriptions.indexOf(subscription); | ||
| if (-1 !== index) this._subscriptions.splice(index, 1); | ||
| } | ||
| }; | ||
| return createDisposable(()=>{ | ||
| subscription.isActive = false; | ||
| const index = this._subscriptions.indexOf(subscription); | ||
| if (-1 !== index) this._subscriptions.splice(index, 1); | ||
| }); | ||
| }; | ||
@@ -32,0 +30,0 @@ } |
+11
-3
| { | ||
| "name": "@posthog/browser-common", | ||
| "version": "0.2.1", | ||
| "version": "0.2.2", | ||
| "description": "Internal shared browser utilities and extension primitives for PostHog Browser SDKs", | ||
@@ -32,2 +32,5 @@ "license": "MIT", | ||
| ], | ||
| "extension-runtime": [ | ||
| "dist/extension-runtime.d.ts" | ||
| ], | ||
| "utils/*": [ | ||
@@ -54,2 +57,7 @@ "dist/utils/*.d.ts" | ||
| }, | ||
| "./extension-runtime": { | ||
| "types": "./dist/extension-runtime.d.ts", | ||
| "require": "./dist/extension-runtime.js", | ||
| "import": "./dist/extension-runtime.mjs" | ||
| }, | ||
| "./utils/*": { | ||
@@ -62,4 +70,4 @@ "types": "./dist/utils/*.d.ts", | ||
| "dependencies": { | ||
| "@posthog/types": "^1.398.0", | ||
| "@posthog/core": "^1.45.1" | ||
| "@posthog/core": "^1.45.1", | ||
| "@posthog/types": "^1.398.0" | ||
| }, | ||
@@ -66,0 +74,0 @@ "devDependencies": { |
+47
-23
@@ -9,13 +9,14 @@ # @posthog/browser-common | ||
| The shared extension contract includes the interface an extension implements | ||
| (`Extension`), the host capabilities it is handed (`Client`), and small shared | ||
| runtime primitives such as `Publisher`. | ||
| (`Extension`), the host services it is handed (`Client`), the core analytics | ||
| capability (`CoreExtension`), and small shared runtime primitives such as | ||
| `Publisher`. | ||
| An extension written against this contract runs unchanged across major versions of the web SDK: | ||
| This contract is designed so an extension can run unchanged across major | ||
| versions of the web SDK. Concrete host adapters remain owned by their SDK | ||
| packages; browser-v1 and browser-v2 composition and loading integration are | ||
| separate from this shared runtime. | ||
| - **v1** is synchronous; extensions are registered statically. | ||
| - **v2** is asynchronous; extensions are loaded dynamically. | ||
| A conforming SDK provides a _client adapter_ that implements `Client` over its | ||
| own internals, so extension code never depends on a specific SDK. | ||
| Each SDK provides a _client adapter_ that implements `Client` over its own | ||
| internals, so extension code never depends on a specific SDK. | ||
| ## Concepts | ||
@@ -28,3 +29,3 @@ | ||
| ```ts | ||
| import type { Disposable, Extension } from '@posthog/browser-common' | ||
| import { CoreExtension, type Disposable, type Extension } from '@posthog/browser-common' | ||
@@ -37,3 +38,7 @@ export function webContext(): Extension { | ||
| setup(client) { | ||
| removeProperties = client.registerDynamicEventProperties(() => ({ | ||
| const core = client.getExtension(CoreExtension) | ||
| if (!core) { | ||
| throw new Error('CoreExtension is required') | ||
| } | ||
| removeProperties = core.registerDynamicEventProperties(() => ({ | ||
| $current_url: window.location.href, | ||
@@ -54,20 +59,41 @@ })) | ||
| Anything in `setup` that returns a `Disposable` must be held by the extension | ||
| and disposed in `dispose()`. | ||
| and disposed in `dispose()`. Use `createDisposable(teardown)` when adapting a | ||
| callback into idempotent teardown. | ||
| ### `Client` | ||
| What an extension is given in `setup` — the host's capability surface: | ||
| What an extension is given in `setup` — the host's extension services: | ||
| - **identity & session** (synchronous reads): `distinctId`, `anonymousId`, `groups`, `session` | ||
| - **events**: `capture(...)`, `registerDynamicEventProperties(...)` (contribute properties), `onEvent(...)` (observe) | ||
| - **transport**: `apiRequest(path, init?)` | ||
| - **server config**: `getRemoteConfig()` (current), `onRemoteConfig(...)` (changes) | ||
| - **lifecycle**: `onNewSession(...)` | ||
| - **registry**: `getExtension(token)` | ||
| - **storage & logging**: `kv`, `logger` | ||
| Synchronous members are always-ready in-memory reads; everything that does I/O | ||
| or waits for readiness (`capture`, `apiRequest`, `kv`, `getRemoteConfig`) is | ||
| asynchronous. | ||
| ### `CoreExtension` | ||
| A conforming host must register one `CoreExtension` before setting up product | ||
| extensions. Resolve it through `client.getExtension(CoreExtension)` for behavior | ||
| owned by the PostHog client's analytics core: | ||
| - **identity & session**: `distinctId`, `anonymousId`, `groups`, `session` | ||
| - **events**: `capture(...)`, `registerDynamicEventProperties(...)`, `onEvent(...)` | ||
| - **lifecycle**: `onNewSession(...)` | ||
| - **server config**: `getRemoteConfig()` (current), `onRemoteConfig(...)` (changes) | ||
| Identity and session are always-ready synchronous reads. Operations that perform | ||
| I/O, including `capture`, `apiRequest`, `kv`, and `getRemoteConfig`, are | ||
| awaitable. | ||
| ### Host runtime | ||
| PostHog browser SDK implementations share extension registration and teardown | ||
| through `ExtensionRuntime`, imported from the dedicated | ||
| `@posthog/browser-common/extension-runtime` subpath. It reserves names and | ||
| capability tokens during setup, publishes providers only after successful | ||
| readiness, and disposes extensions once in reverse registration order. Concrete | ||
| SDKs still own the `Client` adapter, Core implementation, and SDK lifecycle | ||
| hooks. | ||
| `ExtensionRuntime` is host infrastructure, not part of the extension-author | ||
| surface exported from the package root. | ||
| ### `Publisher` | ||
@@ -134,5 +160,3 @@ | ||
| Early and internal. The package currently defines the extension contract, the | ||
| shared `Publisher` helper, and directly imported browser utilities under | ||
| `utils/*` subpaths. Additional shared runtime helpers — key-value stores, the | ||
| registry implementation, and a test `Client` — will land alongside the first | ||
| ported extension. | ||
| core analytics capability, a shared host runtime, shared lifecycle helpers, and | ||
| directly imported browser utilities under `utils/*` subpaths. |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
253224
23.73%118
21.65%5831
26.6%158
17.91%