diff --git a/packages/analytics-plugin-posthog/.babelrc b/packages/analytics-plugin-posthog/.babelrc new file mode 100644 index 00000000..b5e3023e --- /dev/null +++ b/packages/analytics-plugin-posthog/.babelrc @@ -0,0 +1,9 @@ +{ + "presets": [ + [ + "@babel/env", { + "modules": false + } + ] + ] +} diff --git a/packages/analytics-plugin-posthog/.gitignore b/packages/analytics-plugin-posthog/.gitignore new file mode 100644 index 00000000..6efda609 --- /dev/null +++ b/packages/analytics-plugin-posthog/.gitignore @@ -0,0 +1,4 @@ +lib +dist +.size-snapshot.json +node_modules diff --git a/packages/analytics-plugin-posthog/CHANGELOG.md b/packages/analytics-plugin-posthog/CHANGELOG.md new file mode 100644 index 00000000..568c6005 --- /dev/null +++ b/packages/analytics-plugin-posthog/CHANGELOG.md @@ -0,0 +1,11 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +## 0.1.0 + +- Initial release +- Browser support via PostHog JS snippet +- Node.js support via posthog-node +- Page, track, identify, reset lifecycle methods +- Custom methods: group, alias, feature flags, privacy controls diff --git a/packages/analytics-plugin-posthog/README.md b/packages/analytics-plugin-posthog/README.md new file mode 100644 index 00000000..a576bbe3 --- /dev/null +++ b/packages/analytics-plugin-posthog/README.md @@ -0,0 +1,184 @@ + + +# PostHog plugin for `analytics` + +Integration with [PostHog](https://posthog.com/) for [analytics](https://www.npmjs.com/package/analytics) + +PostHog is an open-source product analytics platform. This plugin works in the browser and in Node.js. + +[View the docs](https://getanalytics.io/plugins/posthog/) + + +
+Click to expand + +- [Installation](#installation) +- [How to use](#how-to-use) +- [Browser usage](#browser-usage) +- [Node.js usage](#nodejs-usage) +- [Plugin options](#plugin-options) +- [Custom methods](#custom-methods) + +
+ + +## Installation + +```bash +npm install analytics @analytics/posthog +``` + +## How to use + +The `@analytics/posthog` package works in the [browser](#browser-usage) and in [Node.js](#nodejs-usage). + +## Browser usage + +```js +import Analytics from 'analytics' +import posthogPlugin from '@analytics/posthog' + +const analytics = Analytics({ + app: 'awesome-app', + plugins: [ + posthogPlugin({ + apiKey: 'phc_your_api_key', + apiHost: 'https://us.i.posthog.com', + }) + ] +}) + +// Track a page view +analytics.page() + +// Track a custom event +analytics.track('buttonClicked', { + label: 'Sign Up' +}) + +// Identify a user +analytics.identify('user-123', { + name: 'Jane Doe', + email: 'jane@example.com' +}) +``` + +### Browser plugin options + +| Option | Type | Default | Description | +|:--|:--|:--|:--| +| `apiKey` | `string` | **required** | Your PostHog project API key | +| `apiHost` | `string` | `https://us.i.posthog.com` | PostHog API host | +| `autocapture` | `boolean` | `true` | Enable autocapture of clicks, inputs, and page views | +| `capturePageview` | `boolean\|string` | `false` | Auto-capture `$pageview` events (see note below) | +| `capturePageleave` | `boolean` | `false` | Auto-capture `$pageleave` events | +| `customScriptSrc` | `string` | `null` | Override the PostHog JS library URL | +| `options` | `object` | `{}` | Additional [posthog.init options](https://posthog.com/docs/libraries/js#config) | + +### Pageview tracking + +PostHog normally defaults `capture_pageview` to `true`, which auto-captures a `$pageview` on initialization (and on history changes in SPAs). However, the `analytics` library has its own page tracking via `analytics.page()`, which this plugin maps to `posthog.capture('$pageview')`. + +**To avoid duplicate pageview events**, this plugin defaults both `capturePageview` and `capturePageleave` to `false` and lets `analytics.page()` be the single source of truth for page lifecycle tracking. + +| Scenario | `capturePageview` | How to track pages | +|:--|:--|:--| +| **Default (recommended)** | `false` | Call `analytics.page()` on route changes | +| **PostHog auto-tracking** | `true` or `'history_change'` | Do NOT call `analytics.page()` — PostHog handles it | + +If you use a framework router (React Router, Next.js, etc.), the recommended approach is to keep the default (`false`) and call `analytics.page()` in your route change handler. + +### Autocapture + +PostHog's [autocapture](https://posthog.com/docs/product-analytics/autocapture) is enabled by default (`autocapture: true`). It automatically tracks clicks, form inputs, and other DOM interactions. This is independent of pageview tracking and works alongside the analytics library without conflicts. Set `autocapture: false` to disable it. + +## Node.js usage + +```js +import Analytics from 'analytics' +import posthogPlugin from '@analytics/posthog' + +const analytics = Analytics({ + app: 'awesome-app', + plugins: [ + posthogPlugin({ + apiKey: 'phc_your_api_key', + apiHost: 'https://us.i.posthog.com', + }) + ] +}) + +// Track an event (userId or anonymousId is required) +analytics.track('userSignedUp', { + plan: 'pro' +}) + +// Identify a user +analytics.identify('user-123', { + name: 'Jane Doe', + email: 'jane@example.com' +}) +``` + +### Node.js plugin options + +| Option | Type | Default | Description | +|:--|:--|:--|:--| +| `apiKey` | `string` | **required** | Your PostHog project API key | +| `apiHost` | `string` | `https://us.i.posthog.com` | PostHog API host | +| `flushInterval` | `number` | `10000` | Flush interval in ms | +| `flushAt` | `number` | `20` | Flush at this number of events | +| `disableAnonymousTraffic` | `boolean` | `false` | Disable anonymous traffic | + +## Custom methods + +PostHog-specific methods are available via the analytics instance: + +### Browser + +```js +// Group analytics (e.g., associate user with a company) +analytics.plugins.posthog.group('company', 'company-id-123', { + name: 'Acme Corp' +}) + +// Feature flags +analytics.plugins.posthog.isFeatureEnabled('new-dashboard') +analytics.plugins.posthog.getFeatureFlag('new-dashboard') +analytics.plugins.posthog.getFeatureFlagPayload('new-dashboard') +analytics.plugins.posthog.reloadFeatureFlags() + +// Register super properties (sent with every event) +analytics.plugins.posthog.register({ environment: 'production' }) + +// Privacy controls +analytics.plugins.posthog.optOut() +analytics.plugins.posthog.optIn() + +// Access the underlying posthog-js instance +const posthog = analytics.plugins.posthog.getClient() +``` + +### Node.js + +```js +// Group analytics +analytics.plugins.posthog.group('company', 'company-id-123', { + name: 'Acme Corp' +}) + +// Alias +analytics.plugins.posthog.alias('user-123', 'old-anonymous-id') + +// Flush pending events +await analytics.plugins.posthog.flush() + +// Shutdown client +await analytics.plugins.posthog.shutdown() + +// Access the underlying posthog-node instance +const client = analytics.plugins.posthog.getClient() +``` diff --git a/packages/analytics-plugin-posthog/package.json b/packages/analytics-plugin-posthog/package.json new file mode 100644 index 00000000..16ad13d5 --- /dev/null +++ b/packages/analytics-plugin-posthog/package.json @@ -0,0 +1,59 @@ +{ + "name": "@analytics/posthog", + "version": "0.1.0", + "description": "PostHog plugin for 'analytics' module for browser & node", + "projectMeta": { + "provider": { + "name": "PostHog", + "url": "https://posthog.com/" + }, + "platforms": { + "browser": "./src/browser.js", + "node": "./src/node.js" + } + }, + "keywords": [ + "analytics", + "analytics-project", + "analytics-plugin", + "posthog", + "product-analytics", + "open-source-analytics" + ], + "author": "Fernando Gomes", + "license": "MIT", + "scripts": { + "docs": "node ../analytics-cli/bin/run docs", + "build": "node ../../scripts/build/index.js", + "watch": "node ../../scripts/build/_watch.js", + "release:patch": "npm version patch && npm publish", + "release:minor": "npm version minor && npm publish", + "release:major": "npm version major && npm publish", + "es": "../../node_modules/.bin/babel-node ./testBabel.js" + }, + "main": "lib/analytics-plugin-posthog.cjs.js", + "globalName": "analyticsPosthog", + "jsnext:main": "lib/analytics-plugin-posthog.es.js", + "module": "lib/analytics-plugin-posthog.es.js", + "browser": { + "./lib/analytics-plugin-posthog.cjs.js": "./lib/analytics-plugin-posthog.browser.cjs.js", + "./lib/analytics-plugin-posthog.es.js": "./lib/analytics-plugin-posthog.browser.es.js" + }, + "files": [ + "dist", + "lib", + "README.md" + ], + "homepage": "https://github.com/DavidWells/analytics#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/DavidWells/analytics.git" + }, + "devDependencies": { + "@babel/core": "^7.2.2", + "@babel/preset-env": "^7.3.1" + }, + "dependencies": { + "posthog-node": "^4.0.0" + } +} diff --git a/packages/analytics-plugin-posthog/src/browser.js b/packages/analytics-plugin-posthog/src/browser.js new file mode 100644 index 00000000..793516b9 --- /dev/null +++ b/packages/analytics-plugin-posthog/src/browser.js @@ -0,0 +1,235 @@ +/* global posthog */ + +const defaultConfig = { + /* Your PostHog API key */ + apiKey: null, + /* PostHog API host. Default: https://us.i.posthog.com */ + apiHost: 'https://us.i.posthog.com', + /* Autocapture clicks, inputs, and page views */ + autocapture: true, + /* + * Automatically capture $pageview events. + * Default: false — the analytics library manages page tracking via analytics.page(), + * which calls posthog.capture('$pageview'). Setting this to true will cause duplicate + * pageview events. Set to 'history_change' only if you are NOT calling analytics.page(). + */ + capturePageview: false, + /* + * Automatically capture $pageleave events when the user navigates away. + * Default: false — disabled alongside capturePageview so the analytics library + * has full control over page lifecycle tracking. + */ + capturePageleave: false, + /* Override the PostHog JS library URL */ + customScriptSrc: null, + /* Additional posthog.init options https://posthog.com/docs/libraries/js#config */ + options: {}, +} + +/** + * PostHog analytics plugin (browser) + * @link https://getanalytics.io/plugins/posthog/ + * @link https://posthog.com/docs/libraries/js + * @param {object} pluginConfig - Plugin settings + * @param {string} pluginConfig.apiKey - Your PostHog project API key + * @param {string} [pluginConfig.apiHost] - PostHog API host (default: https://us.i.posthog.com) + * @param {boolean} [pluginConfig.autocapture] - Enable autocapture (default: true) + * @param {boolean|string} [pluginConfig.capturePageview] - Auto-capture $pageview (default: false). Disabled to avoid duplicates with analytics.page(). Set to 'history_change' only if NOT calling analytics.page(). + * @param {boolean} [pluginConfig.capturePageleave] - Auto-capture $pageleave events (default: false) + * @param {string} [pluginConfig.customScriptSrc] - Override the PostHog JS library URL + * @param {object} [pluginConfig.options] - Additional posthog.init options + * @return {object} Analytics plugin + * @example + * + * posthogPlugin({ + * apiKey: 'phc_xxx', + * apiHost: 'https://us.i.posthog.com' + * }) + */ +function posthogPlugin(pluginConfig = {}) { + const config = { + ...defaultConfig, + ...pluginConfig, + } + + return { + name: 'posthog', + config: config, + /* Load PostHog JS snippet on page */ + initialize: ({ config }) => { + const { apiKey, apiHost, autocapture, capturePageview, capturePageleave, customScriptSrc, options } = config + if (!apiKey) { + throw new Error('No PostHog apiKey defined') + } + + // NoOp if posthog already loaded by external source + if (typeof window.posthog !== 'undefined' && window.posthog.__loaded) { + return + } + + /* eslint-disable */ + // PostHog JS snippet — sourced from https://posthog.com/docs/integrate/snippet + !function(t, e) { + var o, n, p, r + if (!e.__SV) { + window.posthog = e + e._i = [] + e.init = function(i, s, a) { + function g(t, e) { + var o = e.split('.') + 2 == o.length && (t = t[o[0]], e = o[1]) + t[e] = function() { + t.push([e].concat(Array.prototype.slice.call(arguments, 0))) + } + } + (p = t.createElement('script')).type = 'text/javascript' + p.crossOrigin = 'anonymous' + p.async = !0 + p.src = customScriptSrc || s.api_host.replace('.i.posthog.com', '-assets.i.posthog.com') + '/static/array.js' + ;(r = t.getElementsByTagName('script')[0]).parentNode.insertBefore(p, r) + var u = e + for (void 0 !== a ? u = e[a] = [] : a = 'posthog', u.people = u.people || [], u.toString = function(t) { + var e = 'posthog' + 'posthog' !== a && (e += '.' + a) + t || (e += ' (stub)') + return e + }, u.people.toString = function() { + return u.toString(1) + '.people (stub)' + }, o = 'init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug'.split(' '), n = 0; n < o.length; n++) g(u, o[n]) + e._i.push([i, s, a]) + } + e.__SV = 1 + } + }(document, window.posthog || []) + /* eslint-enable */ + + posthog.init(apiKey, { + api_host: apiHost, + autocapture: autocapture, + capture_pageview: capturePageview, + capture_pageleave: capturePageleave, + ...options, + }) + }, + /* Track page views https://posthog.com/docs/product-analytics/capture-events#single-page-apps-spa */ + page: ({ payload }) => { + if (typeof posthog === 'undefined') return + posthog.capture('$pageview', payload.properties || {}) + }, + /* Track custom events https://posthog.com/docs/product-analytics/capture-events */ + track: ({ payload }) => { + if (typeof posthog === 'undefined') return + posthog.capture(payload.event, payload.properties) + }, + /* Identify a user https://posthog.com/docs/product-analytics/identify */ + identify: ({ payload }) => { + if (typeof posthog === 'undefined') return + const { userId, traits } = payload + if (typeof userId === 'string') { + posthog.identify(userId, traits) + } else if (traits) { + posthog.setPersonProperties(traits) + } + }, + /* Check if PostHog has loaded */ + loaded: () => { + return !!(window.posthog && window.posthog.__loaded) + }, + /* Reset user data on logout https://posthog.com/docs/product-analytics/identify#reset-after-logout */ + reset: () => { + if (typeof posthog === 'undefined') return + posthog.reset() + }, + /* Custom methods for PostHog-specific functionality */ + methods: { + /** + * Associate user with a group (e.g., company, team) + * @link https://posthog.com/docs/product-analytics/group-analytics + * @param {string} groupType - Type of group (e.g., 'company') + * @param {string} groupKey - Unique group identifier + * @param {object} [groupProperties] - Optional properties to set on the group + */ + group(groupType, groupKey, groupProperties) { + if (typeof posthog === 'undefined') return + posthog.group(groupType, groupKey, groupProperties) + }, + /** + * Create an alias for the current user + * @link https://posthog.com/docs/product-analytics/identify#alias-assigning-multiple-distinct-ids-to-the-same-user + * @param {string} alias - The alias to assign + */ + alias(alias) { + if (typeof posthog === 'undefined') return + posthog.alias(alias) + }, + /** + * Check if a feature flag is enabled + * @link https://posthog.com/docs/feature-flags + * @param {string} flagKey - The feature flag key + * @return {boolean|undefined} Whether the flag is enabled + */ + isFeatureEnabled(flagKey) { + if (typeof posthog === 'undefined') return + return posthog.isFeatureEnabled(flagKey) + }, + /** + * Get a feature flag value (supports multivariate flags) + * @link https://posthog.com/docs/feature-flags + * @param {string} flagKey - The feature flag key + * @return {string|boolean|undefined} The flag value + */ + getFeatureFlag(flagKey) { + if (typeof posthog === 'undefined') return + return posthog.getFeatureFlag(flagKey) + }, + /** + * Get a feature flag's JSON payload + * @link https://posthog.com/docs/feature-flags + * @param {string} flagKey - The feature flag key + * @return {object|undefined} The flag payload + */ + getFeatureFlagPayload(flagKey) { + if (typeof posthog === 'undefined') return + return posthog.getFeatureFlagPayload(flagKey) + }, + /** + * Reload feature flags from PostHog + */ + reloadFeatureFlags() { + if (typeof posthog === 'undefined') return + posthog.reloadFeatureFlags() + }, + /** + * Register super properties that are sent with every event + * @param {object} properties - Properties to register + */ + register(properties) { + if (typeof posthog === 'undefined') return + posthog.register(properties) + }, + /** + * Opt user out of capturing + */ + optOut() { + if (typeof posthog === 'undefined') return + posthog.opt_out_capturing() + }, + /** + * Opt user in to capturing + */ + optIn() { + if (typeof posthog === 'undefined') return + posthog.opt_in_capturing() + }, + /** + * Get the underlying PostHog client instance + * @return {object} The posthog-js instance + */ + getClient() { + return typeof posthog !== 'undefined' ? posthog : null + }, + }, + } +} + +export default posthogPlugin diff --git a/packages/analytics-plugin-posthog/src/index.js b/packages/analytics-plugin-posthog/src/index.js new file mode 100644 index 00000000..5268c910 --- /dev/null +++ b/packages/analytics-plugin-posthog/src/index.js @@ -0,0 +1,5 @@ +import nodeCode from './node' +import browser from './browser' + +/* This module will shake out unused code + work in browser and node */ +export default process.browser ? browser : nodeCode diff --git a/packages/analytics-plugin-posthog/src/node.js b/packages/analytics-plugin-posthog/src/node.js new file mode 100644 index 00000000..56eaa27e --- /dev/null +++ b/packages/analytics-plugin-posthog/src/node.js @@ -0,0 +1,157 @@ + +let PostHog +if (!process.browser) { + PostHog = require('posthog-node').PostHog +} + +const defaultConfig = { + /* Your PostHog API key */ + apiKey: null, + /* PostHog API host. Default: https://us.i.posthog.com */ + apiHost: 'https://us.i.posthog.com', + /* Flush interval in milliseconds */ + flushInterval: 10000, + /* Flush at this number of events */ + flushAt: 20, + /* Disable anonymous traffic to save on events */ + disableAnonymousTraffic: false, +} + +/** + * PostHog serverside analytics plugin + * @link https://getanalytics.io/plugins/posthog/ + * @link https://posthog.com/docs/libraries/node + * @param {object} pluginConfig - Plugin settings + * @param {string} pluginConfig.apiKey - Your PostHog project API key + * @param {string} [pluginConfig.apiHost] - PostHog API host (default: https://us.i.posthog.com) + * @param {number} [pluginConfig.flushInterval] - Flush interval in ms (default: 10000) + * @param {number} [pluginConfig.flushAt] - Flush at this number of events (default: 20) + * @param {boolean} [pluginConfig.disableAnonymousTraffic] - Disable anonymous traffic (default: false) + * @return {object} Analytics plugin + * @example + * + * posthogPlugin({ + * apiKey: 'phc_xxx', + * apiHost: 'https://us.i.posthog.com' + * }) + */ +function posthogPlugin(userConfig = {}) { + const config = { + ...defaultConfig, + ...userConfig, + } + + const { apiKey, apiHost, flushInterval, flushAt, disableAnonymousTraffic } = config + + if (!apiKey) { + throw new Error('No PostHog apiKey defined') + } + + const client = new PostHog(apiKey, { + host: apiHost, + flushInterval: flushInterval, + flushAt: flushAt, + }) + + return { + name: 'posthog', + config: config, + /* Track page views */ + page: ({ payload }) => { + const { userId, anonymousId } = payload + const distinctId = userId || anonymousId + if (!distinctId) { + throw new Error('Missing userId and anonymousId. You must include one to make a PostHog call') + } + + client.capture({ + distinctId: distinctId, + event: '$pageview', + properties: payload.properties, + }) + }, + /* Track custom events */ + track: ({ payload }) => { + const { userId, anonymousId } = payload + const distinctId = userId || anonymousId + if (!distinctId) { + throw new Error('Missing userId and anonymousId. You must include one to make a PostHog call') + } + + if (disableAnonymousTraffic && !userId) { + return false + } + + client.capture({ + distinctId: distinctId, + event: payload.event, + properties: payload.properties, + }) + }, + /* Identify a user and set person properties */ + identify: ({ payload }) => { + const { userId, traits } = payload + if (!userId) return + + client.identify({ + distinctId: userId, + properties: traits, + }) + }, + /* Custom methods */ + methods: { + /** + * Associate user with a group + * @param {string} groupType - Type of group (e.g., 'company') + * @param {string} groupKey - Unique group identifier + * @param {object} [groupProperties] - Optional properties to set on the group + */ + group(groupType, groupKey, groupProperties) { + const analyticsInstance = this.instance + const user = analyticsInstance.user() + const distinctId = user.userId || user.anonymousId + if (!distinctId) return + + client.groupIdentify({ + groupType: groupType, + groupKey: groupKey, + properties: groupProperties, + }) + }, + /** + * Create an alias for a user + * @param {string} distinctId - The distinct ID to alias + * @param {string} alias - The alias to assign + */ + alias(distinctId, alias) { + client.alias({ + distinctId: distinctId, + alias: alias, + }) + }, + /** + * Flush all pending events + * @return {Promise} Resolves when flush completes + */ + flush() { + return client.flush() + }, + /** + * Shutdown the client (flush + close) + * @return {Promise} Resolves when shutdown completes + */ + shutdown() { + return client.shutdown() + }, + /** + * Get the underlying PostHog Node client instance + * @return {object} The posthog-node client + */ + getClient() { + return client + }, + }, + } +} + +export default posthogPlugin diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7737dd35..86b92b7b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -558,6 +558,19 @@ importers: specifier: ^7.4.5 version: 7.16.11(@babel/core@7.17.0) + packages/analytics-plugin-posthog: + dependencies: + posthog-node: + specifier: ^4.0.0 + version: 4.18.0 + devDependencies: + '@babel/core': + specifier: ^7.2.2 + version: 7.17.0 + '@babel/preset-env': + specifier: ^7.3.1 + version: 7.16.11(@babel/core@7.17.0) + packages/analytics-plugin-segment: dependencies: analytics-node: @@ -3635,6 +3648,9 @@ packages: axios@0.27.2: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} + axios@1.13.6: + resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + babel-eslint@10.1.0: resolution: {integrity: sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==} engines: {node: '>=6'} @@ -5277,6 +5293,15 @@ packages: flush-write-stream@1.1.1: resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + follow-redirects@1.15.9: resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} @@ -5320,6 +5345,10 @@ packages: resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} engines: {node: '>= 6'} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} @@ -7989,6 +8018,10 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + posthog-node@4.18.0: + resolution: {integrity: sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw==} + engines: {node: '>=15.0.0'} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -8077,6 +8110,9 @@ packages: protoduck@5.0.1: resolution: {integrity: sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==} + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + prr@0.0.0: resolution: {integrity: sha512-LmUECmrW7RVj6mDWKjTXfKug7TFGdiz9P18HMcO4RHL+RW7MCOGNvpj5j47Rnp6ne6r4fZ2VzyUWEpKbg+tsjQ==} @@ -14285,6 +14321,14 @@ snapshots: transitivePeerDependencies: - debug + axios@1.13.6: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + babel-eslint@10.1.0(eslint@9.29.0): dependencies: '@babel/code-frame': 7.27.1 @@ -16356,6 +16400,8 @@ snapshots: inherits: 2.0.4 readable-stream: 2.3.8 + follow-redirects@1.15.11: {} + follow-redirects@1.15.9: {} for-each@0.3.5: @@ -16402,6 +16448,14 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + format@0.2.2: {} fp-and-or@0.1.4: {} @@ -19615,6 +19669,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + posthog-node@4.18.0: + dependencies: + axios: 1.13.6 + transitivePeerDependencies: + - debug + prelude-ls@1.2.1: {} prepend-http@2.0.0: {} @@ -19689,6 +19749,8 @@ snapshots: dependencies: genfun: 5.0.0 + proxy-from-env@1.1.0: {} + prr@0.0.0: {} prr@1.0.1: {}