@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.
- package/dist/index.js +158 -0
- 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
|
+
}
|