Giter Club home page Giter Club logo

posthog-js's Introduction

PostHog Browser JS Library

npm package MIT License

For information on using this library in your app, see PostHog Docs.
This README is intended for developing the library itself.

Testing

Unit tests: run pnpm test. Cypress: run pnpm start to have a test server running and separately pnpm cypress to launch Cypress test engine.

Running TestCafe E2E tests with BrowserStack

Testing on IE11 requires a bit more setup. TestCafe tests will use the playground application to test the locally built array.full.js bundle. It will also verify that the events emitted during the testing of playground are loaded into the PostHog app. By default it uses https://us.i.posthog.com and the project with ID 11213. See the testcafe tests to see how to override these if needed. For PostHog internal users ask @benjackwhite or @hazzadous to invite you to the Project. You'll need to set POSTHOG_API_KEY to your personal API key, and POSTHOG_PROJECT_KEY to the key for the project you are using.

You'll also need to sign up to BrowserStack. Note that if you are using CodeSpaces, these variables will already be available in your shell env variables.

After all this, you'll be able to run through the below steps:

  1. Optional: rebuild array.js on changes: nodemon -w src/ --exec bash -c "pnpm build-rollup".
  2. Export browserstack credentials: export BROWSERSTACK_USERNAME=xxx BROWSERSTACK_ACCESS_KEY=xxx.
  3. Run tests: npx testcafe "browserstack:ie" testcafe/e2e.spec.js.

Running local create react app example

You can use the create react app setup in playground/nextjs to test posthog-js as an npm module in a Nextjs application.

  1. Run posthog locally on port 8000 (DEBUG=1 TEST=1 ./bin/start).
  2. Run python manage.py setup_dev --no-data on posthog repo, which sets up a demo account.
  3. Copy posthog token found in http://localhost:8000/project/settings and then
  4. cd playground/nextjsand run NEXT_PUBLIC_POSTHOG_KEY='<your-local-api-key>' pnpm dev

Tiers of testing

  1. Unit tests - this verifies the behavior of the library in bite-sized chunks. Keep this coverage close to 100%, test corner cases and internal behavior here
  2. Cypress tests - integrates with a real chrome browser and is capable of testing timing, browser requests, etc. Useful for testing high-level library behavior, ordering and verifying requests. We shouldn't aim for 100% coverage here as it's impossible to test all possible combinations.
  3. TestCafe E2E tests - integrates with a real posthog instance sends data to it. Hardest to write and maintain - keep these very high level

Developing together with another project

Install pnpm to link a local version of posthog-js in another JS project: npm install -g pnpm

Run this to link the local version

We have 2 options for linking this project to your local version: via pnpm link or via local paths

local paths (preferred)

  • from whichever repo needs to require posthog-js, go to the package.json of that file, and replace the posthog-js dependency version number with file:<relative_or_absolute_path_to_local_module>
  • e.g. from the package.json within posthog, replace "posthog-js": "1.131.4" with "posthog-js": "file:../posthog-js"
  • run pnpm install from the root of the project in which you just created a local path

Then, once this link has been created, any time you need to make a change to posthog-js, you can run pnpm build from the posthog-js root and the changes will appear in the other repo.

pnpm link

  • In the posthog-js directory: pnpm link --global
  • (for posthog this means: pnpm link --global posthog-js && pnpm i && pnpm copy-scripts)
  • You can then remove the link by, e.g., running pnpm link --global posthog-js from within posthog

Releasing a new version

Just put a bump patch/minor/major label on your PR! Once the PR is merged, a new version with the appropriate version bump will be released, and the dependency will be updated in posthog/PostHog โ€“ automatically.

If you forget to add the label, don't try to update the version locally as you won't be able to push that commit to the main branch. Instead, just make a new PR.

Prereleases

To release an alpha or beta version, you'll need to use the CLI locally:

  1. Make sure you're a collaborator on posthog-js in npm (check here).

  2. Make sure you're logged into the npm CLI (npm login).

  3. Check out your work-in-progress branch (do not release an alpha/beta from main).

  4. Run the following commands, using the same bump level (major/minor/patch) as your PR:

    npm version [premajor | preminor | prepatch] --preid=beta
    npm publish --tag beta
    git push --tags
  5. Enjoy the new prerelease version. You can now use it locally, in a dummy app, or in the main repo.

posthog-js's People

Contributors

alexkim205 avatar benjackwhite avatar daibhin avatar dependabot[bot] avatar dmarticus avatar edscode avatar frankh avatar hazzadous avatar jamesefhawkins avatar jurajmajerik avatar lharries avatar liyiy avatar macobo avatar marandaneto avatar mariusandra avatar neilkakkar avatar paolodamico avatar pauldambra avatar phanatic avatar raquelmsmith avatar rcmarron avatar robbie-c avatar samwinslow avatar tapico-weyert avatar thmsobrmlr avatar timgl avatar tmunayyer avatar twixes avatar weyert avatar yakkomajuri avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

posthog-js's Issues

"TypeError: this.people.delete_user is undefined" when calling posthog.opt_out_capturing()

I'm loading PostHog via the npm module.

Then, in my React app's logic, when I run the following (awhile after init has already completed):

posthog.identify('testUser')
posthog.opt_out_capturing();

I get the error:
TypeError: this.people.delete_user is not a function. (In 'this.people.delete_user()', 'this.people.delete_user' is undefined)

I also tried switching the order of those two lines, which results in no errors thrown. However, in both cases autocapture events are still sent through.

I might be following an incorrect usage pattern. Am I missing something?

Not all pageviews/clicks captured

A user has reported they're not seeing all clicks appearing as expected.
image

While trying to reproduce I had the opposite problem where all the clicks were sent but not all the pageviews
image

2 things happen: when we click, I think we wait 300 ms to try to send that click to the server before sending the user on. Depending on where the user is, 300ms might not be enough, or our server might be too slow.

The second thing is we batch requests, which might be playing havoc.

Setting `xhr_headers` will cause CORS failures if posthog on a different domain

Tested this by running posthog.com on port 8001 and setting a random header. This triggered an OPTIONS query which failed:

Access to XMLHttpRequest at 'http://localhost:8000/e/?ip=1&_=1607005825101' from origin 'http://localhost:8001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Version 1.6.0 broke server-side rendering

User @weyert reported in slack:

Anyone know why these lines 4-22 got deleted in 1.6.0 of posthog-js? v1.5.2...v1.6.0#diff-3274f1a37032fb0ae4e2823def0007c634e869ae0dfc304ff6a12c36513c3a52L4 (edited)
this change makes is harder to use posthog-js with server-side rendering React e.g. Next.js

The change in question removed some appearing-to-be unneeded test scaffolding code which seems to have helped with server-side rendering.

In addition to re-adding the missing pieces back in it would be great to have a sample of this setup to add a test for it to avoid breaking it in the future.

This library is large

As it stands now, posthog-js v1.3.0 is about 52.4kb of minified code.

image

While nowhere near as bad as some of our competitors (mixpanel-browser is 95KB and segment's analytics.js is 187KB), this can still be improved upon.

Should we improve on this and with what priority? We've covered the low hanging fruit and the next step is to refactor and modernise the library's internals. This would greatly help writing proper tests as well.

Granular sanitization tools

We're building with posthog-js (note: huge fans overall) and rapidly found out how much our user-specified text is winding up in autocaptured events. This makes things difficult to follow in the paths diagram. We tried the ph-no-capture and realized that this prevents any events from being captured. Really, we just don't want user-specific text anywhere in the analytics system. So far, you're already stripping some stuff out automatically (e.g. credit card numbers), but we want to go further. To do so, we added a sanitize_properties callback (#74), which lets us do everything we need. In addition to this callback, some way of substituting a generic label for user-specified text would give us most of what we need (and reduce the complexity of our callback by ~80%).

`people.set_once` doesn't seem to be working

I'm trying to use people.set_once but it doesn't seem to be working.

I have a new user and if I tried to set some data using the people.set_once method, but it never gets set. I've double checked, the user doesn't have that data set. I even deleted all the data on an existing user and tried again, but no luck.

If I use the people.set method the data is set, but then it gets overwritten with each subsequent call, which is why I want to use people.set_once instead.

I'm using posthog-js v1.4.5.

Only alias IDs if one of them is anonymous

We automatically alias IDs if posthog.identify is called with a new ID. This is great for making sure users are linked up, but this falls apart when people have a "log in as" button in their admin interface.

Request batching doesn't work when library is initialized after dom load

Hey,

When using posthog-js as a module there can be a problem with starting the _event_queue_poll.

I think the problem is when the library is initialized after the dom is loaded.

Looks like dom_loaded_handler runs on all posthog instances when the dom is loaded, but if there ins't any instances yet, they aren't loaded.

A quick fix suggestion is to add this._dom_loaded() to _loaded.

PostHogLib.prototype._loaded = function() {
    this.get_config('loaded')(this);

    this._dom_loaded()

    // this happens after so a user can call identify in
    // the loaded callback
    if (this.get_config('capture_pageview')) {
        this.capture_pageview();
    }
};

And add a clearInterval to _event_queue_poll to make sure there is only one instance of the timer.

PostHogLib.prototype._event_queue_poll = function () {

    clearInterval(this._poller);
    
    const POLL_INTERVAL = 3000;
    this._poller = setTimeout(() => {  
      ...

A workaround is to set request_batching: false when initializing posthog.

Test Suite

We need a proper test suite for this library, including tests for all the new features.

2.0 release

Over the last months we've been adding a ton of new functionality to posthog-js and got the library under control testing-wise.

I think it's time to clear house - make the library smaller, remove things that are broken or we don't want to support any longer. However this is by definition a breaking change.

I've created a list of things I'd like to do for 2.0 release. Let me know if any of them are controversial or if you have ideas of your own.

  1. โœ… Make browser support explicit - We currently support IE11 and newer -> see #673
  2. โœ… Replace parcel with rollup, minify - I have a WIP branch with this working, before all of the changes below it seems to reduce array.js size ~30-40% (and module.js much more as it isn't minified correctly.
  3. Drop deprecated methods
  4. Remove return values, callbacks from .capture calls and elsewhere
  5. โœ… Remove (broken) GET and image request support, api_method and img config options - #138 removed in #651
  6. Remove custom overrides for console.log and others - This would remove some package size
  7. โœ… Remove lz64 support along with application/x-www-form-urlencoded - after gzip support this just bloats the library. Semi-breaking change - ideally all users would update to posthog 1.19 as well when doing this update. -> remove lz64 support in #654
  8. Remove application/x-www-form-urlencoded - after gzip support this just bloats the library. Semi-breaking change - ideally all users would update to posthog 1.19 as well when doing this update.
  9. Remove multi-instance support - One legacy feature we have is support for array.js being included multiple times. I don't think anyone has ever used this functionality or if it even works still - it might be worth dropping.
  10. Remove xhr_headers config option - #147
  11. Drop posthog.set_config method
  12. Drop posthog.capture_pageview method
  13. Drop posthog.toString method
  14. Drop trim polyfill, other unused polyfills https://caniuse.com/?search=trim
  15. Drop upgrade config option - this seems completely dead
  16. Deprecate snake_cased public methods, add camelCased alternatives

Except for packaging improvements all of this is just deleting code - should be easy to do.

We might also want to consider dropping base64 support - again, would help drop a few KB off the package.

@mariusandra I know you have opinions on the casing as well. Do you think it's time to make camelCase methods a thing? It might be too annoying of an update though - everything else above should require 0 action from users unless they're doing something weird.

cc @timgl

Publish ES module build

Hello,
I'd love to import posthog directly as an ES module build. Looking at the src code, it is probably trivial to do such a thing. (adding another rollup build command + module field in package.json)

Are you guys considering it ?

I'd be happy to PR if need be.
Thanks

Fall back on XHR when sendBeacon returns false

As per https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon:

The sendBeacon() method returns true if the user agent successfully queued the data for transfer. Otherwise, it returns false.

We currently send requests using sendbeacon on unload and also allow setting it as a transport option in .capture calls or in config.

Let's fall back on XHR when this fails.

Other interesting reading: https://volument.com/blog/sendbeacon-is-broken

Question to @timgl - why do we expose transport as an option in the first place?

Feature flag issue for SPA

Bug description

For single-page apps(React in my case, but it seems it would affect anything that doesn't refresh after login) feature flags don't refresh upon logging in.

We are using an 'anonymous' user with PostHog on our login page, and once logged in we update the user info with posthog.identify and posthog.people.set. We are also using onFeatureFlags to handle the feature flags. I don't think the react specifics some into play too much, the main issue is that after identifying the user, by associating its email address and other information, the event callback isn't hit again with the updated feature flags.

How to reproduce

  1. Create a simple SPA with a button that sets the user via posthog.identify and posthog.people.set
  2. Add an onFeatureFlag event handler
  3. Push the button.

Expected behavior

I would expect the feature flags of the newly identified user to trigger the handler.

Environment

  • PostHog cloud or self-managed? self-managed
  • PostHog version/commit 1.10.1 docker.io/posthog/posthog@sha256:2682fd9e5cb07f90c86de068ab45e1a0e8396ff8a81fac1c0dcd4fffafc0381c
    posthog-js 1.3.0

React component can't be easily consumed in Next.js

If you try to use the React components in Next.js you can't easily use them due to the way it's bundled at the moment, it now it requires to be transpilled before:

export * from './context';
^^^^^^

SyntaxError: Unexpected token 'export'
    at wrapSafe (internal/modules/cjs/loader.js:1117:16)
    at Module._compile (internal/modules/cjs/loader.js:1165:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1221:10)
    at Module.load (internal/modules/cjs/loader.js:1050:32)
    at Function.Module._load (internal/modules/cjs/loader.js:938:14)
    at Module.require (internal/modules/cjs/loader.js:1090:19)
    at require (internal/modules/cjs/helpers.js:75:18)
    at eval (webpack-internal:///posthog-js/react:1:18)
    at Object.posthog-js/react (/Users/weyertdeboer/Development/Projects/demo/build/server/pages/_app.js:331:1)

I think it would be convenient if we don't need to transpile. The tsconfig.json suggests it should output es5 but it isn't.

Polling and feature_flag_called event spam

If I update a feature flag, I want my UI to update quickly once the flag is changed. For context, this is a react app. To do this, I

  1. Set a global interval on my app to call reloadFeatureFlags every minute. This lets me control how many network calls will happen and how often I will check for flag updates.
  2. Have a react hook that returns the value of a feature flag. The hooks sets an interval to call isFeatureEnabled every second.

This works well because I do not need to coordinate reloadFeatureFlags and isFeatureEnabled. One handles the network updates, the other the UI refresh rate and a change is reflected in at most 61 seconds.

This was working fine since there is no network call for isFeatureEnabled (just the local cache lookup). However the addition of $feature_flag_called generated 1 event per second event spam. I was forced to increase the interval to 2 minutes. But this is not ideal since now it delays flag updates by up to 3 minutes and still generates these extra events.

this.instance.capture('$feature_flag_called', { $feature_flag: key, $feature_flag_response: response })

Anyway, just wanted to give this feedback, and possibly recommend allowing the user to disable $feature_flag_called and / or if you have a better system to manage timely flag updates for react, updating the docs with a recommend hooks pattern.

$pageleave events are sent when capture_pageview: false

Bug description

I ran posthog-js with { autocapture: false, capture_pageview: false } to avoid any automatic event capturing, however pageviews were still captured (below screenshot of request with pageview payload).

image

Expected behavior

  • Page view events should not be sent on capture_pageview: false

How to reproduce

  1. Init posthog-js with { capture_pageview: false } and review the JS requests console.

Thank you for your bug report โ€“ we love squashing them!

'img' config option does not work

img config option is supposed to create an image tag and do a request to the server based on the url in src attribute.

Sending the request almost works, except it doesn't encode the event name or properties into the url it generates.

posthog.identify should accept user properties

This caused PostHog/posthog#1955, a bug in our own main codebase undetected for months ๐Ÿ˜…

All other client libraries do it! [https://github.com/PostHog/posthog/issues/1955#issuecomment-714291266]

Implementation

@mariusandra @timgl - identify currently accepts two callback arguments. These are not documented on the integrations page, not in the module.d.ts, in the main comment body nor anywhere in public github I could find.

Do you agree killing this will be okay in a minor release?

IE11 support

A user reported a need for IE11 support (their use case is running PostHog on an embedded MS Word app). He mentioned that there's an issue with bundled dependencies not being compatible with IE11. The user shared his code for converting them with Babel (note that the snippet contains dependencies for Papercups, which they also use in their app).

 {
          test: /\.(ts|js)x?$/,
          loader: "babel-loader",
          // most of these are papercups transitive properties
          exclude: /node_modules\/(?!(?:@papercups-io|posthog-js|query-string|@emotion|strict-uri-encode|camelcase-keys|camelcase|map-obj|quick-lru|split-on-first|framer-motion))/
}

Deprecate callback arguments on public methods

Several of our methods (e.g. capture, people.set_once) have callback arguments which are sometimes called with the data being sent to the server. These callbacks are however never called when batching is turned on (which is by default).

This callback is undocumented and I believe we can kill it safely in a 2.x release after deprecating it.

Disable tracking for users that are logged in to PostHog

// Continuing the discussion in #48 here.

I'd personally prefer to always remove "local" traffic, as a site admins almost always have different behaviour patterns than the real users. Thus they tend to skew the results and quite substantially on a low traffic site. For example, I was once investigating an interesting user's behaviour for kea.js.org, only to later discover it was me.

On all my other projects I always have lines like <% if logged_out or not_admin %><%= google_javascript_snippet %><% endif %> to hide all tracking for admins. Since posthog gives me a nice toolbar to play with, I'd definitely enable it, but would like an option to disable sending all events for myself. Probably the existing posthog.init('KEY', { opt_out_capturing_by_default: true }) would do the trick, but I didn't test it. This is anyway an option only for dynamic websites with an authentication system... and not static ones like posthog.com or kea.js.org.

I found this behaviour with kea.js.org for three different cases:

  1. Random unauthenticated site visitor: autocapture enabled and all pageviews are captured
  2. User who has logged in to posthog (/decide returns isAuthenticated=true), but not authorized for the toolbar (temporaryToken is null): autocapture enabled and all pageviews are captured... like for random users.
  3. Logged in and authorized the toolbar - this is where it gets weird: autocapture is disabled, yet $pageviews are captured.

Debugging the third case further, I found the issue for those tracked $pageviews. I'm using the wonderful posthog docusaurus integration, which manually calls window.posthog.capture('$pageview') every time the URL changes.

I'd suggest for case 3 to disable sending of all events, so that we wouldn't have to add very specific "if using toolbar do nothing" code to all the integrations.

Personally I would also disable sending all events (autocapture and manual) for case 2. I would remove them from my site myself, but since it's a static page, there's no way to know that I'm visiting... other than from within the library after /decide.

For users we can say simply to test their page incognito... or we could make "Track users that are logged in to posthog" a configurable option under /setup.

Unifying code style โ€“ linting with Standard

Might be good to lint/fix all of the code to keep it all in the same style without wasting time on that personally. I really love Standard (based on ESLint) for this โ€“ basically Black for JS, zero tinkering. Would it be OK to add this?

Feature flag gives incorrect warning

If there are no feature flags matching my user, I get this warning when checking for any of them:

image

That's not true. The flags did load, there just wasn't anything there.

allow customization of shouldCaptureDomEvent / event targes?

Is there a way to allow the user to customize what elements should be auto-captured? I'm having a code base with lots of div.button elements and would simply like to add elements if they include the .button class.

Alternatively, but to a lesser degree, the opposite of ph-no-capture would be helpful as well.

Thanks!

Temporary Token Auth

Hard to pinpoint and will follow up with more details in conversation. User who tried to use posthog-js in a react app on localhost could not create actions using toolbar. It was reaching the null temporary token error

Discussion: Base64 encoding?

I'm wondering what our rationale is for base64 encoding our data at this point.

The endpoints used to only accept base64 data, but that was changed (actually by me).

So there are a few points that come to mind:

Pros

  • Being able to understand the request data in the network tab
  • We should be able to kill a bunch more code?

Cons

  • Same as Pros number 1: Request data will be more transparent
  • ...?

@macobo

$snapshot events being sent for users that opted out

Bug description

Please describe.
If this affects the front-end, screenshots would be of great help.

Related to Session Recording (Alpha, for internal use only).

Users who opted out of capturing still get their sessions recorded.

Expected behavior

If I opt out of capturing e.g. by setting posthog.opt_out_capturing() my sessions should not be recorded (i.e. there should be no $snapshot events).

How to reproduce

  1. Opt out of capturing (posthog.opt_out_capturing())
  2. See your session recordings continue to come into app

Environment

  • PostHog cloud or self-managed?
  • PostHog version/commit

Additional context

@macobo

Thank you for your bug report โ€“ we love squashing them!

Unify casing

Proposal

Currently original parts of the code use snake_case, while newer additions use pascalCase. It's JS convention though to use pascalCase in most cases and it might be a good idea to refactor at least exported things (that users see) with that convention in mind.

Upsides

The current mix doesn't look very professional and might be leaving a poor impression on developers wanting to use PostHog.

Downsides

This maneuver would leave quite a lot of these snake_case parts and nuking them is not really an option at this point, so they'd have to be left functioning, just deprecated โ€“ that wouldn't be good for code readability.

Default persistence to localStorage over cookies

Currently, posthog-js uses cookies as its default data storage mechanism.

Should we change the default to localStorage and fall back on cookies if it's unavailable?

Currently, we don't read our cookies server-side so as users persist more information about the user in the cookies, we're also making their users requests larger -> slower.

CORS policy issue

I am getting following error during posthog metrics send.

Access to XMLHttpRequest at 'https://posthog.mysite-ops.com/decide/?ip=1&_=1592583273915' 
from origin 'https://app.mysite.com' has been blocked by CORS policy: The value of the 
'Access-Control-Allow-Credentials' header in the response is 'true, true' which must be 'true' 
when the request's credentials mode is 'include'. The credentials mode of requests initiated by 
the XMLHttpRequest is controlled by the withCredentials attribute.

How do I resolve it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.