@farm.js/cli 0.1.0-beta.37 → 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 +1 -1
- package/dist/telemetry.js +21 -12
- package/dist/telemetry.js.map +1 -1
- package/dist/telemetry.mjs +21 -12
- package/dist/telemetry.mjs.map +1 -1
- package/package.json +2 -2
package/bin/farm.js
CHANGED
|
@@ -539,7 +539,7 @@ telemetryCommand
|
|
|
539
539
|
|
|
540
540
|
telemetryCommand
|
|
541
541
|
.command("enable")
|
|
542
|
-
.description("
|
|
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:
|
|
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
|
|
87
|
+
if (parsed.version !== TELEMETRY_SCHEMA_VERSION) return {
|
|
88
|
+
config: defaultConfig(),
|
|
89
|
+
stored: false
|
|
90
|
+
};
|
|
88
91
|
return {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
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" :
|
|
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
|
|
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
|
package/dist/telemetry.js.map
CHANGED
|
@@ -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":";;;;;;;;;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;AA2E3E,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,aAA2C;CACxD,IAAI;EACF,MAAM,SAAS,KAAK,MAClB,OAAA,GAAME,iBAAAA,SAAAA,CAAS,2BAA2B,GAAG,MAAM,CACrD;EACA,IAAI,OAAO,YAAY,0BAA0B,OAAO,cAAc;EACtE,OAAO;GACL,SAAS;GACT,SAAS,OAAO,YAAY;GAC5B,aAAa,OAAO,gBAAgB;GACpC,aAAa,OAAO,OAAO,WAAW,IAAI,OAAO,cAAc,KAAA;EACjE;CACF,QAAQ;EACN,OAAO,cAAc;CACvB;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,SAAS,MAAM,WAAW;CAChC,MAAM,cAAc,oBAAoB;CACxC,MAAM,UAAU,YAAY,WAAW,OAAO;CAC9C,MAAM,SACJ,YAAY,YAAY,KAAA,IACpB,gBACA,OAAO,UACL,kBACA;CAER,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,UAAU,MAAM,WAAW;CACjC,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,SAAS,MAAM,WAAW;CAChC,IAAI,OAAO,aAAa;CACxB,QAAQ,OAAO,MACb,uGAAuG,qBAAqB,GAC9H;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"}
|
|
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":"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"}
|
package/dist/telemetry.mjs
CHANGED
|
@@ -64,7 +64,7 @@ const DEPLOY_TARGETS = [
|
|
|
64
64
|
function defaultConfig() {
|
|
65
65
|
return {
|
|
66
66
|
version: TELEMETRY_SCHEMA_VERSION,
|
|
67
|
-
enabled:
|
|
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
|
|
83
|
+
if (parsed.version !== TELEMETRY_SCHEMA_VERSION) return {
|
|
84
|
+
config: defaultConfig(),
|
|
85
|
+
stored: false
|
|
86
|
+
};
|
|
84
87
|
return {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
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" :
|
|
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
|
|
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
|
package/dist/telemetry.mjs.map
CHANGED
|
@@ -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.
|
|
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.
|
|
33
|
+
"@farm.js/core": "0.1.0-beta.39"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^20.10.5",
|