PostHog / posthog-js-lite

Reimplementation of posthog-js to be as light and modular as possible.
https://posthog.com/docs/libraries
MIT License
70 stars 36 forks source link

Does not work on the Edge [Cloudflare Pages] #67

Closed marchellodev closed 1 year ago

marchellodev commented 1 year ago

Bug description

Hello, I am trying to use posthog-node on the backend of Cloudflare pages.

How to reproduce

  1. Use posthog-node
  2. Deploy to Cloudflare Pages

Related sub-libraries

Additional context

This is the log I get when Cloudflare tries to build my app for its environment:

14:17:38.143 | > Using @sveltejs/adapter-cloudflare
14:17:38.260 | ✘ [ERROR] Could not resolve "crypto"
14:17:38.260 |  
14:17:38.260 | node_modules/posthog-node/lib/index.esm.js:1:27:
14:17:38.260 | 1 │ import { createHash } from 'crypto';
14:17:38.260 | ╵                            ~~~~~~~~
14:17:38.260 |  
14:17:38.260 | The package "crypto" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
14:17:38.261 |  
14:17:38.292 | ✘ [ERROR] Could not resolve "https"
14:17:38.292 |  
14:17:38.293 | node_modules/gaxios/build/src/gaxios.js:20:24:
14:17:38.293 | 20 │ const https_1 = require("https");
14:17:38.293 | ╵                         ~~~~~~~
14:17:38.293 |  
14:17:38.293 | The package "https" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
14:17:38.293 |  
14:17:38.294 | ✘ [ERROR] Could not resolve "querystring"
14:17:38.294 |  
14:17:38.294 | node_modules/gaxios/build/src/gaxios.js:22:46:
14:17:38.294 | 22 │ const querystring_1 = __importDefault(require("querystring"));
14:17:38.294 | ╵                                               ~~~~~~~~~~~~~
14:17:38.294 |  
14:17:38.294 | The package "querystring" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
14:17:38.295 |  
14:17:38.302 | ✘ [ERROR] Could not resolve "url"
14:17:38.302 |  
14:17:38.303 | node_modules/gaxios/build/src/gaxios.js:24:22:
14:17:38.303 | 24 │ const url_1 = require("url");
14:17:38.303 | ╵                       ~~~~~
14:17:38.303 |  
14:17:38.303 | The package "url" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
14:17:38.303 |  
14:17:38.318 | ✘ [ERROR] Could not resolve "net"
14:17:38.318 |  
14:17:38.319 | node_modules/https-proxy-agent/dist/agent.js:15:38:
14:17:38.319 | 15 │ const net_1 = __importDefault(require("net"));
14:17:38.320 | ╵                                       ~~~~~
14:17:38.320 |  
14:17:38.320 | The package "net" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
14:17:38.320 |  
14:17:38.321 | ✘ [ERROR] Could not resolve "tls"
14:17:38.321 |  
14:17:38.321 | node_modules/https-proxy-agent/dist/agent.js:16:38:
14:17:38.321 | 16 │ const tls_1 = __importDefault(require("tls"));
14:17:38.321 | ╵                                       ~~~~~
14:17:38.321 |  
14:17:38.321 | The package "tls" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
14:17:38.322 |  
14:17:38.322 | ✘ [ERROR] Could not resolve "url"
14:17:38.322 |  
14:17:38.322 | node_modules/https-proxy-agent/dist/agent.js:17:38:
14:17:38.322 | 17 │ const url_1 = __importDefault(require("url"));
14:17:38.322 | ╵                                       ~~~~~
14:17:38.322 |  
14:17:38.322 | The package "url" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
14:17:38.323 |  
14:17:38.323 | ✘ [ERROR] Could not resolve "assert"
14:17:38.323 |  
14:17:38.324 | node_modules/https-proxy-agent/dist/agent.js:18:41:
14:17:38.324 | 18 │ const assert_1 = __importDefault(require("assert"));
14:17:38.324 | ╵                                          ~~~~~~~~
14:17:38.324 |  
14:17:38.324 | The package "assert" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
14:17:38.324 |  
14:17:38.325 | ✘ [ERROR] Could not resolve "events"
14:17:38.325 |  
14:17:38.325 | node_modules/agent-base/dist/src/index.js:5:25:
14:17:38.325 | 5 │ const events_1 = require("events");
14:17:38.325 | ╵                          ~~~~~~~~
14:17:38.325 |  
14:17:38.326 | The package "events" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
14:17:38.326 |  
14:17:38.338 | error during build:
14:17:38.338 | Error: Build failed with 9 errors:
14:17:38.338 | node_modules/agent-base/dist/src/index.js:5:25: ERROR: Could not resolve "events"
14:17:38.338 | node_modules/gaxios/build/src/gaxios.js:20:24: ERROR: Could not resolve "https"
14:17:38.339 | node_modules/gaxios/build/src/gaxios.js:22:46: ERROR: Could not resolve "querystring"
14:17:38.339 | node_modules/gaxios/build/src/gaxios.js:24:22: ERROR: Could not resolve "url"
14:17:38.339 | node_modules/https-proxy-agent/dist/agent.js:15:38: ERROR: Could not resolve "net"
14:17:38.339 | ...
14:17:38.339 | at failureErrorWithLog (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:1604:15)
14:17:38.339 | at /opt/buildhome/repo/node_modules/esbuild/lib/main.js:1056:28
14:17:38.339 | at /opt/buildhome/repo/node_modules/esbuild/lib/main.js:1001:67
14:17:38.339 | at buildResponseToResult (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:1054:7)
14:17:38.339 | at /opt/buildhome/repo/node_modules/esbuild/lib/main.js:1166:14
14:17:38.340 | at responseCallbacks.<computed> (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:701:9)
14:17:38.340 | at handleIncomingPacket (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:756:9)
14:17:38.340 | at Socket.readFromStdout (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:677:7)
14:17:38.340 | at Socket.emit (node:events:513:28)
14:17:38.340 | at addChunk (node:internal/streams/readable:315:12)
14:17:38.369 | Failed: build command exited with code: 1
14:17:39.276 | Failed: an internal error occurred
weyert commented 1 year ago

I don't think the library supports other runtimes than Node.js at the moment.

And isn't Cloudflare Pages meant for static content? Wouldn't you need Cloudfare Workers instead?

3x071c commented 1 year ago

@weyert Cloudflare Pages has been capable of running server-side apps using Workers for some time now (competing against Vercel and the likes). As it currently stands, posthog doesn't work with Workers or any non-node runtime which is a bummer. The biggest issue appears to be the use of the crypto library, which even with the node-compat compatibility flag on can't get properly polyfilled. There are solutions to this, but they would either require 1) Cloudflare going out of their way to engineer 100% compatibility with node APIs 2) Posthog using a runtime-independent crypto library or the Web Crypto API, which is now supported natively in Node.js too

With many more alternative runtimes gaining traction (deno, bun, Fastly V8 etc.), I'd suggest switching out the crypto library.

Clafou commented 1 year ago

Having the same issue with posthog-node and I second the comment above by @3x071c. I tried switching to posthog-js-lite but it throws a "window is not defined" at runtime so it sounds like it's not ready for node?

weyert commented 1 year ago

@3x071c If you switch out crypto for Webcrypto which would need to be polyfilled for Node v14.

Wouldn't you still have issues with Axios?

hansottowirtz commented 1 year ago

We're using PostHog with posthog-node in Workers. I've made library to fix issues like with crypto: https://github.com/bubblydoo/cloudflare-workers-compat

It only works with esbuild, but I see that's what you're using as well.

@weyert PostHog takes a fetch argument:

new PostHog({
  fetch: (...args) => fetch(...args)
})
hansottowirtz commented 1 year ago

I have verified that this works in Next.js, but I'm not 100% sure this will work in Cloudflare Pages, so it would be nice if someone could verify it works in the latest version.

Please make sure to use:

new PostHog({
  fetch: (...args) => fetch(...args)
})
huw commented 1 year ago

Just FYI—you need to make sure to use waitUntil when closing the client (by default, Cloudflare shuts down the worker after sending the response; we have to tell it to send the response immediately but continue executing the shutdown promise). So the most idiomatic version of the above should be:

new PostHog(environment.POSTHOG_TOKEN, {
  fetch: fetch.bind(globalThis)
});

// Your work here
const response = await fetch("https://www.google.com");

context.waitUntil(posthog.shutdownAsync());

return response;