Added telemetry to the CLI (#242)
* Basic CLI telemetry * Telemetry for init command * Better error handling * Fix for “Module not found: Can't resolve 'encoding' in” node-fetch warning * Telemetry for dev and tidied up init * Minor improvements to the telemetry * Latest package lock file * Set the PostHog key to the prod one * Changesets * Formatted
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Fix for a console warning about "encoding" with node-fetch
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Added basic telemetry
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { LoaderArgs } from "@remix-run/server-runtime";
|
||||
import { json } from "@remix-run/server-runtime";
|
||||
import { prisma } from "~/db.server";
|
||||
import { authenticateApiRequest } from "~/services/apiAuth.server";
|
||||
|
||||
export async function loader({ request }: LoaderArgs) {
|
||||
@@ -10,5 +11,23 @@ export async function loader({ request }: LoaderArgs) {
|
||||
return json({ error: "Invalid or Missing API key" }, { status: 401 });
|
||||
}
|
||||
|
||||
return json(authenticationResult.environment);
|
||||
const environmentWithUser = await prisma.runtimeEnvironment.findUnique({
|
||||
select: {
|
||||
orgMember: {
|
||||
select: {
|
||||
userId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: authenticationResult.environment.id,
|
||||
},
|
||||
});
|
||||
|
||||
const result = {
|
||||
...authenticationResult.environment,
|
||||
userId: environmentWithUser?.orgMember?.userId,
|
||||
};
|
||||
|
||||
return json(result);
|
||||
}
|
||||
|
||||
@@ -62,11 +62,13 @@
|
||||
"gradient-string": "^2.0.2",
|
||||
"inquirer": "^9.1.4",
|
||||
"localtunnel": "^2.0.2",
|
||||
"nanoid": "^4.0.2",
|
||||
"ngrok": "5.0.0-beta.2",
|
||||
"node-fetch": "^3.3.0",
|
||||
"openai": "^3.3.0",
|
||||
"ora": "^6.1.2",
|
||||
"path-to-regexp": "^6.2.1",
|
||||
"posthog-node": "^3.1.1",
|
||||
"simple-git": "^3.19.0",
|
||||
"terminal-link": "^3.0.0",
|
||||
"tsconfck": "^2.1.2",
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Command } from "commander";
|
||||
import inquirer from "inquirer";
|
||||
import { initCommand } from "../commands/init.js";
|
||||
import { COMMAND_NAME, CLOUD_TRIGGER_URL } from "../consts.js";
|
||||
import { getVersion } from "../utils/getVersion.js";
|
||||
import pathModule from "node:path";
|
||||
import { devCommand } from "../commands/dev.js";
|
||||
import { createIntegrationCommand } from "../commands/createIntegration.js";
|
||||
import { devCommand } from "../commands/dev.js";
|
||||
import { initCommand } from "../commands/init.js";
|
||||
import { CLOUD_TRIGGER_URL, COMMAND_NAME } from "../consts.js";
|
||||
import { TelemetryClient, telemetryClient } from "../telemetry/telemetry.js";
|
||||
import { getVersion } from "../utils/getVersion.js";
|
||||
|
||||
export const program = new Command();
|
||||
|
||||
@@ -27,7 +28,12 @@ program
|
||||
)
|
||||
.version(getVersion(), "-v, --version", "Display the version number")
|
||||
.action(async (options) => {
|
||||
await initCommand(options);
|
||||
try {
|
||||
await initCommand(options);
|
||||
} catch (e) {
|
||||
telemetryClient.init.failed("unknown", options, e);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
@@ -47,7 +53,12 @@ program
|
||||
)
|
||||
.version(getVersion(), "-v, --version", "Display the version number")
|
||||
.action(async (path, options) => {
|
||||
await devCommand(path, options);
|
||||
try {
|
||||
await devCommand(path, options);
|
||||
} catch (e) {
|
||||
telemetryClient.dev.failed("unknown", options, e);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
|
||||
@@ -10,6 +10,7 @@ import { resolvePath } from "../utils/parseNameAndPath.js";
|
||||
import { TriggerApi } from "../utils/triggerApi.js";
|
||||
import dotenv from "dotenv";
|
||||
import fetch from "node-fetch";
|
||||
import { TelemetryClient, telemetryClient } from "../telemetry/telemetry.js";
|
||||
|
||||
export const DevCommandOptionsSchema = z.object({
|
||||
port: z.coerce.number(),
|
||||
@@ -18,7 +19,7 @@ export const DevCommandOptionsSchema = z.object({
|
||||
clientId: z.string().optional(),
|
||||
});
|
||||
|
||||
type DevCommandOptions = z.infer<typeof DevCommandOptionsSchema>;
|
||||
export type DevCommandOptions = z.infer<typeof DevCommandOptionsSchema>;
|
||||
|
||||
const throttleTimeMs = 1000;
|
||||
|
||||
@@ -29,10 +30,13 @@ const formattedDate = new Intl.DateTimeFormat("en", {
|
||||
});
|
||||
|
||||
export async function devCommand(path: string, anyOptions: any) {
|
||||
telemetryClient.dev.started(path, anyOptions);
|
||||
|
||||
const result = DevCommandOptionsSchema.safeParse(anyOptions);
|
||||
if (!result.success) {
|
||||
logger.error(result.error.message);
|
||||
process.exit(1);
|
||||
telemetryClient.dev.failed("invalid_options", anyOptions, result.error);
|
||||
return;
|
||||
}
|
||||
const options = result.data;
|
||||
|
||||
@@ -44,12 +48,20 @@ export async function devCommand(path: string, anyOptions: any) {
|
||||
logger.error(
|
||||
"You must run the `init` command first to setup the project – you are missing \n'trigger.dev': { 'endpointId': 'your-client-id' } from your package.json file, or pass in the --client-id option to this command"
|
||||
);
|
||||
process.exit(1);
|
||||
telemetryClient.dev.failed("missing_endpoint_id", options);
|
||||
return;
|
||||
}
|
||||
logger.success(`✔️ [trigger.dev] Detected TriggerClient id: ${endpointId}`);
|
||||
|
||||
// Read from .env.local or .env to get the TRIGGER_API_KEY and TRIGGER_API_URL
|
||||
const { apiUrl, envFile, apiKey } = await getTriggerApiDetails(resolvedPath, options.envFile);
|
||||
const apiDetails = await getTriggerApiDetails(resolvedPath, options.envFile);
|
||||
|
||||
if (!apiDetails) {
|
||||
telemetryClient.dev.failed("missing_api_key", options);
|
||||
return;
|
||||
}
|
||||
|
||||
const { apiUrl, envFile, apiKey } = apiDetails;
|
||||
|
||||
logger.success(`✔️ [trigger.dev] Found API Key in ${envFile} file`);
|
||||
|
||||
@@ -68,16 +80,26 @@ export async function devCommand(path: string, anyOptions: any) {
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`❌ [trigger.dev] No server found on port ${options.port}.`);
|
||||
process.exit(1);
|
||||
telemetryClient.dev.failed("no_server_found", options);
|
||||
return;
|
||||
}
|
||||
|
||||
telemetryClient.dev.serverRunning(path, options);
|
||||
|
||||
// Setup tunnel
|
||||
const endpointUrl = await resolveEndpointUrl(apiUrl, options.port);
|
||||
if (!endpointUrl) {
|
||||
telemetryClient.dev.failed("failed_to_create_tunnel", options);
|
||||
return;
|
||||
}
|
||||
|
||||
const endpointHandlerUrl = `${endpointUrl}${options.handlerPath}`;
|
||||
telemetryClient.dev.tunnelRunning(path, options);
|
||||
|
||||
const connectingSpinner = ora(`[trigger.dev] Registering endpoint ${endpointHandlerUrl}...`);
|
||||
|
||||
//refresh function
|
||||
let hasConnected = false;
|
||||
let attemptCount = 0;
|
||||
const refresh = async () => {
|
||||
connectingSpinner.start();
|
||||
@@ -85,10 +107,34 @@ export async function devCommand(path: string, anyOptions: any) {
|
||||
const refreshedEndpointId = await getEndpointIdFromPackageJson(resolvedPath, options);
|
||||
|
||||
// Read from .env.local to get the TRIGGER_API_KEY and TRIGGER_API_URL
|
||||
const { apiKey, apiUrl } = await getTriggerApiDetails(resolvedPath, envFile);
|
||||
const apiDetails = await getTriggerApiDetails(resolvedPath, envFile);
|
||||
|
||||
if (!apiDetails) {
|
||||
connectingSpinner.fail(`❌ [trigger.dev] Failed to connect: Missing API Key`);
|
||||
logger.info(`Will attempt again on the next file change…`);
|
||||
attemptCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const { apiKey, apiUrl } = apiDetails;
|
||||
const apiClient = new TriggerApi(apiKey, apiUrl);
|
||||
|
||||
const authorizedKey = await apiClient.whoami(apiKey);
|
||||
if (!authorizedKey) {
|
||||
logger.error(
|
||||
`🛑 The API key you provided is not authorized. Try visiting your dashboard to get a new API key.`
|
||||
);
|
||||
|
||||
telemetryClient.dev.failed("invalid_api_key", options);
|
||||
return;
|
||||
}
|
||||
|
||||
telemetryClient.identify(
|
||||
authorizedKey.organization.id,
|
||||
authorizedKey.project.id,
|
||||
authorizedKey.userId
|
||||
);
|
||||
|
||||
const result = await refreshEndpoint(
|
||||
apiClient,
|
||||
refreshedEndpointId ?? endpointId,
|
||||
@@ -101,6 +147,11 @@ export async function devCommand(path: string, anyOptions: any) {
|
||||
new Date(result.data.updatedAt)
|
||||
)}`
|
||||
);
|
||||
|
||||
if (!hasConnected) {
|
||||
hasConnected = true;
|
||||
telemetryClient.dev.connected(path, options);
|
||||
}
|
||||
} else {
|
||||
attemptCount++;
|
||||
|
||||
@@ -108,6 +159,10 @@ export async function devCommand(path: string, anyOptions: any) {
|
||||
connectingSpinner.fail(`🚨 Failed to connect: ${result.error}`);
|
||||
logger.info(`Will attempt again on the next file change…`);
|
||||
attemptCount = 0;
|
||||
|
||||
if (!hasConnected) {
|
||||
telemetryClient.dev.failed("failed_to_connect", options);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -196,14 +251,14 @@ async function getTriggerApiDetails(path: string, envFile: string) {
|
||||
|
||||
if (!resolvedEnvFile) {
|
||||
logger.error(`You must add TRIGGER_API_KEY and TRIGGER_API_URL to your ${envFile} file.`);
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedEnvFile = dotenv.parse(resolvedEnvFile.content);
|
||||
|
||||
if (!parsedEnvFile.TRIGGER_API_KEY || !parsedEnvFile.TRIGGER_API_KEY) {
|
||||
logger.error(`You must add TRIGGER_API_KEY and TRIGGER_API_URL to your ${envFile} file.`);
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
const apiKey = parsedEnvFile.TRIGGER_API_KEY;
|
||||
@@ -211,7 +266,7 @@ async function getTriggerApiDetails(path: string, envFile: string) {
|
||||
|
||||
if (!apiKey || !apiUrl) {
|
||||
logger.error(`You must add TRIGGER_API_KEY and TRIGGER_API_URL to your ${envFile} file.`);
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
return { apiKey, apiUrl, envFile: resolvedEnvFile.fileName };
|
||||
@@ -227,7 +282,9 @@ async function resolveEndpointUrl(apiUrl: string, port: number) {
|
||||
// Setup tunnel
|
||||
const tunnelSpinner = ora(`🚇 Creating tunnel`).start();
|
||||
const tunnelUrl = await createTunnel(port);
|
||||
tunnelSpinner.succeed(`🚇 Created tunnel: ${tunnelUrl}`);
|
||||
if (tunnelUrl) {
|
||||
tunnelSpinner.succeed(`🚇 Created tunnel: ${tunnelUrl}`);
|
||||
}
|
||||
|
||||
return tunnelUrl;
|
||||
}
|
||||
@@ -237,7 +294,7 @@ async function createTunnel(port: number) {
|
||||
return await ngrok.connect(port);
|
||||
} catch (e) {
|
||||
logger.error(`Ngrok failed to create a tunnel for port ${port}.\n${e}`);
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,19 @@ import inquirer from "inquirer";
|
||||
import pathModule from "path";
|
||||
import { pathToRegexp } from "path-to-regexp";
|
||||
import { simpleGit } from "simple-git";
|
||||
import { parse } from "tsconfck";
|
||||
import { pathToFileURL } from "url";
|
||||
import { promptApiKey, promptEndpointSlug, promptTriggerUrl } from "../cli/index.js";
|
||||
import { COMMAND_NAME, CLOUD_TRIGGER_URL, CLOUD_API_URL } from "../consts.js";
|
||||
import { CLOUD_API_URL, CLOUD_TRIGGER_URL, COMMAND_NAME } from "../consts.js";
|
||||
import { TelemetryClient, telemetryClient } from "../telemetry/telemetry.js";
|
||||
import { addDependencies } from "../utils/addDependencies.js";
|
||||
import { detectNextJsProject } from "../utils/detectNextJsProject.js";
|
||||
import { pathExists, readJSONFile } from "../utils/fileSystem.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
import { resolvePath } from "../utils/parseNameAndPath.js";
|
||||
import { renderApiKey } from "../utils/renderApiKey.js";
|
||||
import { renderTitle } from "../utils/renderTitle.js";
|
||||
import { detectNextJsProject } from "../utils/detectNextJsProject.js";
|
||||
import { TriggerApi, WhoamiResponse } from "../utils/triggerApi.js";
|
||||
import { parse } from "tsconfck";
|
||||
import { pathExists, readJSONFile } from "../utils/fileSystem.js";
|
||||
import { pathToFileURL } from "url";
|
||||
|
||||
export type InitCommandOptions = {
|
||||
projectPath: string;
|
||||
@@ -31,6 +32,8 @@ type ResolvedOptions = Required<InitCommandOptions>;
|
||||
export const initCommand = async (options: InitCommandOptions) => {
|
||||
renderTitle();
|
||||
|
||||
telemetryClient.init.started(options);
|
||||
|
||||
if (options.triggerUrl === CLOUD_TRIGGER_URL) {
|
||||
logger.info(`✨ Initializing project in Trigger.dev Cloud`);
|
||||
} else if (typeof options.triggerUrl === "string") {
|
||||
@@ -45,7 +48,8 @@ export const initCommand = async (options: InitCommandOptions) => {
|
||||
|
||||
if (!isNextJsProject) {
|
||||
logger.error("You must run this command in a Next.js project.");
|
||||
process.exit(1);
|
||||
telemetryClient.init.failed("not_nextjs_project", options);
|
||||
return;
|
||||
} else {
|
||||
logger.success("✅ Detected Next.js project");
|
||||
}
|
||||
@@ -60,14 +64,15 @@ export const initCommand = async (options: InitCommandOptions) => {
|
||||
}
|
||||
|
||||
const isTypescriptProject = await detectTypescriptProject(resolvedPath);
|
||||
telemetryClient.init.isTypescriptProject(isTypescriptProject, options);
|
||||
|
||||
const resolvedOptions = await resolveOptionsWithPrompts(options, resolvedPath);
|
||||
|
||||
const resolvedOptions = await resolveOptionsWithPrompts(options, resolvedPath, telemetryClient);
|
||||
const apiKey = resolvedOptions.apiKey;
|
||||
|
||||
if (!apiKey) {
|
||||
logger.error("You must provide an API key to continue.");
|
||||
process.exit(1);
|
||||
telemetryClient.init.failed("no_api_key", resolvedOptions);
|
||||
return;
|
||||
}
|
||||
|
||||
const apiClient = new TriggerApi(apiKey, resolvedOptions.apiUrl);
|
||||
@@ -78,14 +83,23 @@ export const initCommand = async (options: InitCommandOptions) => {
|
||||
`🛑 The API key you provided is not authorized. Try visiting your dashboard at ${resolvedOptions.triggerUrl} to get a new API key.`
|
||||
);
|
||||
|
||||
process.exit(1);
|
||||
telemetryClient.init.failed("invalid_api_key", resolvedOptions);
|
||||
return;
|
||||
}
|
||||
|
||||
telemetryClient.identify(
|
||||
authorizedKey.organization.id,
|
||||
authorizedKey.project.id,
|
||||
authorizedKey.userId
|
||||
);
|
||||
|
||||
await addDependencies(resolvedPath, [
|
||||
{ name: "@trigger.dev/sdk", tag: "latest" },
|
||||
{ name: "@trigger.dev/nextjs", tag: "latest" },
|
||||
]);
|
||||
|
||||
telemetryClient.init.addedDependencies(resolvedOptions);
|
||||
|
||||
// Setup environment variables
|
||||
await setupEnvironmentVariables(resolvedPath, resolvedOptions);
|
||||
|
||||
@@ -100,6 +114,7 @@ export const initCommand = async (options: InitCommandOptions) => {
|
||||
const routeDir = pathModule.join(resolvedPath, usesSrcDir ? "src" : "");
|
||||
|
||||
if (nextJsDir === "pages") {
|
||||
telemetryClient.init.createFiles(resolvedOptions, "pages");
|
||||
await createTriggerPageRoute(
|
||||
resolvedPath,
|
||||
routeDir,
|
||||
@@ -108,6 +123,7 @@ export const initCommand = async (options: InitCommandOptions) => {
|
||||
usesSrcDir
|
||||
);
|
||||
} else {
|
||||
telemetryClient.init.createFiles(resolvedOptions, "app");
|
||||
await createTriggerAppRoute(
|
||||
resolvedPath,
|
||||
routeDir,
|
||||
@@ -122,8 +138,7 @@ export const initCommand = async (options: InitCommandOptions) => {
|
||||
await addConfigurationToPackageJson(resolvedPath, resolvedOptions);
|
||||
|
||||
await printNextSteps(resolvedOptions, authorizedKey);
|
||||
|
||||
process.exit(0);
|
||||
telemetryClient.init.completed(resolvedOptions);
|
||||
};
|
||||
|
||||
async function printNextSteps(options: ResolvedOptions, authorizedKey: WhoamiResponse) {
|
||||
@@ -160,7 +175,8 @@ async function addConfigurationToPackageJson(path: string, options: ResolvedOpti
|
||||
|
||||
const resolveOptionsWithPrompts = async (
|
||||
options: InitCommandOptions,
|
||||
path: string
|
||||
path: string,
|
||||
telemetryClient: TelemetryClient
|
||||
): Promise<ResolvedOptions> => {
|
||||
const resolvedOptions: InitCommandOptions = { ...options };
|
||||
|
||||
@@ -175,21 +191,24 @@ const resolveOptionsWithPrompts = async (
|
||||
resolvedOptions.apiUrl = resolvedOptions.triggerUrl;
|
||||
}
|
||||
|
||||
telemetryClient.init.resolvedApiUrl(resolvedOptions.apiUrl, resolvedOptions);
|
||||
|
||||
if (!options.apiKey) {
|
||||
resolvedOptions.apiKey = await promptApiKey(resolvedOptions.triggerUrl!);
|
||||
}
|
||||
telemetryClient.init.resolvedApiKey(resolvedOptions);
|
||||
|
||||
if (!options.endpointSlug) {
|
||||
const packageJSONPath = pathModule.join(path, "package.json");
|
||||
const packageJSON = await readJSONFile(packageJSONPath);
|
||||
|
||||
if (packageJSON && packageJSON["trigger.dev"] && packageJSON["trigger.dev"].endpointId) {
|
||||
options.endpointSlug = packageJSON["trigger.dev"].endpointId;
|
||||
resolvedOptions.endpointSlug = packageJSON["trigger.dev"].endpointId;
|
||||
} else {
|
||||
options.endpointSlug = await promptEndpointSlug(path);
|
||||
resolvedOptions.endpointSlug = await promptEndpointSlug(path);
|
||||
}
|
||||
|
||||
resolvedOptions.endpointSlug = await promptEndpointSlug(path);
|
||||
telemetryClient.init.resolvedEndpointSlug(resolvedOptions);
|
||||
}
|
||||
} catch (err) {
|
||||
// If the user is not calling the command from an interactive terminal, inquirer will throw an error with isTTYError = true
|
||||
@@ -209,10 +228,12 @@ const resolveOptionsWithPrompts = async (
|
||||
});
|
||||
|
||||
if (!shouldContinue) {
|
||||
telemetryClient.init.failed("non_interactive_terminal", options);
|
||||
logger.info("Exiting...");
|
||||
process.exit(0);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
telemetryClient.init.failed("unknown", options, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -299,6 +320,7 @@ async function detectMiddlewareUsage(path: string, usesSrcDir = false) {
|
||||
)} which can cause issues with Trigger.dev. Please see https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware`
|
||||
);
|
||||
|
||||
telemetryClient.init.warning("middleware_conflict", { projectPath: path });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -317,6 +339,7 @@ async function detectMiddlewareUsage(path: string, usesSrcDir = false) {
|
||||
middlewarePath
|
||||
)} which will cause issues with Trigger.dev. Please see https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware`
|
||||
);
|
||||
telemetryClient.init.warning("middleware_conflict_api_trigger", { projectPath: path });
|
||||
}
|
||||
} else if (Array.isArray(matcher) && matcher.every((m) => typeof m === "string")) {
|
||||
const matcherRegexes = matcher.map((m) => pathToRegexp(m));
|
||||
@@ -328,6 +351,7 @@ async function detectMiddlewareUsage(path: string, usesSrcDir = false) {
|
||||
middlewarePath
|
||||
)} which will cause issues with Trigger.dev. Please see https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware`
|
||||
);
|
||||
telemetryClient.init.warning("middleware_conflict", { projectPath: path });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,11 @@ const main = async () => {
|
||||
};
|
||||
|
||||
main().catch((err) => {
|
||||
logger.error("Aborting installation...");
|
||||
if (err instanceof Error) {
|
||||
logger.error(err);
|
||||
} else {
|
||||
logger.error("An unknown error has occurred. Please open an issue on github with the below:");
|
||||
console.log(err);
|
||||
logger.error(err);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
import { PostHog } from "posthog-node";
|
||||
import { InitCommandOptions } from "../commands/init.js";
|
||||
import { nanoid } from "nanoid";
|
||||
import { getVersion } from "../utils/getVersion.js";
|
||||
import { DevCommandOptions } from "../commands/dev.js";
|
||||
|
||||
const postHogApiKey = "phc_hwYmedO564b3Ik8nhA4Csrb5SueY0EwFJWCbseGwWW";
|
||||
|
||||
export class TelemetryClient {
|
||||
#client: PostHog;
|
||||
#sessionId: string;
|
||||
#version: string;
|
||||
|
||||
constructor() {
|
||||
this.#client = new PostHog(postHogApiKey, {
|
||||
host: "https://app.posthog.com",
|
||||
flushAt: 1,
|
||||
});
|
||||
this.#sessionId = `cli-${nanoid()}`;
|
||||
this.#version = getVersion();
|
||||
}
|
||||
|
||||
identify(organizationId: string, projectId: string, userId?: string) {
|
||||
if (userId) {
|
||||
this.#client.alias({
|
||||
distinctId: userId,
|
||||
alias: this.#sessionId,
|
||||
});
|
||||
}
|
||||
|
||||
this.#client.groupIdentify({
|
||||
groupType: "organization",
|
||||
groupKey: organizationId,
|
||||
});
|
||||
|
||||
this.#client.groupIdentify({
|
||||
groupType: "project",
|
||||
groupKey: projectId,
|
||||
});
|
||||
}
|
||||
|
||||
init = {
|
||||
started: (options: InitCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_started",
|
||||
properties: this.#initProperties(options),
|
||||
});
|
||||
},
|
||||
isTypescriptProject: (isTypescriptProject: boolean, options: InitCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_typescriptproject",
|
||||
properties: { ...this.#initProperties(options), isTypescriptProject },
|
||||
});
|
||||
},
|
||||
resolvedApiUrl: (apiUrl: string | undefined, options: InitCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_resolved_apiurl",
|
||||
properties: { ...this.#initProperties(options), apiUrl },
|
||||
});
|
||||
},
|
||||
resolvedApiKey: (options: InitCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_resolved_apikey",
|
||||
properties: this.#initProperties(options),
|
||||
});
|
||||
},
|
||||
resolvedEndpointSlug: (options: InitCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_resolved_endpoint_slug",
|
||||
properties: this.#initProperties(options),
|
||||
});
|
||||
},
|
||||
addedDependencies: (options: InitCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_added_dependencies",
|
||||
properties: this.#initProperties(options),
|
||||
});
|
||||
},
|
||||
createFiles: (options: InitCommandOptions, routerType: "pages" | "app") => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_create_files",
|
||||
properties: { ...this.#initProperties(options), routerType },
|
||||
});
|
||||
},
|
||||
completed: (options: InitCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_completed",
|
||||
properties: this.#initProperties(options),
|
||||
});
|
||||
},
|
||||
warning: (reason: string, options: InitCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_warning",
|
||||
properties: {
|
||||
...this.#initProperties(options),
|
||||
reason,
|
||||
},
|
||||
});
|
||||
},
|
||||
failed: (reason: string, options: InitCommandOptions, error?: unknown) => {
|
||||
const errorString = error instanceof Error ? error.message : String(error);
|
||||
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_init_failed",
|
||||
properties: {
|
||||
...this.#initProperties(options),
|
||||
reason,
|
||||
error: errorString,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
dev = {
|
||||
started: (path: string, options: Record<string, string | number | boolean>) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_dev_started",
|
||||
properties: { ...options, path },
|
||||
});
|
||||
},
|
||||
serverRunning: (path: string, options: DevCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_dev_server_running",
|
||||
properties: { ...options, path },
|
||||
});
|
||||
},
|
||||
tunnelRunning: (path: string, options: DevCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_dev_tunnel_running",
|
||||
properties: { ...options, path },
|
||||
});
|
||||
},
|
||||
connected: (path: string, options: DevCommandOptions) => {
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_dev_connected",
|
||||
properties: { ...options, path },
|
||||
});
|
||||
},
|
||||
failed: (
|
||||
reason: string,
|
||||
options: Record<string, string | number | boolean>,
|
||||
error?: unknown
|
||||
) => {
|
||||
const errorString = error instanceof Error ? error.message : String(error);
|
||||
|
||||
this.#client.capture({
|
||||
distinctId: this.#sessionId,
|
||||
event: "cli_dev_failed",
|
||||
properties: {
|
||||
...options,
|
||||
reason,
|
||||
error: errorString,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
#initProperties(options: InitCommandOptions) {
|
||||
return {
|
||||
version: this.#version,
|
||||
hadApiKey: options.apiKey !== undefined,
|
||||
triggerUrl: options.triggerUrl,
|
||||
endpointSlug: options.endpointSlug,
|
||||
apiUrl: options.apiUrl,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const telemetryClient = new TelemetryClient();
|
||||
@@ -47,6 +47,7 @@ const WhoamiResponseSchema = z.object({
|
||||
createdAt: z.coerce.date(),
|
||||
updatedAt: z.coerce.date(),
|
||||
}),
|
||||
userId: z.string().optional(),
|
||||
});
|
||||
|
||||
export type WhoamiResponse = z.infer<typeof WhoamiResponseSchema>;
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"@types/slug": "^5.0.3",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"@types/ws": "^8.5.3",
|
||||
"encoding": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"tsup": "^6.5.0",
|
||||
"tsx": "^3.12.1",
|
||||
|
||||
Generated
+42
-2
@@ -755,11 +755,13 @@ importers:
|
||||
gradient-string: ^2.0.2
|
||||
inquirer: ^9.1.4
|
||||
localtunnel: ^2.0.2
|
||||
nanoid: ^4.0.2
|
||||
ngrok: 5.0.0-beta.2
|
||||
node-fetch: ^3.3.0
|
||||
openai: ^3.3.0
|
||||
ora: ^6.1.2
|
||||
path-to-regexp: ^6.2.1
|
||||
posthog-node: ^3.1.1
|
||||
rimraf: ^3.0.2
|
||||
simple-git: ^3.19.0
|
||||
terminal-link: ^3.0.0
|
||||
@@ -780,11 +782,13 @@ importers:
|
||||
gradient-string: 2.0.2
|
||||
inquirer: 9.1.4
|
||||
localtunnel: 2.0.2
|
||||
nanoid: 4.0.2
|
||||
ngrok: 5.0.0-beta.2
|
||||
node-fetch: 3.3.0
|
||||
openai: 3.3.0
|
||||
ora: 6.1.2
|
||||
path-to-regexp: 6.2.1
|
||||
posthog-node: 3.1.1
|
||||
simple-git: 3.19.0
|
||||
terminal-link: 3.0.0
|
||||
tsconfck: 2.1.2_typescript@4.9.5
|
||||
@@ -988,6 +992,7 @@ importers:
|
||||
chalk: ^5.2.0
|
||||
cronstrue: ^2.21.0
|
||||
debug: ^4.3.4
|
||||
encoding: ^0.1.13
|
||||
evt: ^2.4.13
|
||||
get-caller-file: ^2.0.5
|
||||
git-remote-origin-url: ^4.0.0
|
||||
@@ -1012,7 +1017,7 @@ importers:
|
||||
get-caller-file: 2.0.5
|
||||
git-remote-origin-url: 4.0.0
|
||||
git-repo-info: 2.1.1
|
||||
node-fetch: 2.6.7
|
||||
node-fetch: 2.6.7_encoding@0.1.13
|
||||
slug: 6.1.0
|
||||
terminal-link: 3.0.0
|
||||
ulid: 2.3.0
|
||||
@@ -1029,6 +1034,7 @@ importers:
|
||||
'@types/slug': 5.0.3
|
||||
'@types/uuid': 9.0.0
|
||||
'@types/ws': 8.5.4
|
||||
encoding: 0.1.13
|
||||
rimraf: 3.0.2
|
||||
tsup: 6.5.0_typescript@4.9.5
|
||||
tsx: 3.12.2
|
||||
@@ -11235,7 +11241,7 @@ packages:
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.59.6
|
||||
eslint-visitor-keys: 3.4.1
|
||||
eslint-visitor-keys: 3.4.2
|
||||
|
||||
/@uiw/codemirror-extensions-basic-setup/4.19.5_wd2tsis3in55bkaiwnc2c46tom:
|
||||
resolution: {integrity: sha512-1zt7ZPJ01xKkSW/KDy0FZNga0bngN1fC594wCVG7FBi60ehfcAucpooQ+JSPScKXopxcb+ugPKZvVLzr9/OfzA==}
|
||||
@@ -13915,6 +13921,11 @@ packages:
|
||||
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
/encoding/0.1.13:
|
||||
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
|
||||
/end-of-stream/1.4.4:
|
||||
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
|
||||
dependencies:
|
||||
@@ -15393,6 +15404,10 @@ packages:
|
||||
resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
/eslint-visitor-keys/3.4.2:
|
||||
resolution: {integrity: sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
/eslint/8.31.0:
|
||||
resolution: {integrity: sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -17134,6 +17149,12 @@ packages:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
/iconv-lite/0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
/icss-utils/5.1.0_postcss@8.4.24:
|
||||
resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
|
||||
engines: {node: ^10 || ^12 || >= 14}
|
||||
@@ -19319,6 +19340,12 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
/nanoid/4.0.2:
|
||||
resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==}
|
||||
engines: {node: ^14 || ^16 || >=18}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/nanomatch/1.2.13:
|
||||
resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -19668,6 +19695,19 @@ packages:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
/node-fetch/2.6.7_encoding@0.1.13:
|
||||
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
dependencies:
|
||||
encoding: 0.1.13
|
||||
whatwg-url: 5.0.0
|
||||
dev: false
|
||||
|
||||
/node-fetch/3.3.0:
|
||||
resolution: {integrity: sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
Reference in New Issue
Block a user