@kenkaiiii/gg-pixel 4.3.62 → 4.3.64
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/cli.js +155 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +137 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +138 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/install.ts
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from "fs";
|
|
5
5
|
import { homedir } from "os";
|
|
6
|
-
import { dirname, join, resolve } from "path";
|
|
6
|
+
import { dirname, join, relative, resolve, sep } from "path";
|
|
7
7
|
import { spawnSync } from "child_process";
|
|
8
8
|
var DEFAULT_INGEST_URL = "https://gg-pixel-server.buzzbeamaustralia.workers.dev";
|
|
9
9
|
async function install(opts = {}) {
|
|
@@ -17,16 +17,26 @@ async function install(opts = {}) {
|
|
|
17
17
|
const pkgPath = join(projectRoot, "package.json");
|
|
18
18
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
19
19
|
const projectName = opts.projectName ?? pkg.name ?? projectRoot.split("/").pop() ?? "unnamed";
|
|
20
|
-
const
|
|
20
|
+
const home = opts.homeDir ?? homedir();
|
|
21
|
+
const projectsJsonPath = join(home, ".gg", "projects.json");
|
|
22
|
+
const envFilePath = join(projectRoot, ".env");
|
|
23
|
+
const existing = findMappingByPath(projectsJsonPath, projectRoot);
|
|
24
|
+
const existingKey = readEnvKey(envFilePath, "GG_PIXEL_KEY");
|
|
25
|
+
let created;
|
|
26
|
+
let reused = false;
|
|
27
|
+
if (existing && existingKey) {
|
|
28
|
+
created = { id: existing.id, key: existingKey };
|
|
29
|
+
reused = true;
|
|
30
|
+
} else {
|
|
31
|
+
created = await createProject(fetchFn, ingestUrl, projectName);
|
|
32
|
+
}
|
|
21
33
|
const pm = detectPackageManager(projectRoot);
|
|
22
34
|
const packageInstalled = opts.skipPackageInstall ? false : runInstall(projectRoot, pm, "@kenkaiiii/gg-pixel");
|
|
23
35
|
const initFilePath = join(projectRoot, "gg-pixel.init.mjs");
|
|
24
36
|
writeFileSync(initFilePath, renderInitFile(ingestUrl), "utf8");
|
|
25
|
-
const envFilePath = join(projectRoot, ".env");
|
|
26
37
|
writeEnvKey(envFilePath, "GG_PIXEL_KEY", created.key);
|
|
27
|
-
const home = opts.homeDir ?? homedir();
|
|
28
|
-
const projectsJsonPath = join(home, ".gg", "projects.json");
|
|
29
38
|
writeProjectsMapping(projectsJsonPath, created.id, projectName, projectRoot);
|
|
39
|
+
const entryWiring = wireEntryFile(projectRoot, initFilePath, pkg);
|
|
30
40
|
return {
|
|
31
41
|
projectId: created.id,
|
|
32
42
|
projectKey: created.key,
|
|
@@ -35,9 +45,34 @@ async function install(opts = {}) {
|
|
|
35
45
|
envFilePath,
|
|
36
46
|
projectsJsonPath,
|
|
37
47
|
packageManager: pm,
|
|
38
|
-
packageInstalled
|
|
48
|
+
packageInstalled,
|
|
49
|
+
entryWiring,
|
|
50
|
+
reused
|
|
39
51
|
};
|
|
40
52
|
}
|
|
53
|
+
function findMappingByPath(projectsJsonPath, projectRoot) {
|
|
54
|
+
if (!existsSync(projectsJsonPath)) return null;
|
|
55
|
+
let map;
|
|
56
|
+
try {
|
|
57
|
+
map = JSON.parse(readFileSync(projectsJsonPath, "utf8"));
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
for (const [id, entry] of Object.entries(map)) {
|
|
62
|
+
if (entry.path === projectRoot) return { id, ...entry };
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function readEnvKey(envPath, key) {
|
|
67
|
+
if (!existsSync(envPath)) return null;
|
|
68
|
+
try {
|
|
69
|
+
const content = readFileSync(envPath, "utf8");
|
|
70
|
+
const match = new RegExp(`^${key}=(.+)$`, "m").exec(content);
|
|
71
|
+
return match?.[1]?.trim() ?? null;
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
41
76
|
function findProjectRoot(start) {
|
|
42
77
|
let dir = start;
|
|
43
78
|
for (let i = 0; i < 20; i++) {
|
|
@@ -76,7 +111,7 @@ function detectPackageManager(projectRoot) {
|
|
|
76
111
|
}
|
|
77
112
|
function runInstall(projectRoot, pm, pkg) {
|
|
78
113
|
const cmd = pm;
|
|
79
|
-
const args = pm === "npm" ? ["install", pkg] : ["add", pkg];
|
|
114
|
+
const args = pm === "npm" ? ["install", pkg, "--no-audit", "--no-fund"] : ["add", pkg];
|
|
80
115
|
const result = spawnSync(cmd, args, { cwd: projectRoot, stdio: "inherit" });
|
|
81
116
|
return result.status === 0;
|
|
82
117
|
}
|
|
@@ -100,14 +135,108 @@ function writeEnvKey(envPath, key, value) {
|
|
|
100
135
|
writeFileSync(envPath, current.replace(lineRegex, `${key}=${value}`), "utf8");
|
|
101
136
|
return;
|
|
102
137
|
}
|
|
103
|
-
const
|
|
104
|
-
appendFileSync(envPath, `${
|
|
138
|
+
const sep2 = current.endsWith("\n") || current.length === 0 ? "" : "\n";
|
|
139
|
+
appendFileSync(envPath, `${sep2}${key}=${value}
|
|
105
140
|
`, "utf8");
|
|
106
141
|
return;
|
|
107
142
|
}
|
|
108
143
|
writeFileSync(envPath, `${key}=${value}
|
|
109
144
|
`, "utf8");
|
|
110
145
|
}
|
|
146
|
+
function wireEntryFile(projectRoot, initFilePath, pkg) {
|
|
147
|
+
const entryPath = findEntryFile(projectRoot, pkg);
|
|
148
|
+
if (!entryPath) return { kind: "no_entry_found" };
|
|
149
|
+
let content;
|
|
150
|
+
try {
|
|
151
|
+
content = readFileSync(entryPath, "utf8");
|
|
152
|
+
} catch (err) {
|
|
153
|
+
return { kind: "skipped", reason: `unreadable: ${err.message}` };
|
|
154
|
+
}
|
|
155
|
+
if (content.includes("gg-pixel.init")) {
|
|
156
|
+
return { kind: "already_present", entryPath };
|
|
157
|
+
}
|
|
158
|
+
const fromDir = dirname(entryPath);
|
|
159
|
+
let spec = relative(fromDir, initFilePath).split(sep).join("/");
|
|
160
|
+
if (!spec.startsWith(".")) spec = "./" + spec;
|
|
161
|
+
const isCjs = isCommonJsEntry(entryPath, pkg);
|
|
162
|
+
const importLine = isCjs ? `require(${JSON.stringify(spec)});` : `import ${JSON.stringify(spec)};`;
|
|
163
|
+
const lines = content.split("\n");
|
|
164
|
+
let insertAt = 0;
|
|
165
|
+
if (lines[0]?.startsWith("#!")) insertAt = 1;
|
|
166
|
+
while (insertAt < lines.length && /^\s*(?:["']use strict["']|\/\/|\/\*)/.test(lines[insertAt] ?? "")) {
|
|
167
|
+
insertAt++;
|
|
168
|
+
}
|
|
169
|
+
const updated = [...lines.slice(0, insertAt), importLine, ...lines.slice(insertAt)].join("\n");
|
|
170
|
+
writeFileSync(entryPath, updated, "utf8");
|
|
171
|
+
return { kind: "injected", entryPath };
|
|
172
|
+
}
|
|
173
|
+
function findEntryFile(projectRoot, pkg) {
|
|
174
|
+
const tryPath = (rel) => {
|
|
175
|
+
const p = join(projectRoot, rel);
|
|
176
|
+
if (existsSync(p)) return p;
|
|
177
|
+
if (rel.endsWith(".js")) {
|
|
178
|
+
const ts = join(projectRoot, rel.replace(/\.js$/, ".ts"));
|
|
179
|
+
if (existsSync(ts)) return ts;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
};
|
|
183
|
+
if (typeof pkg.bin === "string") {
|
|
184
|
+
const found = tryPath(pkg.bin);
|
|
185
|
+
if (found) return found;
|
|
186
|
+
}
|
|
187
|
+
if (pkg.bin && typeof pkg.bin === "object") {
|
|
188
|
+
for (const value of Object.values(pkg.bin)) {
|
|
189
|
+
if (typeof value === "string") {
|
|
190
|
+
const found = tryPath(value);
|
|
191
|
+
if (found) return found;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (pkg.main) {
|
|
196
|
+
const found = tryPath(pkg.main);
|
|
197
|
+
if (found) return found;
|
|
198
|
+
}
|
|
199
|
+
if (pkg.module) {
|
|
200
|
+
const found = tryPath(pkg.module);
|
|
201
|
+
if (found) return found;
|
|
202
|
+
}
|
|
203
|
+
const candidates = [
|
|
204
|
+
"src/index.ts",
|
|
205
|
+
"src/index.tsx",
|
|
206
|
+
"src/index.js",
|
|
207
|
+
"src/index.mjs",
|
|
208
|
+
"src/main.ts",
|
|
209
|
+
"src/main.tsx",
|
|
210
|
+
"src/main.js",
|
|
211
|
+
"src/server.ts",
|
|
212
|
+
"src/server.js",
|
|
213
|
+
"src/app.ts",
|
|
214
|
+
"src/app.js",
|
|
215
|
+
"src/cli.ts",
|
|
216
|
+
"src/cli.js",
|
|
217
|
+
"index.ts",
|
|
218
|
+
"index.tsx",
|
|
219
|
+
"index.js",
|
|
220
|
+
"index.mjs",
|
|
221
|
+
"main.ts",
|
|
222
|
+
"main.js",
|
|
223
|
+
"server.ts",
|
|
224
|
+
"server.js",
|
|
225
|
+
"app.ts",
|
|
226
|
+
"app.js"
|
|
227
|
+
];
|
|
228
|
+
for (const c of candidates) {
|
|
229
|
+
const found = tryPath(c);
|
|
230
|
+
if (found) return found;
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
function isCommonJsEntry(entryPath, pkg) {
|
|
235
|
+
if (entryPath.endsWith(".cjs")) return true;
|
|
236
|
+
if (entryPath.endsWith(".mjs")) return false;
|
|
237
|
+
if (entryPath.endsWith(".ts") || entryPath.endsWith(".tsx")) return false;
|
|
238
|
+
return pkg.type !== "module";
|
|
239
|
+
}
|
|
111
240
|
function writeProjectsMapping(projectsJsonPath, projectId, name, path) {
|
|
112
241
|
mkdirSync(dirname(projectsJsonPath), { recursive: true });
|
|
113
242
|
let map = {};
|
|
@@ -164,19 +293,31 @@ async function main(argv) {
|
|
|
164
293
|
skipPackageInstall: args.skipPackageInstall
|
|
165
294
|
});
|
|
166
295
|
console.log("");
|
|
167
|
-
console.log("Pixel installed.");
|
|
296
|
+
console.log(result.reused ? "Pixel re-wired (existing project)." : "Pixel installed.");
|
|
168
297
|
console.log(` Project: ${result.projectName} (${result.projectId})`);
|
|
169
298
|
console.log(` Wrote: ${result.initFilePath}`);
|
|
170
299
|
console.log(` Wrote env: ${result.envFilePath}`);
|
|
171
300
|
console.log(` Mapping saved: ${result.projectsJsonPath}`);
|
|
301
|
+
switch (result.entryWiring.kind) {
|
|
302
|
+
case "injected":
|
|
303
|
+
console.log(` Wired entry: ${result.entryWiring.entryPath}`);
|
|
304
|
+
break;
|
|
305
|
+
case "already_present":
|
|
306
|
+
console.log(` Entry: ${result.entryWiring.entryPath} (already wired)`);
|
|
307
|
+
break;
|
|
308
|
+
case "no_entry_found":
|
|
309
|
+
console.log(` \u26A0 Could not auto-detect your entry file.`);
|
|
310
|
+
console.log(` Add this line to the TOP of your entry file manually:`);
|
|
311
|
+
console.log(` import "./gg-pixel.init.mjs";`);
|
|
312
|
+
break;
|
|
313
|
+
case "skipped":
|
|
314
|
+
console.log(` \u26A0 Entry wiring skipped: ${result.entryWiring.reason}`);
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
172
317
|
if (!result.packageInstalled && !args.skipPackageInstall) {
|
|
173
318
|
console.log(` \u26A0 Package install failed via ${result.packageManager}. Run it manually.`);
|
|
174
319
|
}
|
|
175
320
|
console.log("");
|
|
176
|
-
console.log("Last step: add this line to the TOP of your entry file:");
|
|
177
|
-
console.log("");
|
|
178
|
-
console.log(` import "./gg-pixel.init.mjs";`);
|
|
179
|
-
console.log("");
|
|
180
321
|
}
|
|
181
322
|
main(process.argv.slice(2)).catch((err) => {
|
|
182
323
|
console.error(err instanceof Error ? err.message : String(err));
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/install.ts","../src/cli.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\n\nexport const DEFAULT_INGEST_URL = \"https://gg-pixel-server.buzzbeamaustralia.workers.dev\";\n\nexport interface InstallOptions {\n cwd?: string;\n ingestUrl?: string;\n projectName?: string;\n fetchFn?: typeof fetch;\n skipPackageInstall?: boolean;\n homeDir?: string;\n}\n\nexport interface InstallResult {\n projectId: string;\n projectKey: string;\n projectName: string;\n initFilePath: string;\n envFilePath: string;\n projectsJsonPath: string;\n packageManager: PackageManager;\n packageInstalled: boolean;\n}\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\nexport async function install(opts: InstallOptions = {}): Promise<InstallResult> {\n const cwd = resolve(opts.cwd ?? process.cwd());\n const ingestUrl = (opts.ingestUrl ?? DEFAULT_INGEST_URL).replace(/\\/+$/, \"\");\n const fetchFn = opts.fetchFn ?? fetch;\n\n const projectRoot = findProjectRoot(cwd);\n if (!projectRoot) {\n throw new Error(`No package.json found in ${cwd} or any parent directory.`);\n }\n\n const pkgPath = join(projectRoot, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as { name?: string };\n const projectName = opts.projectName ?? pkg.name ?? projectRoot.split(\"/\").pop() ?? \"unnamed\";\n\n const created = await createProject(fetchFn, ingestUrl, projectName);\n\n const pm = detectPackageManager(projectRoot);\n const packageInstalled = opts.skipPackageInstall\n ? false\n : runInstall(projectRoot, pm, \"@kenkaiiii/gg-pixel\");\n\n const initFilePath = join(projectRoot, \"gg-pixel.init.mjs\");\n writeFileSync(initFilePath, renderInitFile(ingestUrl), \"utf8\");\n\n const envFilePath = join(projectRoot, \".env\");\n writeEnvKey(envFilePath, \"GG_PIXEL_KEY\", created.key);\n\n const home = opts.homeDir ?? homedir();\n const projectsJsonPath = join(home, \".gg\", \"projects.json\");\n writeProjectsMapping(projectsJsonPath, created.id, projectName, projectRoot);\n\n return {\n projectId: created.id,\n projectKey: created.key,\n projectName,\n initFilePath,\n envFilePath,\n projectsJsonPath,\n packageManager: pm,\n packageInstalled,\n };\n}\n\nfunction findProjectRoot(start: string): string | null {\n let dir = start;\n for (let i = 0; i < 20; i++) {\n if (existsSync(join(dir, \"package.json\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n return null;\n}\n\nasync function createProject(\n fetchFn: typeof fetch,\n ingestUrl: string,\n name: string,\n): Promise<{ id: string; key: string }> {\n const res = await fetchFn(`${ingestUrl}/api/projects`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`POST /api/projects failed: ${res.status} ${await safeText(res)}`);\n }\n const body = (await res.json()) as { id: string; key: string };\n if (!body.id || !body.key) throw new Error(\"response missing id/key\");\n return { id: body.id, key: body.key };\n}\n\nasync function safeText(r: Response): Promise<string> {\n try {\n return await r.text();\n } catch {\n return \"\";\n }\n}\n\nexport function detectPackageManager(projectRoot: string): PackageManager {\n if (existsSync(join(projectRoot, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(projectRoot, \"bun.lockb\"))) return \"bun\";\n if (existsSync(join(projectRoot, \"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\nfunction runInstall(projectRoot: string, pm: PackageManager, pkg: string): boolean {\n const cmd = pm;\n const args = pm === \"npm\" ? [\"install\", pkg] : [\"add\", pkg];\n const result = spawnSync(cmd, args, { cwd: projectRoot, stdio: \"inherit\" });\n return result.status === 0;\n}\n\nexport function renderInitFile(ingestUrl: string): string {\n return `import { initPixel } from \"@kenkaiiii/gg-pixel\";\n\nconst key = process.env.GG_PIXEL_KEY;\nif (key) {\n initPixel({\n projectKey: key,\n sink: { kind: \"http\", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },\n });\n}\n`;\n}\n\nexport function writeEnvKey(envPath: string, key: string, value: string): void {\n if (existsSync(envPath)) {\n const current = readFileSync(envPath, \"utf8\");\n const lineRegex = new RegExp(`^${key}=.*$`, \"m\");\n if (lineRegex.test(current)) {\n writeFileSync(envPath, current.replace(lineRegex, `${key}=${value}`), \"utf8\");\n return;\n }\n const sep = current.endsWith(\"\\n\") || current.length === 0 ? \"\" : \"\\n\";\n appendFileSync(envPath, `${sep}${key}=${value}\\n`, \"utf8\");\n return;\n }\n writeFileSync(envPath, `${key}=${value}\\n`, \"utf8\");\n}\n\nexport function writeProjectsMapping(\n projectsJsonPath: string,\n projectId: string,\n name: string,\n path: string,\n): void {\n mkdirSync(dirname(projectsJsonPath), { recursive: true });\n let map: Record<string, { name: string; path: string }> = {};\n if (existsSync(projectsJsonPath)) {\n try {\n map = JSON.parse(readFileSync(projectsJsonPath, \"utf8\")) as typeof map;\n } catch {\n // start fresh on corrupt file\n }\n }\n map[projectId] = { name, path };\n writeFileSync(projectsJsonPath, `${JSON.stringify(map, null, 2)}\\n`, \"utf8\");\n}\n","import { install, DEFAULT_INGEST_URL } from \"./install.js\";\n\ninterface ParsedArgs {\n command: string;\n ingestUrl?: string;\n name?: string;\n skipPackageInstall: boolean;\n help: boolean;\n}\n\nfunction parse(argv: string[]): ParsedArgs {\n const out: ParsedArgs = { command: argv[0] ?? \"\", skipPackageInstall: false, help: false };\n for (let i = 1; i < argv.length; i++) {\n const a = argv[i];\n if (a === \"--ingest-url\") out.ingestUrl = argv[++i];\n else if (a === \"--name\") out.name = argv[++i];\n else if (a === \"--skip-install\") out.skipPackageInstall = true;\n else if (a === \"--help\" || a === \"-h\") out.help = true;\n }\n return out;\n}\n\nfunction printUsage(): void {\n console.log(`gg-pixel install — drop the pixel into the current project\n\nUsage:\n gg-pixel install [--name <project-name>] [--ingest-url <url>] [--skip-install]\n\nOptions:\n --name Project name to register (defaults to package.json name)\n --ingest-url Backend URL (defaults to ${DEFAULT_INGEST_URL})\n --skip-install Skip the package-manager install step (useful for testing)\n`);\n}\n\nasync function main(argv: string[]): Promise<void> {\n const args = parse(argv);\n if (args.help || !args.command) {\n printUsage();\n return;\n }\n if (args.command !== \"install\") {\n console.error(`Unknown command: ${args.command}`);\n printUsage();\n process.exitCode = 1;\n return;\n }\n\n const result = await install({\n ingestUrl: args.ingestUrl,\n projectName: args.name,\n skipPackageInstall: args.skipPackageInstall,\n });\n\n console.log(\"\");\n console.log(\"Pixel installed.\");\n console.log(` Project: ${result.projectName} (${result.projectId})`);\n console.log(` Wrote: ${result.initFilePath}`);\n console.log(` Wrote env: ${result.envFilePath}`);\n console.log(` Mapping saved: ${result.projectsJsonPath}`);\n if (!result.packageInstalled && !args.skipPackageInstall) {\n console.log(` ⚠ Package install failed via ${result.packageManager}. Run it manually.`);\n }\n console.log(\"\");\n console.log(\"Last step: add this line to the TOP of your entry file:\");\n console.log(\"\");\n console.log(` import \"./gg-pixel.init.mjs\";`);\n console.log(\"\");\n}\n\nmain(process.argv.slice(2)).catch((err: unknown) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/install.ts","../src/cli.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\n\nexport const DEFAULT_INGEST_URL = \"https://gg-pixel-server.buzzbeamaustralia.workers.dev\";\n\nexport interface InstallOptions {\n cwd?: string;\n ingestUrl?: string;\n projectName?: string;\n fetchFn?: typeof fetch;\n skipPackageInstall?: boolean;\n homeDir?: string;\n}\n\nexport interface InstallResult {\n projectId: string;\n projectKey: string;\n projectName: string;\n initFilePath: string;\n envFilePath: string;\n projectsJsonPath: string;\n packageManager: PackageManager;\n packageInstalled: boolean;\n entryWiring: EntryWiringResult;\n /** True when an existing project mapping was reused instead of minting a fresh one. */\n reused: boolean;\n}\n\nexport type EntryWiringResult =\n | { kind: \"injected\"; entryPath: string }\n | { kind: \"already_present\"; entryPath: string }\n | { kind: \"no_entry_found\" }\n | { kind: \"skipped\"; reason: string };\n\ninterface PackageJson {\n name?: string;\n type?: string;\n main?: string;\n module?: string;\n bin?: string | Record<string, string>;\n}\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\nexport async function install(opts: InstallOptions = {}): Promise<InstallResult> {\n const cwd = resolve(opts.cwd ?? process.cwd());\n const ingestUrl = (opts.ingestUrl ?? DEFAULT_INGEST_URL).replace(/\\/+$/, \"\");\n const fetchFn = opts.fetchFn ?? fetch;\n\n const projectRoot = findProjectRoot(cwd);\n if (!projectRoot) {\n throw new Error(`No package.json found in ${cwd} or any parent directory.`);\n }\n\n const pkgPath = join(projectRoot, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as PackageJson;\n const projectName = opts.projectName ?? pkg.name ?? projectRoot.split(\"/\").pop() ?? \"unnamed\";\n\n const home = opts.homeDir ?? homedir();\n const projectsJsonPath = join(home, \".gg\", \"projects.json\");\n const envFilePath = join(projectRoot, \".env\");\n\n // Idempotency: if we already have a mapping for this directory AND the .env\n // still has its key, reuse it instead of minting a fresh project.\n const existing = findMappingByPath(projectsJsonPath, projectRoot);\n const existingKey = readEnvKey(envFilePath, \"GG_PIXEL_KEY\");\n let created: { id: string; key: string };\n let reused = false;\n if (existing && existingKey) {\n created = { id: existing.id, key: existingKey };\n reused = true;\n } else {\n created = await createProject(fetchFn, ingestUrl, projectName);\n }\n\n const pm = detectPackageManager(projectRoot);\n const packageInstalled = opts.skipPackageInstall\n ? false\n : runInstall(projectRoot, pm, \"@kenkaiiii/gg-pixel\");\n\n const initFilePath = join(projectRoot, \"gg-pixel.init.mjs\");\n writeFileSync(initFilePath, renderInitFile(ingestUrl), \"utf8\");\n\n writeEnvKey(envFilePath, \"GG_PIXEL_KEY\", created.key);\n\n writeProjectsMapping(projectsJsonPath, created.id, projectName, projectRoot);\n\n const entryWiring = wireEntryFile(projectRoot, initFilePath, pkg);\n\n return {\n projectId: created.id,\n projectKey: created.key,\n projectName,\n initFilePath,\n envFilePath,\n projectsJsonPath,\n packageManager: pm,\n packageInstalled,\n entryWiring,\n reused,\n };\n}\n\nfunction findMappingByPath(\n projectsJsonPath: string,\n projectRoot: string,\n): { id: string; name: string; path: string } | null {\n if (!existsSync(projectsJsonPath)) return null;\n let map: Record<string, { name: string; path: string }>;\n try {\n map = JSON.parse(readFileSync(projectsJsonPath, \"utf8\")) as typeof map;\n } catch {\n return null;\n }\n for (const [id, entry] of Object.entries(map)) {\n if (entry.path === projectRoot) return { id, ...entry };\n }\n return null;\n}\n\nfunction readEnvKey(envPath: string, key: string): string | null {\n if (!existsSync(envPath)) return null;\n try {\n const content = readFileSync(envPath, \"utf8\");\n const match = new RegExp(`^${key}=(.+)$`, \"m\").exec(content);\n return match?.[1]?.trim() ?? null;\n } catch {\n return null;\n }\n}\n\nfunction findProjectRoot(start: string): string | null {\n let dir = start;\n for (let i = 0; i < 20; i++) {\n if (existsSync(join(dir, \"package.json\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n return null;\n}\n\nasync function createProject(\n fetchFn: typeof fetch,\n ingestUrl: string,\n name: string,\n): Promise<{ id: string; key: string }> {\n const res = await fetchFn(`${ingestUrl}/api/projects`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`POST /api/projects failed: ${res.status} ${await safeText(res)}`);\n }\n const body = (await res.json()) as { id: string; key: string };\n if (!body.id || !body.key) throw new Error(\"response missing id/key\");\n return { id: body.id, key: body.key };\n}\n\nasync function safeText(r: Response): Promise<string> {\n try {\n return await r.text();\n } catch {\n return \"\";\n }\n}\n\nexport function detectPackageManager(projectRoot: string): PackageManager {\n if (existsSync(join(projectRoot, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(projectRoot, \"bun.lockb\"))) return \"bun\";\n if (existsSync(join(projectRoot, \"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\nfunction runInstall(projectRoot: string, pm: PackageManager, pkg: string): boolean {\n const cmd = pm;\n // npm prints `npm audit` warnings + `npm fund` solicitations on every install.\n // That output is about the user's *existing* project — irrelevant to pixel.\n // The other package managers don't show this noise by default.\n const args = pm === \"npm\" ? [\"install\", pkg, \"--no-audit\", \"--no-fund\"] : [\"add\", pkg];\n const result = spawnSync(cmd, args, { cwd: projectRoot, stdio: \"inherit\" });\n return result.status === 0;\n}\n\nexport function renderInitFile(ingestUrl: string): string {\n return `import { initPixel } from \"@kenkaiiii/gg-pixel\";\n\nconst key = process.env.GG_PIXEL_KEY;\nif (key) {\n initPixel({\n projectKey: key,\n sink: { kind: \"http\", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },\n });\n}\n`;\n}\n\nexport function writeEnvKey(envPath: string, key: string, value: string): void {\n if (existsSync(envPath)) {\n const current = readFileSync(envPath, \"utf8\");\n const lineRegex = new RegExp(`^${key}=.*$`, \"m\");\n if (lineRegex.test(current)) {\n writeFileSync(envPath, current.replace(lineRegex, `${key}=${value}`), \"utf8\");\n return;\n }\n const sep = current.endsWith(\"\\n\") || current.length === 0 ? \"\" : \"\\n\";\n appendFileSync(envPath, `${sep}${key}=${value}\\n`, \"utf8\");\n return;\n }\n writeFileSync(envPath, `${key}=${value}\\n`, \"utf8\");\n}\n\nexport function wireEntryFile(\n projectRoot: string,\n initFilePath: string,\n pkg: PackageJson,\n): EntryWiringResult {\n const entryPath = findEntryFile(projectRoot, pkg);\n if (!entryPath) return { kind: \"no_entry_found\" };\n\n let content: string;\n try {\n content = readFileSync(entryPath, \"utf8\");\n } catch (err) {\n return { kind: \"skipped\", reason: `unreadable: ${(err as Error).message}` };\n }\n\n if (content.includes(\"gg-pixel.init\")) {\n return { kind: \"already_present\", entryPath };\n }\n\n // Compute import specifier relative to the entry file.\n const fromDir = dirname(entryPath);\n let spec = relative(fromDir, initFilePath).split(sep).join(\"/\");\n if (!spec.startsWith(\".\")) spec = \"./\" + spec;\n\n const isCjs = isCommonJsEntry(entryPath, pkg);\n const importLine = isCjs\n ? `require(${JSON.stringify(spec)});`\n : `import ${JSON.stringify(spec)};`;\n\n // Inject at the top — after a shebang line and any leading \"use strict\",\n // but before all other code, so pixel hooks run before anything else.\n const lines = content.split(\"\\n\");\n let insertAt = 0;\n if (lines[0]?.startsWith(\"#!\")) insertAt = 1;\n while (\n insertAt < lines.length &&\n /^\\s*(?:[\"']use strict[\"']|\\/\\/|\\/\\*)/.test(lines[insertAt] ?? \"\")\n ) {\n insertAt++;\n }\n\n const updated = [...lines.slice(0, insertAt), importLine, ...lines.slice(insertAt)].join(\"\\n\");\n writeFileSync(entryPath, updated, \"utf8\");\n return { kind: \"injected\", entryPath };\n}\n\nfunction findEntryFile(projectRoot: string, pkg: PackageJson): string | null {\n const tryPath = (rel: string): string | null => {\n const p = join(projectRoot, rel);\n if (existsSync(p)) return p;\n // If user pointed `main` at .js but only the .ts source exists (common in TS projects).\n if (rel.endsWith(\".js\")) {\n const ts = join(projectRoot, rel.replace(/\\.js$/, \".ts\"));\n if (existsSync(ts)) return ts;\n }\n return null;\n };\n\n if (typeof pkg.bin === \"string\") {\n const found = tryPath(pkg.bin);\n if (found) return found;\n }\n if (pkg.bin && typeof pkg.bin === \"object\") {\n for (const value of Object.values(pkg.bin)) {\n if (typeof value === \"string\") {\n const found = tryPath(value);\n if (found) return found;\n }\n }\n }\n if (pkg.main) {\n const found = tryPath(pkg.main);\n if (found) return found;\n }\n if (pkg.module) {\n const found = tryPath(pkg.module);\n if (found) return found;\n }\n\n // Fall back to common conventions.\n const candidates = [\n \"src/index.ts\",\n \"src/index.tsx\",\n \"src/index.js\",\n \"src/index.mjs\",\n \"src/main.ts\",\n \"src/main.tsx\",\n \"src/main.js\",\n \"src/server.ts\",\n \"src/server.js\",\n \"src/app.ts\",\n \"src/app.js\",\n \"src/cli.ts\",\n \"src/cli.js\",\n \"index.ts\",\n \"index.tsx\",\n \"index.js\",\n \"index.mjs\",\n \"main.ts\",\n \"main.js\",\n \"server.ts\",\n \"server.js\",\n \"app.ts\",\n \"app.js\",\n ];\n for (const c of candidates) {\n const found = tryPath(c);\n if (found) return found;\n }\n return null;\n}\n\nfunction isCommonJsEntry(entryPath: string, pkg: PackageJson): boolean {\n if (entryPath.endsWith(\".cjs\")) return true;\n if (entryPath.endsWith(\".mjs\")) return false;\n if (entryPath.endsWith(\".ts\") || entryPath.endsWith(\".tsx\")) return false;\n // .js → governed by package.json type (default is \"commonjs\")\n return pkg.type !== \"module\";\n}\n\nexport function writeProjectsMapping(\n projectsJsonPath: string,\n projectId: string,\n name: string,\n path: string,\n): void {\n mkdirSync(dirname(projectsJsonPath), { recursive: true });\n let map: Record<string, { name: string; path: string }> = {};\n if (existsSync(projectsJsonPath)) {\n try {\n map = JSON.parse(readFileSync(projectsJsonPath, \"utf8\")) as typeof map;\n } catch {\n // start fresh on corrupt file\n }\n }\n map[projectId] = { name, path };\n writeFileSync(projectsJsonPath, `${JSON.stringify(map, null, 2)}\\n`, \"utf8\");\n}\n","import { install, DEFAULT_INGEST_URL } from \"./install.js\";\n\ninterface ParsedArgs {\n command: string;\n ingestUrl?: string;\n name?: string;\n skipPackageInstall: boolean;\n help: boolean;\n}\n\nfunction parse(argv: string[]): ParsedArgs {\n const out: ParsedArgs = { command: argv[0] ?? \"\", skipPackageInstall: false, help: false };\n for (let i = 1; i < argv.length; i++) {\n const a = argv[i];\n if (a === \"--ingest-url\") out.ingestUrl = argv[++i];\n else if (a === \"--name\") out.name = argv[++i];\n else if (a === \"--skip-install\") out.skipPackageInstall = true;\n else if (a === \"--help\" || a === \"-h\") out.help = true;\n }\n return out;\n}\n\nfunction printUsage(): void {\n console.log(`gg-pixel install — drop the pixel into the current project\n\nUsage:\n gg-pixel install [--name <project-name>] [--ingest-url <url>] [--skip-install]\n\nOptions:\n --name Project name to register (defaults to package.json name)\n --ingest-url Backend URL (defaults to ${DEFAULT_INGEST_URL})\n --skip-install Skip the package-manager install step (useful for testing)\n`);\n}\n\nasync function main(argv: string[]): Promise<void> {\n const args = parse(argv);\n if (args.help || !args.command) {\n printUsage();\n return;\n }\n if (args.command !== \"install\") {\n console.error(`Unknown command: ${args.command}`);\n printUsage();\n process.exitCode = 1;\n return;\n }\n\n const result = await install({\n ingestUrl: args.ingestUrl,\n projectName: args.name,\n skipPackageInstall: args.skipPackageInstall,\n });\n\n console.log(\"\");\n console.log(result.reused ? \"Pixel re-wired (existing project).\" : \"Pixel installed.\");\n console.log(` Project: ${result.projectName} (${result.projectId})`);\n console.log(` Wrote: ${result.initFilePath}`);\n console.log(` Wrote env: ${result.envFilePath}`);\n console.log(` Mapping saved: ${result.projectsJsonPath}`);\n switch (result.entryWiring.kind) {\n case \"injected\":\n console.log(` Wired entry: ${result.entryWiring.entryPath}`);\n break;\n case \"already_present\":\n console.log(` Entry: ${result.entryWiring.entryPath} (already wired)`);\n break;\n case \"no_entry_found\":\n console.log(` ⚠ Could not auto-detect your entry file.`);\n console.log(` Add this line to the TOP of your entry file manually:`);\n console.log(` import \"./gg-pixel.init.mjs\";`);\n break;\n case \"skipped\":\n console.log(` ⚠ Entry wiring skipped: ${result.entryWiring.reason}`);\n break;\n }\n if (!result.packageInstalled && !args.skipPackageInstall) {\n console.log(` ⚠ Package install failed via ${result.packageManager}. Run it manually.`);\n }\n console.log(\"\");\n}\n\nmain(process.argv.slice(2)).catch((err: unknown) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exitCode = 1;\n});\n"],"mappings":"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","names":["sep"]}
|
package/dist/index.cjs
CHANGED
|
@@ -420,16 +420,26 @@ async function install(opts = {}) {
|
|
|
420
420
|
const pkgPath = (0, import_node_path2.join)(projectRoot, "package.json");
|
|
421
421
|
const pkg = JSON.parse((0, import_node_fs3.readFileSync)(pkgPath, "utf8"));
|
|
422
422
|
const projectName = opts.projectName ?? pkg.name ?? projectRoot.split("/").pop() ?? "unnamed";
|
|
423
|
-
const
|
|
423
|
+
const home = opts.homeDir ?? (0, import_node_os2.homedir)();
|
|
424
|
+
const projectsJsonPath = (0, import_node_path2.join)(home, ".gg", "projects.json");
|
|
425
|
+
const envFilePath = (0, import_node_path2.join)(projectRoot, ".env");
|
|
426
|
+
const existing = findMappingByPath(projectsJsonPath, projectRoot);
|
|
427
|
+
const existingKey = readEnvKey(envFilePath, "GG_PIXEL_KEY");
|
|
428
|
+
let created;
|
|
429
|
+
let reused = false;
|
|
430
|
+
if (existing && existingKey) {
|
|
431
|
+
created = { id: existing.id, key: existingKey };
|
|
432
|
+
reused = true;
|
|
433
|
+
} else {
|
|
434
|
+
created = await createProject(fetchFn, ingestUrl, projectName);
|
|
435
|
+
}
|
|
424
436
|
const pm = detectPackageManager(projectRoot);
|
|
425
437
|
const packageInstalled = opts.skipPackageInstall ? false : runInstall(projectRoot, pm, "@kenkaiiii/gg-pixel");
|
|
426
438
|
const initFilePath = (0, import_node_path2.join)(projectRoot, "gg-pixel.init.mjs");
|
|
427
439
|
(0, import_node_fs3.writeFileSync)(initFilePath, renderInitFile(ingestUrl), "utf8");
|
|
428
|
-
const envFilePath = (0, import_node_path2.join)(projectRoot, ".env");
|
|
429
440
|
writeEnvKey(envFilePath, "GG_PIXEL_KEY", created.key);
|
|
430
|
-
const home = opts.homeDir ?? (0, import_node_os2.homedir)();
|
|
431
|
-
const projectsJsonPath = (0, import_node_path2.join)(home, ".gg", "projects.json");
|
|
432
441
|
writeProjectsMapping(projectsJsonPath, created.id, projectName, projectRoot);
|
|
442
|
+
const entryWiring = wireEntryFile(projectRoot, initFilePath, pkg);
|
|
433
443
|
return {
|
|
434
444
|
projectId: created.id,
|
|
435
445
|
projectKey: created.key,
|
|
@@ -438,9 +448,34 @@ async function install(opts = {}) {
|
|
|
438
448
|
envFilePath,
|
|
439
449
|
projectsJsonPath,
|
|
440
450
|
packageManager: pm,
|
|
441
|
-
packageInstalled
|
|
451
|
+
packageInstalled,
|
|
452
|
+
entryWiring,
|
|
453
|
+
reused
|
|
442
454
|
};
|
|
443
455
|
}
|
|
456
|
+
function findMappingByPath(projectsJsonPath, projectRoot) {
|
|
457
|
+
if (!(0, import_node_fs3.existsSync)(projectsJsonPath)) return null;
|
|
458
|
+
let map;
|
|
459
|
+
try {
|
|
460
|
+
map = JSON.parse((0, import_node_fs3.readFileSync)(projectsJsonPath, "utf8"));
|
|
461
|
+
} catch {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
for (const [id, entry] of Object.entries(map)) {
|
|
465
|
+
if (entry.path === projectRoot) return { id, ...entry };
|
|
466
|
+
}
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
function readEnvKey(envPath, key) {
|
|
470
|
+
if (!(0, import_node_fs3.existsSync)(envPath)) return null;
|
|
471
|
+
try {
|
|
472
|
+
const content = (0, import_node_fs3.readFileSync)(envPath, "utf8");
|
|
473
|
+
const match = new RegExp(`^${key}=(.+)$`, "m").exec(content);
|
|
474
|
+
return match?.[1]?.trim() ?? null;
|
|
475
|
+
} catch {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
444
479
|
function findProjectRoot(start) {
|
|
445
480
|
let dir = start;
|
|
446
481
|
for (let i = 0; i < 20; i++) {
|
|
@@ -479,7 +514,7 @@ function detectPackageManager(projectRoot) {
|
|
|
479
514
|
}
|
|
480
515
|
function runInstall(projectRoot, pm, pkg) {
|
|
481
516
|
const cmd = pm;
|
|
482
|
-
const args = pm === "npm" ? ["install", pkg] : ["add", pkg];
|
|
517
|
+
const args = pm === "npm" ? ["install", pkg, "--no-audit", "--no-fund"] : ["add", pkg];
|
|
483
518
|
const result = (0, import_node_child_process.spawnSync)(cmd, args, { cwd: projectRoot, stdio: "inherit" });
|
|
484
519
|
return result.status === 0;
|
|
485
520
|
}
|
|
@@ -503,14 +538,108 @@ function writeEnvKey(envPath, key, value) {
|
|
|
503
538
|
(0, import_node_fs3.writeFileSync)(envPath, current.replace(lineRegex, `${key}=${value}`), "utf8");
|
|
504
539
|
return;
|
|
505
540
|
}
|
|
506
|
-
const
|
|
507
|
-
(0, import_node_fs3.appendFileSync)(envPath, `${
|
|
541
|
+
const sep2 = current.endsWith("\n") || current.length === 0 ? "" : "\n";
|
|
542
|
+
(0, import_node_fs3.appendFileSync)(envPath, `${sep2}${key}=${value}
|
|
508
543
|
`, "utf8");
|
|
509
544
|
return;
|
|
510
545
|
}
|
|
511
546
|
(0, import_node_fs3.writeFileSync)(envPath, `${key}=${value}
|
|
512
547
|
`, "utf8");
|
|
513
548
|
}
|
|
549
|
+
function wireEntryFile(projectRoot, initFilePath, pkg) {
|
|
550
|
+
const entryPath = findEntryFile(projectRoot, pkg);
|
|
551
|
+
if (!entryPath) return { kind: "no_entry_found" };
|
|
552
|
+
let content;
|
|
553
|
+
try {
|
|
554
|
+
content = (0, import_node_fs3.readFileSync)(entryPath, "utf8");
|
|
555
|
+
} catch (err) {
|
|
556
|
+
return { kind: "skipped", reason: `unreadable: ${err.message}` };
|
|
557
|
+
}
|
|
558
|
+
if (content.includes("gg-pixel.init")) {
|
|
559
|
+
return { kind: "already_present", entryPath };
|
|
560
|
+
}
|
|
561
|
+
const fromDir = (0, import_node_path2.dirname)(entryPath);
|
|
562
|
+
let spec = (0, import_node_path2.relative)(fromDir, initFilePath).split(import_node_path2.sep).join("/");
|
|
563
|
+
if (!spec.startsWith(".")) spec = "./" + spec;
|
|
564
|
+
const isCjs = isCommonJsEntry(entryPath, pkg);
|
|
565
|
+
const importLine = isCjs ? `require(${JSON.stringify(spec)});` : `import ${JSON.stringify(spec)};`;
|
|
566
|
+
const lines = content.split("\n");
|
|
567
|
+
let insertAt = 0;
|
|
568
|
+
if (lines[0]?.startsWith("#!")) insertAt = 1;
|
|
569
|
+
while (insertAt < lines.length && /^\s*(?:["']use strict["']|\/\/|\/\*)/.test(lines[insertAt] ?? "")) {
|
|
570
|
+
insertAt++;
|
|
571
|
+
}
|
|
572
|
+
const updated = [...lines.slice(0, insertAt), importLine, ...lines.slice(insertAt)].join("\n");
|
|
573
|
+
(0, import_node_fs3.writeFileSync)(entryPath, updated, "utf8");
|
|
574
|
+
return { kind: "injected", entryPath };
|
|
575
|
+
}
|
|
576
|
+
function findEntryFile(projectRoot, pkg) {
|
|
577
|
+
const tryPath = (rel) => {
|
|
578
|
+
const p = (0, import_node_path2.join)(projectRoot, rel);
|
|
579
|
+
if ((0, import_node_fs3.existsSync)(p)) return p;
|
|
580
|
+
if (rel.endsWith(".js")) {
|
|
581
|
+
const ts = (0, import_node_path2.join)(projectRoot, rel.replace(/\.js$/, ".ts"));
|
|
582
|
+
if ((0, import_node_fs3.existsSync)(ts)) return ts;
|
|
583
|
+
}
|
|
584
|
+
return null;
|
|
585
|
+
};
|
|
586
|
+
if (typeof pkg.bin === "string") {
|
|
587
|
+
const found = tryPath(pkg.bin);
|
|
588
|
+
if (found) return found;
|
|
589
|
+
}
|
|
590
|
+
if (pkg.bin && typeof pkg.bin === "object") {
|
|
591
|
+
for (const value of Object.values(pkg.bin)) {
|
|
592
|
+
if (typeof value === "string") {
|
|
593
|
+
const found = tryPath(value);
|
|
594
|
+
if (found) return found;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (pkg.main) {
|
|
599
|
+
const found = tryPath(pkg.main);
|
|
600
|
+
if (found) return found;
|
|
601
|
+
}
|
|
602
|
+
if (pkg.module) {
|
|
603
|
+
const found = tryPath(pkg.module);
|
|
604
|
+
if (found) return found;
|
|
605
|
+
}
|
|
606
|
+
const candidates = [
|
|
607
|
+
"src/index.ts",
|
|
608
|
+
"src/index.tsx",
|
|
609
|
+
"src/index.js",
|
|
610
|
+
"src/index.mjs",
|
|
611
|
+
"src/main.ts",
|
|
612
|
+
"src/main.tsx",
|
|
613
|
+
"src/main.js",
|
|
614
|
+
"src/server.ts",
|
|
615
|
+
"src/server.js",
|
|
616
|
+
"src/app.ts",
|
|
617
|
+
"src/app.js",
|
|
618
|
+
"src/cli.ts",
|
|
619
|
+
"src/cli.js",
|
|
620
|
+
"index.ts",
|
|
621
|
+
"index.tsx",
|
|
622
|
+
"index.js",
|
|
623
|
+
"index.mjs",
|
|
624
|
+
"main.ts",
|
|
625
|
+
"main.js",
|
|
626
|
+
"server.ts",
|
|
627
|
+
"server.js",
|
|
628
|
+
"app.ts",
|
|
629
|
+
"app.js"
|
|
630
|
+
];
|
|
631
|
+
for (const c of candidates) {
|
|
632
|
+
const found = tryPath(c);
|
|
633
|
+
if (found) return found;
|
|
634
|
+
}
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
function isCommonJsEntry(entryPath, pkg) {
|
|
638
|
+
if (entryPath.endsWith(".cjs")) return true;
|
|
639
|
+
if (entryPath.endsWith(".mjs")) return false;
|
|
640
|
+
if (entryPath.endsWith(".ts") || entryPath.endsWith(".tsx")) return false;
|
|
641
|
+
return pkg.type !== "module";
|
|
642
|
+
}
|
|
514
643
|
function writeProjectsMapping(projectsJsonPath, projectId, name, path) {
|
|
515
644
|
(0, import_node_fs3.mkdirSync)((0, import_node_path2.dirname)(projectsJsonPath), { recursive: true });
|
|
516
645
|
let map = {};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/adapters/node.ts","../src/core/stack.ts","../src/core/fingerprint.ts","../src/code-context.ts","../src/core/queue.ts","../src/core/sinks/http.ts","../src/core/sinks/local-sqlite.ts","../src/install.ts"],"sourcesContent":["import { installNodeAdapter, type NodeAdapter } from \"./adapters/node.js\";\nimport { HttpSink } from \"./core/sinks/http.js\";\nimport { LocalSqliteSink } from \"./core/sinks/local-sqlite.js\";\nimport type { PixelOptions, ReportInput, Sink, SinkConfig } from \"./core/types.js\";\n\nlet active: NodeAdapter | null = null;\n\nexport function initPixel(options: PixelOptions): NodeAdapter {\n if (active) {\n throw new Error(\"gg-pixel is already initialized; call closePixel() first\");\n }\n const sink = buildSink(options.sink);\n active = installNodeAdapter({\n projectKey: options.projectKey,\n runtime: options.runtime ?? defaultRuntime(),\n sink,\n captureConsoleErrors: options.captureConsoleErrors ?? true,\n captureConsoleWarnings: options.captureConsoleWarnings ?? false,\n captureUnhandledRejections: options.captureUnhandledRejections ?? true,\n captureUncaughtExceptions: options.captureUncaughtExceptions ?? true,\n });\n return active;\n}\n\nexport function reportPixel(input: ReportInput): void {\n if (!active) return;\n active.report(input);\n}\n\nexport async function flushPixel(): Promise<void> {\n if (!active) return;\n await active.flush();\n}\n\nexport async function closePixel(): Promise<void> {\n if (!active) return;\n await active.close();\n active = null;\n}\n\nfunction buildSink(config: SinkConfig): Sink {\n switch (config.kind) {\n case \"http\":\n return new HttpSink(config.ingestUrl, config.fetchFn);\n case \"local\":\n return new LocalSqliteSink(config.path);\n case \"custom\":\n return config.sink;\n }\n}\n\nfunction defaultRuntime(): string {\n const v = process.versions.node;\n return `node-${v}`;\n}\n\nexport type {\n Level,\n PixelOptions,\n ReportInput,\n Sink,\n SinkConfig,\n StackFrame,\n CodeContext,\n WireEvent,\n} from \"./core/types.js\";\n\nexport { install, DEFAULT_INGEST_URL } from \"./install.js\";\nexport type { InstallOptions, InstallResult, PackageManager } from \"./install.js\";\n","import { randomUUID } from \"node:crypto\";\nimport { parseStack } from \"../core/stack.js\";\nimport { fingerprint } from \"../core/fingerprint.js\";\nimport { captureCodeContext } from \"../code-context.js\";\nimport { EventQueue } from \"../core/queue.js\";\nimport type { Level, ReportInput, Sink, WireEvent } from \"../core/types.js\";\n\nexport interface NodeAdapterOptions {\n projectKey: string;\n runtime: string;\n sink: Sink;\n captureConsoleErrors: boolean;\n captureConsoleWarnings: boolean;\n captureUnhandledRejections: boolean;\n captureUncaughtExceptions: boolean;\n}\n\nexport interface NodeAdapter {\n report(input: ReportInput): void;\n flush(): Promise<void>;\n close(): Promise<void>;\n}\n\nexport function installNodeAdapter(opts: NodeAdapterOptions): NodeAdapter {\n const queue = new EventQueue(opts.sink);\n const detach: Array<() => void> = [];\n\n const enqueueError = (err: unknown, level: Level, manual: boolean) => {\n try {\n const event = buildEvent(err, level, manual, opts.projectKey, opts.runtime);\n queue.enqueue(event);\n } catch {\n // never let pixel break the host program\n }\n };\n\n const enqueueErrorSync = (err: unknown, level: Level, manual: boolean) => {\n try {\n const event = buildEvent(err, level, manual, opts.projectKey, opts.runtime);\n queue.enqueueSync(event);\n } catch {\n // never let pixel break the host program\n }\n };\n\n if (opts.captureUncaughtExceptions) {\n const handler = (err: Error) => enqueueErrorSync(err, \"fatal\", false);\n process.on(\"uncaughtExceptionMonitor\", handler);\n detach.push(() => process.off(\"uncaughtExceptionMonitor\", handler));\n }\n\n if (opts.captureUnhandledRejections) {\n const handler = (reason: unknown) => enqueueErrorSync(reason, \"error\", false);\n process.on(\"unhandledRejection\", handler);\n detach.push(() => process.off(\"unhandledRejection\", handler));\n }\n\n if (opts.captureConsoleErrors) {\n detach.push(patchConsole(\"error\", (args) => enqueueError(consoleError(args), \"error\", false)));\n }\n\n if (opts.captureConsoleWarnings) {\n detach.push(patchConsole(\"warn\", (args) => enqueueError(consoleError(args), \"warning\", false)));\n }\n\n const onBeforeExit = () => {\n void queue.flush();\n };\n process.on(\"beforeExit\", onBeforeExit);\n detach.push(() => process.off(\"beforeExit\", onBeforeExit));\n\n return {\n report(input: ReportInput) {\n const level = input.level ?? \"error\";\n if (input.error !== undefined) {\n try {\n const event = buildEvent(input.error, level, true, opts.projectKey, opts.runtime);\n if (input.message) event.message = input.message;\n queue.enqueue(event);\n } catch {\n // never let pixel break the host program\n }\n return;\n }\n const err = new Error(input.message);\n err.name = \"ManualReport\";\n enqueueError(err, level, true);\n },\n flush: () => queue.flush(),\n close: async () => {\n for (const fn of detach) fn();\n await queue.close();\n },\n };\n}\n\nfunction buildEvent(\n err: unknown,\n level: Level,\n manual: boolean,\n projectKey: string,\n runtime: string,\n): WireEvent {\n const { type, message, stackString } = normalize(err);\n const stack = parseStack(stackString);\n return {\n event_id: randomUUID(),\n project_key: projectKey,\n fingerprint: fingerprint(type, stack),\n type,\n message,\n stack,\n code_context: captureCodeContext(stack),\n runtime,\n manual_report: manual,\n level,\n occurred_at: new Date().toISOString(),\n };\n}\n\nfunction normalize(err: unknown): { type: string; message: string; stackString?: string } {\n if (err instanceof Error) {\n return { type: err.name || \"Error\", message: err.message, stackString: err.stack };\n }\n if (typeof err === \"string\") {\n return { type: \"StringError\", message: err };\n }\n try {\n return { type: \"UnknownError\", message: JSON.stringify(err) };\n } catch {\n return { type: \"UnknownError\", message: String(err) };\n }\n}\n\nfunction consoleError(args: unknown[]): unknown {\n for (const a of args) if (a instanceof Error) return a;\n return new Error(args.map(stringify).join(\" \"));\n}\n\nfunction stringify(x: unknown): string {\n if (typeof x === \"string\") return x;\n try {\n return JSON.stringify(x);\n } catch {\n return String(x);\n }\n}\n\ntype ConsoleMethod = \"error\" | \"warn\";\n\nfunction patchConsole(method: ConsoleMethod, onCall: (args: unknown[]) => void): () => void {\n const original = console[method];\n console[method] = (...args: unknown[]) => {\n try {\n onCall(args);\n } catch {\n // never let pixel break the host program\n }\n original.apply(console, args);\n };\n return () => {\n console[method] = original;\n };\n}\n","import type { StackFrame } from \"./types.js\";\n\nconst FRAME_WITH_FN = /^\\s*at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)\\s*$/;\nconst FRAME_NO_FN = /^\\s*at\\s+(.+?):(\\d+):(\\d+)\\s*$/;\n\nexport function parseStack(stack: string | undefined): StackFrame[] {\n if (!stack) return [];\n const frames: StackFrame[] = [];\n for (const line of stack.split(\"\\n\")) {\n const withFn = FRAME_WITH_FN.exec(line);\n if (withFn) {\n const file = withFn[2];\n frames.push({\n fn: withFn[1],\n file,\n line: Number(withFn[3]),\n col: Number(withFn[4]),\n in_app: isInApp(file),\n });\n continue;\n }\n const noFn = FRAME_NO_FN.exec(line);\n if (noFn) {\n const file = noFn[1];\n frames.push({\n fn: \"<anon>\",\n file,\n line: Number(noFn[2]),\n col: Number(noFn[3]),\n in_app: isInApp(file),\n });\n }\n }\n return frames;\n}\n\nfunction isInApp(file: string): boolean {\n if (!file) return false;\n if (file.startsWith(\"node:\")) return false;\n if (file.startsWith(\"internal/\")) return false;\n if (file.includes(\"/node_modules/\")) return false;\n return true;\n}\n","import { createHash } from \"node:crypto\";\nimport type { StackFrame } from \"./types.js\";\n\nexport function fingerprint(type: string, stack: StackFrame[]): string {\n const top = stack[0];\n const normalized = top\n ? `${type}|${normalizeFile(top.file)}|${top.fn || \"<anon>\"}|${top.line}`\n : `${type}|<no-stack>`;\n return createHash(\"sha256\").update(normalized).digest(\"hex\").slice(0, 16);\n}\n\nfunction normalizeFile(file: string): string {\n return file\n .replace(/^file:\\/\\//, \"\")\n .replace(/^.*\\/node_modules\\//, \"node_modules/\")\n .replace(/\\?.*$/, \"\");\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport type { CodeContext, StackFrame } from \"./core/types.js\";\n\nconst WINDOW = 2;\nconst cache = new Map<string, string[] | null>();\n\nexport function captureCodeContext(stack: StackFrame[]): CodeContext | null {\n const top = stack.find((f) => isReadable(f.file));\n if (!top) return null;\n const lines = loadLines(top.file);\n if (!lines) return null;\n const start = Math.max(0, top.line - 1 - WINDOW);\n const end = Math.min(lines.length, top.line + WINDOW);\n return {\n file: top.file,\n error_line: top.line,\n lines: lines.slice(start, end),\n };\n}\n\nfunction isReadable(file: string): boolean {\n if (!file || file.startsWith(\"node:\")) return false;\n if (file.includes(\"/node_modules/\")) return false;\n return file.startsWith(\"/\") || file.startsWith(\"file://\");\n}\n\nfunction loadLines(file: string): string[] | null {\n const path = file.replace(/^file:\\/\\//, \"\");\n if (cache.has(path)) return cache.get(path) ?? null;\n if (!existsSync(path)) {\n cache.set(path, null);\n return null;\n }\n try {\n const lines = readFileSync(path, \"utf8\").split(\"\\n\");\n cache.set(path, lines);\n return lines;\n } catch {\n cache.set(path, null);\n return null;\n }\n}\n","import type { Sink, WireEvent } from \"./types.js\";\n\nconst MAX_BUFFER = 100;\nconst BASE_DELAY_MS = 200;\nconst MAX_DELAY_MS = 5_000;\n\nexport class EventQueue {\n private readonly buffer: WireEvent[] = [];\n private draining = false;\n private closed = false;\n\n constructor(private readonly sink: Sink) {}\n\n enqueue(event: WireEvent): void {\n if (this.closed) return;\n if (this.buffer.length >= MAX_BUFFER) {\n this.buffer.shift();\n }\n this.buffer.push(event);\n void this.drain();\n }\n\n enqueueSync(event: WireEvent): void {\n if (this.closed) return;\n if (this.sink.emitSync) {\n try {\n this.sink.emitSync(event);\n return;\n } catch {\n // fall through to async path\n }\n }\n this.enqueue(event);\n }\n\n async flush(): Promise<void> {\n while (this.buffer.length > 0 || this.draining) {\n await new Promise((r) => setTimeout(r, 10));\n }\n }\n\n async close(): Promise<void> {\n await this.flush();\n this.closed = true;\n if (this.sink.close) await this.sink.close();\n }\n\n private async drain(): Promise<void> {\n if (this.draining) return;\n this.draining = true;\n let attempt = 0;\n while (this.buffer.length > 0) {\n const event = this.buffer[0];\n try {\n await this.sink.emit(event);\n this.buffer.shift();\n attempt = 0;\n } catch {\n attempt++;\n if (attempt >= 5) {\n this.buffer.shift();\n attempt = 0;\n continue;\n }\n const delay = Math.min(BASE_DELAY_MS * 2 ** (attempt - 1), MAX_DELAY_MS);\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n this.draining = false;\n }\n}\n","import type { Sink, WireEvent } from \"../types.js\";\n\nexport class HttpSink implements Sink {\n constructor(\n private readonly ingestUrl: string,\n private readonly fetchFn: typeof fetch = fetch,\n ) {}\n\n async emit(event: WireEvent): Promise<void> {\n const res = await this.fetchFn(this.ingestUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-pixel-key\": event.project_key,\n },\n body: JSON.stringify(event),\n });\n if (!res.ok) {\n throw new Error(`pixel ingest failed: ${res.status}`);\n }\n }\n}\n","import { homedir } from \"node:os\";\nimport { mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport Database from \"better-sqlite3\";\nimport type { Sink, WireEvent } from \"../types.js\";\n\nconst SCHEMA = `\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n event_id TEXT NOT NULL UNIQUE,\n project_key TEXT NOT NULL,\n fingerprint TEXT NOT NULL,\n type TEXT NOT NULL,\n message TEXT NOT NULL,\n stack TEXT NOT NULL,\n code_context TEXT,\n runtime TEXT NOT NULL,\n manual_report INTEGER NOT NULL DEFAULT 0,\n level TEXT NOT NULL,\n occurred_at TEXT NOT NULL,\n ingested_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS events_fingerprint ON events(project_key, fingerprint);\n CREATE INDEX IF NOT EXISTS events_occurred ON events(occurred_at);\n`;\n\nexport class LocalSqliteSink implements Sink {\n private readonly db: Database.Database;\n private readonly insert: Database.Statement;\n\n constructor(path?: string) {\n const resolved = path ?? join(homedir(), \".gg\", \"errors.db\");\n mkdirSync(dirname(resolved), { recursive: true });\n this.db = new Database(resolved);\n this.db.pragma(\"journal_mode = WAL\");\n this.db.exec(SCHEMA);\n this.insert = this.db.prepare(`\n INSERT INTO events (\n event_id, project_key, fingerprint, type, message, stack, code_context,\n runtime, manual_report, level, occurred_at\n ) VALUES (\n @event_id, @project_key, @fingerprint, @type, @message, @stack, @code_context,\n @runtime, @manual_report, @level, @occurred_at\n )\n `);\n }\n\n emitSync(event: WireEvent): void {\n this.insert.run({\n event_id: event.event_id,\n project_key: event.project_key,\n fingerprint: event.fingerprint,\n type: event.type,\n message: event.message,\n stack: JSON.stringify(event.stack),\n code_context: event.code_context ? JSON.stringify(event.code_context) : null,\n runtime: event.runtime,\n manual_report: event.manual_report ? 1 : 0,\n level: event.level,\n occurred_at: event.occurred_at,\n });\n }\n\n async emit(event: WireEvent): Promise<void> {\n this.emitSync(event);\n }\n\n async close(): Promise<void> {\n this.db.close();\n }\n}\n","import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\n\nexport const DEFAULT_INGEST_URL = \"https://gg-pixel-server.buzzbeamaustralia.workers.dev\";\n\nexport interface InstallOptions {\n cwd?: string;\n ingestUrl?: string;\n projectName?: string;\n fetchFn?: typeof fetch;\n skipPackageInstall?: boolean;\n homeDir?: string;\n}\n\nexport interface InstallResult {\n projectId: string;\n projectKey: string;\n projectName: string;\n initFilePath: string;\n envFilePath: string;\n projectsJsonPath: string;\n packageManager: PackageManager;\n packageInstalled: boolean;\n}\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\nexport async function install(opts: InstallOptions = {}): Promise<InstallResult> {\n const cwd = resolve(opts.cwd ?? process.cwd());\n const ingestUrl = (opts.ingestUrl ?? DEFAULT_INGEST_URL).replace(/\\/+$/, \"\");\n const fetchFn = opts.fetchFn ?? fetch;\n\n const projectRoot = findProjectRoot(cwd);\n if (!projectRoot) {\n throw new Error(`No package.json found in ${cwd} or any parent directory.`);\n }\n\n const pkgPath = join(projectRoot, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as { name?: string };\n const projectName = opts.projectName ?? pkg.name ?? projectRoot.split(\"/\").pop() ?? \"unnamed\";\n\n const created = await createProject(fetchFn, ingestUrl, projectName);\n\n const pm = detectPackageManager(projectRoot);\n const packageInstalled = opts.skipPackageInstall\n ? false\n : runInstall(projectRoot, pm, \"@kenkaiiii/gg-pixel\");\n\n const initFilePath = join(projectRoot, \"gg-pixel.init.mjs\");\n writeFileSync(initFilePath, renderInitFile(ingestUrl), \"utf8\");\n\n const envFilePath = join(projectRoot, \".env\");\n writeEnvKey(envFilePath, \"GG_PIXEL_KEY\", created.key);\n\n const home = opts.homeDir ?? homedir();\n const projectsJsonPath = join(home, \".gg\", \"projects.json\");\n writeProjectsMapping(projectsJsonPath, created.id, projectName, projectRoot);\n\n return {\n projectId: created.id,\n projectKey: created.key,\n projectName,\n initFilePath,\n envFilePath,\n projectsJsonPath,\n packageManager: pm,\n packageInstalled,\n };\n}\n\nfunction findProjectRoot(start: string): string | null {\n let dir = start;\n for (let i = 0; i < 20; i++) {\n if (existsSync(join(dir, \"package.json\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n return null;\n}\n\nasync function createProject(\n fetchFn: typeof fetch,\n ingestUrl: string,\n name: string,\n): Promise<{ id: string; key: string }> {\n const res = await fetchFn(`${ingestUrl}/api/projects`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`POST /api/projects failed: ${res.status} ${await safeText(res)}`);\n }\n const body = (await res.json()) as { id: string; key: string };\n if (!body.id || !body.key) throw new Error(\"response missing id/key\");\n return { id: body.id, key: body.key };\n}\n\nasync function safeText(r: Response): Promise<string> {\n try {\n return await r.text();\n } catch {\n return \"\";\n }\n}\n\nexport function detectPackageManager(projectRoot: string): PackageManager {\n if (existsSync(join(projectRoot, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(projectRoot, \"bun.lockb\"))) return \"bun\";\n if (existsSync(join(projectRoot, \"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\nfunction runInstall(projectRoot: string, pm: PackageManager, pkg: string): boolean {\n const cmd = pm;\n const args = pm === \"npm\" ? [\"install\", pkg] : [\"add\", pkg];\n const result = spawnSync(cmd, args, { cwd: projectRoot, stdio: \"inherit\" });\n return result.status === 0;\n}\n\nexport function renderInitFile(ingestUrl: string): string {\n return `import { initPixel } from \"@kenkaiiii/gg-pixel\";\n\nconst key = process.env.GG_PIXEL_KEY;\nif (key) {\n initPixel({\n projectKey: key,\n sink: { kind: \"http\", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },\n });\n}\n`;\n}\n\nexport function writeEnvKey(envPath: string, key: string, value: string): void {\n if (existsSync(envPath)) {\n const current = readFileSync(envPath, \"utf8\");\n const lineRegex = new RegExp(`^${key}=.*$`, \"m\");\n if (lineRegex.test(current)) {\n writeFileSync(envPath, current.replace(lineRegex, `${key}=${value}`), \"utf8\");\n return;\n }\n const sep = current.endsWith(\"\\n\") || current.length === 0 ? \"\" : \"\\n\";\n appendFileSync(envPath, `${sep}${key}=${value}\\n`, \"utf8\");\n return;\n }\n writeFileSync(envPath, `${key}=${value}\\n`, \"utf8\");\n}\n\nexport function writeProjectsMapping(\n projectsJsonPath: string,\n projectId: string,\n name: string,\n path: string,\n): void {\n mkdirSync(dirname(projectsJsonPath), { recursive: true });\n let map: Record<string, { name: string; path: string }> = {};\n if (existsSync(projectsJsonPath)) {\n try {\n map = JSON.parse(readFileSync(projectsJsonPath, \"utf8\")) as typeof map;\n } catch {\n // start fresh on corrupt file\n }\n }\n map[projectId] = { name, path };\n writeFileSync(projectsJsonPath, `${JSON.stringify(map, null, 2)}\\n`, \"utf8\");\n}\n"],"mappings":"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","names":["import_node_crypto","import_node_fs","Database","import_node_fs","import_node_os","import_node_path"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapters/node.ts","../src/core/stack.ts","../src/core/fingerprint.ts","../src/code-context.ts","../src/core/queue.ts","../src/core/sinks/http.ts","../src/core/sinks/local-sqlite.ts","../src/install.ts"],"sourcesContent":["import { installNodeAdapter, type NodeAdapter } from \"./adapters/node.js\";\nimport { HttpSink } from \"./core/sinks/http.js\";\nimport { LocalSqliteSink } from \"./core/sinks/local-sqlite.js\";\nimport type { PixelOptions, ReportInput, Sink, SinkConfig } from \"./core/types.js\";\n\nlet active: NodeAdapter | null = null;\n\nexport function initPixel(options: PixelOptions): NodeAdapter {\n if (active) {\n throw new Error(\"gg-pixel is already initialized; call closePixel() first\");\n }\n const sink = buildSink(options.sink);\n active = installNodeAdapter({\n projectKey: options.projectKey,\n runtime: options.runtime ?? defaultRuntime(),\n sink,\n captureConsoleErrors: options.captureConsoleErrors ?? true,\n captureConsoleWarnings: options.captureConsoleWarnings ?? false,\n captureUnhandledRejections: options.captureUnhandledRejections ?? true,\n captureUncaughtExceptions: options.captureUncaughtExceptions ?? true,\n });\n return active;\n}\n\nexport function reportPixel(input: ReportInput): void {\n if (!active) return;\n active.report(input);\n}\n\nexport async function flushPixel(): Promise<void> {\n if (!active) return;\n await active.flush();\n}\n\nexport async function closePixel(): Promise<void> {\n if (!active) return;\n await active.close();\n active = null;\n}\n\nfunction buildSink(config: SinkConfig): Sink {\n switch (config.kind) {\n case \"http\":\n return new HttpSink(config.ingestUrl, config.fetchFn);\n case \"local\":\n return new LocalSqliteSink(config.path);\n case \"custom\":\n return config.sink;\n }\n}\n\nfunction defaultRuntime(): string {\n const v = process.versions.node;\n return `node-${v}`;\n}\n\nexport type {\n Level,\n PixelOptions,\n ReportInput,\n Sink,\n SinkConfig,\n StackFrame,\n CodeContext,\n WireEvent,\n} from \"./core/types.js\";\n\nexport { install, DEFAULT_INGEST_URL } from \"./install.js\";\nexport type { InstallOptions, InstallResult, PackageManager } from \"./install.js\";\n","import { randomUUID } from \"node:crypto\";\nimport { parseStack } from \"../core/stack.js\";\nimport { fingerprint } from \"../core/fingerprint.js\";\nimport { captureCodeContext } from \"../code-context.js\";\nimport { EventQueue } from \"../core/queue.js\";\nimport type { Level, ReportInput, Sink, WireEvent } from \"../core/types.js\";\n\nexport interface NodeAdapterOptions {\n projectKey: string;\n runtime: string;\n sink: Sink;\n captureConsoleErrors: boolean;\n captureConsoleWarnings: boolean;\n captureUnhandledRejections: boolean;\n captureUncaughtExceptions: boolean;\n}\n\nexport interface NodeAdapter {\n report(input: ReportInput): void;\n flush(): Promise<void>;\n close(): Promise<void>;\n}\n\nexport function installNodeAdapter(opts: NodeAdapterOptions): NodeAdapter {\n const queue = new EventQueue(opts.sink);\n const detach: Array<() => void> = [];\n\n const enqueueError = (err: unknown, level: Level, manual: boolean) => {\n try {\n const event = buildEvent(err, level, manual, opts.projectKey, opts.runtime);\n queue.enqueue(event);\n } catch {\n // never let pixel break the host program\n }\n };\n\n const enqueueErrorSync = (err: unknown, level: Level, manual: boolean) => {\n try {\n const event = buildEvent(err, level, manual, opts.projectKey, opts.runtime);\n queue.enqueueSync(event);\n } catch {\n // never let pixel break the host program\n }\n };\n\n if (opts.captureUncaughtExceptions) {\n const handler = (err: Error) => enqueueErrorSync(err, \"fatal\", false);\n process.on(\"uncaughtExceptionMonitor\", handler);\n detach.push(() => process.off(\"uncaughtExceptionMonitor\", handler));\n }\n\n if (opts.captureUnhandledRejections) {\n const handler = (reason: unknown) => enqueueErrorSync(reason, \"error\", false);\n process.on(\"unhandledRejection\", handler);\n detach.push(() => process.off(\"unhandledRejection\", handler));\n }\n\n if (opts.captureConsoleErrors) {\n detach.push(patchConsole(\"error\", (args) => enqueueError(consoleError(args), \"error\", false)));\n }\n\n if (opts.captureConsoleWarnings) {\n detach.push(patchConsole(\"warn\", (args) => enqueueError(consoleError(args), \"warning\", false)));\n }\n\n const onBeforeExit = () => {\n void queue.flush();\n };\n process.on(\"beforeExit\", onBeforeExit);\n detach.push(() => process.off(\"beforeExit\", onBeforeExit));\n\n return {\n report(input: ReportInput) {\n const level = input.level ?? \"error\";\n if (input.error !== undefined) {\n try {\n const event = buildEvent(input.error, level, true, opts.projectKey, opts.runtime);\n if (input.message) event.message = input.message;\n queue.enqueue(event);\n } catch {\n // never let pixel break the host program\n }\n return;\n }\n const err = new Error(input.message);\n err.name = \"ManualReport\";\n enqueueError(err, level, true);\n },\n flush: () => queue.flush(),\n close: async () => {\n for (const fn of detach) fn();\n await queue.close();\n },\n };\n}\n\nfunction buildEvent(\n err: unknown,\n level: Level,\n manual: boolean,\n projectKey: string,\n runtime: string,\n): WireEvent {\n const { type, message, stackString } = normalize(err);\n const stack = parseStack(stackString);\n return {\n event_id: randomUUID(),\n project_key: projectKey,\n fingerprint: fingerprint(type, stack),\n type,\n message,\n stack,\n code_context: captureCodeContext(stack),\n runtime,\n manual_report: manual,\n level,\n occurred_at: new Date().toISOString(),\n };\n}\n\nfunction normalize(err: unknown): { type: string; message: string; stackString?: string } {\n if (err instanceof Error) {\n return { type: err.name || \"Error\", message: err.message, stackString: err.stack };\n }\n if (typeof err === \"string\") {\n return { type: \"StringError\", message: err };\n }\n try {\n return { type: \"UnknownError\", message: JSON.stringify(err) };\n } catch {\n return { type: \"UnknownError\", message: String(err) };\n }\n}\n\nfunction consoleError(args: unknown[]): unknown {\n for (const a of args) if (a instanceof Error) return a;\n return new Error(args.map(stringify).join(\" \"));\n}\n\nfunction stringify(x: unknown): string {\n if (typeof x === \"string\") return x;\n try {\n return JSON.stringify(x);\n } catch {\n return String(x);\n }\n}\n\ntype ConsoleMethod = \"error\" | \"warn\";\n\nfunction patchConsole(method: ConsoleMethod, onCall: (args: unknown[]) => void): () => void {\n const original = console[method];\n console[method] = (...args: unknown[]) => {\n try {\n onCall(args);\n } catch {\n // never let pixel break the host program\n }\n original.apply(console, args);\n };\n return () => {\n console[method] = original;\n };\n}\n","import type { StackFrame } from \"./types.js\";\n\nconst FRAME_WITH_FN = /^\\s*at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)\\s*$/;\nconst FRAME_NO_FN = /^\\s*at\\s+(.+?):(\\d+):(\\d+)\\s*$/;\n\nexport function parseStack(stack: string | undefined): StackFrame[] {\n if (!stack) return [];\n const frames: StackFrame[] = [];\n for (const line of stack.split(\"\\n\")) {\n const withFn = FRAME_WITH_FN.exec(line);\n if (withFn) {\n const file = withFn[2];\n frames.push({\n fn: withFn[1],\n file,\n line: Number(withFn[3]),\n col: Number(withFn[4]),\n in_app: isInApp(file),\n });\n continue;\n }\n const noFn = FRAME_NO_FN.exec(line);\n if (noFn) {\n const file = noFn[1];\n frames.push({\n fn: \"<anon>\",\n file,\n line: Number(noFn[2]),\n col: Number(noFn[3]),\n in_app: isInApp(file),\n });\n }\n }\n return frames;\n}\n\nfunction isInApp(file: string): boolean {\n if (!file) return false;\n if (file.startsWith(\"node:\")) return false;\n if (file.startsWith(\"internal/\")) return false;\n if (file.includes(\"/node_modules/\")) return false;\n return true;\n}\n","import { createHash } from \"node:crypto\";\nimport type { StackFrame } from \"./types.js\";\n\nexport function fingerprint(type: string, stack: StackFrame[]): string {\n const top = stack[0];\n const normalized = top\n ? `${type}|${normalizeFile(top.file)}|${top.fn || \"<anon>\"}|${top.line}`\n : `${type}|<no-stack>`;\n return createHash(\"sha256\").update(normalized).digest(\"hex\").slice(0, 16);\n}\n\nfunction normalizeFile(file: string): string {\n return file\n .replace(/^file:\\/\\//, \"\")\n .replace(/^.*\\/node_modules\\//, \"node_modules/\")\n .replace(/\\?.*$/, \"\");\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport type { CodeContext, StackFrame } from \"./core/types.js\";\n\nconst WINDOW = 2;\nconst cache = new Map<string, string[] | null>();\n\nexport function captureCodeContext(stack: StackFrame[]): CodeContext | null {\n const top = stack.find((f) => isReadable(f.file));\n if (!top) return null;\n const lines = loadLines(top.file);\n if (!lines) return null;\n const start = Math.max(0, top.line - 1 - WINDOW);\n const end = Math.min(lines.length, top.line + WINDOW);\n return {\n file: top.file,\n error_line: top.line,\n lines: lines.slice(start, end),\n };\n}\n\nfunction isReadable(file: string): boolean {\n if (!file || file.startsWith(\"node:\")) return false;\n if (file.includes(\"/node_modules/\")) return false;\n return file.startsWith(\"/\") || file.startsWith(\"file://\");\n}\n\nfunction loadLines(file: string): string[] | null {\n const path = file.replace(/^file:\\/\\//, \"\");\n if (cache.has(path)) return cache.get(path) ?? null;\n if (!existsSync(path)) {\n cache.set(path, null);\n return null;\n }\n try {\n const lines = readFileSync(path, \"utf8\").split(\"\\n\");\n cache.set(path, lines);\n return lines;\n } catch {\n cache.set(path, null);\n return null;\n }\n}\n","import type { Sink, WireEvent } from \"./types.js\";\n\nconst MAX_BUFFER = 100;\nconst BASE_DELAY_MS = 200;\nconst MAX_DELAY_MS = 5_000;\n\nexport class EventQueue {\n private readonly buffer: WireEvent[] = [];\n private draining = false;\n private closed = false;\n\n constructor(private readonly sink: Sink) {}\n\n enqueue(event: WireEvent): void {\n if (this.closed) return;\n if (this.buffer.length >= MAX_BUFFER) {\n this.buffer.shift();\n }\n this.buffer.push(event);\n void this.drain();\n }\n\n enqueueSync(event: WireEvent): void {\n if (this.closed) return;\n if (this.sink.emitSync) {\n try {\n this.sink.emitSync(event);\n return;\n } catch {\n // fall through to async path\n }\n }\n this.enqueue(event);\n }\n\n async flush(): Promise<void> {\n while (this.buffer.length > 0 || this.draining) {\n await new Promise((r) => setTimeout(r, 10));\n }\n }\n\n async close(): Promise<void> {\n await this.flush();\n this.closed = true;\n if (this.sink.close) await this.sink.close();\n }\n\n private async drain(): Promise<void> {\n if (this.draining) return;\n this.draining = true;\n let attempt = 0;\n while (this.buffer.length > 0) {\n const event = this.buffer[0];\n try {\n await this.sink.emit(event);\n this.buffer.shift();\n attempt = 0;\n } catch {\n attempt++;\n if (attempt >= 5) {\n this.buffer.shift();\n attempt = 0;\n continue;\n }\n const delay = Math.min(BASE_DELAY_MS * 2 ** (attempt - 1), MAX_DELAY_MS);\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n this.draining = false;\n }\n}\n","import type { Sink, WireEvent } from \"../types.js\";\n\nexport class HttpSink implements Sink {\n constructor(\n private readonly ingestUrl: string,\n private readonly fetchFn: typeof fetch = fetch,\n ) {}\n\n async emit(event: WireEvent): Promise<void> {\n const res = await this.fetchFn(this.ingestUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-pixel-key\": event.project_key,\n },\n body: JSON.stringify(event),\n });\n if (!res.ok) {\n throw new Error(`pixel ingest failed: ${res.status}`);\n }\n }\n}\n","import { homedir } from \"node:os\";\nimport { mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport Database from \"better-sqlite3\";\nimport type { Sink, WireEvent } from \"../types.js\";\n\nconst SCHEMA = `\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n event_id TEXT NOT NULL UNIQUE,\n project_key TEXT NOT NULL,\n fingerprint TEXT NOT NULL,\n type TEXT NOT NULL,\n message TEXT NOT NULL,\n stack TEXT NOT NULL,\n code_context TEXT,\n runtime TEXT NOT NULL,\n manual_report INTEGER NOT NULL DEFAULT 0,\n level TEXT NOT NULL,\n occurred_at TEXT NOT NULL,\n ingested_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS events_fingerprint ON events(project_key, fingerprint);\n CREATE INDEX IF NOT EXISTS events_occurred ON events(occurred_at);\n`;\n\nexport class LocalSqliteSink implements Sink {\n private readonly db: Database.Database;\n private readonly insert: Database.Statement;\n\n constructor(path?: string) {\n const resolved = path ?? join(homedir(), \".gg\", \"errors.db\");\n mkdirSync(dirname(resolved), { recursive: true });\n this.db = new Database(resolved);\n this.db.pragma(\"journal_mode = WAL\");\n this.db.exec(SCHEMA);\n this.insert = this.db.prepare(`\n INSERT INTO events (\n event_id, project_key, fingerprint, type, message, stack, code_context,\n runtime, manual_report, level, occurred_at\n ) VALUES (\n @event_id, @project_key, @fingerprint, @type, @message, @stack, @code_context,\n @runtime, @manual_report, @level, @occurred_at\n )\n `);\n }\n\n emitSync(event: WireEvent): void {\n this.insert.run({\n event_id: event.event_id,\n project_key: event.project_key,\n fingerprint: event.fingerprint,\n type: event.type,\n message: event.message,\n stack: JSON.stringify(event.stack),\n code_context: event.code_context ? JSON.stringify(event.code_context) : null,\n runtime: event.runtime,\n manual_report: event.manual_report ? 1 : 0,\n level: event.level,\n occurred_at: event.occurred_at,\n });\n }\n\n async emit(event: WireEvent): Promise<void> {\n this.emitSync(event);\n }\n\n async close(): Promise<void> {\n this.db.close();\n }\n}\n","import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\n\nexport const DEFAULT_INGEST_URL = \"https://gg-pixel-server.buzzbeamaustralia.workers.dev\";\n\nexport interface InstallOptions {\n cwd?: string;\n ingestUrl?: string;\n projectName?: string;\n fetchFn?: typeof fetch;\n skipPackageInstall?: boolean;\n homeDir?: string;\n}\n\nexport interface InstallResult {\n projectId: string;\n projectKey: string;\n projectName: string;\n initFilePath: string;\n envFilePath: string;\n projectsJsonPath: string;\n packageManager: PackageManager;\n packageInstalled: boolean;\n entryWiring: EntryWiringResult;\n /** True when an existing project mapping was reused instead of minting a fresh one. */\n reused: boolean;\n}\n\nexport type EntryWiringResult =\n | { kind: \"injected\"; entryPath: string }\n | { kind: \"already_present\"; entryPath: string }\n | { kind: \"no_entry_found\" }\n | { kind: \"skipped\"; reason: string };\n\ninterface PackageJson {\n name?: string;\n type?: string;\n main?: string;\n module?: string;\n bin?: string | Record<string, string>;\n}\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\nexport async function install(opts: InstallOptions = {}): Promise<InstallResult> {\n const cwd = resolve(opts.cwd ?? process.cwd());\n const ingestUrl = (opts.ingestUrl ?? DEFAULT_INGEST_URL).replace(/\\/+$/, \"\");\n const fetchFn = opts.fetchFn ?? fetch;\n\n const projectRoot = findProjectRoot(cwd);\n if (!projectRoot) {\n throw new Error(`No package.json found in ${cwd} or any parent directory.`);\n }\n\n const pkgPath = join(projectRoot, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as PackageJson;\n const projectName = opts.projectName ?? pkg.name ?? projectRoot.split(\"/\").pop() ?? \"unnamed\";\n\n const home = opts.homeDir ?? homedir();\n const projectsJsonPath = join(home, \".gg\", \"projects.json\");\n const envFilePath = join(projectRoot, \".env\");\n\n // Idempotency: if we already have a mapping for this directory AND the .env\n // still has its key, reuse it instead of minting a fresh project.\n const existing = findMappingByPath(projectsJsonPath, projectRoot);\n const existingKey = readEnvKey(envFilePath, \"GG_PIXEL_KEY\");\n let created: { id: string; key: string };\n let reused = false;\n if (existing && existingKey) {\n created = { id: existing.id, key: existingKey };\n reused = true;\n } else {\n created = await createProject(fetchFn, ingestUrl, projectName);\n }\n\n const pm = detectPackageManager(projectRoot);\n const packageInstalled = opts.skipPackageInstall\n ? false\n : runInstall(projectRoot, pm, \"@kenkaiiii/gg-pixel\");\n\n const initFilePath = join(projectRoot, \"gg-pixel.init.mjs\");\n writeFileSync(initFilePath, renderInitFile(ingestUrl), \"utf8\");\n\n writeEnvKey(envFilePath, \"GG_PIXEL_KEY\", created.key);\n\n writeProjectsMapping(projectsJsonPath, created.id, projectName, projectRoot);\n\n const entryWiring = wireEntryFile(projectRoot, initFilePath, pkg);\n\n return {\n projectId: created.id,\n projectKey: created.key,\n projectName,\n initFilePath,\n envFilePath,\n projectsJsonPath,\n packageManager: pm,\n packageInstalled,\n entryWiring,\n reused,\n };\n}\n\nfunction findMappingByPath(\n projectsJsonPath: string,\n projectRoot: string,\n): { id: string; name: string; path: string } | null {\n if (!existsSync(projectsJsonPath)) return null;\n let map: Record<string, { name: string; path: string }>;\n try {\n map = JSON.parse(readFileSync(projectsJsonPath, \"utf8\")) as typeof map;\n } catch {\n return null;\n }\n for (const [id, entry] of Object.entries(map)) {\n if (entry.path === projectRoot) return { id, ...entry };\n }\n return null;\n}\n\nfunction readEnvKey(envPath: string, key: string): string | null {\n if (!existsSync(envPath)) return null;\n try {\n const content = readFileSync(envPath, \"utf8\");\n const match = new RegExp(`^${key}=(.+)$`, \"m\").exec(content);\n return match?.[1]?.trim() ?? null;\n } catch {\n return null;\n }\n}\n\nfunction findProjectRoot(start: string): string | null {\n let dir = start;\n for (let i = 0; i < 20; i++) {\n if (existsSync(join(dir, \"package.json\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n return null;\n}\n\nasync function createProject(\n fetchFn: typeof fetch,\n ingestUrl: string,\n name: string,\n): Promise<{ id: string; key: string }> {\n const res = await fetchFn(`${ingestUrl}/api/projects`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`POST /api/projects failed: ${res.status} ${await safeText(res)}`);\n }\n const body = (await res.json()) as { id: string; key: string };\n if (!body.id || !body.key) throw new Error(\"response missing id/key\");\n return { id: body.id, key: body.key };\n}\n\nasync function safeText(r: Response): Promise<string> {\n try {\n return await r.text();\n } catch {\n return \"\";\n }\n}\n\nexport function detectPackageManager(projectRoot: string): PackageManager {\n if (existsSync(join(projectRoot, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(projectRoot, \"bun.lockb\"))) return \"bun\";\n if (existsSync(join(projectRoot, \"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\nfunction runInstall(projectRoot: string, pm: PackageManager, pkg: string): boolean {\n const cmd = pm;\n // npm prints `npm audit` warnings + `npm fund` solicitations on every install.\n // That output is about the user's *existing* project — irrelevant to pixel.\n // The other package managers don't show this noise by default.\n const args = pm === \"npm\" ? [\"install\", pkg, \"--no-audit\", \"--no-fund\"] : [\"add\", pkg];\n const result = spawnSync(cmd, args, { cwd: projectRoot, stdio: \"inherit\" });\n return result.status === 0;\n}\n\nexport function renderInitFile(ingestUrl: string): string {\n return `import { initPixel } from \"@kenkaiiii/gg-pixel\";\n\nconst key = process.env.GG_PIXEL_KEY;\nif (key) {\n initPixel({\n projectKey: key,\n sink: { kind: \"http\", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },\n });\n}\n`;\n}\n\nexport function writeEnvKey(envPath: string, key: string, value: string): void {\n if (existsSync(envPath)) {\n const current = readFileSync(envPath, \"utf8\");\n const lineRegex = new RegExp(`^${key}=.*$`, \"m\");\n if (lineRegex.test(current)) {\n writeFileSync(envPath, current.replace(lineRegex, `${key}=${value}`), \"utf8\");\n return;\n }\n const sep = current.endsWith(\"\\n\") || current.length === 0 ? \"\" : \"\\n\";\n appendFileSync(envPath, `${sep}${key}=${value}\\n`, \"utf8\");\n return;\n }\n writeFileSync(envPath, `${key}=${value}\\n`, \"utf8\");\n}\n\nexport function wireEntryFile(\n projectRoot: string,\n initFilePath: string,\n pkg: PackageJson,\n): EntryWiringResult {\n const entryPath = findEntryFile(projectRoot, pkg);\n if (!entryPath) return { kind: \"no_entry_found\" };\n\n let content: string;\n try {\n content = readFileSync(entryPath, \"utf8\");\n } catch (err) {\n return { kind: \"skipped\", reason: `unreadable: ${(err as Error).message}` };\n }\n\n if (content.includes(\"gg-pixel.init\")) {\n return { kind: \"already_present\", entryPath };\n }\n\n // Compute import specifier relative to the entry file.\n const fromDir = dirname(entryPath);\n let spec = relative(fromDir, initFilePath).split(sep).join(\"/\");\n if (!spec.startsWith(\".\")) spec = \"./\" + spec;\n\n const isCjs = isCommonJsEntry(entryPath, pkg);\n const importLine = isCjs\n ? `require(${JSON.stringify(spec)});`\n : `import ${JSON.stringify(spec)};`;\n\n // Inject at the top — after a shebang line and any leading \"use strict\",\n // but before all other code, so pixel hooks run before anything else.\n const lines = content.split(\"\\n\");\n let insertAt = 0;\n if (lines[0]?.startsWith(\"#!\")) insertAt = 1;\n while (\n insertAt < lines.length &&\n /^\\s*(?:[\"']use strict[\"']|\\/\\/|\\/\\*)/.test(lines[insertAt] ?? \"\")\n ) {\n insertAt++;\n }\n\n const updated = [...lines.slice(0, insertAt), importLine, ...lines.slice(insertAt)].join(\"\\n\");\n writeFileSync(entryPath, updated, \"utf8\");\n return { kind: \"injected\", entryPath };\n}\n\nfunction findEntryFile(projectRoot: string, pkg: PackageJson): string | null {\n const tryPath = (rel: string): string | null => {\n const p = join(projectRoot, rel);\n if (existsSync(p)) return p;\n // If user pointed `main` at .js but only the .ts source exists (common in TS projects).\n if (rel.endsWith(\".js\")) {\n const ts = join(projectRoot, rel.replace(/\\.js$/, \".ts\"));\n if (existsSync(ts)) return ts;\n }\n return null;\n };\n\n if (typeof pkg.bin === \"string\") {\n const found = tryPath(pkg.bin);\n if (found) return found;\n }\n if (pkg.bin && typeof pkg.bin === \"object\") {\n for (const value of Object.values(pkg.bin)) {\n if (typeof value === \"string\") {\n const found = tryPath(value);\n if (found) return found;\n }\n }\n }\n if (pkg.main) {\n const found = tryPath(pkg.main);\n if (found) return found;\n }\n if (pkg.module) {\n const found = tryPath(pkg.module);\n if (found) return found;\n }\n\n // Fall back to common conventions.\n const candidates = [\n \"src/index.ts\",\n \"src/index.tsx\",\n \"src/index.js\",\n \"src/index.mjs\",\n \"src/main.ts\",\n \"src/main.tsx\",\n \"src/main.js\",\n \"src/server.ts\",\n \"src/server.js\",\n \"src/app.ts\",\n \"src/app.js\",\n \"src/cli.ts\",\n \"src/cli.js\",\n \"index.ts\",\n \"index.tsx\",\n \"index.js\",\n \"index.mjs\",\n \"main.ts\",\n \"main.js\",\n \"server.ts\",\n \"server.js\",\n \"app.ts\",\n \"app.js\",\n ];\n for (const c of candidates) {\n const found = tryPath(c);\n if (found) return found;\n }\n return null;\n}\n\nfunction isCommonJsEntry(entryPath: string, pkg: PackageJson): boolean {\n if (entryPath.endsWith(\".cjs\")) return true;\n if (entryPath.endsWith(\".mjs\")) return false;\n if (entryPath.endsWith(\".ts\") || entryPath.endsWith(\".tsx\")) return false;\n // .js → governed by package.json type (default is \"commonjs\")\n return pkg.type !== \"module\";\n}\n\nexport function writeProjectsMapping(\n projectsJsonPath: string,\n projectId: string,\n name: string,\n path: string,\n): void {\n mkdirSync(dirname(projectsJsonPath), { recursive: true });\n let map: Record<string, { name: string; path: string }> = {};\n if (existsSync(projectsJsonPath)) {\n try {\n map = JSON.parse(readFileSync(projectsJsonPath, \"utf8\")) as typeof map;\n } catch {\n // start fresh on corrupt file\n }\n }\n map[projectId] = { name, path };\n writeFileSync(projectsJsonPath, `${JSON.stringify(map, null, 2)}\\n`, \"utf8\");\n}\n"],"mappings":"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","names":["import_node_crypto","import_node_fs","Database","import_node_fs","import_node_os","import_node_path","sep"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -80,7 +80,22 @@ interface InstallResult {
|
|
|
80
80
|
projectsJsonPath: string;
|
|
81
81
|
packageManager: PackageManager;
|
|
82
82
|
packageInstalled: boolean;
|
|
83
|
+
entryWiring: EntryWiringResult;
|
|
84
|
+
/** True when an existing project mapping was reused instead of minting a fresh one. */
|
|
85
|
+
reused: boolean;
|
|
83
86
|
}
|
|
87
|
+
type EntryWiringResult = {
|
|
88
|
+
kind: "injected";
|
|
89
|
+
entryPath: string;
|
|
90
|
+
} | {
|
|
91
|
+
kind: "already_present";
|
|
92
|
+
entryPath: string;
|
|
93
|
+
} | {
|
|
94
|
+
kind: "no_entry_found";
|
|
95
|
+
} | {
|
|
96
|
+
kind: "skipped";
|
|
97
|
+
reason: string;
|
|
98
|
+
};
|
|
84
99
|
type PackageManager = "pnpm" | "yarn" | "bun" | "npm";
|
|
85
100
|
declare function install(opts?: InstallOptions): Promise<InstallResult>;
|
|
86
101
|
|
package/dist/index.d.ts
CHANGED
|
@@ -80,7 +80,22 @@ interface InstallResult {
|
|
|
80
80
|
projectsJsonPath: string;
|
|
81
81
|
packageManager: PackageManager;
|
|
82
82
|
packageInstalled: boolean;
|
|
83
|
+
entryWiring: EntryWiringResult;
|
|
84
|
+
/** True when an existing project mapping was reused instead of minting a fresh one. */
|
|
85
|
+
reused: boolean;
|
|
83
86
|
}
|
|
87
|
+
type EntryWiringResult = {
|
|
88
|
+
kind: "injected";
|
|
89
|
+
entryPath: string;
|
|
90
|
+
} | {
|
|
91
|
+
kind: "already_present";
|
|
92
|
+
entryPath: string;
|
|
93
|
+
} | {
|
|
94
|
+
kind: "no_entry_found";
|
|
95
|
+
} | {
|
|
96
|
+
kind: "skipped";
|
|
97
|
+
reason: string;
|
|
98
|
+
};
|
|
84
99
|
type PackageManager = "pnpm" | "yarn" | "bun" | "npm";
|
|
85
100
|
declare function install(opts?: InstallOptions): Promise<InstallResult>;
|
|
86
101
|
|
package/dist/index.js
CHANGED
|
@@ -365,7 +365,7 @@ var LocalSqliteSink = class {
|
|
|
365
365
|
// src/install.ts
|
|
366
366
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, appendFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
367
367
|
import { homedir as homedir2 } from "os";
|
|
368
|
-
import { dirname as dirname2, join as join2, resolve } from "path";
|
|
368
|
+
import { dirname as dirname2, join as join2, relative, resolve, sep } from "path";
|
|
369
369
|
import { spawnSync } from "child_process";
|
|
370
370
|
var DEFAULT_INGEST_URL = "https://gg-pixel-server.buzzbeamaustralia.workers.dev";
|
|
371
371
|
async function install(opts = {}) {
|
|
@@ -379,16 +379,26 @@ async function install(opts = {}) {
|
|
|
379
379
|
const pkgPath = join2(projectRoot, "package.json");
|
|
380
380
|
const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
381
381
|
const projectName = opts.projectName ?? pkg.name ?? projectRoot.split("/").pop() ?? "unnamed";
|
|
382
|
-
const
|
|
382
|
+
const home = opts.homeDir ?? homedir2();
|
|
383
|
+
const projectsJsonPath = join2(home, ".gg", "projects.json");
|
|
384
|
+
const envFilePath = join2(projectRoot, ".env");
|
|
385
|
+
const existing = findMappingByPath(projectsJsonPath, projectRoot);
|
|
386
|
+
const existingKey = readEnvKey(envFilePath, "GG_PIXEL_KEY");
|
|
387
|
+
let created;
|
|
388
|
+
let reused = false;
|
|
389
|
+
if (existing && existingKey) {
|
|
390
|
+
created = { id: existing.id, key: existingKey };
|
|
391
|
+
reused = true;
|
|
392
|
+
} else {
|
|
393
|
+
created = await createProject(fetchFn, ingestUrl, projectName);
|
|
394
|
+
}
|
|
383
395
|
const pm = detectPackageManager(projectRoot);
|
|
384
396
|
const packageInstalled = opts.skipPackageInstall ? false : runInstall(projectRoot, pm, "@kenkaiiii/gg-pixel");
|
|
385
397
|
const initFilePath = join2(projectRoot, "gg-pixel.init.mjs");
|
|
386
398
|
writeFileSync(initFilePath, renderInitFile(ingestUrl), "utf8");
|
|
387
|
-
const envFilePath = join2(projectRoot, ".env");
|
|
388
399
|
writeEnvKey(envFilePath, "GG_PIXEL_KEY", created.key);
|
|
389
|
-
const home = opts.homeDir ?? homedir2();
|
|
390
|
-
const projectsJsonPath = join2(home, ".gg", "projects.json");
|
|
391
400
|
writeProjectsMapping(projectsJsonPath, created.id, projectName, projectRoot);
|
|
401
|
+
const entryWiring = wireEntryFile(projectRoot, initFilePath, pkg);
|
|
392
402
|
return {
|
|
393
403
|
projectId: created.id,
|
|
394
404
|
projectKey: created.key,
|
|
@@ -397,9 +407,34 @@ async function install(opts = {}) {
|
|
|
397
407
|
envFilePath,
|
|
398
408
|
projectsJsonPath,
|
|
399
409
|
packageManager: pm,
|
|
400
|
-
packageInstalled
|
|
410
|
+
packageInstalled,
|
|
411
|
+
entryWiring,
|
|
412
|
+
reused
|
|
401
413
|
};
|
|
402
414
|
}
|
|
415
|
+
function findMappingByPath(projectsJsonPath, projectRoot) {
|
|
416
|
+
if (!existsSync2(projectsJsonPath)) return null;
|
|
417
|
+
let map;
|
|
418
|
+
try {
|
|
419
|
+
map = JSON.parse(readFileSync2(projectsJsonPath, "utf8"));
|
|
420
|
+
} catch {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
for (const [id, entry] of Object.entries(map)) {
|
|
424
|
+
if (entry.path === projectRoot) return { id, ...entry };
|
|
425
|
+
}
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
function readEnvKey(envPath, key) {
|
|
429
|
+
if (!existsSync2(envPath)) return null;
|
|
430
|
+
try {
|
|
431
|
+
const content = readFileSync2(envPath, "utf8");
|
|
432
|
+
const match = new RegExp(`^${key}=(.+)$`, "m").exec(content);
|
|
433
|
+
return match?.[1]?.trim() ?? null;
|
|
434
|
+
} catch {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
403
438
|
function findProjectRoot(start) {
|
|
404
439
|
let dir = start;
|
|
405
440
|
for (let i = 0; i < 20; i++) {
|
|
@@ -438,7 +473,7 @@ function detectPackageManager(projectRoot) {
|
|
|
438
473
|
}
|
|
439
474
|
function runInstall(projectRoot, pm, pkg) {
|
|
440
475
|
const cmd = pm;
|
|
441
|
-
const args = pm === "npm" ? ["install", pkg] : ["add", pkg];
|
|
476
|
+
const args = pm === "npm" ? ["install", pkg, "--no-audit", "--no-fund"] : ["add", pkg];
|
|
442
477
|
const result = spawnSync(cmd, args, { cwd: projectRoot, stdio: "inherit" });
|
|
443
478
|
return result.status === 0;
|
|
444
479
|
}
|
|
@@ -462,14 +497,108 @@ function writeEnvKey(envPath, key, value) {
|
|
|
462
497
|
writeFileSync(envPath, current.replace(lineRegex, `${key}=${value}`), "utf8");
|
|
463
498
|
return;
|
|
464
499
|
}
|
|
465
|
-
const
|
|
466
|
-
appendFileSync(envPath, `${
|
|
500
|
+
const sep2 = current.endsWith("\n") || current.length === 0 ? "" : "\n";
|
|
501
|
+
appendFileSync(envPath, `${sep2}${key}=${value}
|
|
467
502
|
`, "utf8");
|
|
468
503
|
return;
|
|
469
504
|
}
|
|
470
505
|
writeFileSync(envPath, `${key}=${value}
|
|
471
506
|
`, "utf8");
|
|
472
507
|
}
|
|
508
|
+
function wireEntryFile(projectRoot, initFilePath, pkg) {
|
|
509
|
+
const entryPath = findEntryFile(projectRoot, pkg);
|
|
510
|
+
if (!entryPath) return { kind: "no_entry_found" };
|
|
511
|
+
let content;
|
|
512
|
+
try {
|
|
513
|
+
content = readFileSync2(entryPath, "utf8");
|
|
514
|
+
} catch (err) {
|
|
515
|
+
return { kind: "skipped", reason: `unreadable: ${err.message}` };
|
|
516
|
+
}
|
|
517
|
+
if (content.includes("gg-pixel.init")) {
|
|
518
|
+
return { kind: "already_present", entryPath };
|
|
519
|
+
}
|
|
520
|
+
const fromDir = dirname2(entryPath);
|
|
521
|
+
let spec = relative(fromDir, initFilePath).split(sep).join("/");
|
|
522
|
+
if (!spec.startsWith(".")) spec = "./" + spec;
|
|
523
|
+
const isCjs = isCommonJsEntry(entryPath, pkg);
|
|
524
|
+
const importLine = isCjs ? `require(${JSON.stringify(spec)});` : `import ${JSON.stringify(spec)};`;
|
|
525
|
+
const lines = content.split("\n");
|
|
526
|
+
let insertAt = 0;
|
|
527
|
+
if (lines[0]?.startsWith("#!")) insertAt = 1;
|
|
528
|
+
while (insertAt < lines.length && /^\s*(?:["']use strict["']|\/\/|\/\*)/.test(lines[insertAt] ?? "")) {
|
|
529
|
+
insertAt++;
|
|
530
|
+
}
|
|
531
|
+
const updated = [...lines.slice(0, insertAt), importLine, ...lines.slice(insertAt)].join("\n");
|
|
532
|
+
writeFileSync(entryPath, updated, "utf8");
|
|
533
|
+
return { kind: "injected", entryPath };
|
|
534
|
+
}
|
|
535
|
+
function findEntryFile(projectRoot, pkg) {
|
|
536
|
+
const tryPath = (rel) => {
|
|
537
|
+
const p = join2(projectRoot, rel);
|
|
538
|
+
if (existsSync2(p)) return p;
|
|
539
|
+
if (rel.endsWith(".js")) {
|
|
540
|
+
const ts = join2(projectRoot, rel.replace(/\.js$/, ".ts"));
|
|
541
|
+
if (existsSync2(ts)) return ts;
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
};
|
|
545
|
+
if (typeof pkg.bin === "string") {
|
|
546
|
+
const found = tryPath(pkg.bin);
|
|
547
|
+
if (found) return found;
|
|
548
|
+
}
|
|
549
|
+
if (pkg.bin && typeof pkg.bin === "object") {
|
|
550
|
+
for (const value of Object.values(pkg.bin)) {
|
|
551
|
+
if (typeof value === "string") {
|
|
552
|
+
const found = tryPath(value);
|
|
553
|
+
if (found) return found;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (pkg.main) {
|
|
558
|
+
const found = tryPath(pkg.main);
|
|
559
|
+
if (found) return found;
|
|
560
|
+
}
|
|
561
|
+
if (pkg.module) {
|
|
562
|
+
const found = tryPath(pkg.module);
|
|
563
|
+
if (found) return found;
|
|
564
|
+
}
|
|
565
|
+
const candidates = [
|
|
566
|
+
"src/index.ts",
|
|
567
|
+
"src/index.tsx",
|
|
568
|
+
"src/index.js",
|
|
569
|
+
"src/index.mjs",
|
|
570
|
+
"src/main.ts",
|
|
571
|
+
"src/main.tsx",
|
|
572
|
+
"src/main.js",
|
|
573
|
+
"src/server.ts",
|
|
574
|
+
"src/server.js",
|
|
575
|
+
"src/app.ts",
|
|
576
|
+
"src/app.js",
|
|
577
|
+
"src/cli.ts",
|
|
578
|
+
"src/cli.js",
|
|
579
|
+
"index.ts",
|
|
580
|
+
"index.tsx",
|
|
581
|
+
"index.js",
|
|
582
|
+
"index.mjs",
|
|
583
|
+
"main.ts",
|
|
584
|
+
"main.js",
|
|
585
|
+
"server.ts",
|
|
586
|
+
"server.js",
|
|
587
|
+
"app.ts",
|
|
588
|
+
"app.js"
|
|
589
|
+
];
|
|
590
|
+
for (const c of candidates) {
|
|
591
|
+
const found = tryPath(c);
|
|
592
|
+
if (found) return found;
|
|
593
|
+
}
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
function isCommonJsEntry(entryPath, pkg) {
|
|
597
|
+
if (entryPath.endsWith(".cjs")) return true;
|
|
598
|
+
if (entryPath.endsWith(".mjs")) return false;
|
|
599
|
+
if (entryPath.endsWith(".ts") || entryPath.endsWith(".tsx")) return false;
|
|
600
|
+
return pkg.type !== "module";
|
|
601
|
+
}
|
|
473
602
|
function writeProjectsMapping(projectsJsonPath, projectId, name, path) {
|
|
474
603
|
mkdirSync2(dirname2(projectsJsonPath), { recursive: true });
|
|
475
604
|
let map = {};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/node.ts","../src/core/stack.ts","../src/core/fingerprint.ts","../src/code-context.ts","../src/core/queue.ts","../src/core/sinks/http.ts","../src/core/sinks/local-sqlite.ts","../src/install.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { parseStack } from \"../core/stack.js\";\nimport { fingerprint } from \"../core/fingerprint.js\";\nimport { captureCodeContext } from \"../code-context.js\";\nimport { EventQueue } from \"../core/queue.js\";\nimport type { Level, ReportInput, Sink, WireEvent } from \"../core/types.js\";\n\nexport interface NodeAdapterOptions {\n projectKey: string;\n runtime: string;\n sink: Sink;\n captureConsoleErrors: boolean;\n captureConsoleWarnings: boolean;\n captureUnhandledRejections: boolean;\n captureUncaughtExceptions: boolean;\n}\n\nexport interface NodeAdapter {\n report(input: ReportInput): void;\n flush(): Promise<void>;\n close(): Promise<void>;\n}\n\nexport function installNodeAdapter(opts: NodeAdapterOptions): NodeAdapter {\n const queue = new EventQueue(opts.sink);\n const detach: Array<() => void> = [];\n\n const enqueueError = (err: unknown, level: Level, manual: boolean) => {\n try {\n const event = buildEvent(err, level, manual, opts.projectKey, opts.runtime);\n queue.enqueue(event);\n } catch {\n // never let pixel break the host program\n }\n };\n\n const enqueueErrorSync = (err: unknown, level: Level, manual: boolean) => {\n try {\n const event = buildEvent(err, level, manual, opts.projectKey, opts.runtime);\n queue.enqueueSync(event);\n } catch {\n // never let pixel break the host program\n }\n };\n\n if (opts.captureUncaughtExceptions) {\n const handler = (err: Error) => enqueueErrorSync(err, \"fatal\", false);\n process.on(\"uncaughtExceptionMonitor\", handler);\n detach.push(() => process.off(\"uncaughtExceptionMonitor\", handler));\n }\n\n if (opts.captureUnhandledRejections) {\n const handler = (reason: unknown) => enqueueErrorSync(reason, \"error\", false);\n process.on(\"unhandledRejection\", handler);\n detach.push(() => process.off(\"unhandledRejection\", handler));\n }\n\n if (opts.captureConsoleErrors) {\n detach.push(patchConsole(\"error\", (args) => enqueueError(consoleError(args), \"error\", false)));\n }\n\n if (opts.captureConsoleWarnings) {\n detach.push(patchConsole(\"warn\", (args) => enqueueError(consoleError(args), \"warning\", false)));\n }\n\n const onBeforeExit = () => {\n void queue.flush();\n };\n process.on(\"beforeExit\", onBeforeExit);\n detach.push(() => process.off(\"beforeExit\", onBeforeExit));\n\n return {\n report(input: ReportInput) {\n const level = input.level ?? \"error\";\n if (input.error !== undefined) {\n try {\n const event = buildEvent(input.error, level, true, opts.projectKey, opts.runtime);\n if (input.message) event.message = input.message;\n queue.enqueue(event);\n } catch {\n // never let pixel break the host program\n }\n return;\n }\n const err = new Error(input.message);\n err.name = \"ManualReport\";\n enqueueError(err, level, true);\n },\n flush: () => queue.flush(),\n close: async () => {\n for (const fn of detach) fn();\n await queue.close();\n },\n };\n}\n\nfunction buildEvent(\n err: unknown,\n level: Level,\n manual: boolean,\n projectKey: string,\n runtime: string,\n): WireEvent {\n const { type, message, stackString } = normalize(err);\n const stack = parseStack(stackString);\n return {\n event_id: randomUUID(),\n project_key: projectKey,\n fingerprint: fingerprint(type, stack),\n type,\n message,\n stack,\n code_context: captureCodeContext(stack),\n runtime,\n manual_report: manual,\n level,\n occurred_at: new Date().toISOString(),\n };\n}\n\nfunction normalize(err: unknown): { type: string; message: string; stackString?: string } {\n if (err instanceof Error) {\n return { type: err.name || \"Error\", message: err.message, stackString: err.stack };\n }\n if (typeof err === \"string\") {\n return { type: \"StringError\", message: err };\n }\n try {\n return { type: \"UnknownError\", message: JSON.stringify(err) };\n } catch {\n return { type: \"UnknownError\", message: String(err) };\n }\n}\n\nfunction consoleError(args: unknown[]): unknown {\n for (const a of args) if (a instanceof Error) return a;\n return new Error(args.map(stringify).join(\" \"));\n}\n\nfunction stringify(x: unknown): string {\n if (typeof x === \"string\") return x;\n try {\n return JSON.stringify(x);\n } catch {\n return String(x);\n }\n}\n\ntype ConsoleMethod = \"error\" | \"warn\";\n\nfunction patchConsole(method: ConsoleMethod, onCall: (args: unknown[]) => void): () => void {\n const original = console[method];\n console[method] = (...args: unknown[]) => {\n try {\n onCall(args);\n } catch {\n // never let pixel break the host program\n }\n original.apply(console, args);\n };\n return () => {\n console[method] = original;\n };\n}\n","import type { StackFrame } from \"./types.js\";\n\nconst FRAME_WITH_FN = /^\\s*at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)\\s*$/;\nconst FRAME_NO_FN = /^\\s*at\\s+(.+?):(\\d+):(\\d+)\\s*$/;\n\nexport function parseStack(stack: string | undefined): StackFrame[] {\n if (!stack) return [];\n const frames: StackFrame[] = [];\n for (const line of stack.split(\"\\n\")) {\n const withFn = FRAME_WITH_FN.exec(line);\n if (withFn) {\n const file = withFn[2];\n frames.push({\n fn: withFn[1],\n file,\n line: Number(withFn[3]),\n col: Number(withFn[4]),\n in_app: isInApp(file),\n });\n continue;\n }\n const noFn = FRAME_NO_FN.exec(line);\n if (noFn) {\n const file = noFn[1];\n frames.push({\n fn: \"<anon>\",\n file,\n line: Number(noFn[2]),\n col: Number(noFn[3]),\n in_app: isInApp(file),\n });\n }\n }\n return frames;\n}\n\nfunction isInApp(file: string): boolean {\n if (!file) return false;\n if (file.startsWith(\"node:\")) return false;\n if (file.startsWith(\"internal/\")) return false;\n if (file.includes(\"/node_modules/\")) return false;\n return true;\n}\n","import { createHash } from \"node:crypto\";\nimport type { StackFrame } from \"./types.js\";\n\nexport function fingerprint(type: string, stack: StackFrame[]): string {\n const top = stack[0];\n const normalized = top\n ? `${type}|${normalizeFile(top.file)}|${top.fn || \"<anon>\"}|${top.line}`\n : `${type}|<no-stack>`;\n return createHash(\"sha256\").update(normalized).digest(\"hex\").slice(0, 16);\n}\n\nfunction normalizeFile(file: string): string {\n return file\n .replace(/^file:\\/\\//, \"\")\n .replace(/^.*\\/node_modules\\//, \"node_modules/\")\n .replace(/\\?.*$/, \"\");\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport type { CodeContext, StackFrame } from \"./core/types.js\";\n\nconst WINDOW = 2;\nconst cache = new Map<string, string[] | null>();\n\nexport function captureCodeContext(stack: StackFrame[]): CodeContext | null {\n const top = stack.find((f) => isReadable(f.file));\n if (!top) return null;\n const lines = loadLines(top.file);\n if (!lines) return null;\n const start = Math.max(0, top.line - 1 - WINDOW);\n const end = Math.min(lines.length, top.line + WINDOW);\n return {\n file: top.file,\n error_line: top.line,\n lines: lines.slice(start, end),\n };\n}\n\nfunction isReadable(file: string): boolean {\n if (!file || file.startsWith(\"node:\")) return false;\n if (file.includes(\"/node_modules/\")) return false;\n return file.startsWith(\"/\") || file.startsWith(\"file://\");\n}\n\nfunction loadLines(file: string): string[] | null {\n const path = file.replace(/^file:\\/\\//, \"\");\n if (cache.has(path)) return cache.get(path) ?? null;\n if (!existsSync(path)) {\n cache.set(path, null);\n return null;\n }\n try {\n const lines = readFileSync(path, \"utf8\").split(\"\\n\");\n cache.set(path, lines);\n return lines;\n } catch {\n cache.set(path, null);\n return null;\n }\n}\n","import type { Sink, WireEvent } from \"./types.js\";\n\nconst MAX_BUFFER = 100;\nconst BASE_DELAY_MS = 200;\nconst MAX_DELAY_MS = 5_000;\n\nexport class EventQueue {\n private readonly buffer: WireEvent[] = [];\n private draining = false;\n private closed = false;\n\n constructor(private readonly sink: Sink) {}\n\n enqueue(event: WireEvent): void {\n if (this.closed) return;\n if (this.buffer.length >= MAX_BUFFER) {\n this.buffer.shift();\n }\n this.buffer.push(event);\n void this.drain();\n }\n\n enqueueSync(event: WireEvent): void {\n if (this.closed) return;\n if (this.sink.emitSync) {\n try {\n this.sink.emitSync(event);\n return;\n } catch {\n // fall through to async path\n }\n }\n this.enqueue(event);\n }\n\n async flush(): Promise<void> {\n while (this.buffer.length > 0 || this.draining) {\n await new Promise((r) => setTimeout(r, 10));\n }\n }\n\n async close(): Promise<void> {\n await this.flush();\n this.closed = true;\n if (this.sink.close) await this.sink.close();\n }\n\n private async drain(): Promise<void> {\n if (this.draining) return;\n this.draining = true;\n let attempt = 0;\n while (this.buffer.length > 0) {\n const event = this.buffer[0];\n try {\n await this.sink.emit(event);\n this.buffer.shift();\n attempt = 0;\n } catch {\n attempt++;\n if (attempt >= 5) {\n this.buffer.shift();\n attempt = 0;\n continue;\n }\n const delay = Math.min(BASE_DELAY_MS * 2 ** (attempt - 1), MAX_DELAY_MS);\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n this.draining = false;\n }\n}\n","import type { Sink, WireEvent } from \"../types.js\";\n\nexport class HttpSink implements Sink {\n constructor(\n private readonly ingestUrl: string,\n private readonly fetchFn: typeof fetch = fetch,\n ) {}\n\n async emit(event: WireEvent): Promise<void> {\n const res = await this.fetchFn(this.ingestUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-pixel-key\": event.project_key,\n },\n body: JSON.stringify(event),\n });\n if (!res.ok) {\n throw new Error(`pixel ingest failed: ${res.status}`);\n }\n }\n}\n","import { homedir } from \"node:os\";\nimport { mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport Database from \"better-sqlite3\";\nimport type { Sink, WireEvent } from \"../types.js\";\n\nconst SCHEMA = `\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n event_id TEXT NOT NULL UNIQUE,\n project_key TEXT NOT NULL,\n fingerprint TEXT NOT NULL,\n type TEXT NOT NULL,\n message TEXT NOT NULL,\n stack TEXT NOT NULL,\n code_context TEXT,\n runtime TEXT NOT NULL,\n manual_report INTEGER NOT NULL DEFAULT 0,\n level TEXT NOT NULL,\n occurred_at TEXT NOT NULL,\n ingested_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS events_fingerprint ON events(project_key, fingerprint);\n CREATE INDEX IF NOT EXISTS events_occurred ON events(occurred_at);\n`;\n\nexport class LocalSqliteSink implements Sink {\n private readonly db: Database.Database;\n private readonly insert: Database.Statement;\n\n constructor(path?: string) {\n const resolved = path ?? join(homedir(), \".gg\", \"errors.db\");\n mkdirSync(dirname(resolved), { recursive: true });\n this.db = new Database(resolved);\n this.db.pragma(\"journal_mode = WAL\");\n this.db.exec(SCHEMA);\n this.insert = this.db.prepare(`\n INSERT INTO events (\n event_id, project_key, fingerprint, type, message, stack, code_context,\n runtime, manual_report, level, occurred_at\n ) VALUES (\n @event_id, @project_key, @fingerprint, @type, @message, @stack, @code_context,\n @runtime, @manual_report, @level, @occurred_at\n )\n `);\n }\n\n emitSync(event: WireEvent): void {\n this.insert.run({\n event_id: event.event_id,\n project_key: event.project_key,\n fingerprint: event.fingerprint,\n type: event.type,\n message: event.message,\n stack: JSON.stringify(event.stack),\n code_context: event.code_context ? JSON.stringify(event.code_context) : null,\n runtime: event.runtime,\n manual_report: event.manual_report ? 1 : 0,\n level: event.level,\n occurred_at: event.occurred_at,\n });\n }\n\n async emit(event: WireEvent): Promise<void> {\n this.emitSync(event);\n }\n\n async close(): Promise<void> {\n this.db.close();\n }\n}\n","import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\n\nexport const DEFAULT_INGEST_URL = \"https://gg-pixel-server.buzzbeamaustralia.workers.dev\";\n\nexport interface InstallOptions {\n cwd?: string;\n ingestUrl?: string;\n projectName?: string;\n fetchFn?: typeof fetch;\n skipPackageInstall?: boolean;\n homeDir?: string;\n}\n\nexport interface InstallResult {\n projectId: string;\n projectKey: string;\n projectName: string;\n initFilePath: string;\n envFilePath: string;\n projectsJsonPath: string;\n packageManager: PackageManager;\n packageInstalled: boolean;\n}\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\nexport async function install(opts: InstallOptions = {}): Promise<InstallResult> {\n const cwd = resolve(opts.cwd ?? process.cwd());\n const ingestUrl = (opts.ingestUrl ?? DEFAULT_INGEST_URL).replace(/\\/+$/, \"\");\n const fetchFn = opts.fetchFn ?? fetch;\n\n const projectRoot = findProjectRoot(cwd);\n if (!projectRoot) {\n throw new Error(`No package.json found in ${cwd} or any parent directory.`);\n }\n\n const pkgPath = join(projectRoot, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as { name?: string };\n const projectName = opts.projectName ?? pkg.name ?? projectRoot.split(\"/\").pop() ?? \"unnamed\";\n\n const created = await createProject(fetchFn, ingestUrl, projectName);\n\n const pm = detectPackageManager(projectRoot);\n const packageInstalled = opts.skipPackageInstall\n ? false\n : runInstall(projectRoot, pm, \"@kenkaiiii/gg-pixel\");\n\n const initFilePath = join(projectRoot, \"gg-pixel.init.mjs\");\n writeFileSync(initFilePath, renderInitFile(ingestUrl), \"utf8\");\n\n const envFilePath = join(projectRoot, \".env\");\n writeEnvKey(envFilePath, \"GG_PIXEL_KEY\", created.key);\n\n const home = opts.homeDir ?? homedir();\n const projectsJsonPath = join(home, \".gg\", \"projects.json\");\n writeProjectsMapping(projectsJsonPath, created.id, projectName, projectRoot);\n\n return {\n projectId: created.id,\n projectKey: created.key,\n projectName,\n initFilePath,\n envFilePath,\n projectsJsonPath,\n packageManager: pm,\n packageInstalled,\n };\n}\n\nfunction findProjectRoot(start: string): string | null {\n let dir = start;\n for (let i = 0; i < 20; i++) {\n if (existsSync(join(dir, \"package.json\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n return null;\n}\n\nasync function createProject(\n fetchFn: typeof fetch,\n ingestUrl: string,\n name: string,\n): Promise<{ id: string; key: string }> {\n const res = await fetchFn(`${ingestUrl}/api/projects`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`POST /api/projects failed: ${res.status} ${await safeText(res)}`);\n }\n const body = (await res.json()) as { id: string; key: string };\n if (!body.id || !body.key) throw new Error(\"response missing id/key\");\n return { id: body.id, key: body.key };\n}\n\nasync function safeText(r: Response): Promise<string> {\n try {\n return await r.text();\n } catch {\n return \"\";\n }\n}\n\nexport function detectPackageManager(projectRoot: string): PackageManager {\n if (existsSync(join(projectRoot, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(projectRoot, \"bun.lockb\"))) return \"bun\";\n if (existsSync(join(projectRoot, \"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\nfunction runInstall(projectRoot: string, pm: PackageManager, pkg: string): boolean {\n const cmd = pm;\n const args = pm === \"npm\" ? [\"install\", pkg] : [\"add\", pkg];\n const result = spawnSync(cmd, args, { cwd: projectRoot, stdio: \"inherit\" });\n return result.status === 0;\n}\n\nexport function renderInitFile(ingestUrl: string): string {\n return `import { initPixel } from \"@kenkaiiii/gg-pixel\";\n\nconst key = process.env.GG_PIXEL_KEY;\nif (key) {\n initPixel({\n projectKey: key,\n sink: { kind: \"http\", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },\n });\n}\n`;\n}\n\nexport function writeEnvKey(envPath: string, key: string, value: string): void {\n if (existsSync(envPath)) {\n const current = readFileSync(envPath, \"utf8\");\n const lineRegex = new RegExp(`^${key}=.*$`, \"m\");\n if (lineRegex.test(current)) {\n writeFileSync(envPath, current.replace(lineRegex, `${key}=${value}`), \"utf8\");\n return;\n }\n const sep = current.endsWith(\"\\n\") || current.length === 0 ? \"\" : \"\\n\";\n appendFileSync(envPath, `${sep}${key}=${value}\\n`, \"utf8\");\n return;\n }\n writeFileSync(envPath, `${key}=${value}\\n`, \"utf8\");\n}\n\nexport function writeProjectsMapping(\n projectsJsonPath: string,\n projectId: string,\n name: string,\n path: string,\n): void {\n mkdirSync(dirname(projectsJsonPath), { recursive: true });\n let map: Record<string, { name: string; path: string }> = {};\n if (existsSync(projectsJsonPath)) {\n try {\n map = JSON.parse(readFileSync(projectsJsonPath, \"utf8\")) as typeof map;\n } catch {\n // start fresh on corrupt file\n }\n }\n map[projectId] = { name, path };\n writeFileSync(projectsJsonPath, `${JSON.stringify(map, null, 2)}\\n`, \"utf8\");\n}\n","import { installNodeAdapter, type NodeAdapter } from \"./adapters/node.js\";\nimport { HttpSink } from \"./core/sinks/http.js\";\nimport { LocalSqliteSink } from \"./core/sinks/local-sqlite.js\";\nimport type { PixelOptions, ReportInput, Sink, SinkConfig } from \"./core/types.js\";\n\nlet active: NodeAdapter | null = null;\n\nexport function initPixel(options: PixelOptions): NodeAdapter {\n if (active) {\n throw new Error(\"gg-pixel is already initialized; call closePixel() first\");\n }\n const sink = buildSink(options.sink);\n active = installNodeAdapter({\n projectKey: options.projectKey,\n runtime: options.runtime ?? defaultRuntime(),\n sink,\n captureConsoleErrors: options.captureConsoleErrors ?? true,\n captureConsoleWarnings: options.captureConsoleWarnings ?? false,\n captureUnhandledRejections: options.captureUnhandledRejections ?? true,\n captureUncaughtExceptions: options.captureUncaughtExceptions ?? true,\n });\n return active;\n}\n\nexport function reportPixel(input: ReportInput): void {\n if (!active) return;\n active.report(input);\n}\n\nexport async function flushPixel(): Promise<void> {\n if (!active) return;\n await active.flush();\n}\n\nexport async function closePixel(): Promise<void> {\n if (!active) return;\n await active.close();\n active = null;\n}\n\nfunction buildSink(config: SinkConfig): Sink {\n switch (config.kind) {\n case \"http\":\n return new HttpSink(config.ingestUrl, config.fetchFn);\n case \"local\":\n return new LocalSqliteSink(config.path);\n case \"custom\":\n return config.sink;\n }\n}\n\nfunction defaultRuntime(): string {\n const v = process.versions.node;\n return `node-${v}`;\n}\n\nexport type {\n Level,\n PixelOptions,\n ReportInput,\n Sink,\n SinkConfig,\n StackFrame,\n CodeContext,\n WireEvent,\n} from \"./core/types.js\";\n\nexport { install, DEFAULT_INGEST_URL } from \"./install.js\";\nexport type { InstallOptions, InstallResult, PackageManager } from \"./install.js\";\n"],"mappings":"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","names":["existsSync","readFileSync","mkdirSync","homedir","dirname","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapters/node.ts","../src/core/stack.ts","../src/core/fingerprint.ts","../src/code-context.ts","../src/core/queue.ts","../src/core/sinks/http.ts","../src/core/sinks/local-sqlite.ts","../src/install.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { parseStack } from \"../core/stack.js\";\nimport { fingerprint } from \"../core/fingerprint.js\";\nimport { captureCodeContext } from \"../code-context.js\";\nimport { EventQueue } from \"../core/queue.js\";\nimport type { Level, ReportInput, Sink, WireEvent } from \"../core/types.js\";\n\nexport interface NodeAdapterOptions {\n projectKey: string;\n runtime: string;\n sink: Sink;\n captureConsoleErrors: boolean;\n captureConsoleWarnings: boolean;\n captureUnhandledRejections: boolean;\n captureUncaughtExceptions: boolean;\n}\n\nexport interface NodeAdapter {\n report(input: ReportInput): void;\n flush(): Promise<void>;\n close(): Promise<void>;\n}\n\nexport function installNodeAdapter(opts: NodeAdapterOptions): NodeAdapter {\n const queue = new EventQueue(opts.sink);\n const detach: Array<() => void> = [];\n\n const enqueueError = (err: unknown, level: Level, manual: boolean) => {\n try {\n const event = buildEvent(err, level, manual, opts.projectKey, opts.runtime);\n queue.enqueue(event);\n } catch {\n // never let pixel break the host program\n }\n };\n\n const enqueueErrorSync = (err: unknown, level: Level, manual: boolean) => {\n try {\n const event = buildEvent(err, level, manual, opts.projectKey, opts.runtime);\n queue.enqueueSync(event);\n } catch {\n // never let pixel break the host program\n }\n };\n\n if (opts.captureUncaughtExceptions) {\n const handler = (err: Error) => enqueueErrorSync(err, \"fatal\", false);\n process.on(\"uncaughtExceptionMonitor\", handler);\n detach.push(() => process.off(\"uncaughtExceptionMonitor\", handler));\n }\n\n if (opts.captureUnhandledRejections) {\n const handler = (reason: unknown) => enqueueErrorSync(reason, \"error\", false);\n process.on(\"unhandledRejection\", handler);\n detach.push(() => process.off(\"unhandledRejection\", handler));\n }\n\n if (opts.captureConsoleErrors) {\n detach.push(patchConsole(\"error\", (args) => enqueueError(consoleError(args), \"error\", false)));\n }\n\n if (opts.captureConsoleWarnings) {\n detach.push(patchConsole(\"warn\", (args) => enqueueError(consoleError(args), \"warning\", false)));\n }\n\n const onBeforeExit = () => {\n void queue.flush();\n };\n process.on(\"beforeExit\", onBeforeExit);\n detach.push(() => process.off(\"beforeExit\", onBeforeExit));\n\n return {\n report(input: ReportInput) {\n const level = input.level ?? \"error\";\n if (input.error !== undefined) {\n try {\n const event = buildEvent(input.error, level, true, opts.projectKey, opts.runtime);\n if (input.message) event.message = input.message;\n queue.enqueue(event);\n } catch {\n // never let pixel break the host program\n }\n return;\n }\n const err = new Error(input.message);\n err.name = \"ManualReport\";\n enqueueError(err, level, true);\n },\n flush: () => queue.flush(),\n close: async () => {\n for (const fn of detach) fn();\n await queue.close();\n },\n };\n}\n\nfunction buildEvent(\n err: unknown,\n level: Level,\n manual: boolean,\n projectKey: string,\n runtime: string,\n): WireEvent {\n const { type, message, stackString } = normalize(err);\n const stack = parseStack(stackString);\n return {\n event_id: randomUUID(),\n project_key: projectKey,\n fingerprint: fingerprint(type, stack),\n type,\n message,\n stack,\n code_context: captureCodeContext(stack),\n runtime,\n manual_report: manual,\n level,\n occurred_at: new Date().toISOString(),\n };\n}\n\nfunction normalize(err: unknown): { type: string; message: string; stackString?: string } {\n if (err instanceof Error) {\n return { type: err.name || \"Error\", message: err.message, stackString: err.stack };\n }\n if (typeof err === \"string\") {\n return { type: \"StringError\", message: err };\n }\n try {\n return { type: \"UnknownError\", message: JSON.stringify(err) };\n } catch {\n return { type: \"UnknownError\", message: String(err) };\n }\n}\n\nfunction consoleError(args: unknown[]): unknown {\n for (const a of args) if (a instanceof Error) return a;\n return new Error(args.map(stringify).join(\" \"));\n}\n\nfunction stringify(x: unknown): string {\n if (typeof x === \"string\") return x;\n try {\n return JSON.stringify(x);\n } catch {\n return String(x);\n }\n}\n\ntype ConsoleMethod = \"error\" | \"warn\";\n\nfunction patchConsole(method: ConsoleMethod, onCall: (args: unknown[]) => void): () => void {\n const original = console[method];\n console[method] = (...args: unknown[]) => {\n try {\n onCall(args);\n } catch {\n // never let pixel break the host program\n }\n original.apply(console, args);\n };\n return () => {\n console[method] = original;\n };\n}\n","import type { StackFrame } from \"./types.js\";\n\nconst FRAME_WITH_FN = /^\\s*at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)\\s*$/;\nconst FRAME_NO_FN = /^\\s*at\\s+(.+?):(\\d+):(\\d+)\\s*$/;\n\nexport function parseStack(stack: string | undefined): StackFrame[] {\n if (!stack) return [];\n const frames: StackFrame[] = [];\n for (const line of stack.split(\"\\n\")) {\n const withFn = FRAME_WITH_FN.exec(line);\n if (withFn) {\n const file = withFn[2];\n frames.push({\n fn: withFn[1],\n file,\n line: Number(withFn[3]),\n col: Number(withFn[4]),\n in_app: isInApp(file),\n });\n continue;\n }\n const noFn = FRAME_NO_FN.exec(line);\n if (noFn) {\n const file = noFn[1];\n frames.push({\n fn: \"<anon>\",\n file,\n line: Number(noFn[2]),\n col: Number(noFn[3]),\n in_app: isInApp(file),\n });\n }\n }\n return frames;\n}\n\nfunction isInApp(file: string): boolean {\n if (!file) return false;\n if (file.startsWith(\"node:\")) return false;\n if (file.startsWith(\"internal/\")) return false;\n if (file.includes(\"/node_modules/\")) return false;\n return true;\n}\n","import { createHash } from \"node:crypto\";\nimport type { StackFrame } from \"./types.js\";\n\nexport function fingerprint(type: string, stack: StackFrame[]): string {\n const top = stack[0];\n const normalized = top\n ? `${type}|${normalizeFile(top.file)}|${top.fn || \"<anon>\"}|${top.line}`\n : `${type}|<no-stack>`;\n return createHash(\"sha256\").update(normalized).digest(\"hex\").slice(0, 16);\n}\n\nfunction normalizeFile(file: string): string {\n return file\n .replace(/^file:\\/\\//, \"\")\n .replace(/^.*\\/node_modules\\//, \"node_modules/\")\n .replace(/\\?.*$/, \"\");\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport type { CodeContext, StackFrame } from \"./core/types.js\";\n\nconst WINDOW = 2;\nconst cache = new Map<string, string[] | null>();\n\nexport function captureCodeContext(stack: StackFrame[]): CodeContext | null {\n const top = stack.find((f) => isReadable(f.file));\n if (!top) return null;\n const lines = loadLines(top.file);\n if (!lines) return null;\n const start = Math.max(0, top.line - 1 - WINDOW);\n const end = Math.min(lines.length, top.line + WINDOW);\n return {\n file: top.file,\n error_line: top.line,\n lines: lines.slice(start, end),\n };\n}\n\nfunction isReadable(file: string): boolean {\n if (!file || file.startsWith(\"node:\")) return false;\n if (file.includes(\"/node_modules/\")) return false;\n return file.startsWith(\"/\") || file.startsWith(\"file://\");\n}\n\nfunction loadLines(file: string): string[] | null {\n const path = file.replace(/^file:\\/\\//, \"\");\n if (cache.has(path)) return cache.get(path) ?? null;\n if (!existsSync(path)) {\n cache.set(path, null);\n return null;\n }\n try {\n const lines = readFileSync(path, \"utf8\").split(\"\\n\");\n cache.set(path, lines);\n return lines;\n } catch {\n cache.set(path, null);\n return null;\n }\n}\n","import type { Sink, WireEvent } from \"./types.js\";\n\nconst MAX_BUFFER = 100;\nconst BASE_DELAY_MS = 200;\nconst MAX_DELAY_MS = 5_000;\n\nexport class EventQueue {\n private readonly buffer: WireEvent[] = [];\n private draining = false;\n private closed = false;\n\n constructor(private readonly sink: Sink) {}\n\n enqueue(event: WireEvent): void {\n if (this.closed) return;\n if (this.buffer.length >= MAX_BUFFER) {\n this.buffer.shift();\n }\n this.buffer.push(event);\n void this.drain();\n }\n\n enqueueSync(event: WireEvent): void {\n if (this.closed) return;\n if (this.sink.emitSync) {\n try {\n this.sink.emitSync(event);\n return;\n } catch {\n // fall through to async path\n }\n }\n this.enqueue(event);\n }\n\n async flush(): Promise<void> {\n while (this.buffer.length > 0 || this.draining) {\n await new Promise((r) => setTimeout(r, 10));\n }\n }\n\n async close(): Promise<void> {\n await this.flush();\n this.closed = true;\n if (this.sink.close) await this.sink.close();\n }\n\n private async drain(): Promise<void> {\n if (this.draining) return;\n this.draining = true;\n let attempt = 0;\n while (this.buffer.length > 0) {\n const event = this.buffer[0];\n try {\n await this.sink.emit(event);\n this.buffer.shift();\n attempt = 0;\n } catch {\n attempt++;\n if (attempt >= 5) {\n this.buffer.shift();\n attempt = 0;\n continue;\n }\n const delay = Math.min(BASE_DELAY_MS * 2 ** (attempt - 1), MAX_DELAY_MS);\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n this.draining = false;\n }\n}\n","import type { Sink, WireEvent } from \"../types.js\";\n\nexport class HttpSink implements Sink {\n constructor(\n private readonly ingestUrl: string,\n private readonly fetchFn: typeof fetch = fetch,\n ) {}\n\n async emit(event: WireEvent): Promise<void> {\n const res = await this.fetchFn(this.ingestUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-pixel-key\": event.project_key,\n },\n body: JSON.stringify(event),\n });\n if (!res.ok) {\n throw new Error(`pixel ingest failed: ${res.status}`);\n }\n }\n}\n","import { homedir } from \"node:os\";\nimport { mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport Database from \"better-sqlite3\";\nimport type { Sink, WireEvent } from \"../types.js\";\n\nconst SCHEMA = `\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n event_id TEXT NOT NULL UNIQUE,\n project_key TEXT NOT NULL,\n fingerprint TEXT NOT NULL,\n type TEXT NOT NULL,\n message TEXT NOT NULL,\n stack TEXT NOT NULL,\n code_context TEXT,\n runtime TEXT NOT NULL,\n manual_report INTEGER NOT NULL DEFAULT 0,\n level TEXT NOT NULL,\n occurred_at TEXT NOT NULL,\n ingested_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n CREATE INDEX IF NOT EXISTS events_fingerprint ON events(project_key, fingerprint);\n CREATE INDEX IF NOT EXISTS events_occurred ON events(occurred_at);\n`;\n\nexport class LocalSqliteSink implements Sink {\n private readonly db: Database.Database;\n private readonly insert: Database.Statement;\n\n constructor(path?: string) {\n const resolved = path ?? join(homedir(), \".gg\", \"errors.db\");\n mkdirSync(dirname(resolved), { recursive: true });\n this.db = new Database(resolved);\n this.db.pragma(\"journal_mode = WAL\");\n this.db.exec(SCHEMA);\n this.insert = this.db.prepare(`\n INSERT INTO events (\n event_id, project_key, fingerprint, type, message, stack, code_context,\n runtime, manual_report, level, occurred_at\n ) VALUES (\n @event_id, @project_key, @fingerprint, @type, @message, @stack, @code_context,\n @runtime, @manual_report, @level, @occurred_at\n )\n `);\n }\n\n emitSync(event: WireEvent): void {\n this.insert.run({\n event_id: event.event_id,\n project_key: event.project_key,\n fingerprint: event.fingerprint,\n type: event.type,\n message: event.message,\n stack: JSON.stringify(event.stack),\n code_context: event.code_context ? JSON.stringify(event.code_context) : null,\n runtime: event.runtime,\n manual_report: event.manual_report ? 1 : 0,\n level: event.level,\n occurred_at: event.occurred_at,\n });\n }\n\n async emit(event: WireEvent): Promise<void> {\n this.emitSync(event);\n }\n\n async close(): Promise<void> {\n this.db.close();\n }\n}\n","import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\n\nexport const DEFAULT_INGEST_URL = \"https://gg-pixel-server.buzzbeamaustralia.workers.dev\";\n\nexport interface InstallOptions {\n cwd?: string;\n ingestUrl?: string;\n projectName?: string;\n fetchFn?: typeof fetch;\n skipPackageInstall?: boolean;\n homeDir?: string;\n}\n\nexport interface InstallResult {\n projectId: string;\n projectKey: string;\n projectName: string;\n initFilePath: string;\n envFilePath: string;\n projectsJsonPath: string;\n packageManager: PackageManager;\n packageInstalled: boolean;\n entryWiring: EntryWiringResult;\n /** True when an existing project mapping was reused instead of minting a fresh one. */\n reused: boolean;\n}\n\nexport type EntryWiringResult =\n | { kind: \"injected\"; entryPath: string }\n | { kind: \"already_present\"; entryPath: string }\n | { kind: \"no_entry_found\" }\n | { kind: \"skipped\"; reason: string };\n\ninterface PackageJson {\n name?: string;\n type?: string;\n main?: string;\n module?: string;\n bin?: string | Record<string, string>;\n}\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\nexport async function install(opts: InstallOptions = {}): Promise<InstallResult> {\n const cwd = resolve(opts.cwd ?? process.cwd());\n const ingestUrl = (opts.ingestUrl ?? DEFAULT_INGEST_URL).replace(/\\/+$/, \"\");\n const fetchFn = opts.fetchFn ?? fetch;\n\n const projectRoot = findProjectRoot(cwd);\n if (!projectRoot) {\n throw new Error(`No package.json found in ${cwd} or any parent directory.`);\n }\n\n const pkgPath = join(projectRoot, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as PackageJson;\n const projectName = opts.projectName ?? pkg.name ?? projectRoot.split(\"/\").pop() ?? \"unnamed\";\n\n const home = opts.homeDir ?? homedir();\n const projectsJsonPath = join(home, \".gg\", \"projects.json\");\n const envFilePath = join(projectRoot, \".env\");\n\n // Idempotency: if we already have a mapping for this directory AND the .env\n // still has its key, reuse it instead of minting a fresh project.\n const existing = findMappingByPath(projectsJsonPath, projectRoot);\n const existingKey = readEnvKey(envFilePath, \"GG_PIXEL_KEY\");\n let created: { id: string; key: string };\n let reused = false;\n if (existing && existingKey) {\n created = { id: existing.id, key: existingKey };\n reused = true;\n } else {\n created = await createProject(fetchFn, ingestUrl, projectName);\n }\n\n const pm = detectPackageManager(projectRoot);\n const packageInstalled = opts.skipPackageInstall\n ? false\n : runInstall(projectRoot, pm, \"@kenkaiiii/gg-pixel\");\n\n const initFilePath = join(projectRoot, \"gg-pixel.init.mjs\");\n writeFileSync(initFilePath, renderInitFile(ingestUrl), \"utf8\");\n\n writeEnvKey(envFilePath, \"GG_PIXEL_KEY\", created.key);\n\n writeProjectsMapping(projectsJsonPath, created.id, projectName, projectRoot);\n\n const entryWiring = wireEntryFile(projectRoot, initFilePath, pkg);\n\n return {\n projectId: created.id,\n projectKey: created.key,\n projectName,\n initFilePath,\n envFilePath,\n projectsJsonPath,\n packageManager: pm,\n packageInstalled,\n entryWiring,\n reused,\n };\n}\n\nfunction findMappingByPath(\n projectsJsonPath: string,\n projectRoot: string,\n): { id: string; name: string; path: string } | null {\n if (!existsSync(projectsJsonPath)) return null;\n let map: Record<string, { name: string; path: string }>;\n try {\n map = JSON.parse(readFileSync(projectsJsonPath, \"utf8\")) as typeof map;\n } catch {\n return null;\n }\n for (const [id, entry] of Object.entries(map)) {\n if (entry.path === projectRoot) return { id, ...entry };\n }\n return null;\n}\n\nfunction readEnvKey(envPath: string, key: string): string | null {\n if (!existsSync(envPath)) return null;\n try {\n const content = readFileSync(envPath, \"utf8\");\n const match = new RegExp(`^${key}=(.+)$`, \"m\").exec(content);\n return match?.[1]?.trim() ?? null;\n } catch {\n return null;\n }\n}\n\nfunction findProjectRoot(start: string): string | null {\n let dir = start;\n for (let i = 0; i < 20; i++) {\n if (existsSync(join(dir, \"package.json\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n return null;\n}\n\nasync function createProject(\n fetchFn: typeof fetch,\n ingestUrl: string,\n name: string,\n): Promise<{ id: string; key: string }> {\n const res = await fetchFn(`${ingestUrl}/api/projects`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`POST /api/projects failed: ${res.status} ${await safeText(res)}`);\n }\n const body = (await res.json()) as { id: string; key: string };\n if (!body.id || !body.key) throw new Error(\"response missing id/key\");\n return { id: body.id, key: body.key };\n}\n\nasync function safeText(r: Response): Promise<string> {\n try {\n return await r.text();\n } catch {\n return \"\";\n }\n}\n\nexport function detectPackageManager(projectRoot: string): PackageManager {\n if (existsSync(join(projectRoot, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(projectRoot, \"bun.lockb\"))) return \"bun\";\n if (existsSync(join(projectRoot, \"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\nfunction runInstall(projectRoot: string, pm: PackageManager, pkg: string): boolean {\n const cmd = pm;\n // npm prints `npm audit` warnings + `npm fund` solicitations on every install.\n // That output is about the user's *existing* project — irrelevant to pixel.\n // The other package managers don't show this noise by default.\n const args = pm === \"npm\" ? [\"install\", pkg, \"--no-audit\", \"--no-fund\"] : [\"add\", pkg];\n const result = spawnSync(cmd, args, { cwd: projectRoot, stdio: \"inherit\" });\n return result.status === 0;\n}\n\nexport function renderInitFile(ingestUrl: string): string {\n return `import { initPixel } from \"@kenkaiiii/gg-pixel\";\n\nconst key = process.env.GG_PIXEL_KEY;\nif (key) {\n initPixel({\n projectKey: key,\n sink: { kind: \"http\", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },\n });\n}\n`;\n}\n\nexport function writeEnvKey(envPath: string, key: string, value: string): void {\n if (existsSync(envPath)) {\n const current = readFileSync(envPath, \"utf8\");\n const lineRegex = new RegExp(`^${key}=.*$`, \"m\");\n if (lineRegex.test(current)) {\n writeFileSync(envPath, current.replace(lineRegex, `${key}=${value}`), \"utf8\");\n return;\n }\n const sep = current.endsWith(\"\\n\") || current.length === 0 ? \"\" : \"\\n\";\n appendFileSync(envPath, `${sep}${key}=${value}\\n`, \"utf8\");\n return;\n }\n writeFileSync(envPath, `${key}=${value}\\n`, \"utf8\");\n}\n\nexport function wireEntryFile(\n projectRoot: string,\n initFilePath: string,\n pkg: PackageJson,\n): EntryWiringResult {\n const entryPath = findEntryFile(projectRoot, pkg);\n if (!entryPath) return { kind: \"no_entry_found\" };\n\n let content: string;\n try {\n content = readFileSync(entryPath, \"utf8\");\n } catch (err) {\n return { kind: \"skipped\", reason: `unreadable: ${(err as Error).message}` };\n }\n\n if (content.includes(\"gg-pixel.init\")) {\n return { kind: \"already_present\", entryPath };\n }\n\n // Compute import specifier relative to the entry file.\n const fromDir = dirname(entryPath);\n let spec = relative(fromDir, initFilePath).split(sep).join(\"/\");\n if (!spec.startsWith(\".\")) spec = \"./\" + spec;\n\n const isCjs = isCommonJsEntry(entryPath, pkg);\n const importLine = isCjs\n ? `require(${JSON.stringify(spec)});`\n : `import ${JSON.stringify(spec)};`;\n\n // Inject at the top — after a shebang line and any leading \"use strict\",\n // but before all other code, so pixel hooks run before anything else.\n const lines = content.split(\"\\n\");\n let insertAt = 0;\n if (lines[0]?.startsWith(\"#!\")) insertAt = 1;\n while (\n insertAt < lines.length &&\n /^\\s*(?:[\"']use strict[\"']|\\/\\/|\\/\\*)/.test(lines[insertAt] ?? \"\")\n ) {\n insertAt++;\n }\n\n const updated = [...lines.slice(0, insertAt), importLine, ...lines.slice(insertAt)].join(\"\\n\");\n writeFileSync(entryPath, updated, \"utf8\");\n return { kind: \"injected\", entryPath };\n}\n\nfunction findEntryFile(projectRoot: string, pkg: PackageJson): string | null {\n const tryPath = (rel: string): string | null => {\n const p = join(projectRoot, rel);\n if (existsSync(p)) return p;\n // If user pointed `main` at .js but only the .ts source exists (common in TS projects).\n if (rel.endsWith(\".js\")) {\n const ts = join(projectRoot, rel.replace(/\\.js$/, \".ts\"));\n if (existsSync(ts)) return ts;\n }\n return null;\n };\n\n if (typeof pkg.bin === \"string\") {\n const found = tryPath(pkg.bin);\n if (found) return found;\n }\n if (pkg.bin && typeof pkg.bin === \"object\") {\n for (const value of Object.values(pkg.bin)) {\n if (typeof value === \"string\") {\n const found = tryPath(value);\n if (found) return found;\n }\n }\n }\n if (pkg.main) {\n const found = tryPath(pkg.main);\n if (found) return found;\n }\n if (pkg.module) {\n const found = tryPath(pkg.module);\n if (found) return found;\n }\n\n // Fall back to common conventions.\n const candidates = [\n \"src/index.ts\",\n \"src/index.tsx\",\n \"src/index.js\",\n \"src/index.mjs\",\n \"src/main.ts\",\n \"src/main.tsx\",\n \"src/main.js\",\n \"src/server.ts\",\n \"src/server.js\",\n \"src/app.ts\",\n \"src/app.js\",\n \"src/cli.ts\",\n \"src/cli.js\",\n \"index.ts\",\n \"index.tsx\",\n \"index.js\",\n \"index.mjs\",\n \"main.ts\",\n \"main.js\",\n \"server.ts\",\n \"server.js\",\n \"app.ts\",\n \"app.js\",\n ];\n for (const c of candidates) {\n const found = tryPath(c);\n if (found) return found;\n }\n return null;\n}\n\nfunction isCommonJsEntry(entryPath: string, pkg: PackageJson): boolean {\n if (entryPath.endsWith(\".cjs\")) return true;\n if (entryPath.endsWith(\".mjs\")) return false;\n if (entryPath.endsWith(\".ts\") || entryPath.endsWith(\".tsx\")) return false;\n // .js → governed by package.json type (default is \"commonjs\")\n return pkg.type !== \"module\";\n}\n\nexport function writeProjectsMapping(\n projectsJsonPath: string,\n projectId: string,\n name: string,\n path: string,\n): void {\n mkdirSync(dirname(projectsJsonPath), { recursive: true });\n let map: Record<string, { name: string; path: string }> = {};\n if (existsSync(projectsJsonPath)) {\n try {\n map = JSON.parse(readFileSync(projectsJsonPath, \"utf8\")) as typeof map;\n } catch {\n // start fresh on corrupt file\n }\n }\n map[projectId] = { name, path };\n writeFileSync(projectsJsonPath, `${JSON.stringify(map, null, 2)}\\n`, \"utf8\");\n}\n","import { installNodeAdapter, type NodeAdapter } from \"./adapters/node.js\";\nimport { HttpSink } from \"./core/sinks/http.js\";\nimport { LocalSqliteSink } from \"./core/sinks/local-sqlite.js\";\nimport type { PixelOptions, ReportInput, Sink, SinkConfig } from \"./core/types.js\";\n\nlet active: NodeAdapter | null = null;\n\nexport function initPixel(options: PixelOptions): NodeAdapter {\n if (active) {\n throw new Error(\"gg-pixel is already initialized; call closePixel() first\");\n }\n const sink = buildSink(options.sink);\n active = installNodeAdapter({\n projectKey: options.projectKey,\n runtime: options.runtime ?? defaultRuntime(),\n sink,\n captureConsoleErrors: options.captureConsoleErrors ?? true,\n captureConsoleWarnings: options.captureConsoleWarnings ?? false,\n captureUnhandledRejections: options.captureUnhandledRejections ?? true,\n captureUncaughtExceptions: options.captureUncaughtExceptions ?? true,\n });\n return active;\n}\n\nexport function reportPixel(input: ReportInput): void {\n if (!active) return;\n active.report(input);\n}\n\nexport async function flushPixel(): Promise<void> {\n if (!active) return;\n await active.flush();\n}\n\nexport async function closePixel(): Promise<void> {\n if (!active) return;\n await active.close();\n active = null;\n}\n\nfunction buildSink(config: SinkConfig): Sink {\n switch (config.kind) {\n case \"http\":\n return new HttpSink(config.ingestUrl, config.fetchFn);\n case \"local\":\n return new LocalSqliteSink(config.path);\n case \"custom\":\n return config.sink;\n }\n}\n\nfunction defaultRuntime(): string {\n const v = process.versions.node;\n return `node-${v}`;\n}\n\nexport type {\n Level,\n PixelOptions,\n ReportInput,\n Sink,\n SinkConfig,\n StackFrame,\n CodeContext,\n WireEvent,\n} from \"./core/types.js\";\n\nexport { install, DEFAULT_INGEST_URL } from \"./install.js\";\nexport type { InstallOptions, InstallResult, PackageManager } from \"./install.js\";\n"],"mappings":"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","names":["existsSync","readFileSync","mkdirSync","homedir","dirname","join","sep"]}
|