@farm.js/cli 0.1.0-beta.38 → 0.1.0-beta.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/farm.js CHANGED
@@ -539,7 +539,7 @@ telemetryCommand
539
539
 
540
540
  telemetryCommand
541
541
  .command("enable")
542
- .description("Opt in to anonymous Farm.js product telemetry")
542
+ .description("Enable anonymous Farm.js product telemetry")
543
543
  .action(async () => {
544
544
  const { setFarmTelemetryEnabled } = require("../dist/telemetry.js");
545
545
  const status = await setFarmTelemetryEnabled(true);
package/dist/telemetry.js CHANGED
@@ -68,7 +68,7 @@ const DEPLOY_TARGETS = [
68
68
  function defaultConfig() {
69
69
  return {
70
70
  version: TELEMETRY_SCHEMA_VERSION,
71
- enabled: false,
71
+ enabled: true,
72
72
  noticeShown: false
73
73
  };
74
74
  }
@@ -84,15 +84,24 @@ function getFarmTelemetryConfigFile() {
84
84
  async function readConfig() {
85
85
  try {
86
86
  const parsed = JSON.parse(await (0, node_fs_promises.readFile)(getFarmTelemetryConfigFile(), "utf8"));
87
- if (parsed.version !== TELEMETRY_SCHEMA_VERSION) return defaultConfig();
87
+ if (parsed.version !== TELEMETRY_SCHEMA_VERSION) return {
88
+ config: defaultConfig(),
89
+ stored: false
90
+ };
88
91
  return {
89
- version: TELEMETRY_SCHEMA_VERSION,
90
- enabled: parsed.enabled === true,
91
- noticeShown: parsed.noticeShown === true,
92
- anonymousId: isUuid(parsed.anonymousId) ? parsed.anonymousId : void 0
92
+ config: {
93
+ version: TELEMETRY_SCHEMA_VERSION,
94
+ enabled: parsed.enabled === true,
95
+ noticeShown: parsed.noticeShown === true,
96
+ anonymousId: isUuid(parsed.anonymousId) ? parsed.anonymousId : void 0
97
+ },
98
+ stored: true
93
99
  };
94
100
  } catch {
95
- return defaultConfig();
101
+ return {
102
+ config: defaultConfig(),
103
+ stored: false
104
+ };
96
105
  }
97
106
  }
98
107
  async function writeConfig(config) {
@@ -168,10 +177,10 @@ function getEndpoint() {
168
177
  }
169
178
  }
170
179
  async function resolveState() {
171
- const config = await readConfig();
180
+ const { config, stored } = await readConfig();
172
181
  const environment = environmentDecision();
173
182
  const enabled = environment.enabled ?? config.enabled;
174
- const source = environment.enabled !== void 0 ? "environment" : config.enabled ? "configuration" : "default";
183
+ const source = environment.enabled !== void 0 ? "environment" : stored ? "configuration" : "default";
175
184
  if (!enabled) return {
176
185
  config,
177
186
  enabled,
@@ -226,7 +235,7 @@ async function getFarmTelemetryStatus() {
226
235
  };
227
236
  }
228
237
  async function setFarmTelemetryEnabled(enabled) {
229
- const current = await readConfig();
238
+ const { config: current } = await readConfig();
230
239
  await writeConfig({
231
240
  version: TELEMETRY_SCHEMA_VERSION,
232
241
  enabled,
@@ -238,9 +247,9 @@ async function setFarmTelemetryEnabled(enabled) {
238
247
  async function showFarmTelemetryNotice() {
239
248
  if (!isInteractive() || isContinuousIntegration() || process.env.NODE_ENV === "test") return;
240
249
  if (environmentDecision().enabled !== void 0) return;
241
- const config = await readConfig();
250
+ const { config } = await readConfig();
242
251
  if (config.noticeShown) return;
243
- process.stderr.write(`Farm.js anonymous telemetry is off during beta. Run "farm telemetry enable" to opt in.\nLearn more: ${TELEMETRY_NOTICE_URL}\n`);
252
+ process.stderr.write(`Farm.js collects anonymous CLI telemetry by default. Run "farm telemetry disable" to opt out.\nLearn more: ${TELEMETRY_NOTICE_URL}\n`);
244
253
  await writeConfig({
245
254
  ...config,
246
255
  noticeShown: true
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.js","names":["path","os","readFile","randomUUID","mkdir","writeFile","rename","chmod","unlink"],"sources":["../src/telemetry.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { chmod, mkdir, readFile, rename, unlink, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nconst TELEMETRY_SCHEMA_VERSION = 1 as const;\nconst DEFAULT_TELEMETRY_ENDPOINT = \"https://farmjs.dev/api/telemetry/v1/events\";\nconst TELEMETRY_NOTICE_URL = \"https://farmjs.dev/docs/telemetry\";\nconst REQUEST_TIMEOUT_MS = 750;\n\nconst FARM_COMMANDS = [\n \"dev\",\n \"build\",\n \"auth:migrate\",\n \"upgrade\",\n \"generate\",\n \"doctor\",\n \"explain\",\n \"preview\",\n \"migrate\",\n \"cron:list\",\n \"cron:run\",\n \"add:integration\",\n \"deploy\",\n] as const;\n\nconst FARM_TEMPLATES = [\n \"basic\",\n \"react-compiler\",\n \"auth\",\n \"better-auth\",\n \"ai\",\n \"auth0\",\n \"authjs\",\n \"autumn\",\n \"clerk\",\n \"jobs-inngest\",\n \"jobs-trigger\",\n \"polar\",\n \"resend\",\n \"stripe\",\n \"supabase\",\n \"unkey\",\n \"workos\",\n] as const;\n\nconst RENDERERS = [\"react\", \"preact\", \"solid\", \"vue\", \"svelte\"] as const;\nconst PACKAGE_MANAGERS = [\"npm\", \"pnpm\", \"yarn\", \"bun\"] as const;\nconst DEPLOY_TARGETS = [\"vercel\", \"cloudflare\", \"netlify\", \"node\", \"custom\"] as const;\n\nexport type FarmTelemetryCommand = (typeof FARM_COMMANDS)[number];\nexport type FarmTelemetryTemplate = (typeof FARM_TEMPLATES)[number];\nexport type FarmTelemetryRenderer = (typeof RENDERERS)[number];\nexport type FarmTelemetryPackageManager = (typeof PACKAGE_MANAGERS)[number];\nexport type FarmTelemetryDeployTarget = (typeof DEPLOY_TARGETS)[number];\n\ninterface FarmTelemetryConfig {\n version: 1;\n enabled: boolean;\n noticeShown: boolean;\n anonymousId?: string;\n}\n\ninterface FarmTelemetryEventBase {\n schemaVersion: typeof TELEMETRY_SCHEMA_VERSION;\n eventId: string;\n anonymousId: string;\n source: \"cli\" | \"create-app\";\n packageName: \"@farm.js/cli\" | \"@farm.js/create-app\";\n packageVersion: string;\n nodeMajor: number;\n platform: \"darwin\" | \"linux\" | \"windows\" | \"other\";\n architecture: \"arm64\" | \"x64\" | \"other\";\n}\n\nexport interface FarmCommandTelemetryInput {\n command: FarmTelemetryCommand;\n packageVersion: string;\n deployTarget?: string;\n}\n\nexport interface FarmProjectCreatedTelemetryInput {\n packageVersion: string;\n template?: string;\n renderer?: string;\n packageManager?: string;\n typescript?: boolean;\n installedDependencies?: boolean;\n}\n\nexport interface FarmTelemetryStatus {\n enabled: boolean;\n active: boolean;\n source: \"configuration\" | \"environment\" | \"default\";\n endpoint: string;\n configFile: string;\n anonymousId?: string;\n reason?: string;\n}\n\ntype FarmTelemetryEvent =\n | (FarmTelemetryEventBase & {\n eventType: \"command_invoked\";\n command: FarmTelemetryCommand;\n deployTarget?: FarmTelemetryDeployTarget;\n })\n | (FarmTelemetryEventBase & {\n eventType: \"project_created\";\n template?: FarmTelemetryTemplate;\n renderer?: FarmTelemetryRenderer;\n packageManager?: FarmTelemetryPackageManager;\n typescript?: boolean;\n installedDependencies?: boolean;\n });\n\ntype FarmTelemetryGeneratedFields = Pick<\n FarmTelemetryEventBase,\n \"schemaVersion\" | \"eventId\" | \"anonymousId\" | \"nodeMajor\" | \"platform\" | \"architecture\"\n>;\ntype FarmTelemetryEventInput<T = FarmTelemetryEvent> = T extends FarmTelemetryEvent\n ? Omit<T, keyof FarmTelemetryGeneratedFields>\n : never;\n\nfunction defaultConfig(): FarmTelemetryConfig {\n return {\n version: TELEMETRY_SCHEMA_VERSION,\n enabled: false,\n noticeShown: false,\n };\n}\n\nfunction configDirectory(): string {\n if (process.env.FARM_TELEMETRY_CONFIG_DIR) {\n return path.resolve(process.env.FARM_TELEMETRY_CONFIG_DIR);\n }\n if (process.platform === \"win32\") {\n return path.join(\n process.env.APPDATA || path.join(os.homedir(), \"AppData\", \"Roaming\"),\n \"farmjs\",\n );\n }\n if (process.platform === \"darwin\") {\n return path.join(os.homedir(), \"Library\", \"Application Support\", \"farmjs\");\n }\n return path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), \".config\"), \"farmjs\");\n}\n\nexport function getFarmTelemetryConfigFile(): string {\n return path.join(configDirectory(), \"telemetry.json\");\n}\n\nasync function readConfig(): Promise<FarmTelemetryConfig> {\n try {\n const parsed = JSON.parse(\n await readFile(getFarmTelemetryConfigFile(), \"utf8\"),\n ) as Partial<FarmTelemetryConfig>;\n if (parsed.version !== TELEMETRY_SCHEMA_VERSION) return defaultConfig();\n return {\n version: TELEMETRY_SCHEMA_VERSION,\n enabled: parsed.enabled === true,\n noticeShown: parsed.noticeShown === true,\n anonymousId: isUuid(parsed.anonymousId) ? parsed.anonymousId : undefined,\n };\n } catch {\n return defaultConfig();\n }\n}\n\nasync function writeConfig(config: FarmTelemetryConfig): Promise<void> {\n const file = getFarmTelemetryConfigFile();\n const directory = path.dirname(file);\n const temporaryFile = `${file}.${process.pid}.${randomUUID()}.tmp`;\n try {\n await mkdir(directory, { recursive: true, mode: 0o700 });\n await writeFile(temporaryFile, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n await rename(temporaryFile, file);\n await chmod(file, 0o600).catch(() => undefined);\n } catch {\n await unlink(temporaryFile).catch(() => undefined);\n // Telemetry is best-effort and must never make a Farm command fail.\n }\n}\n\nfunction isUuid(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)\n );\n}\n\nfunction isTrue(value: string | undefined): boolean {\n return value !== undefined && [\"1\", \"true\", \"yes\", \"on\"].includes(value.toLowerCase());\n}\n\nfunction isFalse(value: string | undefined): boolean {\n return value !== undefined && [\"0\", \"false\", \"no\", \"off\"].includes(value.toLowerCase());\n}\n\nfunction environmentDecision(): { enabled?: boolean; reason?: string } {\n if (process.env.DO_NOT_TRACK !== undefined && !isFalse(process.env.DO_NOT_TRACK)) {\n return { enabled: false, reason: \"DO_NOT_TRACK is set\" };\n }\n if (isTrue(process.env.FARM_TELEMETRY_DISABLED)) {\n return { enabled: false, reason: \"FARM_TELEMETRY_DISABLED is set\" };\n }\n if (isTrue(process.env.FARM_TELEMETRY)) return { enabled: true };\n if (isFalse(process.env.FARM_TELEMETRY)) {\n return { enabled: false, reason: \"FARM_TELEMETRY disables collection\" };\n }\n return {};\n}\n\nfunction isContinuousIntegration(): boolean {\n return (\n isTrue(process.env.CI) ||\n isTrue(process.env.GITHUB_ACTIONS) ||\n isTrue(process.env.BUILDKITE) ||\n isTrue(process.env.CIRCLECI)\n );\n}\n\nfunction isInteractive(): boolean {\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n}\n\nfunction getEndpoint(): string {\n const candidate = process.env.FARM_TELEMETRY_ENDPOINT || DEFAULT_TELEMETRY_ENDPOINT;\n try {\n const url = new URL(candidate);\n const isLocal = [\"localhost\", \"127.0.0.1\", \"::1\"].includes(url.hostname);\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && isLocal)) {\n return DEFAULT_TELEMETRY_ENDPOINT;\n }\n return url.toString();\n } catch {\n return DEFAULT_TELEMETRY_ENDPOINT;\n }\n}\n\nasync function resolveState(): Promise<{\n config: FarmTelemetryConfig;\n enabled: boolean;\n active: boolean;\n source: FarmTelemetryStatus[\"source\"];\n reason?: string;\n}> {\n const config = await readConfig();\n const environment = environmentDecision();\n const enabled = environment.enabled ?? config.enabled;\n const source =\n environment.enabled !== undefined\n ? \"environment\"\n : config.enabled\n ? \"configuration\"\n : \"default\";\n\n if (!enabled) return { config, enabled, active: false, source, reason: environment.reason };\n if (environment.enabled === true) return { config, enabled, active: true, source };\n if (process.env.NODE_ENV === \"test\") {\n return { config, enabled, active: false, source, reason: \"test environments are skipped\" };\n }\n if (isContinuousIntegration()) {\n return { config, enabled, active: false, source, reason: \"CI environments are skipped\" };\n }\n if (!isInteractive()) {\n return {\n config,\n enabled,\n active: false,\n source,\n reason: \"non-interactive commands are skipped\",\n };\n }\n return { config, enabled, active: true, source };\n}\n\nexport async function getFarmTelemetryStatus(): Promise<FarmTelemetryStatus> {\n const state = await resolveState();\n return {\n enabled: state.enabled,\n active: state.active,\n source: state.source,\n endpoint: getEndpoint(),\n configFile: getFarmTelemetryConfigFile(),\n anonymousId: state.config.anonymousId,\n reason: state.reason,\n };\n}\n\nexport async function setFarmTelemetryEnabled(enabled: boolean): Promise<FarmTelemetryStatus> {\n const current = await readConfig();\n await writeConfig({\n version: TELEMETRY_SCHEMA_VERSION,\n enabled,\n noticeShown: true,\n anonymousId: enabled ? current.anonymousId || randomUUID() : undefined,\n });\n return getFarmTelemetryStatus();\n}\n\nexport async function showFarmTelemetryNotice(): Promise<void> {\n if (!isInteractive() || isContinuousIntegration() || process.env.NODE_ENV === \"test\") return;\n if (environmentDecision().enabled !== undefined) return;\n const config = await readConfig();\n if (config.noticeShown) return;\n process.stderr.write(\n `Farm.js anonymous telemetry is off during beta. Run \"farm telemetry enable\" to opt in.\\nLearn more: ${TELEMETRY_NOTICE_URL}\\n`,\n );\n await writeConfig({ ...config, noticeShown: true });\n}\n\nexport function resolveFarmTelemetryCommand(value: string): FarmTelemetryCommand | undefined {\n return (FARM_COMMANDS as readonly string[]).includes(value)\n ? (value as FarmTelemetryCommand)\n : undefined;\n}\n\nexport async function trackFarmCommand(input: FarmCommandTelemetryInput): Promise<void> {\n const deployTarget = allowlisted(input.deployTarget, DEPLOY_TARGETS);\n await track({\n eventType: \"command_invoked\",\n source: \"cli\",\n packageName: \"@farm.js/cli\",\n packageVersion: sanitizeVersion(input.packageVersion),\n command: input.command,\n ...(deployTarget ? { deployTarget } : {}),\n });\n}\n\nexport async function trackFarmProjectCreated(\n input: FarmProjectCreatedTelemetryInput,\n): Promise<void> {\n const template = allowlisted(input.template, FARM_TEMPLATES);\n const renderer = allowlisted(input.renderer, RENDERERS);\n const packageManager = allowlisted(input.packageManager, PACKAGE_MANAGERS);\n await track({\n eventType: \"project_created\",\n source: \"create-app\",\n packageName: \"@farm.js/create-app\",\n packageVersion: sanitizeVersion(input.packageVersion),\n ...(template ? { template } : {}),\n ...(renderer ? { renderer } : {}),\n ...(packageManager ? { packageManager } : {}),\n ...(typeof input.typescript === \"boolean\" ? { typescript: input.typescript } : {}),\n ...(typeof input.installedDependencies === \"boolean\"\n ? { installedDependencies: input.installedDependencies }\n : {}),\n });\n}\n\nasync function track(event: FarmTelemetryEventInput): Promise<void> {\n try {\n const state = await resolveState();\n if (!state.active) return;\n const anonymousId = state.config.anonymousId || randomUUID();\n if (!state.config.anonymousId) {\n await writeConfig({ ...state.config, anonymousId });\n }\n const payload = {\n schemaVersion: TELEMETRY_SCHEMA_VERSION,\n eventId: randomUUID(),\n anonymousId,\n nodeMajor: Number.parseInt(process.versions.node.split(\".\")[0] || \"0\", 10),\n platform: normalizePlatform(process.platform),\n architecture: normalizeArchitecture(process.arch),\n ...event,\n } as FarmTelemetryEvent;\n await send(payload);\n } catch {\n // Telemetry is best-effort and must never make a Farm command fail.\n }\n}\n\nasync function send(payload: FarmTelemetryEvent): Promise<void> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n timeout.unref?.();\n try {\n await fetch(getEndpoint(), {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: controller.signal,\n keepalive: true,\n });\n } catch {\n // Network and endpoint failures are intentionally ignored.\n } finally {\n clearTimeout(timeout);\n }\n}\n\nfunction sanitizeVersion(value: string): string {\n return /^[0-9A-Za-z.+_-]{1,64}$/.test(value) ? value : \"unknown\";\n}\n\nfunction allowlisted<const T extends readonly string[]>(\n value: string | undefined,\n values: T,\n): T[number] | undefined {\n return value && (values as readonly string[]).includes(value) ? (value as T[number]) : undefined;\n}\n\nfunction normalizePlatform(value: NodeJS.Platform): FarmTelemetryEventBase[\"platform\"] {\n if (value === \"darwin\" || value === \"linux\") return value;\n if (value === \"win32\") return \"windows\";\n return \"other\";\n}\n\nfunction normalizeArchitecture(value: string): FarmTelemetryEventBase[\"architecture\"] {\n return value === \"arm64\" || value === \"x64\" ? value : \"other\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"telemetry.js","names":["path","os","readFile","randomUUID","mkdir","writeFile","rename","chmod","unlink"],"sources":["../src/telemetry.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { chmod, mkdir, readFile, rename, unlink, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nconst TELEMETRY_SCHEMA_VERSION = 1 as const;\nconst DEFAULT_TELEMETRY_ENDPOINT = \"https://farmjs.dev/api/telemetry/v1/events\";\nconst TELEMETRY_NOTICE_URL = \"https://farmjs.dev/docs/telemetry\";\nconst REQUEST_TIMEOUT_MS = 750;\n\nconst FARM_COMMANDS = [\n \"dev\",\n \"build\",\n \"auth:migrate\",\n \"upgrade\",\n \"generate\",\n \"doctor\",\n \"explain\",\n \"preview\",\n \"migrate\",\n \"cron:list\",\n \"cron:run\",\n \"add:integration\",\n \"deploy\",\n] as const;\n\nconst FARM_TEMPLATES = [\n \"basic\",\n \"react-compiler\",\n \"auth\",\n \"better-auth\",\n \"ai\",\n \"auth0\",\n \"authjs\",\n \"autumn\",\n \"clerk\",\n \"jobs-inngest\",\n \"jobs-trigger\",\n \"polar\",\n \"resend\",\n \"stripe\",\n \"supabase\",\n \"unkey\",\n \"workos\",\n] as const;\n\nconst RENDERERS = [\"react\", \"preact\", \"solid\", \"vue\", \"svelte\"] as const;\nconst PACKAGE_MANAGERS = [\"npm\", \"pnpm\", \"yarn\", \"bun\"] as const;\nconst DEPLOY_TARGETS = [\"vercel\", \"cloudflare\", \"netlify\", \"node\", \"custom\"] as const;\n\nexport type FarmTelemetryCommand = (typeof FARM_COMMANDS)[number];\nexport type FarmTelemetryTemplate = (typeof FARM_TEMPLATES)[number];\nexport type FarmTelemetryRenderer = (typeof RENDERERS)[number];\nexport type FarmTelemetryPackageManager = (typeof PACKAGE_MANAGERS)[number];\nexport type FarmTelemetryDeployTarget = (typeof DEPLOY_TARGETS)[number];\n\ninterface FarmTelemetryConfig {\n version: 1;\n enabled: boolean;\n noticeShown: boolean;\n anonymousId?: string;\n}\n\ninterface FarmTelemetryConfigState {\n config: FarmTelemetryConfig;\n stored: boolean;\n}\n\ninterface FarmTelemetryEventBase {\n schemaVersion: typeof TELEMETRY_SCHEMA_VERSION;\n eventId: string;\n anonymousId: string;\n source: \"cli\" | \"create-app\";\n packageName: \"@farm.js/cli\" | \"@farm.js/create-app\";\n packageVersion: string;\n nodeMajor: number;\n platform: \"darwin\" | \"linux\" | \"windows\" | \"other\";\n architecture: \"arm64\" | \"x64\" | \"other\";\n}\n\nexport interface FarmCommandTelemetryInput {\n command: FarmTelemetryCommand;\n packageVersion: string;\n deployTarget?: string;\n}\n\nexport interface FarmProjectCreatedTelemetryInput {\n packageVersion: string;\n template?: string;\n renderer?: string;\n packageManager?: string;\n typescript?: boolean;\n installedDependencies?: boolean;\n}\n\nexport interface FarmTelemetryStatus {\n enabled: boolean;\n active: boolean;\n source: \"configuration\" | \"environment\" | \"default\";\n endpoint: string;\n configFile: string;\n anonymousId?: string;\n reason?: string;\n}\n\ntype FarmTelemetryEvent =\n | (FarmTelemetryEventBase & {\n eventType: \"command_invoked\";\n command: FarmTelemetryCommand;\n deployTarget?: FarmTelemetryDeployTarget;\n })\n | (FarmTelemetryEventBase & {\n eventType: \"project_created\";\n template?: FarmTelemetryTemplate;\n renderer?: FarmTelemetryRenderer;\n packageManager?: FarmTelemetryPackageManager;\n typescript?: boolean;\n installedDependencies?: boolean;\n });\n\ntype FarmTelemetryGeneratedFields = Pick<\n FarmTelemetryEventBase,\n \"schemaVersion\" | \"eventId\" | \"anonymousId\" | \"nodeMajor\" | \"platform\" | \"architecture\"\n>;\ntype FarmTelemetryEventInput<T = FarmTelemetryEvent> = T extends FarmTelemetryEvent\n ? Omit<T, keyof FarmTelemetryGeneratedFields>\n : never;\n\nfunction defaultConfig(): FarmTelemetryConfig {\n return {\n version: TELEMETRY_SCHEMA_VERSION,\n enabled: true,\n noticeShown: false,\n };\n}\n\nfunction configDirectory(): string {\n if (process.env.FARM_TELEMETRY_CONFIG_DIR) {\n return path.resolve(process.env.FARM_TELEMETRY_CONFIG_DIR);\n }\n if (process.platform === \"win32\") {\n return path.join(\n process.env.APPDATA || path.join(os.homedir(), \"AppData\", \"Roaming\"),\n \"farmjs\",\n );\n }\n if (process.platform === \"darwin\") {\n return path.join(os.homedir(), \"Library\", \"Application Support\", \"farmjs\");\n }\n return path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), \".config\"), \"farmjs\");\n}\n\nexport function getFarmTelemetryConfigFile(): string {\n return path.join(configDirectory(), \"telemetry.json\");\n}\n\nasync function readConfig(): Promise<FarmTelemetryConfigState> {\n try {\n const parsed = JSON.parse(\n await readFile(getFarmTelemetryConfigFile(), \"utf8\"),\n ) as Partial<FarmTelemetryConfig>;\n if (parsed.version !== TELEMETRY_SCHEMA_VERSION) {\n return { config: defaultConfig(), stored: false };\n }\n return {\n config: {\n version: TELEMETRY_SCHEMA_VERSION,\n enabled: parsed.enabled === true,\n noticeShown: parsed.noticeShown === true,\n anonymousId: isUuid(parsed.anonymousId) ? parsed.anonymousId : undefined,\n },\n stored: true,\n };\n } catch {\n return { config: defaultConfig(), stored: false };\n }\n}\n\nasync function writeConfig(config: FarmTelemetryConfig): Promise<void> {\n const file = getFarmTelemetryConfigFile();\n const directory = path.dirname(file);\n const temporaryFile = `${file}.${process.pid}.${randomUUID()}.tmp`;\n try {\n await mkdir(directory, { recursive: true, mode: 0o700 });\n await writeFile(temporaryFile, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n await rename(temporaryFile, file);\n await chmod(file, 0o600).catch(() => undefined);\n } catch {\n await unlink(temporaryFile).catch(() => undefined);\n // Telemetry is best-effort and must never make a Farm command fail.\n }\n}\n\nfunction isUuid(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)\n );\n}\n\nfunction isTrue(value: string | undefined): boolean {\n return value !== undefined && [\"1\", \"true\", \"yes\", \"on\"].includes(value.toLowerCase());\n}\n\nfunction isFalse(value: string | undefined): boolean {\n return value !== undefined && [\"0\", \"false\", \"no\", \"off\"].includes(value.toLowerCase());\n}\n\nfunction environmentDecision(): { enabled?: boolean; reason?: string } {\n if (process.env.DO_NOT_TRACK !== undefined && !isFalse(process.env.DO_NOT_TRACK)) {\n return { enabled: false, reason: \"DO_NOT_TRACK is set\" };\n }\n if (isTrue(process.env.FARM_TELEMETRY_DISABLED)) {\n return { enabled: false, reason: \"FARM_TELEMETRY_DISABLED is set\" };\n }\n if (isTrue(process.env.FARM_TELEMETRY)) return { enabled: true };\n if (isFalse(process.env.FARM_TELEMETRY)) {\n return { enabled: false, reason: \"FARM_TELEMETRY disables collection\" };\n }\n return {};\n}\n\nfunction isContinuousIntegration(): boolean {\n return (\n isTrue(process.env.CI) ||\n isTrue(process.env.GITHUB_ACTIONS) ||\n isTrue(process.env.BUILDKITE) ||\n isTrue(process.env.CIRCLECI)\n );\n}\n\nfunction isInteractive(): boolean {\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n}\n\nfunction getEndpoint(): string {\n const candidate = process.env.FARM_TELEMETRY_ENDPOINT || DEFAULT_TELEMETRY_ENDPOINT;\n try {\n const url = new URL(candidate);\n const isLocal = [\"localhost\", \"127.0.0.1\", \"::1\"].includes(url.hostname);\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && isLocal)) {\n return DEFAULT_TELEMETRY_ENDPOINT;\n }\n return url.toString();\n } catch {\n return DEFAULT_TELEMETRY_ENDPOINT;\n }\n}\n\nasync function resolveState(): Promise<{\n config: FarmTelemetryConfig;\n enabled: boolean;\n active: boolean;\n source: FarmTelemetryStatus[\"source\"];\n reason?: string;\n}> {\n const { config, stored } = await readConfig();\n const environment = environmentDecision();\n const enabled = environment.enabled ?? config.enabled;\n const source =\n environment.enabled !== undefined ? \"environment\" : stored ? \"configuration\" : \"default\";\n\n if (!enabled) return { config, enabled, active: false, source, reason: environment.reason };\n if (environment.enabled === true) return { config, enabled, active: true, source };\n if (process.env.NODE_ENV === \"test\") {\n return { config, enabled, active: false, source, reason: \"test environments are skipped\" };\n }\n if (isContinuousIntegration()) {\n return { config, enabled, active: false, source, reason: \"CI environments are skipped\" };\n }\n if (!isInteractive()) {\n return {\n config,\n enabled,\n active: false,\n source,\n reason: \"non-interactive commands are skipped\",\n };\n }\n return { config, enabled, active: true, source };\n}\n\nexport async function getFarmTelemetryStatus(): Promise<FarmTelemetryStatus> {\n const state = await resolveState();\n return {\n enabled: state.enabled,\n active: state.active,\n source: state.source,\n endpoint: getEndpoint(),\n configFile: getFarmTelemetryConfigFile(),\n anonymousId: state.config.anonymousId,\n reason: state.reason,\n };\n}\n\nexport async function setFarmTelemetryEnabled(enabled: boolean): Promise<FarmTelemetryStatus> {\n const { config: current } = await readConfig();\n await writeConfig({\n version: TELEMETRY_SCHEMA_VERSION,\n enabled,\n noticeShown: true,\n anonymousId: enabled ? current.anonymousId || randomUUID() : undefined,\n });\n return getFarmTelemetryStatus();\n}\n\nexport async function showFarmTelemetryNotice(): Promise<void> {\n if (!isInteractive() || isContinuousIntegration() || process.env.NODE_ENV === \"test\") return;\n if (environmentDecision().enabled !== undefined) return;\n const { config } = await readConfig();\n if (config.noticeShown) return;\n process.stderr.write(\n `Farm.js collects anonymous CLI telemetry by default. Run \"farm telemetry disable\" to opt out.\\nLearn more: ${TELEMETRY_NOTICE_URL}\\n`,\n );\n await writeConfig({ ...config, noticeShown: true });\n}\n\nexport function resolveFarmTelemetryCommand(value: string): FarmTelemetryCommand | undefined {\n return (FARM_COMMANDS as readonly string[]).includes(value)\n ? (value as FarmTelemetryCommand)\n : undefined;\n}\n\nexport async function trackFarmCommand(input: FarmCommandTelemetryInput): Promise<void> {\n const deployTarget = allowlisted(input.deployTarget, DEPLOY_TARGETS);\n await track({\n eventType: \"command_invoked\",\n source: \"cli\",\n packageName: \"@farm.js/cli\",\n packageVersion: sanitizeVersion(input.packageVersion),\n command: input.command,\n ...(deployTarget ? { deployTarget } : {}),\n });\n}\n\nexport async function trackFarmProjectCreated(\n input: FarmProjectCreatedTelemetryInput,\n): Promise<void> {\n const template = allowlisted(input.template, FARM_TEMPLATES);\n const renderer = allowlisted(input.renderer, RENDERERS);\n const packageManager = allowlisted(input.packageManager, PACKAGE_MANAGERS);\n await track({\n eventType: \"project_created\",\n source: \"create-app\",\n packageName: \"@farm.js/create-app\",\n packageVersion: sanitizeVersion(input.packageVersion),\n ...(template ? { template } : {}),\n ...(renderer ? { renderer } : {}),\n ...(packageManager ? { packageManager } : {}),\n ...(typeof input.typescript === \"boolean\" ? { typescript: input.typescript } : {}),\n ...(typeof input.installedDependencies === \"boolean\"\n ? { installedDependencies: input.installedDependencies }\n : {}),\n });\n}\n\nasync function track(event: FarmTelemetryEventInput): Promise<void> {\n try {\n const state = await resolveState();\n if (!state.active) return;\n const anonymousId = state.config.anonymousId || randomUUID();\n if (!state.config.anonymousId) {\n await writeConfig({ ...state.config, anonymousId });\n }\n const payload = {\n schemaVersion: TELEMETRY_SCHEMA_VERSION,\n eventId: randomUUID(),\n anonymousId,\n nodeMajor: Number.parseInt(process.versions.node.split(\".\")[0] || \"0\", 10),\n platform: normalizePlatform(process.platform),\n architecture: normalizeArchitecture(process.arch),\n ...event,\n } as FarmTelemetryEvent;\n await send(payload);\n } catch {\n // Telemetry is best-effort and must never make a Farm command fail.\n }\n}\n\nasync function send(payload: FarmTelemetryEvent): Promise<void> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n timeout.unref?.();\n try {\n await fetch(getEndpoint(), {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: controller.signal,\n keepalive: true,\n });\n } catch {\n // Network and endpoint failures are intentionally ignored.\n } finally {\n clearTimeout(timeout);\n }\n}\n\nfunction sanitizeVersion(value: string): string {\n return /^[0-9A-Za-z.+_-]{1,64}$/.test(value) ? value : \"unknown\";\n}\n\nfunction allowlisted<const T extends readonly string[]>(\n value: string | undefined,\n values: T,\n): T[number] | undefined {\n return value && (values as readonly string[]).includes(value) ? (value as T[number]) : undefined;\n}\n\nfunction normalizePlatform(value: NodeJS.Platform): FarmTelemetryEventBase[\"platform\"] {\n if (value === \"darwin\" || value === \"linux\") return value;\n if (value === \"win32\") return \"windows\";\n return \"other\";\n}\n\nfunction normalizeArchitecture(value: string): FarmTelemetryEventBase[\"architecture\"] {\n return value === \"arm64\" || value === \"x64\" ? value : \"other\";\n}\n"],"mappings":";;;;;;;;;AAKA,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AACnC,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB;AAE3B,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,YAAY;CAAC;CAAS;CAAU;CAAS;CAAO;AAAQ;AAC9D,MAAM,mBAAmB;CAAC;CAAO;CAAQ;CAAQ;AAAK;AACtD,MAAM,iBAAiB;CAAC;CAAU;CAAc;CAAW;CAAQ;AAAQ;AAgF3E,SAAS,gBAAqC;CAC5C,OAAO;EACL,SAAS;EACT,SAAS;EACT,aAAa;CACf;AACF;AAEA,SAAS,kBAA0B;CACjC,IAAI,QAAQ,IAAI,2BACd,OAAOA,UAAAA,QAAK,QAAQ,QAAQ,IAAI,yBAAyB;CAE3D,IAAI,QAAQ,aAAa,SACvB,OAAOA,UAAAA,QAAK,KACV,QAAQ,IAAI,WAAWA,UAAAA,QAAK,KAAKC,QAAAA,QAAG,QAAQ,GAAG,WAAW,SAAS,GACnE,QACF;CAEF,IAAI,QAAQ,aAAa,UACvB,OAAOD,UAAAA,QAAK,KAAKC,QAAAA,QAAG,QAAQ,GAAG,WAAW,uBAAuB,QAAQ;CAE3E,OAAOD,UAAAA,QAAK,KAAK,QAAQ,IAAI,mBAAmBA,UAAAA,QAAK,KAAKC,QAAAA,QAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ;AAC9F;AAEA,SAAgB,6BAAqC;CACnD,OAAOD,UAAAA,QAAK,KAAK,gBAAgB,GAAG,gBAAgB;AACtD;AAEA,eAAe,aAAgD;CAC7D,IAAI;EACF,MAAM,SAAS,KAAK,MAClB,OAAA,GAAME,iBAAAA,SAAAA,CAAS,2BAA2B,GAAG,MAAM,CACrD;EACA,IAAI,OAAO,YAAY,0BACrB,OAAO;GAAE,QAAQ,cAAc;GAAG,QAAQ;EAAM;EAElD,OAAO;GACL,QAAQ;IACN,SAAS;IACT,SAAS,OAAO,YAAY;IAC5B,aAAa,OAAO,gBAAgB;IACpC,aAAa,OAAO,OAAO,WAAW,IAAI,OAAO,cAAc,KAAA;GACjE;GACA,QAAQ;EACV;CACF,QAAQ;EACN,OAAO;GAAE,QAAQ,cAAc;GAAG,QAAQ;EAAM;CAClD;AACF;AAEA,eAAe,YAAY,QAA4C;CACrE,MAAM,OAAO,2BAA2B;CACxC,MAAM,YAAYF,UAAAA,QAAK,QAAQ,IAAI;CACnC,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,IAAI,IAAA,GAAGG,YAAAA,WAAAA,CAAW,EAAE;CAC7D,IAAI;EACF,OAAA,GAAMC,iBAAAA,MAAAA,CAAM,WAAW;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;EACvD,OAAA,GAAMC,iBAAAA,UAAAA,CAAU,eAAe,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;EACtF,OAAA,GAAMC,iBAAAA,OAAAA,CAAO,eAAe,IAAI;EAChC,OAAA,GAAMC,iBAAAA,MAAAA,CAAM,MAAM,GAAK,CAAC,CAAC,YAAY,KAAA,CAAS;CAChD,QAAQ;EACN,OAAA,GAAMC,iBAAAA,OAAAA,CAAO,aAAa,CAAC,CAAC,YAAY,KAAA,CAAS;CAEnD;AACF;AAEA,SAAS,OAAO,OAAiC;CAC/C,OACE,OAAO,UAAU,YACjB,6EAA6E,KAAK,KAAK;AAE3F;AAEA,SAAS,OAAO,OAAoC;CAClD,OAAO,UAAU,KAAA,KAAa;EAAC;EAAK;EAAQ;EAAO;CAAI,CAAC,CAAC,SAAS,MAAM,YAAY,CAAC;AACvF;AAEA,SAAS,QAAQ,OAAoC;CACnD,OAAO,UAAU,KAAA,KAAa;EAAC;EAAK;EAAS;EAAM;CAAK,CAAC,CAAC,SAAS,MAAM,YAAY,CAAC;AACxF;AAEA,SAAS,sBAA8D;CACrE,IAAI,QAAQ,IAAI,iBAAiB,KAAA,KAAa,CAAC,QAAQ,QAAQ,IAAI,YAAY,GAC7E,OAAO;EAAE,SAAS;EAAO,QAAQ;CAAsB;CAEzD,IAAI,OAAO,QAAQ,IAAI,uBAAuB,GAC5C,OAAO;EAAE,SAAS;EAAO,QAAQ;CAAiC;CAEpE,IAAI,OAAO,QAAQ,IAAI,cAAc,GAAG,OAAO,EAAE,SAAS,KAAK;CAC/D,IAAI,QAAQ,QAAQ,IAAI,cAAc,GACpC,OAAO;EAAE,SAAS;EAAO,QAAQ;CAAqC;CAExE,OAAO,CAAC;AACV;AAEA,SAAS,0BAAmC;CAC1C,OACE,OAAO,QAAQ,IAAI,EAAE,KACrB,OAAO,QAAQ,IAAI,cAAc,KACjC,OAAO,QAAQ,IAAI,SAAS,KAC5B,OAAO,QAAQ,IAAI,QAAQ;AAE/B;AAEA,SAAS,gBAAyB;CAChC,OAAO,QAAQ,MAAM,UAAU,QAAQ,QAAQ,OAAO,UAAU;AAClE;AAEA,SAAS,cAAsB;CAC7B,MAAM,YAAY,QAAQ,IAAI,2BAA2B;CACzD,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,SAAS;EAC7B,MAAM,UAAU;GAAC;GAAa;GAAa;EAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;EACvE,IAAI,IAAI,aAAa,YAAY,EAAE,IAAI,aAAa,WAAW,UAC7D,OAAO;EAET,OAAO,IAAI,SAAS;CACtB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAe,eAMZ;CACD,MAAM,EAAE,QAAQ,WAAW,MAAM,WAAW;CAC5C,MAAM,cAAc,oBAAoB;CACxC,MAAM,UAAU,YAAY,WAAW,OAAO;CAC9C,MAAM,SACJ,YAAY,YAAY,KAAA,IAAY,gBAAgB,SAAS,kBAAkB;CAEjF,IAAI,CAAC,SAAS,OAAO;EAAE;EAAQ;EAAS,QAAQ;EAAO;EAAQ,QAAQ,YAAY;CAAO;CAC1F,IAAI,YAAY,YAAY,MAAM,OAAO;EAAE;EAAQ;EAAS,QAAQ;EAAM;CAAO;CACjF,IAAI,QAAQ,IAAI,aAAa,QAC3B,OAAO;EAAE;EAAQ;EAAS,QAAQ;EAAO;EAAQ,QAAQ;CAAgC;CAE3F,IAAI,wBAAwB,GAC1B,OAAO;EAAE;EAAQ;EAAS,QAAQ;EAAO;EAAQ,QAAQ;CAA8B;CAEzF,IAAI,CAAC,cAAc,GACjB,OAAO;EACL;EACA;EACA,QAAQ;EACR;EACA,QAAQ;CACV;CAEF,OAAO;EAAE;EAAQ;EAAS,QAAQ;EAAM;CAAO;AACjD;AAEA,eAAsB,yBAAuD;CAC3E,MAAM,QAAQ,MAAM,aAAa;CACjC,OAAO;EACL,SAAS,MAAM;EACf,QAAQ,MAAM;EACd,QAAQ,MAAM;EACd,UAAU,YAAY;EACtB,YAAY,2BAA2B;EACvC,aAAa,MAAM,OAAO;EAC1B,QAAQ,MAAM;CAChB;AACF;AAEA,eAAsB,wBAAwB,SAAgD;CAC5F,MAAM,EAAE,QAAQ,YAAY,MAAM,WAAW;CAC7C,MAAM,YAAY;EAChB,SAAS;EACT;EACA,aAAa;EACb,aAAa,UAAU,QAAQ,gBAAA,GAAeL,YAAAA,WAAAA,CAAW,IAAI,KAAA;CAC/D,CAAC;CACD,OAAO,uBAAuB;AAChC;AAEA,eAAsB,0BAAyC;CAC7D,IAAI,CAAC,cAAc,KAAK,wBAAwB,KAAK,QAAQ,IAAI,aAAa,QAAQ;CACtF,IAAI,oBAAoB,CAAC,CAAC,YAAY,KAAA,GAAW;CACjD,MAAM,EAAE,WAAW,MAAM,WAAW;CACpC,IAAI,OAAO,aAAa;CACxB,QAAQ,OAAO,MACb,8GAA8G,qBAAqB,GACrI;CACA,MAAM,YAAY;EAAE,GAAG;EAAQ,aAAa;CAAK,CAAC;AACpD;AAEA,SAAgB,4BAA4B,OAAiD;CAC3F,OAAQ,cAAoC,SAAS,KAAK,IACrD,QACD,KAAA;AACN;AAEA,eAAsB,iBAAiB,OAAiD;CACtF,MAAM,eAAe,YAAY,MAAM,cAAc,cAAc;CACnE,MAAM,MAAM;EACV,WAAW;EACX,QAAQ;EACR,aAAa;EACb,gBAAgB,gBAAgB,MAAM,cAAc;EACpD,SAAS,MAAM;EACf,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;CACzC,CAAC;AACH;AAEA,eAAsB,wBACpB,OACe;CACf,MAAM,WAAW,YAAY,MAAM,UAAU,cAAc;CAC3D,MAAM,WAAW,YAAY,MAAM,UAAU,SAAS;CACtD,MAAM,iBAAiB,YAAY,MAAM,gBAAgB,gBAAgB;CACzE,MAAM,MAAM;EACV,WAAW;EACX,QAAQ;EACR,aAAa;EACb,gBAAgB,gBAAgB,MAAM,cAAc;EACpD,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EAC/B,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EAC/B,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;EAC3C,GAAI,OAAO,MAAM,eAAe,YAAY,EAAE,YAAY,MAAM,WAAW,IAAI,CAAC;EAChF,GAAI,OAAO,MAAM,0BAA0B,YACvC,EAAE,uBAAuB,MAAM,sBAAsB,IACrD,CAAC;CACP,CAAC;AACH;AAEA,eAAe,MAAM,OAA+C;CAClE,IAAI;EACF,MAAM,QAAQ,MAAM,aAAa;EACjC,IAAI,CAAC,MAAM,QAAQ;EACnB,MAAM,cAAc,MAAM,OAAO,gBAAA,GAAeA,YAAAA,WAAAA,CAAW;EAC3D,IAAI,CAAC,MAAM,OAAO,aAChB,MAAM,YAAY;GAAE,GAAG,MAAM;GAAQ;EAAY,CAAC;EAWpD,MAAM,KAAK;GART,eAAe;GACf,UAAA,GAASA,YAAAA,WAAAA,CAAW;GACpB;GACA,WAAW,OAAO,SAAS,QAAQ,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC,MAAM,KAAK,EAAE;GACzE,UAAU,kBAAkB,QAAQ,QAAQ;GAC5C,cAAc,sBAAsB,QAAQ,IAAI;GAChD,GAAG;EAEY,CAAC;CACpB,QAAQ,CAER;AACF;AAEA,eAAe,KAAK,SAA4C;CAC9D,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,UAAU,iBAAiB,WAAW,MAAM,GAAG,kBAAkB;CACvE,QAAQ,QAAQ;CAChB,IAAI;EACF,MAAM,MAAM,YAAY,GAAG;GACzB,QAAQ;GACR,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU,OAAO;GAC5B,QAAQ,WAAW;GACnB,WAAW;EACb,CAAC;CACH,QAAQ,CAER,UAAU;EACR,aAAa,OAAO;CACtB;AACF;AAEA,SAAS,gBAAgB,OAAuB;CAC9C,OAAO,0BAA0B,KAAK,KAAK,IAAI,QAAQ;AACzD;AAEA,SAAS,YACP,OACA,QACuB;CACvB,OAAO,SAAU,OAA6B,SAAS,KAAK,IAAK,QAAsB,KAAA;AACzF;AAEA,SAAS,kBAAkB,OAA4D;CACrF,IAAI,UAAU,YAAY,UAAU,SAAS,OAAO;CACpD,IAAI,UAAU,SAAS,OAAO;CAC9B,OAAO;AACT;AAEA,SAAS,sBAAsB,OAAuD;CACpF,OAAO,UAAU,WAAW,UAAU,QAAQ,QAAQ;AACxD"}
@@ -64,7 +64,7 @@ const DEPLOY_TARGETS = [
64
64
  function defaultConfig() {
65
65
  return {
66
66
  version: TELEMETRY_SCHEMA_VERSION,
67
- enabled: false,
67
+ enabled: true,
68
68
  noticeShown: false
69
69
  };
70
70
  }
@@ -80,15 +80,24 @@ function getFarmTelemetryConfigFile() {
80
80
  async function readConfig() {
81
81
  try {
82
82
  const parsed = JSON.parse(await readFile(getFarmTelemetryConfigFile(), "utf8"));
83
- if (parsed.version !== TELEMETRY_SCHEMA_VERSION) return defaultConfig();
83
+ if (parsed.version !== TELEMETRY_SCHEMA_VERSION) return {
84
+ config: defaultConfig(),
85
+ stored: false
86
+ };
84
87
  return {
85
- version: TELEMETRY_SCHEMA_VERSION,
86
- enabled: parsed.enabled === true,
87
- noticeShown: parsed.noticeShown === true,
88
- anonymousId: isUuid(parsed.anonymousId) ? parsed.anonymousId : void 0
88
+ config: {
89
+ version: TELEMETRY_SCHEMA_VERSION,
90
+ enabled: parsed.enabled === true,
91
+ noticeShown: parsed.noticeShown === true,
92
+ anonymousId: isUuid(parsed.anonymousId) ? parsed.anonymousId : void 0
93
+ },
94
+ stored: true
89
95
  };
90
96
  } catch {
91
- return defaultConfig();
97
+ return {
98
+ config: defaultConfig(),
99
+ stored: false
100
+ };
92
101
  }
93
102
  }
94
103
  async function writeConfig(config) {
@@ -164,10 +173,10 @@ function getEndpoint() {
164
173
  }
165
174
  }
166
175
  async function resolveState() {
167
- const config = await readConfig();
176
+ const { config, stored } = await readConfig();
168
177
  const environment = environmentDecision();
169
178
  const enabled = environment.enabled ?? config.enabled;
170
- const source = environment.enabled !== void 0 ? "environment" : config.enabled ? "configuration" : "default";
179
+ const source = environment.enabled !== void 0 ? "environment" : stored ? "configuration" : "default";
171
180
  if (!enabled) return {
172
181
  config,
173
182
  enabled,
@@ -222,7 +231,7 @@ async function getFarmTelemetryStatus() {
222
231
  };
223
232
  }
224
233
  async function setFarmTelemetryEnabled(enabled) {
225
- const current = await readConfig();
234
+ const { config: current } = await readConfig();
226
235
  await writeConfig({
227
236
  version: TELEMETRY_SCHEMA_VERSION,
228
237
  enabled,
@@ -234,9 +243,9 @@ async function setFarmTelemetryEnabled(enabled) {
234
243
  async function showFarmTelemetryNotice() {
235
244
  if (!isInteractive() || isContinuousIntegration() || process.env.NODE_ENV === "test") return;
236
245
  if (environmentDecision().enabled !== void 0) return;
237
- const config = await readConfig();
246
+ const { config } = await readConfig();
238
247
  if (config.noticeShown) return;
239
- process.stderr.write(`Farm.js anonymous telemetry is off during beta. Run "farm telemetry enable" to opt in.\nLearn more: ${TELEMETRY_NOTICE_URL}\n`);
248
+ process.stderr.write(`Farm.js collects anonymous CLI telemetry by default. Run "farm telemetry disable" to opt out.\nLearn more: ${TELEMETRY_NOTICE_URL}\n`);
240
249
  await writeConfig({
241
250
  ...config,
242
251
  noticeShown: true
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.mjs","names":[],"sources":["../src/telemetry.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { chmod, mkdir, readFile, rename, unlink, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nconst TELEMETRY_SCHEMA_VERSION = 1 as const;\nconst DEFAULT_TELEMETRY_ENDPOINT = \"https://farmjs.dev/api/telemetry/v1/events\";\nconst TELEMETRY_NOTICE_URL = \"https://farmjs.dev/docs/telemetry\";\nconst REQUEST_TIMEOUT_MS = 750;\n\nconst FARM_COMMANDS = [\n \"dev\",\n \"build\",\n \"auth:migrate\",\n \"upgrade\",\n \"generate\",\n \"doctor\",\n \"explain\",\n \"preview\",\n \"migrate\",\n \"cron:list\",\n \"cron:run\",\n \"add:integration\",\n \"deploy\",\n] as const;\n\nconst FARM_TEMPLATES = [\n \"basic\",\n \"react-compiler\",\n \"auth\",\n \"better-auth\",\n \"ai\",\n \"auth0\",\n \"authjs\",\n \"autumn\",\n \"clerk\",\n \"jobs-inngest\",\n \"jobs-trigger\",\n \"polar\",\n \"resend\",\n \"stripe\",\n \"supabase\",\n \"unkey\",\n \"workos\",\n] as const;\n\nconst RENDERERS = [\"react\", \"preact\", \"solid\", \"vue\", \"svelte\"] as const;\nconst PACKAGE_MANAGERS = [\"npm\", \"pnpm\", \"yarn\", \"bun\"] as const;\nconst DEPLOY_TARGETS = [\"vercel\", \"cloudflare\", \"netlify\", \"node\", \"custom\"] as const;\n\nexport type FarmTelemetryCommand = (typeof FARM_COMMANDS)[number];\nexport type FarmTelemetryTemplate = (typeof FARM_TEMPLATES)[number];\nexport type FarmTelemetryRenderer = (typeof RENDERERS)[number];\nexport type FarmTelemetryPackageManager = (typeof PACKAGE_MANAGERS)[number];\nexport type FarmTelemetryDeployTarget = (typeof DEPLOY_TARGETS)[number];\n\ninterface FarmTelemetryConfig {\n version: 1;\n enabled: boolean;\n noticeShown: boolean;\n anonymousId?: string;\n}\n\ninterface FarmTelemetryEventBase {\n schemaVersion: typeof TELEMETRY_SCHEMA_VERSION;\n eventId: string;\n anonymousId: string;\n source: \"cli\" | \"create-app\";\n packageName: \"@farm.js/cli\" | \"@farm.js/create-app\";\n packageVersion: string;\n nodeMajor: number;\n platform: \"darwin\" | \"linux\" | \"windows\" | \"other\";\n architecture: \"arm64\" | \"x64\" | \"other\";\n}\n\nexport interface FarmCommandTelemetryInput {\n command: FarmTelemetryCommand;\n packageVersion: string;\n deployTarget?: string;\n}\n\nexport interface FarmProjectCreatedTelemetryInput {\n packageVersion: string;\n template?: string;\n renderer?: string;\n packageManager?: string;\n typescript?: boolean;\n installedDependencies?: boolean;\n}\n\nexport interface FarmTelemetryStatus {\n enabled: boolean;\n active: boolean;\n source: \"configuration\" | \"environment\" | \"default\";\n endpoint: string;\n configFile: string;\n anonymousId?: string;\n reason?: string;\n}\n\ntype FarmTelemetryEvent =\n | (FarmTelemetryEventBase & {\n eventType: \"command_invoked\";\n command: FarmTelemetryCommand;\n deployTarget?: FarmTelemetryDeployTarget;\n })\n | (FarmTelemetryEventBase & {\n eventType: \"project_created\";\n template?: FarmTelemetryTemplate;\n renderer?: FarmTelemetryRenderer;\n packageManager?: FarmTelemetryPackageManager;\n typescript?: boolean;\n installedDependencies?: boolean;\n });\n\ntype FarmTelemetryGeneratedFields = Pick<\n FarmTelemetryEventBase,\n \"schemaVersion\" | \"eventId\" | \"anonymousId\" | \"nodeMajor\" | \"platform\" | \"architecture\"\n>;\ntype FarmTelemetryEventInput<T = FarmTelemetryEvent> = T extends FarmTelemetryEvent\n ? Omit<T, keyof FarmTelemetryGeneratedFields>\n : never;\n\nfunction defaultConfig(): FarmTelemetryConfig {\n return {\n version: TELEMETRY_SCHEMA_VERSION,\n enabled: false,\n noticeShown: false,\n };\n}\n\nfunction configDirectory(): string {\n if (process.env.FARM_TELEMETRY_CONFIG_DIR) {\n return path.resolve(process.env.FARM_TELEMETRY_CONFIG_DIR);\n }\n if (process.platform === \"win32\") {\n return path.join(\n process.env.APPDATA || path.join(os.homedir(), \"AppData\", \"Roaming\"),\n \"farmjs\",\n );\n }\n if (process.platform === \"darwin\") {\n return path.join(os.homedir(), \"Library\", \"Application Support\", \"farmjs\");\n }\n return path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), \".config\"), \"farmjs\");\n}\n\nexport function getFarmTelemetryConfigFile(): string {\n return path.join(configDirectory(), \"telemetry.json\");\n}\n\nasync function readConfig(): Promise<FarmTelemetryConfig> {\n try {\n const parsed = JSON.parse(\n await readFile(getFarmTelemetryConfigFile(), \"utf8\"),\n ) as Partial<FarmTelemetryConfig>;\n if (parsed.version !== TELEMETRY_SCHEMA_VERSION) return defaultConfig();\n return {\n version: TELEMETRY_SCHEMA_VERSION,\n enabled: parsed.enabled === true,\n noticeShown: parsed.noticeShown === true,\n anonymousId: isUuid(parsed.anonymousId) ? parsed.anonymousId : undefined,\n };\n } catch {\n return defaultConfig();\n }\n}\n\nasync function writeConfig(config: FarmTelemetryConfig): Promise<void> {\n const file = getFarmTelemetryConfigFile();\n const directory = path.dirname(file);\n const temporaryFile = `${file}.${process.pid}.${randomUUID()}.tmp`;\n try {\n await mkdir(directory, { recursive: true, mode: 0o700 });\n await writeFile(temporaryFile, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n await rename(temporaryFile, file);\n await chmod(file, 0o600).catch(() => undefined);\n } catch {\n await unlink(temporaryFile).catch(() => undefined);\n // Telemetry is best-effort and must never make a Farm command fail.\n }\n}\n\nfunction isUuid(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)\n );\n}\n\nfunction isTrue(value: string | undefined): boolean {\n return value !== undefined && [\"1\", \"true\", \"yes\", \"on\"].includes(value.toLowerCase());\n}\n\nfunction isFalse(value: string | undefined): boolean {\n return value !== undefined && [\"0\", \"false\", \"no\", \"off\"].includes(value.toLowerCase());\n}\n\nfunction environmentDecision(): { enabled?: boolean; reason?: string } {\n if (process.env.DO_NOT_TRACK !== undefined && !isFalse(process.env.DO_NOT_TRACK)) {\n return { enabled: false, reason: \"DO_NOT_TRACK is set\" };\n }\n if (isTrue(process.env.FARM_TELEMETRY_DISABLED)) {\n return { enabled: false, reason: \"FARM_TELEMETRY_DISABLED is set\" };\n }\n if (isTrue(process.env.FARM_TELEMETRY)) return { enabled: true };\n if (isFalse(process.env.FARM_TELEMETRY)) {\n return { enabled: false, reason: \"FARM_TELEMETRY disables collection\" };\n }\n return {};\n}\n\nfunction isContinuousIntegration(): boolean {\n return (\n isTrue(process.env.CI) ||\n isTrue(process.env.GITHUB_ACTIONS) ||\n isTrue(process.env.BUILDKITE) ||\n isTrue(process.env.CIRCLECI)\n );\n}\n\nfunction isInteractive(): boolean {\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n}\n\nfunction getEndpoint(): string {\n const candidate = process.env.FARM_TELEMETRY_ENDPOINT || DEFAULT_TELEMETRY_ENDPOINT;\n try {\n const url = new URL(candidate);\n const isLocal = [\"localhost\", \"127.0.0.1\", \"::1\"].includes(url.hostname);\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && isLocal)) {\n return DEFAULT_TELEMETRY_ENDPOINT;\n }\n return url.toString();\n } catch {\n return DEFAULT_TELEMETRY_ENDPOINT;\n }\n}\n\nasync function resolveState(): Promise<{\n config: FarmTelemetryConfig;\n enabled: boolean;\n active: boolean;\n source: FarmTelemetryStatus[\"source\"];\n reason?: string;\n}> {\n const config = await readConfig();\n const environment = environmentDecision();\n const enabled = environment.enabled ?? config.enabled;\n const source =\n environment.enabled !== undefined\n ? \"environment\"\n : config.enabled\n ? \"configuration\"\n : \"default\";\n\n if (!enabled) return { config, enabled, active: false, source, reason: environment.reason };\n if (environment.enabled === true) return { config, enabled, active: true, source };\n if (process.env.NODE_ENV === \"test\") {\n return { config, enabled, active: false, source, reason: \"test environments are skipped\" };\n }\n if (isContinuousIntegration()) {\n return { config, enabled, active: false, source, reason: \"CI environments are skipped\" };\n }\n if (!isInteractive()) {\n return {\n config,\n enabled,\n active: false,\n source,\n reason: \"non-interactive commands are skipped\",\n };\n }\n return { config, enabled, active: true, source };\n}\n\nexport async function getFarmTelemetryStatus(): Promise<FarmTelemetryStatus> {\n const state = await resolveState();\n return {\n enabled: state.enabled,\n active: state.active,\n source: state.source,\n endpoint: getEndpoint(),\n configFile: getFarmTelemetryConfigFile(),\n anonymousId: state.config.anonymousId,\n reason: state.reason,\n };\n}\n\nexport async function setFarmTelemetryEnabled(enabled: boolean): Promise<FarmTelemetryStatus> {\n const current = await readConfig();\n await writeConfig({\n version: TELEMETRY_SCHEMA_VERSION,\n enabled,\n noticeShown: true,\n anonymousId: enabled ? current.anonymousId || randomUUID() : undefined,\n });\n return getFarmTelemetryStatus();\n}\n\nexport async function showFarmTelemetryNotice(): Promise<void> {\n if (!isInteractive() || isContinuousIntegration() || process.env.NODE_ENV === \"test\") return;\n if (environmentDecision().enabled !== undefined) return;\n const config = await readConfig();\n if (config.noticeShown) return;\n process.stderr.write(\n `Farm.js anonymous telemetry is off during beta. Run \"farm telemetry enable\" to opt in.\\nLearn more: ${TELEMETRY_NOTICE_URL}\\n`,\n );\n await writeConfig({ ...config, noticeShown: true });\n}\n\nexport function resolveFarmTelemetryCommand(value: string): FarmTelemetryCommand | undefined {\n return (FARM_COMMANDS as readonly string[]).includes(value)\n ? (value as FarmTelemetryCommand)\n : undefined;\n}\n\nexport async function trackFarmCommand(input: FarmCommandTelemetryInput): Promise<void> {\n const deployTarget = allowlisted(input.deployTarget, DEPLOY_TARGETS);\n await track({\n eventType: \"command_invoked\",\n source: \"cli\",\n packageName: \"@farm.js/cli\",\n packageVersion: sanitizeVersion(input.packageVersion),\n command: input.command,\n ...(deployTarget ? { deployTarget } : {}),\n });\n}\n\nexport async function trackFarmProjectCreated(\n input: FarmProjectCreatedTelemetryInput,\n): Promise<void> {\n const template = allowlisted(input.template, FARM_TEMPLATES);\n const renderer = allowlisted(input.renderer, RENDERERS);\n const packageManager = allowlisted(input.packageManager, PACKAGE_MANAGERS);\n await track({\n eventType: \"project_created\",\n source: \"create-app\",\n packageName: \"@farm.js/create-app\",\n packageVersion: sanitizeVersion(input.packageVersion),\n ...(template ? { template } : {}),\n ...(renderer ? { renderer } : {}),\n ...(packageManager ? { packageManager } : {}),\n ...(typeof input.typescript === \"boolean\" ? { typescript: input.typescript } : {}),\n ...(typeof input.installedDependencies === \"boolean\"\n ? { installedDependencies: input.installedDependencies }\n : {}),\n });\n}\n\nasync function track(event: FarmTelemetryEventInput): Promise<void> {\n try {\n const state = await resolveState();\n if (!state.active) return;\n const anonymousId = state.config.anonymousId || randomUUID();\n if (!state.config.anonymousId) {\n await writeConfig({ ...state.config, anonymousId });\n }\n const payload = {\n schemaVersion: TELEMETRY_SCHEMA_VERSION,\n eventId: randomUUID(),\n anonymousId,\n nodeMajor: Number.parseInt(process.versions.node.split(\".\")[0] || \"0\", 10),\n platform: normalizePlatform(process.platform),\n architecture: normalizeArchitecture(process.arch),\n ...event,\n } as FarmTelemetryEvent;\n await send(payload);\n } catch {\n // Telemetry is best-effort and must never make a Farm command fail.\n }\n}\n\nasync function send(payload: FarmTelemetryEvent): Promise<void> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n timeout.unref?.();\n try {\n await fetch(getEndpoint(), {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: controller.signal,\n keepalive: true,\n });\n } catch {\n // Network and endpoint failures are intentionally ignored.\n } finally {\n clearTimeout(timeout);\n }\n}\n\nfunction sanitizeVersion(value: string): string {\n return /^[0-9A-Za-z.+_-]{1,64}$/.test(value) ? value : \"unknown\";\n}\n\nfunction allowlisted<const T extends readonly string[]>(\n value: string | undefined,\n values: T,\n): T[number] | undefined {\n return value && (values as readonly string[]).includes(value) ? (value as T[number]) : undefined;\n}\n\nfunction normalizePlatform(value: NodeJS.Platform): FarmTelemetryEventBase[\"platform\"] {\n if (value === \"darwin\" || value === \"linux\") return value;\n if (value === \"win32\") return \"windows\";\n return \"other\";\n}\n\nfunction normalizeArchitecture(value: string): FarmTelemetryEventBase[\"architecture\"] {\n return value === \"arm64\" || value === \"x64\" ? value : \"other\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"telemetry.mjs","names":[],"sources":["../src/telemetry.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { chmod, mkdir, readFile, rename, unlink, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nconst TELEMETRY_SCHEMA_VERSION = 1 as const;\nconst DEFAULT_TELEMETRY_ENDPOINT = \"https://farmjs.dev/api/telemetry/v1/events\";\nconst TELEMETRY_NOTICE_URL = \"https://farmjs.dev/docs/telemetry\";\nconst REQUEST_TIMEOUT_MS = 750;\n\nconst FARM_COMMANDS = [\n \"dev\",\n \"build\",\n \"auth:migrate\",\n \"upgrade\",\n \"generate\",\n \"doctor\",\n \"explain\",\n \"preview\",\n \"migrate\",\n \"cron:list\",\n \"cron:run\",\n \"add:integration\",\n \"deploy\",\n] as const;\n\nconst FARM_TEMPLATES = [\n \"basic\",\n \"react-compiler\",\n \"auth\",\n \"better-auth\",\n \"ai\",\n \"auth0\",\n \"authjs\",\n \"autumn\",\n \"clerk\",\n \"jobs-inngest\",\n \"jobs-trigger\",\n \"polar\",\n \"resend\",\n \"stripe\",\n \"supabase\",\n \"unkey\",\n \"workos\",\n] as const;\n\nconst RENDERERS = [\"react\", \"preact\", \"solid\", \"vue\", \"svelte\"] as const;\nconst PACKAGE_MANAGERS = [\"npm\", \"pnpm\", \"yarn\", \"bun\"] as const;\nconst DEPLOY_TARGETS = [\"vercel\", \"cloudflare\", \"netlify\", \"node\", \"custom\"] as const;\n\nexport type FarmTelemetryCommand = (typeof FARM_COMMANDS)[number];\nexport type FarmTelemetryTemplate = (typeof FARM_TEMPLATES)[number];\nexport type FarmTelemetryRenderer = (typeof RENDERERS)[number];\nexport type FarmTelemetryPackageManager = (typeof PACKAGE_MANAGERS)[number];\nexport type FarmTelemetryDeployTarget = (typeof DEPLOY_TARGETS)[number];\n\ninterface FarmTelemetryConfig {\n version: 1;\n enabled: boolean;\n noticeShown: boolean;\n anonymousId?: string;\n}\n\ninterface FarmTelemetryConfigState {\n config: FarmTelemetryConfig;\n stored: boolean;\n}\n\ninterface FarmTelemetryEventBase {\n schemaVersion: typeof TELEMETRY_SCHEMA_VERSION;\n eventId: string;\n anonymousId: string;\n source: \"cli\" | \"create-app\";\n packageName: \"@farm.js/cli\" | \"@farm.js/create-app\";\n packageVersion: string;\n nodeMajor: number;\n platform: \"darwin\" | \"linux\" | \"windows\" | \"other\";\n architecture: \"arm64\" | \"x64\" | \"other\";\n}\n\nexport interface FarmCommandTelemetryInput {\n command: FarmTelemetryCommand;\n packageVersion: string;\n deployTarget?: string;\n}\n\nexport interface FarmProjectCreatedTelemetryInput {\n packageVersion: string;\n template?: string;\n renderer?: string;\n packageManager?: string;\n typescript?: boolean;\n installedDependencies?: boolean;\n}\n\nexport interface FarmTelemetryStatus {\n enabled: boolean;\n active: boolean;\n source: \"configuration\" | \"environment\" | \"default\";\n endpoint: string;\n configFile: string;\n anonymousId?: string;\n reason?: string;\n}\n\ntype FarmTelemetryEvent =\n | (FarmTelemetryEventBase & {\n eventType: \"command_invoked\";\n command: FarmTelemetryCommand;\n deployTarget?: FarmTelemetryDeployTarget;\n })\n | (FarmTelemetryEventBase & {\n eventType: \"project_created\";\n template?: FarmTelemetryTemplate;\n renderer?: FarmTelemetryRenderer;\n packageManager?: FarmTelemetryPackageManager;\n typescript?: boolean;\n installedDependencies?: boolean;\n });\n\ntype FarmTelemetryGeneratedFields = Pick<\n FarmTelemetryEventBase,\n \"schemaVersion\" | \"eventId\" | \"anonymousId\" | \"nodeMajor\" | \"platform\" | \"architecture\"\n>;\ntype FarmTelemetryEventInput<T = FarmTelemetryEvent> = T extends FarmTelemetryEvent\n ? Omit<T, keyof FarmTelemetryGeneratedFields>\n : never;\n\nfunction defaultConfig(): FarmTelemetryConfig {\n return {\n version: TELEMETRY_SCHEMA_VERSION,\n enabled: true,\n noticeShown: false,\n };\n}\n\nfunction configDirectory(): string {\n if (process.env.FARM_TELEMETRY_CONFIG_DIR) {\n return path.resolve(process.env.FARM_TELEMETRY_CONFIG_DIR);\n }\n if (process.platform === \"win32\") {\n return path.join(\n process.env.APPDATA || path.join(os.homedir(), \"AppData\", \"Roaming\"),\n \"farmjs\",\n );\n }\n if (process.platform === \"darwin\") {\n return path.join(os.homedir(), \"Library\", \"Application Support\", \"farmjs\");\n }\n return path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), \".config\"), \"farmjs\");\n}\n\nexport function getFarmTelemetryConfigFile(): string {\n return path.join(configDirectory(), \"telemetry.json\");\n}\n\nasync function readConfig(): Promise<FarmTelemetryConfigState> {\n try {\n const parsed = JSON.parse(\n await readFile(getFarmTelemetryConfigFile(), \"utf8\"),\n ) as Partial<FarmTelemetryConfig>;\n if (parsed.version !== TELEMETRY_SCHEMA_VERSION) {\n return { config: defaultConfig(), stored: false };\n }\n return {\n config: {\n version: TELEMETRY_SCHEMA_VERSION,\n enabled: parsed.enabled === true,\n noticeShown: parsed.noticeShown === true,\n anonymousId: isUuid(parsed.anonymousId) ? parsed.anonymousId : undefined,\n },\n stored: true,\n };\n } catch {\n return { config: defaultConfig(), stored: false };\n }\n}\n\nasync function writeConfig(config: FarmTelemetryConfig): Promise<void> {\n const file = getFarmTelemetryConfigFile();\n const directory = path.dirname(file);\n const temporaryFile = `${file}.${process.pid}.${randomUUID()}.tmp`;\n try {\n await mkdir(directory, { recursive: true, mode: 0o700 });\n await writeFile(temporaryFile, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n await rename(temporaryFile, file);\n await chmod(file, 0o600).catch(() => undefined);\n } catch {\n await unlink(temporaryFile).catch(() => undefined);\n // Telemetry is best-effort and must never make a Farm command fail.\n }\n}\n\nfunction isUuid(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)\n );\n}\n\nfunction isTrue(value: string | undefined): boolean {\n return value !== undefined && [\"1\", \"true\", \"yes\", \"on\"].includes(value.toLowerCase());\n}\n\nfunction isFalse(value: string | undefined): boolean {\n return value !== undefined && [\"0\", \"false\", \"no\", \"off\"].includes(value.toLowerCase());\n}\n\nfunction environmentDecision(): { enabled?: boolean; reason?: string } {\n if (process.env.DO_NOT_TRACK !== undefined && !isFalse(process.env.DO_NOT_TRACK)) {\n return { enabled: false, reason: \"DO_NOT_TRACK is set\" };\n }\n if (isTrue(process.env.FARM_TELEMETRY_DISABLED)) {\n return { enabled: false, reason: \"FARM_TELEMETRY_DISABLED is set\" };\n }\n if (isTrue(process.env.FARM_TELEMETRY)) return { enabled: true };\n if (isFalse(process.env.FARM_TELEMETRY)) {\n return { enabled: false, reason: \"FARM_TELEMETRY disables collection\" };\n }\n return {};\n}\n\nfunction isContinuousIntegration(): boolean {\n return (\n isTrue(process.env.CI) ||\n isTrue(process.env.GITHUB_ACTIONS) ||\n isTrue(process.env.BUILDKITE) ||\n isTrue(process.env.CIRCLECI)\n );\n}\n\nfunction isInteractive(): boolean {\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n}\n\nfunction getEndpoint(): string {\n const candidate = process.env.FARM_TELEMETRY_ENDPOINT || DEFAULT_TELEMETRY_ENDPOINT;\n try {\n const url = new URL(candidate);\n const isLocal = [\"localhost\", \"127.0.0.1\", \"::1\"].includes(url.hostname);\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && isLocal)) {\n return DEFAULT_TELEMETRY_ENDPOINT;\n }\n return url.toString();\n } catch {\n return DEFAULT_TELEMETRY_ENDPOINT;\n }\n}\n\nasync function resolveState(): Promise<{\n config: FarmTelemetryConfig;\n enabled: boolean;\n active: boolean;\n source: FarmTelemetryStatus[\"source\"];\n reason?: string;\n}> {\n const { config, stored } = await readConfig();\n const environment = environmentDecision();\n const enabled = environment.enabled ?? config.enabled;\n const source =\n environment.enabled !== undefined ? \"environment\" : stored ? \"configuration\" : \"default\";\n\n if (!enabled) return { config, enabled, active: false, source, reason: environment.reason };\n if (environment.enabled === true) return { config, enabled, active: true, source };\n if (process.env.NODE_ENV === \"test\") {\n return { config, enabled, active: false, source, reason: \"test environments are skipped\" };\n }\n if (isContinuousIntegration()) {\n return { config, enabled, active: false, source, reason: \"CI environments are skipped\" };\n }\n if (!isInteractive()) {\n return {\n config,\n enabled,\n active: false,\n source,\n reason: \"non-interactive commands are skipped\",\n };\n }\n return { config, enabled, active: true, source };\n}\n\nexport async function getFarmTelemetryStatus(): Promise<FarmTelemetryStatus> {\n const state = await resolveState();\n return {\n enabled: state.enabled,\n active: state.active,\n source: state.source,\n endpoint: getEndpoint(),\n configFile: getFarmTelemetryConfigFile(),\n anonymousId: state.config.anonymousId,\n reason: state.reason,\n };\n}\n\nexport async function setFarmTelemetryEnabled(enabled: boolean): Promise<FarmTelemetryStatus> {\n const { config: current } = await readConfig();\n await writeConfig({\n version: TELEMETRY_SCHEMA_VERSION,\n enabled,\n noticeShown: true,\n anonymousId: enabled ? current.anonymousId || randomUUID() : undefined,\n });\n return getFarmTelemetryStatus();\n}\n\nexport async function showFarmTelemetryNotice(): Promise<void> {\n if (!isInteractive() || isContinuousIntegration() || process.env.NODE_ENV === \"test\") return;\n if (environmentDecision().enabled !== undefined) return;\n const { config } = await readConfig();\n if (config.noticeShown) return;\n process.stderr.write(\n `Farm.js collects anonymous CLI telemetry by default. Run \"farm telemetry disable\" to opt out.\\nLearn more: ${TELEMETRY_NOTICE_URL}\\n`,\n );\n await writeConfig({ ...config, noticeShown: true });\n}\n\nexport function resolveFarmTelemetryCommand(value: string): FarmTelemetryCommand | undefined {\n return (FARM_COMMANDS as readonly string[]).includes(value)\n ? (value as FarmTelemetryCommand)\n : undefined;\n}\n\nexport async function trackFarmCommand(input: FarmCommandTelemetryInput): Promise<void> {\n const deployTarget = allowlisted(input.deployTarget, DEPLOY_TARGETS);\n await track({\n eventType: \"command_invoked\",\n source: \"cli\",\n packageName: \"@farm.js/cli\",\n packageVersion: sanitizeVersion(input.packageVersion),\n command: input.command,\n ...(deployTarget ? { deployTarget } : {}),\n });\n}\n\nexport async function trackFarmProjectCreated(\n input: FarmProjectCreatedTelemetryInput,\n): Promise<void> {\n const template = allowlisted(input.template, FARM_TEMPLATES);\n const renderer = allowlisted(input.renderer, RENDERERS);\n const packageManager = allowlisted(input.packageManager, PACKAGE_MANAGERS);\n await track({\n eventType: \"project_created\",\n source: \"create-app\",\n packageName: \"@farm.js/create-app\",\n packageVersion: sanitizeVersion(input.packageVersion),\n ...(template ? { template } : {}),\n ...(renderer ? { renderer } : {}),\n ...(packageManager ? { packageManager } : {}),\n ...(typeof input.typescript === \"boolean\" ? { typescript: input.typescript } : {}),\n ...(typeof input.installedDependencies === \"boolean\"\n ? { installedDependencies: input.installedDependencies }\n : {}),\n });\n}\n\nasync function track(event: FarmTelemetryEventInput): Promise<void> {\n try {\n const state = await resolveState();\n if (!state.active) return;\n const anonymousId = state.config.anonymousId || randomUUID();\n if (!state.config.anonymousId) {\n await writeConfig({ ...state.config, anonymousId });\n }\n const payload = {\n schemaVersion: TELEMETRY_SCHEMA_VERSION,\n eventId: randomUUID(),\n anonymousId,\n nodeMajor: Number.parseInt(process.versions.node.split(\".\")[0] || \"0\", 10),\n platform: normalizePlatform(process.platform),\n architecture: normalizeArchitecture(process.arch),\n ...event,\n } as FarmTelemetryEvent;\n await send(payload);\n } catch {\n // Telemetry is best-effort and must never make a Farm command fail.\n }\n}\n\nasync function send(payload: FarmTelemetryEvent): Promise<void> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n timeout.unref?.();\n try {\n await fetch(getEndpoint(), {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: controller.signal,\n keepalive: true,\n });\n } catch {\n // Network and endpoint failures are intentionally ignored.\n } finally {\n clearTimeout(timeout);\n }\n}\n\nfunction sanitizeVersion(value: string): string {\n return /^[0-9A-Za-z.+_-]{1,64}$/.test(value) ? value : \"unknown\";\n}\n\nfunction allowlisted<const T extends readonly string[]>(\n value: string | undefined,\n values: T,\n): T[number] | undefined {\n return value && (values as readonly string[]).includes(value) ? (value as T[number]) : undefined;\n}\n\nfunction normalizePlatform(value: NodeJS.Platform): FarmTelemetryEventBase[\"platform\"] {\n if (value === \"darwin\" || value === \"linux\") return value;\n if (value === \"win32\") return \"windows\";\n return \"other\";\n}\n\nfunction normalizeArchitecture(value: string): FarmTelemetryEventBase[\"architecture\"] {\n return value === \"arm64\" || value === \"x64\" ? value : \"other\";\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm.js/cli",
3
- "version": "0.1.0-beta.38",
3
+ "version": "0.1.0-beta.39",
4
4
  "description": "CLI for Farm.js framework",
5
5
  "keywords": [
6
6
  "@farm.js/cli",
@@ -30,7 +30,7 @@
30
30
  "commander": "^11.1.0",
31
31
  "croner": "9.1.0",
32
32
  "picocolors": "^1.0.0",
33
- "@farm.js/core": "0.1.0-beta.38"
33
+ "@farm.js/core": "0.1.0-beta.39"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^20.10.5",