@kenkaiiii/gg-pixel 4.3.90 → 4.3.92
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 +70 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +274 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -1
- package/dist/index.d.ts +56 -1
- package/dist/index.js +270 -6
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
|
|
1
3
|
type Level = "error" | "warning" | "fatal";
|
|
2
4
|
interface StackFrame {
|
|
3
5
|
file: string;
|
|
@@ -82,6 +84,10 @@ interface InstallResult {
|
|
|
82
84
|
projectSecret: string;
|
|
83
85
|
projectName: string;
|
|
84
86
|
projectKind: ProjectKind;
|
|
87
|
+
/** Resolved root of the user's project (the dir containing package.json /
|
|
88
|
+
* pyproject.toml / go.mod / Gemfile, depending on kind). Needed by the
|
|
89
|
+
* verifier so it can spawn a probe child from the right cwd. */
|
|
90
|
+
projectRoot: string;
|
|
85
91
|
initFilePath: string;
|
|
86
92
|
envFilePath: string;
|
|
87
93
|
projectsJsonPath: string;
|
|
@@ -115,9 +121,58 @@ type ProjectKind = "node" | "browser" | "python" | "nextjs" | "sveltekit" | "nux
|
|
|
115
121
|
type PythonPackageManager = "uv" | "poetry" | "pipenv" | "pip";
|
|
116
122
|
declare function install(opts?: InstallOptions): Promise<InstallResult>;
|
|
117
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Post-install verification.
|
|
126
|
+
*
|
|
127
|
+
* Mature SDKs (Sentry, PostHog, Datadog) all skip this — the wizard ends with
|
|
128
|
+
* "looks good!" if file writes succeeded, even when the wired code never ran
|
|
129
|
+
* a single byte. That's how every silent-failure bug we've debugged this
|
|
130
|
+
* session got past the installer (stale project keys, orphaned init files,
|
|
131
|
+
* sandboxed Electron renderers, missing dotenv, broken bundler copy steps).
|
|
132
|
+
*
|
|
133
|
+
* verifyInstall fires a synthetic event end-to-end after wiring completes:
|
|
134
|
+
* spawn a Node child in the user's project, import the SDK, report a probe,
|
|
135
|
+
* then poll the project's error list with the bearer secret. If the probe
|
|
136
|
+
* doesn't round-trip, we know the install is broken NOW instead of weeks
|
|
137
|
+
* later when an end-user finally hits a real error.
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
interface VerifyOptions {
|
|
141
|
+
projectId: string;
|
|
142
|
+
projectKey: string;
|
|
143
|
+
/** Per-project bearer secret used to query the error list. */
|
|
144
|
+
projectSecret: string;
|
|
145
|
+
/** Backend root, e.g. "https://gg-pixel-server.buzzbeamaustralia.workers.dev" (no trailing /ingest). */
|
|
146
|
+
ingestUrl: string;
|
|
147
|
+
/** User's project root — the cwd we spawn the probe from for `node_modules` resolution. */
|
|
148
|
+
projectRoot: string;
|
|
149
|
+
fetchFn?: typeof fetch;
|
|
150
|
+
spawnFn?: typeof spawn;
|
|
151
|
+
/** Skip the spawned child probe entirely; useful for non-Node kinds (browser, RN). */
|
|
152
|
+
skipChildProbe?: boolean;
|
|
153
|
+
/** Max time to wait for the probe to appear in the error list. Default 5s. */
|
|
154
|
+
timeoutMs?: number;
|
|
155
|
+
}
|
|
156
|
+
type VerifyOutcome = {
|
|
157
|
+
kind: "ok";
|
|
158
|
+
/** Which path delivered the probe successfully. */
|
|
159
|
+
method: "child_process" | "direct_ingest";
|
|
160
|
+
latencyMs: number;
|
|
161
|
+
} | {
|
|
162
|
+
kind: "failed";
|
|
163
|
+
reason: string;
|
|
164
|
+
hint?: string;
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Returns true if the fingerprint string was produced by an install probe.
|
|
168
|
+
* Used by the overlay/listing code to hide probes if cleanup ever fails.
|
|
169
|
+
*/
|
|
170
|
+
declare function isInstallProbeFingerprint(fingerprint: string | null | undefined): boolean;
|
|
171
|
+
declare function verifyInstall(opts: VerifyOptions): Promise<VerifyOutcome>;
|
|
172
|
+
|
|
118
173
|
declare function initPixel(options: PixelOptions): NodeAdapter;
|
|
119
174
|
declare function reportPixel(input: ReportInput): void;
|
|
120
175
|
declare function flushPixel(): Promise<void>;
|
|
121
176
|
declare function closePixel(): Promise<void>;
|
|
122
177
|
|
|
123
|
-
export { type CodeContext, DEFAULT_INGEST_URL, type InstallOptions, type InstallResult, type Level, type PackageManager, type PixelOptions, type ReportInput, type Sink, type SinkConfig, type StackFrame, type WireEvent, closePixel, flushPixel, initPixel, install, reportPixel };
|
|
178
|
+
export { type CodeContext, DEFAULT_INGEST_URL, type InstallOptions, type InstallResult, type Level, type PackageManager, type PixelOptions, type ReportInput, type Sink, type SinkConfig, type StackFrame, type VerifyOptions, type VerifyOutcome, type WireEvent, closePixel, flushPixel, initPixel, install, isInstallProbeFingerprint, reportPixel, verifyInstall };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
|
|
1
3
|
type Level = "error" | "warning" | "fatal";
|
|
2
4
|
interface StackFrame {
|
|
3
5
|
file: string;
|
|
@@ -82,6 +84,10 @@ interface InstallResult {
|
|
|
82
84
|
projectSecret: string;
|
|
83
85
|
projectName: string;
|
|
84
86
|
projectKind: ProjectKind;
|
|
87
|
+
/** Resolved root of the user's project (the dir containing package.json /
|
|
88
|
+
* pyproject.toml / go.mod / Gemfile, depending on kind). Needed by the
|
|
89
|
+
* verifier so it can spawn a probe child from the right cwd. */
|
|
90
|
+
projectRoot: string;
|
|
85
91
|
initFilePath: string;
|
|
86
92
|
envFilePath: string;
|
|
87
93
|
projectsJsonPath: string;
|
|
@@ -115,9 +121,58 @@ type ProjectKind = "node" | "browser" | "python" | "nextjs" | "sveltekit" | "nux
|
|
|
115
121
|
type PythonPackageManager = "uv" | "poetry" | "pipenv" | "pip";
|
|
116
122
|
declare function install(opts?: InstallOptions): Promise<InstallResult>;
|
|
117
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Post-install verification.
|
|
126
|
+
*
|
|
127
|
+
* Mature SDKs (Sentry, PostHog, Datadog) all skip this — the wizard ends with
|
|
128
|
+
* "looks good!" if file writes succeeded, even when the wired code never ran
|
|
129
|
+
* a single byte. That's how every silent-failure bug we've debugged this
|
|
130
|
+
* session got past the installer (stale project keys, orphaned init files,
|
|
131
|
+
* sandboxed Electron renderers, missing dotenv, broken bundler copy steps).
|
|
132
|
+
*
|
|
133
|
+
* verifyInstall fires a synthetic event end-to-end after wiring completes:
|
|
134
|
+
* spawn a Node child in the user's project, import the SDK, report a probe,
|
|
135
|
+
* then poll the project's error list with the bearer secret. If the probe
|
|
136
|
+
* doesn't round-trip, we know the install is broken NOW instead of weeks
|
|
137
|
+
* later when an end-user finally hits a real error.
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
interface VerifyOptions {
|
|
141
|
+
projectId: string;
|
|
142
|
+
projectKey: string;
|
|
143
|
+
/** Per-project bearer secret used to query the error list. */
|
|
144
|
+
projectSecret: string;
|
|
145
|
+
/** Backend root, e.g. "https://gg-pixel-server.buzzbeamaustralia.workers.dev" (no trailing /ingest). */
|
|
146
|
+
ingestUrl: string;
|
|
147
|
+
/** User's project root — the cwd we spawn the probe from for `node_modules` resolution. */
|
|
148
|
+
projectRoot: string;
|
|
149
|
+
fetchFn?: typeof fetch;
|
|
150
|
+
spawnFn?: typeof spawn;
|
|
151
|
+
/** Skip the spawned child probe entirely; useful for non-Node kinds (browser, RN). */
|
|
152
|
+
skipChildProbe?: boolean;
|
|
153
|
+
/** Max time to wait for the probe to appear in the error list. Default 5s. */
|
|
154
|
+
timeoutMs?: number;
|
|
155
|
+
}
|
|
156
|
+
type VerifyOutcome = {
|
|
157
|
+
kind: "ok";
|
|
158
|
+
/** Which path delivered the probe successfully. */
|
|
159
|
+
method: "child_process" | "direct_ingest";
|
|
160
|
+
latencyMs: number;
|
|
161
|
+
} | {
|
|
162
|
+
kind: "failed";
|
|
163
|
+
reason: string;
|
|
164
|
+
hint?: string;
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Returns true if the fingerprint string was produced by an install probe.
|
|
168
|
+
* Used by the overlay/listing code to hide probes if cleanup ever fails.
|
|
169
|
+
*/
|
|
170
|
+
declare function isInstallProbeFingerprint(fingerprint: string | null | undefined): boolean;
|
|
171
|
+
declare function verifyInstall(opts: VerifyOptions): Promise<VerifyOutcome>;
|
|
172
|
+
|
|
118
173
|
declare function initPixel(options: PixelOptions): NodeAdapter;
|
|
119
174
|
declare function reportPixel(input: ReportInput): void;
|
|
120
175
|
declare function flushPixel(): Promise<void>;
|
|
121
176
|
declare function closePixel(): Promise<void>;
|
|
122
177
|
|
|
123
|
-
export { type CodeContext, DEFAULT_INGEST_URL, type InstallOptions, type InstallResult, type Level, type PackageManager, type PixelOptions, type ReportInput, type Sink, type SinkConfig, type StackFrame, type WireEvent, closePixel, flushPixel, initPixel, install, reportPixel };
|
|
178
|
+
export { type CodeContext, DEFAULT_INGEST_URL, type InstallOptions, type InstallResult, type Level, type PackageManager, type PixelOptions, type ReportInput, type Sink, type SinkConfig, type StackFrame, type VerifyOptions, type VerifyOutcome, type WireEvent, closePixel, flushPixel, initPixel, install, isInstallProbeFingerprint, reportPixel, verifyInstall };
|
package/dist/index.js
CHANGED
|
@@ -429,9 +429,11 @@ import {
|
|
|
429
429
|
mkdirSync as mkdirSync2,
|
|
430
430
|
readdirSync
|
|
431
431
|
} from "fs";
|
|
432
|
+
import { createRequire as createRequire2 } from "module";
|
|
432
433
|
import { homedir as homedir2 } from "os";
|
|
433
434
|
import { dirname as dirname2, join as join2, relative, resolve, sep } from "path";
|
|
434
435
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
436
|
+
var nodeRequire = createRequire2(import.meta.url);
|
|
435
437
|
var DEFAULT_INGEST_URL = "https://gg-pixel-server.buzzbeamaustralia.workers.dev";
|
|
436
438
|
async function install(opts = {}) {
|
|
437
439
|
const cwd = resolve(opts.cwd ?? process.cwd());
|
|
@@ -495,6 +497,7 @@ async function install(opts = {}) {
|
|
|
495
497
|
projectSecret: created.secret,
|
|
496
498
|
projectName,
|
|
497
499
|
projectKind: kind,
|
|
500
|
+
projectRoot: nodeRoot,
|
|
498
501
|
initFilePath: wired.primaryInitPath,
|
|
499
502
|
envFilePath,
|
|
500
503
|
projectsJsonPath,
|
|
@@ -927,26 +930,85 @@ export default nextConfig;
|
|
|
927
930
|
);
|
|
928
931
|
return;
|
|
929
932
|
}
|
|
930
|
-
|
|
931
|
-
|
|
933
|
+
patchNextConfigViaAst(configPath);
|
|
934
|
+
}
|
|
935
|
+
var PIXEL_PKG = "@kenkaiiii/gg-pixel";
|
|
936
|
+
function patchNextConfigViaAst(configPath) {
|
|
937
|
+
const original = readFileSync2(configPath, "utf8");
|
|
938
|
+
if (original.includes(PIXEL_PKG)) return;
|
|
939
|
+
let parseModule;
|
|
940
|
+
let generateCode;
|
|
941
|
+
try {
|
|
942
|
+
const mod = nodeRequire("magicast");
|
|
943
|
+
parseModule = mod.parseModule;
|
|
944
|
+
generateCode = mod.generateCode;
|
|
945
|
+
} catch {
|
|
946
|
+
return patchNextConfigViaRegex(configPath, original);
|
|
947
|
+
}
|
|
948
|
+
let module_;
|
|
949
|
+
try {
|
|
950
|
+
module_ = parseModule(original);
|
|
951
|
+
} catch {
|
|
952
|
+
return patchNextConfigViaRegex(configPath, original);
|
|
953
|
+
}
|
|
954
|
+
const cfg = resolveNextConfigObject(module_);
|
|
955
|
+
if (!cfg) {
|
|
956
|
+
return patchNextConfigViaRegex(configPath, original);
|
|
957
|
+
}
|
|
958
|
+
const existing = cfg.serverExternalPackages;
|
|
959
|
+
const isArrayLike = Array.isArray(existing) || typeof existing === "object" && existing !== null && existing.$type === "array";
|
|
960
|
+
if (isArrayLike) {
|
|
961
|
+
const arr = existing;
|
|
962
|
+
if (arr.includes(PIXEL_PKG)) return;
|
|
963
|
+
arr.push(PIXEL_PKG);
|
|
964
|
+
} else {
|
|
965
|
+
cfg.serverExternalPackages = [PIXEL_PKG];
|
|
966
|
+
}
|
|
967
|
+
const out = generateCode(module_).code;
|
|
968
|
+
if (out !== original) writeFileSync(configPath, out, "utf8");
|
|
969
|
+
}
|
|
970
|
+
function resolveNextConfigObject(mod) {
|
|
971
|
+
const root = mod.exports.default ?? mod.exports;
|
|
972
|
+
if (!root) return null;
|
|
973
|
+
return unwrapWrappers(root);
|
|
974
|
+
}
|
|
975
|
+
function unwrapWrappers(node) {
|
|
976
|
+
let cur = node;
|
|
977
|
+
for (let i = 0; i < 6; i++) {
|
|
978
|
+
if (cur.$type === "function-call") {
|
|
979
|
+
const args = cur.$args ?? [];
|
|
980
|
+
const objArg = args.find(
|
|
981
|
+
(a) => typeof a === "object" && a !== null && a.$type !== "function-call"
|
|
982
|
+
);
|
|
983
|
+
if (!objArg) return null;
|
|
984
|
+
cur = objArg;
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
if (cur.$type === "object" || cur.$type === void 0) return cur;
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
992
|
+
function patchNextConfigViaRegex(configPath, content) {
|
|
993
|
+
if (content.includes(PIXEL_PKG)) return;
|
|
932
994
|
if (content.includes("serverExternalPackages")) {
|
|
933
995
|
const updated = content.replace(
|
|
934
996
|
/serverExternalPackages\s*:\s*\[([^\]]*)\]/,
|
|
935
997
|
(_match, inside) => {
|
|
936
998
|
const trimmed = inside.trim();
|
|
937
999
|
const sep2 = trimmed.length > 0 ? ", " : "";
|
|
938
|
-
return `serverExternalPackages: [${trimmed}${sep2}
|
|
1000
|
+
return `serverExternalPackages: [${trimmed}${sep2}${JSON.stringify(PIXEL_PKG)}]`;
|
|
939
1001
|
}
|
|
940
1002
|
);
|
|
941
1003
|
if (updated !== content) writeFileSync(configPath, updated, "utf8");
|
|
942
1004
|
return;
|
|
943
1005
|
}
|
|
944
|
-
const objStart = /(const\s+\w+\s
|
|
1006
|
+
const objStart = /(const\s+\w+\s*(?::\s*\w+)?\s*=\s*\{|module\.exports\s*=\s*\{|export\s+default\s*\{)/;
|
|
945
1007
|
const m = objStart.exec(content);
|
|
946
1008
|
if (m) {
|
|
947
1009
|
const insertAt = m.index + m[0].length;
|
|
948
1010
|
const updated = content.slice(0, insertAt) + `
|
|
949
|
-
serverExternalPackages: [
|
|
1011
|
+
serverExternalPackages: [${JSON.stringify(PIXEL_PKG)}],` + content.slice(insertAt);
|
|
950
1012
|
writeFileSync(configPath, updated, "utf8");
|
|
951
1013
|
}
|
|
952
1014
|
}
|
|
@@ -1574,6 +1636,7 @@ func init() {
|
|
|
1574
1636
|
projectSecret: created.secret,
|
|
1575
1637
|
projectName,
|
|
1576
1638
|
projectKind: "go",
|
|
1639
|
+
projectRoot,
|
|
1577
1640
|
initFilePath,
|
|
1578
1641
|
envFilePath,
|
|
1579
1642
|
projectsJsonPath,
|
|
@@ -1639,6 +1702,7 @@ GGPixel.init(
|
|
|
1639
1702
|
projectSecret: created.secret,
|
|
1640
1703
|
projectName,
|
|
1641
1704
|
projectKind: "ruby",
|
|
1705
|
+
projectRoot,
|
|
1642
1706
|
initFilePath,
|
|
1643
1707
|
envFilePath,
|
|
1644
1708
|
projectsJsonPath,
|
|
@@ -1708,6 +1772,7 @@ async function installPython(ctx) {
|
|
|
1708
1772
|
projectSecret: created.secret,
|
|
1709
1773
|
projectName,
|
|
1710
1774
|
projectKind: "python",
|
|
1775
|
+
projectRoot,
|
|
1711
1776
|
initFilePath,
|
|
1712
1777
|
envFilePath,
|
|
1713
1778
|
projectsJsonPath,
|
|
@@ -1839,6 +1904,203 @@ function writeProjectsMapping(projectsJsonPath, projectId, name, path, secret) {
|
|
|
1839
1904
|
`, "utf8");
|
|
1840
1905
|
}
|
|
1841
1906
|
|
|
1907
|
+
// src/verify.ts
|
|
1908
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
1909
|
+
import { writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync3, existsSync as existsSync3 } from "fs";
|
|
1910
|
+
import { join as join3 } from "path";
|
|
1911
|
+
import { randomBytes } from "crypto";
|
|
1912
|
+
var PROBE_FINGERPRINT_PREFIX = "__pixel_install_probe__";
|
|
1913
|
+
function isInstallProbeFingerprint(fingerprint2) {
|
|
1914
|
+
return typeof fingerprint2 === "string" && fingerprint2.startsWith(PROBE_FINGERPRINT_PREFIX);
|
|
1915
|
+
}
|
|
1916
|
+
async function verifyInstall(opts) {
|
|
1917
|
+
const fetchFn = opts.fetchFn ?? fetch;
|
|
1918
|
+
const spawnFn = opts.spawnFn ?? nodeSpawn;
|
|
1919
|
+
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
1920
|
+
const ingest = opts.ingestUrl.replace(/\/+$/, "");
|
|
1921
|
+
const fingerprint2 = `${PROBE_FINGERPRINT_PREFIX}${randomBytes(6).toString("hex")}`;
|
|
1922
|
+
const start = Date.now();
|
|
1923
|
+
let method;
|
|
1924
|
+
let probeError = null;
|
|
1925
|
+
if (opts.skipChildProbe) {
|
|
1926
|
+
try {
|
|
1927
|
+
await postDirectIngest({
|
|
1928
|
+
ingestUrl: ingest,
|
|
1929
|
+
projectKey: opts.projectKey,
|
|
1930
|
+
fingerprint: fingerprint2,
|
|
1931
|
+
fetchFn
|
|
1932
|
+
});
|
|
1933
|
+
method = "direct_ingest";
|
|
1934
|
+
} catch (err) {
|
|
1935
|
+
return {
|
|
1936
|
+
kind: "failed",
|
|
1937
|
+
reason: "Direct ingest failed",
|
|
1938
|
+
hint: err.message
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
} else {
|
|
1942
|
+
try {
|
|
1943
|
+
await runChildProbe({
|
|
1944
|
+
projectRoot: opts.projectRoot,
|
|
1945
|
+
ingestUrl: ingest,
|
|
1946
|
+
projectKey: opts.projectKey,
|
|
1947
|
+
fingerprint: fingerprint2,
|
|
1948
|
+
spawnFn
|
|
1949
|
+
});
|
|
1950
|
+
method = "child_process";
|
|
1951
|
+
} catch (err) {
|
|
1952
|
+
probeError = err.message;
|
|
1953
|
+
try {
|
|
1954
|
+
await postDirectIngest({
|
|
1955
|
+
ingestUrl: ingest,
|
|
1956
|
+
projectKey: opts.projectKey,
|
|
1957
|
+
fingerprint: fingerprint2,
|
|
1958
|
+
fetchFn
|
|
1959
|
+
});
|
|
1960
|
+
method = "direct_ingest";
|
|
1961
|
+
} catch (err2) {
|
|
1962
|
+
return {
|
|
1963
|
+
kind: "failed",
|
|
1964
|
+
reason: "Could not deliver probe event",
|
|
1965
|
+
hint: `child-process: ${probeError}; direct ingest: ${err2.message}`
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
const probeRow = await pollForFingerprint({
|
|
1971
|
+
ingestUrl: ingest,
|
|
1972
|
+
projectId: opts.projectId,
|
|
1973
|
+
projectSecret: opts.projectSecret,
|
|
1974
|
+
fingerprint: fingerprint2,
|
|
1975
|
+
timeoutMs,
|
|
1976
|
+
fetchFn
|
|
1977
|
+
});
|
|
1978
|
+
if (!probeRow) {
|
|
1979
|
+
return {
|
|
1980
|
+
kind: "failed",
|
|
1981
|
+
reason: `Probe sent (${method}) but didn't appear in /api/projects/${opts.projectId}/errors within ${timeoutMs}ms`,
|
|
1982
|
+
hint: probeError ?? void 0
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
try {
|
|
1986
|
+
await fetchFn(`${ingest}/api/errors/${probeRow.id}`, {
|
|
1987
|
+
method: "DELETE",
|
|
1988
|
+
headers: { authorization: `Bearer ${opts.projectSecret}` }
|
|
1989
|
+
});
|
|
1990
|
+
} catch {
|
|
1991
|
+
}
|
|
1992
|
+
return {
|
|
1993
|
+
kind: "ok",
|
|
1994
|
+
method,
|
|
1995
|
+
latencyMs: Date.now() - start
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
async function runChildProbe(args) {
|
|
1999
|
+
const ggDir = join3(args.projectRoot, ".gg");
|
|
2000
|
+
if (!existsSync3(ggDir)) mkdirSync3(ggDir, { recursive: true });
|
|
2001
|
+
const probePath = join3(ggDir, `pixel-probe-${randomBytes(4).toString("hex")}.mjs`);
|
|
2002
|
+
const script = `import "@kenkaiiii/gg-pixel";
|
|
2003
|
+
const body = ${JSON.stringify({
|
|
2004
|
+
project_key: args.projectKey,
|
|
2005
|
+
fingerprint: args.fingerprint,
|
|
2006
|
+
type: "InstallProbe",
|
|
2007
|
+
message: "Install verification probe \u2014 auto-generated, safe to delete",
|
|
2008
|
+
stack: [],
|
|
2009
|
+
code_context: null,
|
|
2010
|
+
runtime: "",
|
|
2011
|
+
manual_report: true,
|
|
2012
|
+
level: "error"
|
|
2013
|
+
})};
|
|
2014
|
+
body.event_id = "evt_probe_" + crypto.randomUUID();
|
|
2015
|
+
body.runtime = "installer-node-" + process.versions.node;
|
|
2016
|
+
body.occurred_at = new Date().toISOString();
|
|
2017
|
+
const res = await fetch(${JSON.stringify(args.ingestUrl + "/ingest")}, {
|
|
2018
|
+
method: "POST",
|
|
2019
|
+
headers: { "content-type": "application/json" },
|
|
2020
|
+
body: JSON.stringify(body),
|
|
2021
|
+
});
|
|
2022
|
+
if (!res.ok) {
|
|
2023
|
+
const txt = await res.text().catch(() => "");
|
|
2024
|
+
console.error("probe ingest failed: status=" + res.status + " body=" + txt.slice(0, 200));
|
|
2025
|
+
process.exit(1);
|
|
2026
|
+
}
|
|
2027
|
+
`;
|
|
2028
|
+
writeFileSync2(probePath, script, "utf8");
|
|
2029
|
+
try {
|
|
2030
|
+
await new Promise((resolve2, reject) => {
|
|
2031
|
+
const opts = { cwd: args.projectRoot, stdio: "pipe" };
|
|
2032
|
+
const child = args.spawnFn("node", [probePath], opts);
|
|
2033
|
+
let stderr = "";
|
|
2034
|
+
child.stderr?.on("data", (b) => {
|
|
2035
|
+
stderr += b.toString();
|
|
2036
|
+
});
|
|
2037
|
+
const timer = setTimeout(() => {
|
|
2038
|
+
child.kill("SIGKILL");
|
|
2039
|
+
reject(new Error("Probe child timed out after 10s"));
|
|
2040
|
+
}, 1e4);
|
|
2041
|
+
child.on("error", (err) => {
|
|
2042
|
+
clearTimeout(timer);
|
|
2043
|
+
reject(err);
|
|
2044
|
+
});
|
|
2045
|
+
child.on("exit", (code) => {
|
|
2046
|
+
clearTimeout(timer);
|
|
2047
|
+
if (code === 0) resolve2();
|
|
2048
|
+
else reject(new Error(`Probe exited code=${code}; stderr: ${stderr.trim().slice(0, 400)}`));
|
|
2049
|
+
});
|
|
2050
|
+
});
|
|
2051
|
+
} finally {
|
|
2052
|
+
try {
|
|
2053
|
+
unlinkSync(probePath);
|
|
2054
|
+
} catch {
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
async function postDirectIngest(args) {
|
|
2059
|
+
const res = await args.fetchFn(`${args.ingestUrl}/ingest`, {
|
|
2060
|
+
method: "POST",
|
|
2061
|
+
headers: { "content-type": "application/json" },
|
|
2062
|
+
body: JSON.stringify({
|
|
2063
|
+
event_id: `evt_probe_${randomBytes(8).toString("hex")}`,
|
|
2064
|
+
project_key: args.projectKey,
|
|
2065
|
+
fingerprint: args.fingerprint,
|
|
2066
|
+
type: "InstallProbe",
|
|
2067
|
+
message: "Install verification probe",
|
|
2068
|
+
stack: [],
|
|
2069
|
+
code_context: null,
|
|
2070
|
+
runtime: `installer-node-${process.versions.node}`,
|
|
2071
|
+
manual_report: true,
|
|
2072
|
+
level: "error",
|
|
2073
|
+
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2074
|
+
})
|
|
2075
|
+
});
|
|
2076
|
+
if (!res.ok) {
|
|
2077
|
+
let body = "";
|
|
2078
|
+
try {
|
|
2079
|
+
body = await res.text();
|
|
2080
|
+
} catch {
|
|
2081
|
+
}
|
|
2082
|
+
throw new Error(`POST /ingest \u2192 ${res.status} ${body.slice(0, 200)}`);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
async function pollForFingerprint(args) {
|
|
2086
|
+
const deadline = Date.now() + args.timeoutMs;
|
|
2087
|
+
while (Date.now() < deadline) {
|
|
2088
|
+
try {
|
|
2089
|
+
const res = await args.fetchFn(`${args.ingestUrl}/api/projects/${args.projectId}/errors`, {
|
|
2090
|
+
headers: { authorization: `Bearer ${args.projectSecret}` }
|
|
2091
|
+
});
|
|
2092
|
+
if (res.ok) {
|
|
2093
|
+
const body = await res.json();
|
|
2094
|
+
const match = body.errors.find((e) => e.fingerprint === args.fingerprint);
|
|
2095
|
+
if (match) return { id: match.id };
|
|
2096
|
+
}
|
|
2097
|
+
} catch {
|
|
2098
|
+
}
|
|
2099
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
2100
|
+
}
|
|
2101
|
+
return null;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
1842
2104
|
// src/index.ts
|
|
1843
2105
|
var active = null;
|
|
1844
2106
|
function initPixel(options) {
|
|
@@ -1890,6 +2152,8 @@ export {
|
|
|
1890
2152
|
flushPixel,
|
|
1891
2153
|
initPixel,
|
|
1892
2154
|
install,
|
|
1893
|
-
|
|
2155
|
+
isInstallProbeFingerprint,
|
|
2156
|
+
reportPixel,
|
|
2157
|
+
verifyInstall
|
|
1894
2158
|
};
|
|
1895
2159
|
//# sourceMappingURL=index.js.map
|