Init script analytics (#611)

This commit is contained in:
Konsti Wohlwend 2025-04-08 16:34:47 -07:00 committed by GitHub
parent 7df6191dc4
commit 3940e581ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 143 additions and 19 deletions

View File

@ -36,7 +36,7 @@
"lucide-react": "^0.378.0",
"next": "15.2.3",
"next-themes": "^0.2.1",
"posthog-js": "^1.149.1",
"posthog-js": "^1.234.9",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-globe.gl": "^2.28.2",

View File

@ -1,6 +1,7 @@
import { Confetti } from "@/components/confetti";
import { Card, CardContent, CardFooter, CardHeader, InlineCode, Typography } from "@stackframe/stack-ui";
import Actions from "./actions";
import PostHog from "./posthog";
export const metadata = {
title: "Setup complete!",
@ -9,6 +10,7 @@ export const metadata = {
export default function WizardCongratsPage() {
return (
<>
<PostHog />
<Confetti />
<style>
{`

View File

@ -0,0 +1,32 @@
"use client";
import { useRouter } from "@/components/router";
import { useSearchParams } from "next/navigation";
import { usePostHog } from "posthog-js/react";
import { useEffect } from "react";
export default function PostHog() {
const posthog = usePostHog();
const searchParams = useSearchParams();
const router = useRouter();
useEffect(() => {
const distinctId = searchParams.get("stack-init-id");
if (distinctId) {
posthog.capture('$merge_dangerously',
{
alias: distinctId,
});
const newSearchParams = new URLSearchParams();
searchParams.forEach((value, key) => {
if (key !== "stack-init-id") {
newSearchParams.append(key, value);
}
});
const newUrl = window.location.pathname +
(newSearchParams.toString() ? `?${newSearchParams.toString()}` : '');
router.replace(newUrl);
}
}, [posthog, searchParams, router]);
return null;
}

View File

@ -40,7 +40,8 @@
"@stackframe/stack-shared": "workspace:*",
"commander": "^13.1.0",
"inquirer": "^9.2.19",
"open": "^10.1.0"
"open": "^10.1.0",
"posthog-node": "^4.1.0"
},
"devDependencies": {
"@types/inquirer": "^9.0.7",

View File

@ -1,9 +1,12 @@
import * as child_process from "child_process";
import { Command } from "commander";
import * as crypto from 'crypto';
import * as fs from "fs";
import inquirer from "inquirer";
import open from "open";
import * as os from 'os';
import * as path from "path";
import { PostHog } from 'posthog-node';
import packageJson from '../package.json';
const jsLikeFileExtensions: string[] = [
@ -113,6 +116,26 @@ const nextSteps: string[] = [
`Create an account and Stack Auth API key for your project on https://app.stack-auth.com`,
];
const STACK_AUTH_PUBLIC_HOG_KEY = "phc_vIUFi0HzHo7oV26OsaZbUASqxvs8qOmap1UBYAutU4k";
const EVENT_PREFIX = "stack-init-";
const ph_client = new PostHog(STACK_AUTH_PUBLIC_HOG_KEY, {
host: "https://eu.i.posthog.com",
flushAt: 1,
flushInterval: 0,
});
const distinctId = crypto.randomUUID();
async function capture(event: string, properties: Record<string, any>) {
ph_client.capture({
event: `${EVENT_PREFIX}${event}`,
distinctId,
properties,
});
}
async function main(): Promise<void> {
// Welcome message
console.log();
@ -132,6 +155,19 @@ async function main(): Promise<void> {
`);
console.log();
await capture("start", {
version: packageJson.version,
isDryRun,
isNeon,
typeFromArgs,
packageManagerFromArgs,
isClient,
isServer,
noBrowser,
platform: os.platform(),
arch: os.arch(),
nodeVersion: process.version,
});
// Wait just briefly so we can use `Steps` in here (it's defined only after the call to `main()`)
await new Promise<void>((resolve) => resolve());
@ -143,8 +179,13 @@ async function main(): Promise<void> {
// Steps
const { packageJson } = await Steps.getProject();
const type = await Steps.getProjectType({ packageJson });
const { packageJson: projectPackageJson } = await Steps.getProject();
const type = await Steps.getProjectType({ packageJson: projectPackageJson });
await capture("project-type-selected", {
type,
wasSpecifiedInArgs: !!typeFromArgs,
});
await Steps.addStackPackage(type);
if (isNeon) packagesToInstall.push('@neondatabase/serverless');
@ -152,7 +193,7 @@ async function main(): Promise<void> {
await Steps.writeEnvVars(type);
if (type === "next") {
const projectInfo = await Steps.getNextProjectInfo({ packageJson });
const projectInfo = await Steps.getNextProjectInfo({ packageJson: projectPackageJson });
await Steps.updateNextLayoutFile(projectInfo);
await Steps.writeStackAppFile(projectInfo, "server");
await Steps.writeNextHandlerFile(projectInfo);
@ -181,6 +222,12 @@ async function main(): Promise<void> {
}
const { packageManager } = await Steps.getPackageManager();
await capture(`package-manager-selected`, {
packageManager,
wasSpecifiedInArgs: !!packageManagerFromArgs,
});
await Steps.ensureReady(type);
@ -193,6 +240,10 @@ async function main(): Promise<void> {
cwd: projectPath,
});
await capture(`dependencies-installed`, {
packageManager,
packages: packagesToInstall,
});
// Write files
console.log();
@ -220,6 +271,18 @@ async function main(): Promise<void> {
}
console.log();
await capture("complete", {
success: true,
type,
packageManager,
isNeon,
isClient,
isServer,
noBrowser,
filesCreated,
filesModified,
commandsExecuted,
});
// Success!
console.log(`
@ -230,8 +293,8 @@ ${colorize.green`Successfully installed Stack! 🚀🚀🚀`}
${colorize.bold`Next steps:`}
1. ${noBrowser ?
`Create a project at https://app.stack-auth.com and get your API keys` :
`Complete the setup in your browser to get your API keys`}
`Create a project at https://app.stack-auth.com and get your API keys` :
`Complete the setup in your browser to get your API keys`}
2. Add the API keys to your .env.local file
3. Import the Stack components in your app
4. Add authentication to your app
@ -239,12 +302,20 @@ ${colorize.bold`Next steps:`}
For more information, please visit https://docs.stack-auth.com/getting-started/setup
`.trim());
if (!process.env.STACK_DISABLE_INTERACTIVE && !noBrowser) {
await open("https://app.stack-auth.com/wizard-congrats");
await open(`https://app.stack-auth.com/wizard-congrats?stack-init-id=${encodeURIComponent(distinctId)}`);
}
await ph_client.shutdown();
}
main()
.catch((err) => {
.catch(async (err) => {
try {
await capture("error", {
error: err.message,
errorType: err instanceof UserError ? "UserError" : "SystemError",
stack: err.stack,
});
} catch (e) { }
if (!(err instanceof UserError)) {
console.error(err);
}
@ -267,6 +338,7 @@ main()
console.error(`Error message: ${err.message}`);
}
console.error();
await ph_client.shutdown();
process.exit(1);
});

View File

@ -323,8 +323,8 @@ importers:
specifier: ^0.2.1
version: 0.2.1(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
posthog-js:
specifier: ^1.149.1
version: 1.149.2
specifier: ^1.234.9
version: 1.234.9
react:
specifier: 19.0.0
version: 19.0.0
@ -844,6 +844,9 @@ importers:
open:
specifier: ^10.1.0
version: 10.1.0
posthog-node:
specifier: ^4.1.0
version: 4.1.0
devDependencies:
'@types/inquirer':
specifier: ^9.0.7
@ -7197,6 +7200,9 @@ packages:
core-js-compat@3.40.0:
resolution: {integrity: sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==}
core-js@3.41.0:
resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
@ -10123,8 +10129,16 @@ packages:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
posthog-js@1.149.2:
resolution: {integrity: sha512-4tNtVJkq3wZ5CvfOEp3Jtl/r3ogZb5To+bdu7JoO5QjkpTY9TV1pfo/Ag4keODpAzRDahC8OaCoIr4mY3dSK4g==}
posthog-js@1.234.9:
resolution: {integrity: sha512-Qxpg9YOlLa59lbkYcONZ9efmB6KTgkePnkhCTSnKIyUd826cs4J6VDTKHu+V/lugRtWLWB9R7CckvFE8QPaTbg==}
peerDependencies:
'@rrweb/types': 2.0.0-alpha.17
rrweb-snapshot: 2.0.0-alpha.17
peerDependenciesMeta:
'@rrweb/types':
optional: true
rrweb-snapshot:
optional: true
posthog-node@4.1.0:
resolution: {integrity: sha512-Fd+aMWLjUttlPrfOniDWs35v62rOEIqP5GBzUvRswsNY8rr1g1KuDobqaRFGMCNnrtDmhzUN8y7QucrcwMY/+w==}
@ -11824,8 +11838,8 @@ packages:
wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
web-vitals@4.2.2:
resolution: {integrity: sha512-nYfoOqb4EmElljyXU2qdeE76KsvoHdftQKY4DzA9Aw8DervCg2bG634pHLrJ/d6+B4mE3nWTSJv8Mo7B2mbZkw==}
web-vitals@4.2.4:
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@ -17930,6 +17944,8 @@ snapshots:
dependencies:
browserslist: 4.24.4
core-js@3.41.0: {}
core-util-is@1.0.3: {}
cors@2.8.5:
@ -21583,15 +21599,16 @@ snapshots:
dependencies:
xtend: 4.0.2
posthog-js@1.149.2:
posthog-js@1.234.9:
dependencies:
core-js: 3.41.0
fflate: 0.4.8
preact: 10.22.0
web-vitals: 4.2.2
web-vitals: 4.2.4
posthog-node@4.1.0:
dependencies:
axios: 1.7.4
axios: 1.7.7
rusha: 0.8.14
transitivePeerDependencies:
- debug
@ -23924,7 +23941,7 @@ snapshots:
dependencies:
defaults: 1.0.4
web-vitals@4.2.2: {}
web-vitals@4.2.4: {}
webidl-conversions@3.0.1: {}