@xray-analytics/analytics-cli 0.0.2

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.
Files changed (2) hide show
  1. package/dist/index.js +158 -0
  2. package/package.json +28 -0
package/dist/index.js ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import process from "process";
5
+
6
+ // src/cli.ts
7
+ import path3 from "path";
8
+ import kleur from "kleur";
9
+
10
+ // src/init.ts
11
+ import path2 from "path";
12
+
13
+ // src/fs-utils.ts
14
+ import fs from "fs";
15
+ import path from "path";
16
+ function exists(filePath) {
17
+ return fs.existsSync(filePath);
18
+ }
19
+ function writeFileIfMissing(filePath, content) {
20
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
21
+ if (exists(filePath)) return "skipped";
22
+ fs.writeFileSync(filePath, content, "utf8");
23
+ return "created";
24
+ }
25
+ function upsertEnv(filePath, key, value) {
26
+ const line = `${key}=${value}`;
27
+ if (!exists(filePath)) {
28
+ fs.writeFileSync(filePath, line + "\n", "utf8");
29
+ return "created";
30
+ }
31
+ const current = fs.readFileSync(filePath, "utf8");
32
+ if (current.includes(`${key}=`)) return "skipped";
33
+ fs.writeFileSync(filePath, current.trimEnd() + "\n" + line + "\n", "utf8");
34
+ return "updated";
35
+ }
36
+
37
+ // src/templates.ts
38
+ function appRouteTemplate() {
39
+ return `import { NextResponse } from "next/server";
40
+
41
+ export async function POST(req: Request) {
42
+ try {
43
+ const body = await req.json();
44
+ const raw = JSON.stringify(body);
45
+ if (raw.length > 50000) {
46
+ return NextResponse.json({ ok: false, error: "payload_too_large" }, { status: 413 });
47
+ }
48
+
49
+ const ingestUrl = process.env.ANALYTICS_INGEST_URL;
50
+ const ingestKey = process.env.ANALYTICS_INGEST_KEY;
51
+
52
+ if (!ingestUrl || !ingestKey) {
53
+ return NextResponse.json({ ok: false, error: "missing_env" }, { status: 500 });
54
+ }
55
+
56
+ const res = await fetch(ingestUrl, {
57
+ method: "POST",
58
+ headers: {
59
+ "content-type": "application/json",
60
+ "x-ingest-key": ingestKey
61
+ },
62
+ body: raw
63
+ });
64
+
65
+ return NextResponse.json({ ok: res.ok }, { status: res.ok ? 200 : 502 });
66
+ } catch (error) {
67
+ console.error('Analytics tracking error:', error);
68
+ return NextResponse.json({ ok: false }, { status: 400 });
69
+ }
70
+ }
71
+ `;
72
+ }
73
+ function pagesRouteTemplate() {
74
+ return `import type { NextApiRequest, NextApiResponse } from "next";
75
+
76
+ export default async function handler(req: NextApiRequest, res: NextApiResponse) {
77
+ if (req.method !== "POST") return res.status(405).json({ ok: false });
78
+
79
+ try {
80
+ const raw = JSON.stringify(req.body);
81
+ if (raw.length > 50000) return res.status(413).json({ ok: false, error: "payload_too_large" });
82
+
83
+ const ingestUrl = process.env.ANALYTICS_INGEST_URL;
84
+ const ingestKey = process.env.ANALYTICS_INGEST_KEY;
85
+ if (!ingestUrl || !ingestKey) return res.status(500).json({ ok: false, error: "missing_env" });
86
+
87
+ const r = await fetch(ingestUrl, {
88
+ method: "POST",
89
+ headers: { "content-type": "application/json", "x-ingest-key": ingestKey },
90
+ body: raw
91
+ });
92
+
93
+ return res.status(r.ok ? 200 : 502).json({ ok: r.ok });
94
+ } catch (error) {
95
+ console.error('Analytics tracking error:', error);
96
+ return res.status(400).json({ ok: false });
97
+ }
98
+ }
99
+ `;
100
+ }
101
+
102
+ // src/init.ts
103
+ function runInit(cwd) {
104
+ const hasRootApp = exists(path2.join(cwd, "app"));
105
+ const hasRootPages = exists(path2.join(cwd, "pages"));
106
+ const hasSrcApp = exists(path2.join(cwd, "src", "app"));
107
+ const hasSrcPages = exists(path2.join(cwd, "src", "pages"));
108
+ if (!hasRootApp && !hasRootPages && !hasSrcApp && !hasSrcPages) {
109
+ throw new Error(
110
+ "Could not find app/ or pages/ directory (or src/app or src/pages). Is this a Next.js project?"
111
+ );
112
+ }
113
+ const sourceRoot = hasRootApp || hasRootPages ? cwd : path2.join(cwd, "src");
114
+ const mode = hasRootApp || hasSrcApp ? "app" : "pages";
115
+ const routePath = mode === "app" ? path2.join(sourceRoot, "app", "api", "track", "route.ts") : path2.join(sourceRoot, "pages", "api", "track.ts");
116
+ const routeContent = mode === "app" ? appRouteTemplate() : pagesRouteTemplate();
117
+ const routeWrite = writeFileIfMissing(routePath, routeContent);
118
+ const envPath = path2.join(cwd, ".env.local");
119
+ const envUrlWrite = upsertEnv(
120
+ envPath,
121
+ "ANALYTICS_INGEST_URL",
122
+ "https://ingest.seudominio.com/track"
123
+ );
124
+ const envKeyWrite = upsertEnv(envPath, "ANALYTICS_INGEST_KEY", "DEV_SECRET_KEY");
125
+ return {
126
+ routePath,
127
+ routeWrite,
128
+ envPath,
129
+ envUrlWrite,
130
+ envKeyWrite,
131
+ mode
132
+ };
133
+ }
134
+
135
+ // src/cli.ts
136
+ function runCommand(args, cwd) {
137
+ const cmd = args[0];
138
+ if (cmd !== "init") {
139
+ console.log(`Use: ${kleur.cyan("npx xray-analytics init")}`);
140
+ return 1;
141
+ }
142
+ try {
143
+ const result = runInit(cwd);
144
+ const routeLabel = result.routeWrite === "created" ? "Created" : "Already exists";
145
+ console.log(kleur.green(`${routeLabel}: ${path3.relative(cwd, result.routePath)}`));
146
+ console.log(kleur.green(`Updated: ${path3.relative(cwd, result.envPath)}`));
147
+ console.log('\nDone! Use <AnalyticsProvider transport="bff" /> with endpoint "/api/track".');
148
+ return 0;
149
+ } catch (error) {
150
+ const message = error instanceof Error ? error.message : "Unexpected error";
151
+ console.log(kleur.red(message));
152
+ return 1;
153
+ }
154
+ }
155
+
156
+ // src/index.ts
157
+ var code = runCommand(process.argv.slice(2), process.cwd());
158
+ process.exit(code);
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@xray-analytics/analytics-cli",
3
+ "version": "0.0.2",
4
+ "private": false,
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public",
8
+ "registry": "https://registry.npmjs.org/"
9
+ },
10
+ "bin": {
11
+ "xray-analytics": "dist/index.js"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsup src/index.ts --format esm",
18
+ "lint": "eslint .",
19
+ "typecheck": "tsc -p tsconfig.json --noEmit",
20
+ "test": "vitest run"
21
+ },
22
+ "dependencies": {
23
+ "kleur": "^4.1.5"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^25.2.3"
27
+ }
28
+ }