@kenkaiiii/gg-pixel 4.3.71 → 4.3.72
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/workers.d.ts +67 -0
- package/dist/workers.js +207 -0
- package/dist/workers.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
type Level = "error" | "warning" | "fatal";
|
|
2
|
+
interface StackFrame {
|
|
3
|
+
file: string;
|
|
4
|
+
line: number;
|
|
5
|
+
col: number;
|
|
6
|
+
fn: string;
|
|
7
|
+
in_app: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface CodeContext {
|
|
10
|
+
file: string;
|
|
11
|
+
error_line: number;
|
|
12
|
+
lines: string[];
|
|
13
|
+
}
|
|
14
|
+
interface WireEvent {
|
|
15
|
+
event_id: string;
|
|
16
|
+
project_key: string;
|
|
17
|
+
fingerprint: string;
|
|
18
|
+
type: string;
|
|
19
|
+
message: string;
|
|
20
|
+
stack: StackFrame[];
|
|
21
|
+
code_context: CodeContext | null;
|
|
22
|
+
runtime: string;
|
|
23
|
+
manual_report: boolean;
|
|
24
|
+
level: Level;
|
|
25
|
+
occurred_at: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Cloudflare Workers / Vercel Edge / Bun edge adapter.
|
|
30
|
+
*
|
|
31
|
+
* These edge runtimes don't have `process.on(...)` (no Node) or `window.onerror`
|
|
32
|
+
* (no DOM). Each request is an isolated invocation. The right pattern is to
|
|
33
|
+
* **wrap the user's exported handler(s)** in a try/catch and `ctx.waitUntil`
|
|
34
|
+
* the ingest POST so the runtime keeps I/O alive while the worker returns.
|
|
35
|
+
*
|
|
36
|
+
* We wrap every standard handler (fetch, scheduled, queue, email, trace) if
|
|
37
|
+
* present on the user's exported handler object.
|
|
38
|
+
*/
|
|
39
|
+
interface WorkersExecutionContext {
|
|
40
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
41
|
+
}
|
|
42
|
+
interface WorkersPixelOptions {
|
|
43
|
+
projectKey: string;
|
|
44
|
+
/** Backend ingest URL. Defaults to the public gg-pixel server. */
|
|
45
|
+
ingestUrl?: string;
|
|
46
|
+
/** Override the runtime label (default: `cloudflare-workers`). */
|
|
47
|
+
runtime?: string;
|
|
48
|
+
}
|
|
49
|
+
declare const HANDLER_KEYS: readonly ["fetch", "scheduled", "queue", "email", "trace"];
|
|
50
|
+
type HandlerKey = (typeof HANDLER_KEYS)[number];
|
|
51
|
+
/**
|
|
52
|
+
* Wrap an exported Worker handler so any thrown error is reported to gg-pixel
|
|
53
|
+
* before being re-thrown. Original error semantics are preserved.
|
|
54
|
+
*/
|
|
55
|
+
declare function withPixel<H extends Partial<Record<HandlerKey, unknown>>>(options: WorkersPixelOptions, handler: H): H;
|
|
56
|
+
/**
|
|
57
|
+
* Manual report from inside a Worker. The `ctx` is required so we can
|
|
58
|
+
* `waitUntil` the ingest POST — without it the worker may terminate before
|
|
59
|
+
* the network request completes.
|
|
60
|
+
*/
|
|
61
|
+
declare function reportPixel(ctx: WorkersExecutionContext, options: WorkersPixelOptions, input: {
|
|
62
|
+
message: string;
|
|
63
|
+
error?: unknown;
|
|
64
|
+
level?: Level;
|
|
65
|
+
}): void;
|
|
66
|
+
|
|
67
|
+
export { type Level, type StackFrame, type WireEvent, type WorkersPixelOptions, reportPixel, withPixel };
|
package/dist/workers.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// src/core/fingerprint-web.ts
|
|
2
|
+
function fingerprintWeb(type, stack) {
|
|
3
|
+
const top = stack[0];
|
|
4
|
+
const normalized = top ? `${type}|${normalizeFile(top.file)}|${top.fn || "<anon>"}|${top.line}` : `${type}|<no-stack>`;
|
|
5
|
+
return fnv1a64(normalized);
|
|
6
|
+
}
|
|
7
|
+
function fnv1a64(input) {
|
|
8
|
+
let hHi = 3421674724;
|
|
9
|
+
let hLo = 2216829733;
|
|
10
|
+
for (let i = 0; i < input.length; i++) {
|
|
11
|
+
const code = input.charCodeAt(i);
|
|
12
|
+
hLo ^= code & 255;
|
|
13
|
+
if (input.charCodeAt(i) > 255) hHi ^= code >>> 8 & 255;
|
|
14
|
+
const lo16 = hLo & 65535;
|
|
15
|
+
const lo32 = hLo >>> 16 & 65535;
|
|
16
|
+
const hi16 = hHi & 65535;
|
|
17
|
+
const hi32 = hHi >>> 16 & 65535;
|
|
18
|
+
let nLo = lo16 * 435;
|
|
19
|
+
let nMid = lo32 * 435 + (nLo >>> 16 & 65535);
|
|
20
|
+
let nHi = hi16 * 435 + (nMid >>> 16 & 65535);
|
|
21
|
+
let nTop = hi32 * 435 + (nHi >>> 16 & 65535);
|
|
22
|
+
nLo += lo16 * 0;
|
|
23
|
+
nMid += lo32 * 0;
|
|
24
|
+
nHi += hi16 * 0;
|
|
25
|
+
nTop += hi32 * 0;
|
|
26
|
+
nHi += lo16;
|
|
27
|
+
nTop += lo32 + (nHi >>> 16 & 65535);
|
|
28
|
+
hLo = ((nMid & 65535) << 16 | nLo & 65535) >>> 0;
|
|
29
|
+
hHi = ((nTop & 65535) << 16 | nHi & 65535) >>> 0;
|
|
30
|
+
}
|
|
31
|
+
return toHex32(hHi) + toHex32(hLo);
|
|
32
|
+
}
|
|
33
|
+
function toHex32(n) {
|
|
34
|
+
return (n >>> 0).toString(16).padStart(8, "0");
|
|
35
|
+
}
|
|
36
|
+
function normalizeFile(file) {
|
|
37
|
+
return file.replace(/\?.*$/, "").replace(/#.*$/, "");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/core/stack-web.ts
|
|
41
|
+
var CHROME_FRAME = /^\s*at\s+(?:(.+?)\s+\()?(?:(.+?):(\d+):(\d+))(?:\))?\s*$/;
|
|
42
|
+
var GECKO_FRAME = /^\s*(.*?)@(.+?):(\d+)(?::(\d+))?\s*$/;
|
|
43
|
+
function parseBrowserStack(stack, sameOrigin) {
|
|
44
|
+
if (!stack) return [];
|
|
45
|
+
const frames = [];
|
|
46
|
+
for (const line of stack.split("\n")) {
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (!trimmed) continue;
|
|
49
|
+
if (!/^(?:at\s|.*@)/.test(trimmed)) continue;
|
|
50
|
+
const chrome = CHROME_FRAME.exec(line);
|
|
51
|
+
if (chrome) {
|
|
52
|
+
const fn = (chrome[1] ?? "").trim() || "<anon>";
|
|
53
|
+
const file = chrome[2];
|
|
54
|
+
if (!file) continue;
|
|
55
|
+
frames.push({
|
|
56
|
+
fn,
|
|
57
|
+
file,
|
|
58
|
+
line: Number(chrome[3]),
|
|
59
|
+
col: Number(chrome[4]),
|
|
60
|
+
in_app: isInApp(file, sameOrigin)
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const gecko = GECKO_FRAME.exec(line);
|
|
65
|
+
if (gecko) {
|
|
66
|
+
const fn = (gecko[1] ?? "").trim() || "<anon>";
|
|
67
|
+
const file = gecko[2];
|
|
68
|
+
if (!file) continue;
|
|
69
|
+
frames.push({
|
|
70
|
+
fn,
|
|
71
|
+
file,
|
|
72
|
+
line: Number(gecko[3]),
|
|
73
|
+
col: gecko[4] ? Number(gecko[4]) : 0,
|
|
74
|
+
in_app: isInApp(file, sameOrigin)
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return frames;
|
|
79
|
+
}
|
|
80
|
+
function isInApp(file, sameOrigin) {
|
|
81
|
+
if (!file) return false;
|
|
82
|
+
if (/^chrome-extension:\/\//.test(file)) return false;
|
|
83
|
+
if (/^moz-extension:\/\//.test(file)) return false;
|
|
84
|
+
if (/^safari-extension:\/\//.test(file)) return false;
|
|
85
|
+
if (/^webkit-masked-url:\/\//.test(file)) return false;
|
|
86
|
+
if (file === "<anonymous>" || file === "[native code]") return false;
|
|
87
|
+
if (!sameOrigin) {
|
|
88
|
+
return /^https?:\/\//.test(file) || /^[a-z]+:\/\//.test(file) === false;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const url = new URL(file, sameOrigin);
|
|
92
|
+
return url.origin === sameOrigin;
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/adapters/workers.ts
|
|
99
|
+
var DEFAULT_INGEST_URL = "https://gg-pixel-server.buzzbeamaustralia.workers.dev";
|
|
100
|
+
var HANDLER_KEYS = ["fetch", "scheduled", "queue", "email", "trace"];
|
|
101
|
+
function withPixel(options, handler) {
|
|
102
|
+
const ingestUrl = buildIngestUrl(options.ingestUrl);
|
|
103
|
+
const projectKey = options.projectKey;
|
|
104
|
+
const runtime = options.runtime ?? "cloudflare-workers";
|
|
105
|
+
const wrapped = { ...handler };
|
|
106
|
+
for (const key of HANDLER_KEYS) {
|
|
107
|
+
const original = handler[key];
|
|
108
|
+
if (typeof original === "function") {
|
|
109
|
+
wrapped[key] = wrapHandler(original, ingestUrl, projectKey, runtime);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return wrapped;
|
|
113
|
+
}
|
|
114
|
+
function wrapHandler(fn, ingestUrl, projectKey, runtime) {
|
|
115
|
+
return async (...args) => {
|
|
116
|
+
const ctx = args[args.length - 1];
|
|
117
|
+
try {
|
|
118
|
+
return await fn(...args);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
try {
|
|
121
|
+
const event = buildEvent(err, projectKey, runtime, false, "fatal");
|
|
122
|
+
if (ctx && typeof ctx.waitUntil === "function") {
|
|
123
|
+
ctx.waitUntil(sendEvent(ingestUrl, projectKey, event));
|
|
124
|
+
} else {
|
|
125
|
+
void sendEvent(ingestUrl, projectKey, event).catch(() => {
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function reportPixel(ctx, options, input) {
|
|
135
|
+
const ingestUrl = buildIngestUrl(options.ingestUrl);
|
|
136
|
+
const runtime = options.runtime ?? "cloudflare-workers";
|
|
137
|
+
const level = input.level ?? "error";
|
|
138
|
+
let event;
|
|
139
|
+
if (input.error !== void 0) {
|
|
140
|
+
event = buildEvent(input.error, options.projectKey, runtime, true, level);
|
|
141
|
+
if (input.message) event.message = input.message;
|
|
142
|
+
} else {
|
|
143
|
+
const err = new Error(input.message);
|
|
144
|
+
err.name = "ManualReport";
|
|
145
|
+
event = buildEvent(err, options.projectKey, runtime, true, level);
|
|
146
|
+
}
|
|
147
|
+
ctx.waitUntil(sendEvent(ingestUrl, options.projectKey, event));
|
|
148
|
+
}
|
|
149
|
+
function buildIngestUrl(base) {
|
|
150
|
+
return (base ?? DEFAULT_INGEST_URL).replace(/\/+$/, "") + "/ingest";
|
|
151
|
+
}
|
|
152
|
+
function buildEvent(err, projectKey, runtime, manual, level) {
|
|
153
|
+
const { type, message, stackString } = normalize(err);
|
|
154
|
+
const stack = parseBrowserStack(stackString);
|
|
155
|
+
return {
|
|
156
|
+
event_id: uuid(),
|
|
157
|
+
project_key: projectKey,
|
|
158
|
+
fingerprint: fingerprintWeb(type, stack),
|
|
159
|
+
type,
|
|
160
|
+
message,
|
|
161
|
+
stack,
|
|
162
|
+
code_context: null,
|
|
163
|
+
runtime,
|
|
164
|
+
manual_report: manual,
|
|
165
|
+
level,
|
|
166
|
+
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function normalize(err) {
|
|
170
|
+
if (err instanceof Error) {
|
|
171
|
+
return { type: err.name || "Error", message: err.message, stackString: err.stack };
|
|
172
|
+
}
|
|
173
|
+
if (typeof err === "string") return { type: "StringError", message: err };
|
|
174
|
+
try {
|
|
175
|
+
return { type: "UnknownError", message: JSON.stringify(err) };
|
|
176
|
+
} catch {
|
|
177
|
+
return { type: "UnknownError", message: String(err) };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function sendEvent(ingestUrl, projectKey, event) {
|
|
181
|
+
try {
|
|
182
|
+
await fetch(ingestUrl, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: {
|
|
185
|
+
"content-type": "application/json",
|
|
186
|
+
"x-pixel-key": projectKey
|
|
187
|
+
},
|
|
188
|
+
body: JSON.stringify(event)
|
|
189
|
+
});
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function uuid() {
|
|
194
|
+
const c = globalThis.crypto;
|
|
195
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
196
|
+
const bytes = new Uint8Array(16);
|
|
197
|
+
if (c?.getRandomValues) c.getRandomValues(bytes);
|
|
198
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
199
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
200
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
201
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
202
|
+
}
|
|
203
|
+
export {
|
|
204
|
+
reportPixel,
|
|
205
|
+
withPixel
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=workers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/fingerprint-web.ts","../src/core/stack-web.ts","../src/adapters/workers.ts"],"sourcesContent":["import type { StackFrame } from \"./types.js\";\n\n/**\n * Browser-safe synchronous fingerprint.\n *\n * `node:crypto` doesn't exist in browsers, and Web Crypto's `subtle.digest`\n * is async — making the queue async would break the fast-path. Sentry's\n * browser SDK uses simple sync hashing for the same reason. Our fingerprint\n * just needs to be stable for the same inputs; cryptographic strength isn't\n * required.\n *\n * Implementation: FNV-1a 64-bit. Fast, sync, ~1KB. Output as 16-char hex.\n */\nexport function fingerprintWeb(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 fnv1a64(normalized);\n}\n\n/** FNV-1a 64-bit hash. Returns 16-char lowercase hex. */\nexport function fnv1a64(input: string): string {\n // 64-bit math via two 32-bit halves, since JS doesn't have native u64.\n // Constants from the FNV-1a spec.\n let hHi = 0xcbf29ce4;\n let hLo = 0x84222325;\n for (let i = 0; i < input.length; i++) {\n const code = input.charCodeAt(i);\n hLo ^= code & 0xff;\n if (input.charCodeAt(i) > 0xff) hHi ^= (code >>> 8) & 0xff;\n\n // h = h * 0x100000001b3 (FNV prime), as 64-bit math:\n // The prime split into hi/lo 32-bit parts is { 0x100, 0x000001b3 }.\n const lo16 = hLo & 0xffff;\n const lo32 = (hLo >>> 16) & 0xffff;\n const hi16 = hHi & 0xffff;\n const hi32 = (hHi >>> 16) & 0xffff;\n\n let nLo = lo16 * 0x1b3;\n let nMid = lo32 * 0x1b3 + ((nLo >>> 16) & 0xffff);\n let nHi = hi16 * 0x1b3 + ((nMid >>> 16) & 0xffff);\n let nTop = hi32 * 0x1b3 + ((nHi >>> 16) & 0xffff);\n\n nLo += lo16 * 0; // *= 0x100... carry from low parts\n nMid += lo32 * 0;\n nHi += hi16 * 0;\n nTop += hi32 * 0;\n\n // Add the * 0x1_00000000_00 portion (lo16 << 32).\n nHi += lo16;\n nTop += lo32 + ((nHi >>> 16) & 0xffff);\n\n hLo = (((nMid & 0xffff) << 16) | (nLo & 0xffff)) >>> 0;\n hHi = (((nTop & 0xffff) << 16) | (nHi & 0xffff)) >>> 0;\n }\n\n return toHex32(hHi) + toHex32(hLo);\n}\n\nfunction toHex32(n: number): string {\n return (n >>> 0).toString(16).padStart(8, \"0\");\n}\n\nfunction normalizeFile(file: string): string {\n return file.replace(/\\?.*$/, \"\").replace(/#.*$/, \"\");\n}\n","import type { StackFrame } from \"./types.js\";\n\n/**\n * Cross-browser stack-trace parser.\n *\n * Browser stack formats differ wildly. We support two — that covers\n * Chrome/Edge/Safari (V8-shaped) and Firefox (Gecko-shaped). Real-world\n * coverage cited by Sentry's parsers in `packages/browser/src/stack-parsers.ts`.\n */\n\n// Chrome / V8 / Edge / modern Safari. Examples:\n// \" at fnName (https://app.com/main.js:42:13)\"\n// \" at https://app.com/main.js:42:13\"\n// \" at fnName (eval at <anonymous> (https://x/y.js:1:1), <anonymous>:1:5)\"\nconst CHROME_FRAME = /^\\s*at\\s+(?:(.+?)\\s+\\()?(?:(.+?):(\\d+):(\\d+))(?:\\))?\\s*$/;\n\n// Firefox / Gecko. Examples:\n// \"fnName@https://app.com/main.js:42:13\"\n// \"@https://app.com/main.js:42:13\"\nconst GECKO_FRAME = /^\\s*(.*?)@(.+?):(\\d+)(?::(\\d+))?\\s*$/;\n\nexport function parseBrowserStack(stack: string | undefined, sameOrigin?: string): StackFrame[] {\n if (!stack) return [];\n const frames: StackFrame[] = [];\n for (const line of stack.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n // Skip the leading \"TypeError: x\" header line — no `at` prefix and no `@`.\n if (!/^(?:at\\s|.*@)/.test(trimmed)) continue;\n\n const chrome = CHROME_FRAME.exec(line);\n if (chrome) {\n const fn = (chrome[1] ?? \"\").trim() || \"<anon>\";\n const file = chrome[2];\n if (!file) continue;\n frames.push({\n fn,\n file,\n line: Number(chrome[3]),\n col: Number(chrome[4]),\n in_app: isInApp(file, sameOrigin),\n });\n continue;\n }\n\n const gecko = GECKO_FRAME.exec(line);\n if (gecko) {\n const fn = (gecko[1] ?? \"\").trim() || \"<anon>\";\n const file = gecko[2];\n if (!file) continue;\n frames.push({\n fn,\n file,\n line: Number(gecko[3]),\n col: gecko[4] ? Number(gecko[4]) : 0,\n in_app: isInApp(file, sameOrigin),\n });\n }\n }\n return frames;\n}\n\n/**\n * Mark a frame as in-app when its URL matches the page's origin (or other\n * provided origin). Cross-origin scripts (CDN bundles, third-party widgets,\n * browser extensions) are framework/library noise — not the user's code.\n */\nfunction isInApp(file: string, sameOrigin?: string): boolean {\n if (!file) return false;\n // Browser-extension and inline-eval frames — never user code.\n if (/^chrome-extension:\\/\\//.test(file)) return false;\n if (/^moz-extension:\\/\\//.test(file)) return false;\n if (/^safari-extension:\\/\\//.test(file)) return false;\n if (/^webkit-masked-url:\\/\\//.test(file)) return false;\n if (file === \"<anonymous>\" || file === \"[native code]\") return false;\n\n if (!sameOrigin) {\n // No origin context — best-effort: any http(s) URL counts as in_app\n // unless we can prove otherwise.\n return /^https?:\\/\\//.test(file) || /^[a-z]+:\\/\\//.test(file) === false;\n }\n\n try {\n const url = new URL(file, sameOrigin);\n return url.origin === sameOrigin;\n } catch {\n return false;\n }\n}\n","import { fingerprintWeb } from \"../core/fingerprint-web.js\";\nimport { parseBrowserStack } from \"../core/stack-web.js\";\nimport type { Level, WireEvent } from \"../core/types.js\";\n\n/**\n * Cloudflare Workers / Vercel Edge / Bun edge adapter.\n *\n * These edge runtimes don't have `process.on(...)` (no Node) or `window.onerror`\n * (no DOM). Each request is an isolated invocation. The right pattern is to\n * **wrap the user's exported handler(s)** in a try/catch and `ctx.waitUntil`\n * the ingest POST so the runtime keeps I/O alive while the worker returns.\n *\n * We wrap every standard handler (fetch, scheduled, queue, email, trace) if\n * present on the user's exported handler object.\n */\n\ninterface WorkersExecutionContext {\n waitUntil(promise: Promise<unknown>): void;\n}\n\nexport interface WorkersPixelOptions {\n projectKey: string;\n /** Backend ingest URL. Defaults to the public gg-pixel server. */\n ingestUrl?: string;\n /** Override the runtime label (default: `cloudflare-workers`). */\n runtime?: string;\n}\n\nconst DEFAULT_INGEST_URL = \"https://gg-pixel-server.buzzbeamaustralia.workers.dev\";\n\nconst HANDLER_KEYS = [\"fetch\", \"scheduled\", \"queue\", \"email\", \"trace\"] as const;\ntype HandlerKey = (typeof HANDLER_KEYS)[number];\ntype AnyFn = (...args: unknown[]) => unknown;\n\n/**\n * Wrap an exported Worker handler so any thrown error is reported to gg-pixel\n * before being re-thrown. Original error semantics are preserved.\n */\nexport function withPixel<H extends Partial<Record<HandlerKey, unknown>>>(\n options: WorkersPixelOptions,\n handler: H,\n): H {\n const ingestUrl = buildIngestUrl(options.ingestUrl);\n const projectKey = options.projectKey;\n const runtime = options.runtime ?? \"cloudflare-workers\";\n\n const wrapped: Record<string, unknown> = { ...handler };\n for (const key of HANDLER_KEYS) {\n const original = handler[key];\n if (typeof original === \"function\") {\n wrapped[key] = wrapHandler(original as AnyFn, ingestUrl, projectKey, runtime);\n }\n }\n return wrapped as H;\n}\n\nfunction wrapHandler(fn: AnyFn, ingestUrl: string, projectKey: string, runtime: string): AnyFn {\n return async (...args: unknown[]) => {\n const ctx = args[args.length - 1] as WorkersExecutionContext | undefined;\n try {\n return await fn(...args);\n } catch (err) {\n try {\n const event = buildEvent(err, projectKey, runtime, false, \"fatal\");\n if (ctx && typeof ctx.waitUntil === \"function\") {\n ctx.waitUntil(sendEvent(ingestUrl, projectKey, event));\n } else {\n // No execution context (rare) — fire-and-forget without waitUntil.\n // The runtime may terminate before the request completes.\n void sendEvent(ingestUrl, projectKey, event).catch(() => {});\n }\n } catch {\n // never let pixel break the host program\n }\n throw err; // preserve original handler semantics\n }\n };\n}\n\n/**\n * Manual report from inside a Worker. The `ctx` is required so we can\n * `waitUntil` the ingest POST — without it the worker may terminate before\n * the network request completes.\n */\nexport function reportPixel(\n ctx: WorkersExecutionContext,\n options: WorkersPixelOptions,\n input: { message: string; error?: unknown; level?: Level },\n): void {\n const ingestUrl = buildIngestUrl(options.ingestUrl);\n const runtime = options.runtime ?? \"cloudflare-workers\";\n const level = input.level ?? \"error\";\n let event: WireEvent;\n if (input.error !== undefined) {\n event = buildEvent(input.error, options.projectKey, runtime, true, level);\n if (input.message) event.message = input.message;\n } else {\n const err = new Error(input.message);\n err.name = \"ManualReport\";\n event = buildEvent(err, options.projectKey, runtime, true, level);\n }\n ctx.waitUntil(sendEvent(ingestUrl, options.projectKey, event));\n}\n\nfunction buildIngestUrl(base?: string): string {\n return (base ?? DEFAULT_INGEST_URL).replace(/\\/+$/, \"\") + \"/ingest\";\n}\n\nfunction buildEvent(\n err: unknown,\n projectKey: string,\n runtime: string,\n manual: boolean,\n level: Level,\n): WireEvent {\n const { type, message, stackString } = normalize(err);\n const stack = parseBrowserStack(stackString);\n return {\n event_id: uuid(),\n project_key: projectKey,\n fingerprint: fingerprintWeb(type, stack),\n type,\n message,\n stack,\n code_context: null,\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\") return { type: \"StringError\", message: err };\n try {\n return { type: \"UnknownError\", message: JSON.stringify(err) };\n } catch {\n return { type: \"UnknownError\", message: String(err) };\n }\n}\n\nasync function sendEvent(ingestUrl: string, projectKey: string, event: WireEvent): Promise<void> {\n try {\n await fetch(ingestUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-pixel-key\": projectKey,\n },\n body: JSON.stringify(event),\n });\n } catch {\n // best-effort\n }\n}\n\nfunction uuid(): string {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n // Fallback (workers always have crypto, but be defensive)\n const bytes = new Uint8Array(16);\n if (c?.getRandomValues) c.getRandomValues(bytes);\n bytes[6] = (bytes[6]! & 0x0f) | 0x40;\n bytes[8] = (bytes[8]! & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenkaiiii/gg-pixel",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.72",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Universal error tracking pixel optimized for autonomous coding agents",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"./deno": {
|
|
23
23
|
"types": "./dist/deno.d.ts",
|
|
24
24
|
"import": "./dist/deno.js"
|
|
25
|
+
},
|
|
26
|
+
"./workers": {
|
|
27
|
+
"types": "./dist/workers.d.ts",
|
|
28
|
+
"import": "./dist/workers.js"
|
|
25
29
|
}
|
|
26
30
|
},
|
|
27
31
|
"bin": {
|