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