@farm.js/create-app 0.1.0-beta.35 → 0.1.0-beta.37

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.
@@ -19,7 +19,7 @@ program
19
19
  .option("--skip-install", "Skip installing dependencies")
20
20
  .action(async (projectName, options) => {
21
21
  try {
22
- await createApp(projectName, options);
22
+ await createApp(projectName, { ...options, telemetryPackageVersion: version });
23
23
  } catch (error) {
24
24
  console.error("Failed to create app:", error);
25
25
  process.exit(1);
package/dist/index.js CHANGED
@@ -12,6 +12,9 @@ let node_fs = require("node:fs");
12
12
  let node_fs_promises = require("node:fs/promises");
13
13
  let node_path = require("node:path");
14
14
  node_path = require_rolldown_runtime.__toESM(node_path);
15
+ let node_crypto = require("node:crypto");
16
+ let node_os = require("node:os");
17
+ node_os = require_rolldown_runtime.__toESM(node_os);
15
18
  //#region ../farm-cli/src/ui-feature-registry.ts
16
19
  async function installUIFeature(input) {
17
20
  const feature = input.definition.ui;
@@ -2526,6 +2529,269 @@ function escapeRegExp(input) {
2526
2529
  return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2527
2530
  }
2528
2531
  //#endregion
2532
+ //#region ../farm-cli/src/telemetry.ts
2533
+ const TELEMETRY_SCHEMA_VERSION = 1;
2534
+ const DEFAULT_TELEMETRY_ENDPOINT = "https://farmjs.dev/api/telemetry/v1/events";
2535
+ const TELEMETRY_NOTICE_URL = "https://farmjs.dev/docs/telemetry";
2536
+ const REQUEST_TIMEOUT_MS = 750;
2537
+ const FARM_TEMPLATES = [
2538
+ "basic",
2539
+ "react-compiler",
2540
+ "auth",
2541
+ "better-auth",
2542
+ "ai",
2543
+ "auth0",
2544
+ "authjs",
2545
+ "autumn",
2546
+ "clerk",
2547
+ "jobs-inngest",
2548
+ "jobs-trigger",
2549
+ "polar",
2550
+ "resend",
2551
+ "stripe",
2552
+ "supabase",
2553
+ "unkey",
2554
+ "workos"
2555
+ ];
2556
+ const RENDERERS = [
2557
+ "react",
2558
+ "preact",
2559
+ "solid",
2560
+ "vue",
2561
+ "svelte"
2562
+ ];
2563
+ const PACKAGE_MANAGERS = [
2564
+ "npm",
2565
+ "pnpm",
2566
+ "yarn",
2567
+ "bun"
2568
+ ];
2569
+ function defaultConfig() {
2570
+ return {
2571
+ version: TELEMETRY_SCHEMA_VERSION,
2572
+ enabled: false,
2573
+ noticeShown: false
2574
+ };
2575
+ }
2576
+ function configDirectory() {
2577
+ if (process.env.FARM_TELEMETRY_CONFIG_DIR) return node_path.default.resolve(process.env.FARM_TELEMETRY_CONFIG_DIR);
2578
+ if (process.platform === "win32") return node_path.default.join(process.env.APPDATA || node_path.default.join(node_os.default.homedir(), "AppData", "Roaming"), "farmjs");
2579
+ if (process.platform === "darwin") return node_path.default.join(node_os.default.homedir(), "Library", "Application Support", "farmjs");
2580
+ return node_path.default.join(process.env.XDG_CONFIG_HOME || node_path.default.join(node_os.default.homedir(), ".config"), "farmjs");
2581
+ }
2582
+ function getFarmTelemetryConfigFile() {
2583
+ return node_path.default.join(configDirectory(), "telemetry.json");
2584
+ }
2585
+ async function readConfig() {
2586
+ try {
2587
+ const parsed = JSON.parse(await (0, node_fs_promises.readFile)(getFarmTelemetryConfigFile(), "utf8"));
2588
+ if (parsed.version !== TELEMETRY_SCHEMA_VERSION) return defaultConfig();
2589
+ return {
2590
+ version: TELEMETRY_SCHEMA_VERSION,
2591
+ enabled: parsed.enabled === true,
2592
+ noticeShown: parsed.noticeShown === true,
2593
+ anonymousId: isUuid(parsed.anonymousId) ? parsed.anonymousId : void 0
2594
+ };
2595
+ } catch {
2596
+ return defaultConfig();
2597
+ }
2598
+ }
2599
+ async function writeConfig(config) {
2600
+ const file = getFarmTelemetryConfigFile();
2601
+ const directory = node_path.default.dirname(file);
2602
+ const temporaryFile = `${file}.${process.pid}.${(0, node_crypto.randomUUID)()}.tmp`;
2603
+ try {
2604
+ await (0, node_fs_promises.mkdir)(directory, {
2605
+ recursive: true,
2606
+ mode: 448
2607
+ });
2608
+ await (0, node_fs_promises.writeFile)(temporaryFile, `${JSON.stringify(config, null, 2)}\n`, { mode: 384 });
2609
+ await (0, node_fs_promises.rename)(temporaryFile, file);
2610
+ await (0, node_fs_promises.chmod)(file, 384).catch(() => void 0);
2611
+ } catch {
2612
+ await (0, node_fs_promises.unlink)(temporaryFile).catch(() => void 0);
2613
+ }
2614
+ }
2615
+ function isUuid(value) {
2616
+ return typeof value === "string" && /^[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);
2617
+ }
2618
+ function isTrue(value) {
2619
+ return value !== void 0 && [
2620
+ "1",
2621
+ "true",
2622
+ "yes",
2623
+ "on"
2624
+ ].includes(value.toLowerCase());
2625
+ }
2626
+ function isFalse(value) {
2627
+ return value !== void 0 && [
2628
+ "0",
2629
+ "false",
2630
+ "no",
2631
+ "off"
2632
+ ].includes(value.toLowerCase());
2633
+ }
2634
+ function environmentDecision() {
2635
+ if (process.env.DO_NOT_TRACK !== void 0 && !isFalse(process.env.DO_NOT_TRACK)) return {
2636
+ enabled: false,
2637
+ reason: "DO_NOT_TRACK is set"
2638
+ };
2639
+ if (isTrue(process.env.FARM_TELEMETRY_DISABLED)) return {
2640
+ enabled: false,
2641
+ reason: "FARM_TELEMETRY_DISABLED is set"
2642
+ };
2643
+ if (isTrue(process.env.FARM_TELEMETRY)) return { enabled: true };
2644
+ if (isFalse(process.env.FARM_TELEMETRY)) return {
2645
+ enabled: false,
2646
+ reason: "FARM_TELEMETRY disables collection"
2647
+ };
2648
+ return {};
2649
+ }
2650
+ function isContinuousIntegration() {
2651
+ return isTrue(process.env.CI) || isTrue(process.env.GITHUB_ACTIONS) || isTrue(process.env.BUILDKITE) || isTrue(process.env.CIRCLECI);
2652
+ }
2653
+ function isInteractive() {
2654
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
2655
+ }
2656
+ function getEndpoint() {
2657
+ const candidate = process.env.FARM_TELEMETRY_ENDPOINT || DEFAULT_TELEMETRY_ENDPOINT;
2658
+ try {
2659
+ const url = new URL(candidate);
2660
+ const isLocal = [
2661
+ "localhost",
2662
+ "127.0.0.1",
2663
+ "::1"
2664
+ ].includes(url.hostname);
2665
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLocal)) return DEFAULT_TELEMETRY_ENDPOINT;
2666
+ return url.toString();
2667
+ } catch {
2668
+ return DEFAULT_TELEMETRY_ENDPOINT;
2669
+ }
2670
+ }
2671
+ async function resolveState() {
2672
+ const config = await readConfig();
2673
+ const environment = environmentDecision();
2674
+ const enabled = environment.enabled ?? config.enabled;
2675
+ const source = environment.enabled !== void 0 ? "environment" : config.enabled ? "configuration" : "default";
2676
+ if (!enabled) return {
2677
+ config,
2678
+ enabled,
2679
+ active: false,
2680
+ source,
2681
+ reason: environment.reason
2682
+ };
2683
+ if (environment.enabled === true) return {
2684
+ config,
2685
+ enabled,
2686
+ active: true,
2687
+ source
2688
+ };
2689
+ if (process.env.NODE_ENV === "test") return {
2690
+ config,
2691
+ enabled,
2692
+ active: false,
2693
+ source,
2694
+ reason: "test environments are skipped"
2695
+ };
2696
+ if (isContinuousIntegration()) return {
2697
+ config,
2698
+ enabled,
2699
+ active: false,
2700
+ source,
2701
+ reason: "CI environments are skipped"
2702
+ };
2703
+ if (!isInteractive()) return {
2704
+ config,
2705
+ enabled,
2706
+ active: false,
2707
+ source,
2708
+ reason: "non-interactive commands are skipped"
2709
+ };
2710
+ return {
2711
+ config,
2712
+ enabled,
2713
+ active: true,
2714
+ source
2715
+ };
2716
+ }
2717
+ async function showFarmTelemetryNotice() {
2718
+ if (!isInteractive() || isContinuousIntegration() || process.env.NODE_ENV === "test") return;
2719
+ if (environmentDecision().enabled !== void 0) return;
2720
+ const config = await readConfig();
2721
+ if (config.noticeShown) return;
2722
+ process.stderr.write(`Farm.js anonymous telemetry is off during beta. Run "farm telemetry enable" to opt in.\nLearn more: ${TELEMETRY_NOTICE_URL}\n`);
2723
+ await writeConfig({
2724
+ ...config,
2725
+ noticeShown: true
2726
+ });
2727
+ }
2728
+ async function trackFarmProjectCreated(input) {
2729
+ const template = allowlisted(input.template, FARM_TEMPLATES);
2730
+ const renderer = allowlisted(input.renderer, RENDERERS);
2731
+ const packageManager = allowlisted(input.packageManager, PACKAGE_MANAGERS);
2732
+ await track({
2733
+ eventType: "project_created",
2734
+ source: "create-app",
2735
+ packageName: "@farm.js/create-app",
2736
+ packageVersion: sanitizeVersion(input.packageVersion),
2737
+ ...template ? { template } : {},
2738
+ ...renderer ? { renderer } : {},
2739
+ ...packageManager ? { packageManager } : {},
2740
+ ...typeof input.typescript === "boolean" ? { typescript: input.typescript } : {},
2741
+ ...typeof input.installedDependencies === "boolean" ? { installedDependencies: input.installedDependencies } : {}
2742
+ });
2743
+ }
2744
+ async function track(event) {
2745
+ try {
2746
+ const state = await resolveState();
2747
+ if (!state.active) return;
2748
+ const anonymousId = state.config.anonymousId || (0, node_crypto.randomUUID)();
2749
+ if (!state.config.anonymousId) await writeConfig({
2750
+ ...state.config,
2751
+ anonymousId
2752
+ });
2753
+ await send({
2754
+ schemaVersion: TELEMETRY_SCHEMA_VERSION,
2755
+ eventId: (0, node_crypto.randomUUID)(),
2756
+ anonymousId,
2757
+ nodeMajor: Number.parseInt(process.versions.node.split(".")[0] || "0", 10),
2758
+ platform: normalizePlatform(process.platform),
2759
+ architecture: normalizeArchitecture(process.arch),
2760
+ ...event
2761
+ });
2762
+ } catch {}
2763
+ }
2764
+ async function send(payload) {
2765
+ const controller = new AbortController();
2766
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
2767
+ timeout.unref?.();
2768
+ try {
2769
+ await fetch(getEndpoint(), {
2770
+ method: "POST",
2771
+ headers: { "content-type": "application/json" },
2772
+ body: JSON.stringify(payload),
2773
+ signal: controller.signal,
2774
+ keepalive: true
2775
+ });
2776
+ } catch {} finally {
2777
+ clearTimeout(timeout);
2778
+ }
2779
+ }
2780
+ function sanitizeVersion(value) {
2781
+ return /^[0-9A-Za-z.+_-]{1,64}$/.test(value) ? value : "unknown";
2782
+ }
2783
+ function allowlisted(value, values) {
2784
+ return value && values.includes(value) ? value : void 0;
2785
+ }
2786
+ function normalizePlatform(value) {
2787
+ if (value === "darwin" || value === "linux") return value;
2788
+ if (value === "win32") return "windows";
2789
+ return "other";
2790
+ }
2791
+ function normalizeArchitecture(value) {
2792
+ return value === "arm64" || value === "x64" ? value : "other";
2793
+ }
2794
+ //#endregion
2529
2795
  //#region src/index.ts
2530
2796
  const templateDetails = {
2531
2797
  basic: {
@@ -2668,6 +2934,7 @@ function integrationTemplate(input) {
2668
2934
  }
2669
2935
  async function createApp(projectName, options = {}) {
2670
2936
  require_utils.showBanner();
2937
+ await showFarmTelemetryNotice();
2671
2938
  const templates = await getAvailableTemplates();
2672
2939
  if (templates.length === 0) {
2673
2940
  require_utils.logger.error("No templates are available in this package.");
@@ -2826,6 +3093,14 @@ async function createApp(projectName, options = {}) {
2826
3093
  if (integrationResult.env.length) require_utils.logger.info("Copy .env.example to .env.local and add your provider credentials.");
2827
3094
  for (const note of integrationResult.notes) require_utils.logger.info(note);
2828
3095
  }
3096
+ await trackFarmProjectCreated({
3097
+ packageVersion: options.telemetryPackageVersion ?? "unknown",
3098
+ template,
3099
+ renderer,
3100
+ packageManager: packageManager.name,
3101
+ typescript: useTypeScript,
3102
+ installedDependencies: !options.skipInstall
3103
+ });
2829
3104
  }
2830
3105
  async function copyTemplate(template, projectPath, useTypeScript, renderer) {
2831
3106
  const details = templateDetails[template];
@@ -3011,9 +3286,9 @@ async function copyDir(src, dest) {
3011
3286
  else await fs_promises.default.copyFile(srcPath, destPath);
3012
3287
  }
3013
3288
  }
3014
- async function dirExists(path$4) {
3289
+ async function dirExists(path$5) {
3015
3290
  try {
3016
- return (await fs_promises.default.stat(path$4)).isDirectory();
3291
+ return (await fs_promises.default.stat(path$5)).isDirectory();
3017
3292
  } catch {
3018
3293
  return false;
3019
3294
  }