@rasputin-ai/core 0.1.4 → 0.3.0
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/announce-deploy/announce-deploy.d.ts +2 -2
- package/dist/announce-deploy/announce-deploy.d.ts.map +1 -1
- package/dist/create-client/create-client-types.d.ts +47 -31
- package/dist/create-client/create-client-types.d.ts.map +1 -1
- package/dist/create-client/create-client.d.ts +1 -1
- package/dist/create-client/create-client.d.ts.map +1 -1
- package/dist/create-client/is-client-enabled.d.ts.map +1 -1
- package/dist/create-client/resolve-endpoint-urls.d.ts +1 -0
- package/dist/create-client/resolve-endpoint-urls.d.ts.map +1 -1
- package/dist/create-ingest-event/create-ingest-event-types.d.ts +6 -2
- package/dist/create-ingest-event/create-ingest-event-types.d.ts.map +1 -1
- package/dist/create-ingest-event/create-ingest-event.d.ts.map +1 -1
- package/dist/create-ingest-event/runtime-execution-state-types.d.ts +87 -0
- package/dist/create-ingest-event/runtime-execution-state-types.d.ts.map +1 -0
- package/dist/create-ingest-event/truncate-ingest-message.d.ts +3 -0
- package/dist/create-ingest-event/truncate-ingest-message.d.ts.map +1 -0
- package/dist/detect-release/detect-release.d.ts +6 -3
- package/dist/detect-release/detect-release.d.ts.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +503 -95
- package/dist/instrumentation-manifest/instrumentation-manifest-types.d.ts +75 -0
- package/dist/instrumentation-manifest/instrumentation-manifest-types.d.ts.map +1 -0
- package/dist/instrumentation-manifest/upload-instrumentation-manifest.d.ts +16 -0
- package/dist/instrumentation-manifest/upload-instrumentation-manifest.d.ts.map +1 -0
- package/dist/normalize-frames/normalize-frames.d.ts +3 -3
- package/dist/normalize-frames/normalize-frames.d.ts.map +1 -1
- package/dist/repo-root/repo-root-from.d.ts +8 -0
- package/dist/repo-root/repo-root-from.d.ts.map +1 -0
- package/dist/repo-root/resolve-repo-root.d.ts +6 -0
- package/dist/repo-root/resolve-repo-root.d.ts.map +1 -0
- package/dist/repo-root/warn-repo-root-setup.d.ts +5 -0
- package/dist/repo-root/warn-repo-root-setup.d.ts.map +1 -0
- package/dist/sdk-meta.d.ts +1 -1
- package/dist/suppression/compute-burst-key.d.ts +0 -4
- package/dist/suppression/compute-burst-key.d.ts.map +1 -1
- package/dist/suppression/create-suppression.d.ts +9 -2
- package/dist/suppression/create-suppression.d.ts.map +1 -1
- package/dist/suppression/identity-needs-message-template.d.ts +3 -1
- package/dist/suppression/identity-needs-message-template.d.ts.map +1 -1
- package/dist/suppression/parameterize-message.d.ts.map +1 -1
- package/dist/suppression/suppression-types.d.ts +2 -2
- package/dist/suppression/suppression-types.d.ts.map +1 -1
- package/dist/transport/create-transport.d.ts.map +1 -1
- package/dist/transport/send-batch.d.ts +1 -0
- package/dist/transport/send-batch.d.ts.map +1 -1
- package/dist/transport/transport-queue.d.ts +7 -0
- package/dist/transport/transport-queue.d.ts.map +1 -1
- package/dist/transport/transport-types.d.ts +17 -4
- package/dist/transport/transport-types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/announce-deploy/announce-deploy.ts
|
|
2
2
|
var announceDeploy = async (options) => {
|
|
3
3
|
if (options.enabled === false) return "skipped";
|
|
4
|
-
const release = options.release?.trim();
|
|
4
|
+
const release = options.release?.trim() ?? "";
|
|
5
5
|
const environment = options.environment.trim();
|
|
6
|
-
if (!
|
|
6
|
+
if (!environment) return "skipped";
|
|
7
7
|
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
8
8
|
try {
|
|
9
9
|
const response = await fetchFn(options.deployUrl, {
|
|
@@ -22,15 +22,116 @@ var announceDeploy = async (options) => {
|
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
// src/create-ingest-event/
|
|
25
|
+
// src/create-ingest-event/truncate-ingest-message.ts
|
|
26
26
|
var MAX_MESSAGE_BYTES = 8 * 1024;
|
|
27
|
-
var
|
|
27
|
+
var utf8Bytes = (value) => Buffer.byteLength(value, "utf8");
|
|
28
28
|
var truncateUtf8 = (value, maxBytes) => {
|
|
29
|
-
if (
|
|
29
|
+
if (utf8Bytes(value) <= maxBytes) return value;
|
|
30
30
|
let end = Math.min(value.length, maxBytes);
|
|
31
|
-
while (end > 0 &&
|
|
31
|
+
while (end > 0 && utf8Bytes(value.slice(0, end)) > maxBytes) end--;
|
|
32
32
|
return value.slice(0, end);
|
|
33
33
|
};
|
|
34
|
+
var truncation = (reason, extra = {}) => ({
|
|
35
|
+
__rasputin_truncated: true,
|
|
36
|
+
__reason: reason,
|
|
37
|
+
...extra
|
|
38
|
+
});
|
|
39
|
+
var mapJson = (value, maxStringChars, maxArrayItems, maxObjectKeys) => {
|
|
40
|
+
if (typeof value === "string") {
|
|
41
|
+
if (value.length <= maxStringChars) return value;
|
|
42
|
+
if (maxStringChars <= 1) return value.slice(0, maxStringChars);
|
|
43
|
+
return `${value.slice(0, maxStringChars - 1)}\u2026`;
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
const keep = maxArrayItems === null ? value.length : Math.min(value.length, maxArrayItems);
|
|
47
|
+
const items = value.slice(0, keep).map((item) => mapJson(item, maxStringChars, maxArrayItems, maxObjectKeys));
|
|
48
|
+
if (keep < value.length) {
|
|
49
|
+
items.push(truncation("max_array_elements", { __original_length: value.length }));
|
|
50
|
+
}
|
|
51
|
+
return items;
|
|
52
|
+
}
|
|
53
|
+
if (value && typeof value === "object") {
|
|
54
|
+
const entries = Object.entries(value);
|
|
55
|
+
const keep = maxObjectKeys === null ? entries.length : Math.min(entries.length, maxObjectKeys);
|
|
56
|
+
const out = {};
|
|
57
|
+
for (const [key, child] of entries.slice(0, keep)) {
|
|
58
|
+
out[key] = mapJson(child, maxStringChars, maxArrayItems, maxObjectKeys);
|
|
59
|
+
}
|
|
60
|
+
if (keep < entries.length) {
|
|
61
|
+
Object.assign(out, truncation("max_object_keys", { __original_length: entries.length }));
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
};
|
|
67
|
+
var jsonIfFits = (value, maxBytes) => {
|
|
68
|
+
const json = JSON.stringify(value);
|
|
69
|
+
return utf8Bytes(json) <= maxBytes ? json : null;
|
|
70
|
+
};
|
|
71
|
+
var boundJsonMessage = (value, maxBytes) => {
|
|
72
|
+
const fit = (maxStringChars, maxArrayItems, maxObjectKeys) => jsonIfFits(mapJson(value, maxStringChars, maxArrayItems, maxObjectKeys), maxBytes);
|
|
73
|
+
const longestStringsThatFit = (maxArrayItems, maxObjectKeys) => {
|
|
74
|
+
let lo = 0;
|
|
75
|
+
let hi = maxBytes;
|
|
76
|
+
let best = null;
|
|
77
|
+
while (lo <= hi) {
|
|
78
|
+
const mid = lo + hi >> 1;
|
|
79
|
+
const candidate = fit(mid, maxArrayItems, maxObjectKeys);
|
|
80
|
+
if (candidate) {
|
|
81
|
+
best = candidate;
|
|
82
|
+
lo = mid + 1;
|
|
83
|
+
} else {
|
|
84
|
+
hi = mid - 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return best;
|
|
88
|
+
};
|
|
89
|
+
const withAllKeys = longestStringsThatFit(null, null);
|
|
90
|
+
if (withAllKeys) return withAllKeys;
|
|
91
|
+
const searchCount = (emptyFits, fillStrings) => {
|
|
92
|
+
let lo = 0;
|
|
93
|
+
let hi = 1;
|
|
94
|
+
while (!emptyFits(hi) && hi < 1048576) hi *= 2;
|
|
95
|
+
let best = 0;
|
|
96
|
+
while (lo <= hi) {
|
|
97
|
+
const mid = lo + hi >> 1;
|
|
98
|
+
if (emptyFits(mid)) {
|
|
99
|
+
best = mid;
|
|
100
|
+
lo = mid + 1;
|
|
101
|
+
} else {
|
|
102
|
+
hi = mid - 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return fillStrings(best);
|
|
106
|
+
};
|
|
107
|
+
const withFewerArrayItems = searchCount(
|
|
108
|
+
(count) => fit(0, count, null) !== null,
|
|
109
|
+
(count) => longestStringsThatFit(count, null)
|
|
110
|
+
);
|
|
111
|
+
if (withFewerArrayItems) return withFewerArrayItems;
|
|
112
|
+
return searchCount(
|
|
113
|
+
(count) => fit(0, 0, count) !== null,
|
|
114
|
+
(count) => longestStringsThatFit(0, count)
|
|
115
|
+
) ?? JSON.stringify(
|
|
116
|
+
Array.isArray(value) ? [truncation("max_array_elements", { __original_length: value.length })] : truncation("max_object_keys", { __original_length: Object.keys(value).length })
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
var truncateIngestMessage = (value, maxBytes = MAX_MESSAGE_BYTES) => {
|
|
120
|
+
if (utf8Bytes(value) <= maxBytes) return value;
|
|
121
|
+
try {
|
|
122
|
+
const parsed = JSON.parse(value);
|
|
123
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
124
|
+
const compact = JSON.stringify(parsed);
|
|
125
|
+
if (utf8Bytes(compact) <= maxBytes) return compact;
|
|
126
|
+
return boundJsonMessage(parsed, maxBytes);
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
return truncateUtf8(value, maxBytes);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// src/create-ingest-event/create-ingest-event.ts
|
|
134
|
+
var MAX_STACK_FRAMES = 50;
|
|
34
135
|
var errorFields = (error) => {
|
|
35
136
|
if (error instanceof Error) {
|
|
36
137
|
const code = "code" in error && (typeof error.code === "string" || typeof error.code === "number") ? String(error.code) : void 0;
|
|
@@ -79,18 +180,19 @@ var createIngestEvent = (input) => {
|
|
|
79
180
|
const occurred = input.occurredAt === void 0 ? (/* @__PURE__ */ new Date()).toISOString() : typeof input.occurredAt === "string" ? input.occurredAt : input.occurredAt.toISOString();
|
|
80
181
|
const event = {
|
|
81
182
|
type: extracted.type,
|
|
82
|
-
message:
|
|
183
|
+
message: truncateIngestMessage(extracted.message),
|
|
83
184
|
stack_frames: framesToStackFrames(input.frames),
|
|
84
185
|
occurred_at: occurred,
|
|
85
186
|
environment: input.environment,
|
|
86
187
|
origin: input.origin,
|
|
87
|
-
resolution: input.resolution
|
|
188
|
+
resolution: input.resolution,
|
|
189
|
+
release: input.release
|
|
88
190
|
};
|
|
89
191
|
const code = input.code !== void 0 ? input.code : extracted.code;
|
|
90
192
|
if (code !== void 0) event.code = code;
|
|
91
|
-
if (input.release !== void 0) event.release = input.release;
|
|
92
193
|
const request = sanitizeRequest(input.request);
|
|
93
194
|
if (request) event.request = request;
|
|
195
|
+
if (input.runtimeState) event.runtime_state = input.runtimeState;
|
|
94
196
|
return event;
|
|
95
197
|
};
|
|
96
198
|
|
|
@@ -111,25 +213,62 @@ var finalizeResolution = (resolution, stackFrames) => {
|
|
|
111
213
|
};
|
|
112
214
|
|
|
113
215
|
// src/detect-release/detect-release.ts
|
|
216
|
+
import { readFileSync } from "node:fs";
|
|
217
|
+
import { join } from "node:path";
|
|
114
218
|
var ENV_KEYS = [
|
|
219
|
+
"RASPUTIN_RELEASE",
|
|
115
220
|
"VERCEL_GIT_COMMIT_SHA",
|
|
116
221
|
"RAILWAY_GIT_COMMIT_SHA",
|
|
117
222
|
"RENDER_GIT_COMMIT",
|
|
118
|
-
"
|
|
223
|
+
"COMMIT_REF",
|
|
224
|
+
"CF_PAGES_COMMIT_SHA",
|
|
225
|
+
"HEROKU_SLUG_COMMIT",
|
|
119
226
|
"GIT_COMMIT",
|
|
120
227
|
"COMMIT_SHA",
|
|
121
228
|
"GIT_SHA"
|
|
122
229
|
];
|
|
230
|
+
var SHA_RE = /^[0-9a-f]{7,40}$/i;
|
|
231
|
+
var isSha = (value) => SHA_RE.test(value);
|
|
232
|
+
var warnInvalid = (source, value) => {
|
|
233
|
+
console.warn(
|
|
234
|
+
`[rasputin] Ignoring invalid release from ${source}: "${value}". Expected a git commit SHA (7\u201340 hex characters).`
|
|
235
|
+
);
|
|
236
|
+
};
|
|
237
|
+
var readGitHead = (cwd) => {
|
|
238
|
+
try {
|
|
239
|
+
const gitDir = join(cwd, ".git");
|
|
240
|
+
const head = readFileSync(join(gitDir, "HEAD"), "utf8").trim();
|
|
241
|
+
if (isSha(head)) return head;
|
|
242
|
+
const ref = head.replace(/^ref:\s*/, "");
|
|
243
|
+
try {
|
|
244
|
+
const sha = readFileSync(join(gitDir, ref), "utf8").trim();
|
|
245
|
+
return isSha(sha) ? sha : void 0;
|
|
246
|
+
} catch {
|
|
247
|
+
const packed = readFileSync(join(gitDir, "packed-refs"), "utf8");
|
|
248
|
+
const match = packed.match(new RegExp(`^([0-9a-f]{40}) ${ref}$`, "m"));
|
|
249
|
+
return match?.[1];
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
return void 0;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
123
255
|
var detectRelease = (options = {}) => {
|
|
124
256
|
const explicit = options.release?.trim();
|
|
125
|
-
if (explicit)
|
|
257
|
+
if (explicit) {
|
|
258
|
+
if (isSha(explicit)) return explicit;
|
|
259
|
+
warnInvalid("release option", explicit);
|
|
260
|
+
}
|
|
126
261
|
const env = options.env ?? process.env;
|
|
127
262
|
for (const key of ENV_KEYS) {
|
|
128
263
|
const value = env[key]?.trim();
|
|
129
|
-
if (value)
|
|
264
|
+
if (!value) continue;
|
|
265
|
+
if (isSha(value)) return value;
|
|
266
|
+
warnInvalid(key, value);
|
|
130
267
|
}
|
|
268
|
+
const fromGit = readGitHead(options.cwd ?? process.cwd());
|
|
269
|
+
if (fromGit) return fromGit;
|
|
131
270
|
console.warn(
|
|
132
|
-
"[rasputin] No release detected. Set `release` in RasputinInit or a commit SHA env var (e.g.
|
|
271
|
+
"[rasputin] No release detected. Set `RASPUTIN_RELEASE` to your git commit SHA, pass `release` in RasputinInit, or deploy on a platform that exposes a commit SHA env var (e.g. VERCEL_GIT_COMMIT_SHA). Events are not sent without a release."
|
|
133
272
|
);
|
|
134
273
|
return void 0;
|
|
135
274
|
};
|
|
@@ -168,8 +307,8 @@ var isInApp = (file, underRoot) => {
|
|
|
168
307
|
if (/(^|\/)node_modules\//.test(file)) return false;
|
|
169
308
|
return true;
|
|
170
309
|
};
|
|
171
|
-
var normalizeFrames = (frames, options
|
|
172
|
-
const appRoot = options.appRoot
|
|
310
|
+
var normalizeFrames = (frames, options) => {
|
|
311
|
+
const appRoot = options.appRoot;
|
|
173
312
|
return frames.map((frame) => {
|
|
174
313
|
const generated = normalizeFile(frame.generated.file, appRoot);
|
|
175
314
|
const original = frame.original ? normalizeFile(frame.original.file, appRoot) : void 0;
|
|
@@ -250,10 +389,93 @@ var parseStack = (stack) => {
|
|
|
250
389
|
return frames;
|
|
251
390
|
};
|
|
252
391
|
|
|
392
|
+
// src/repo-root/repo-root-from.ts
|
|
393
|
+
import { existsSync } from "node:fs";
|
|
394
|
+
import { dirname, join as join2, resolve as resolve2 } from "node:path";
|
|
395
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
396
|
+
var isGitRoot = (dir) => existsSync(join2(dir, ".git"));
|
|
397
|
+
var hasPackageJson = (dir) => existsSync(join2(dir, "package.json"));
|
|
398
|
+
var repoRootFrom = (moduleUrl) => {
|
|
399
|
+
let current = resolve2(dirname(fileURLToPath2(moduleUrl)));
|
|
400
|
+
let packageRoot;
|
|
401
|
+
while (true) {
|
|
402
|
+
if (isGitRoot(current)) return current;
|
|
403
|
+
if (hasPackageJson(current)) packageRoot = current;
|
|
404
|
+
const parent = dirname(current);
|
|
405
|
+
if (parent === current) break;
|
|
406
|
+
current = parent;
|
|
407
|
+
}
|
|
408
|
+
return packageRoot ?? resolve2(dirname(fileURLToPath2(moduleUrl)));
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// src/repo-root/resolve-repo-root.ts
|
|
412
|
+
var resolveRepoRoot = (options) => {
|
|
413
|
+
const repoRoot = options.repoRoot?.trim();
|
|
414
|
+
if (repoRoot) return repoRoot;
|
|
415
|
+
const moduleUrl = options.moduleUrl?.trim();
|
|
416
|
+
if (moduleUrl) return repoRootFrom(moduleUrl);
|
|
417
|
+
return void 0;
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// src/repo-root/warn-repo-root-setup.ts
|
|
421
|
+
var BUILD_OUTPUT_SEGMENTS = /* @__PURE__ */ new Set(["dist", "build", "out", ".next", "coverage"]);
|
|
422
|
+
var warnedMissing = false;
|
|
423
|
+
var warnedSuspicious = false;
|
|
424
|
+
var BANNER = "======== [rasputin] WARNING";
|
|
425
|
+
var looksLikeBuildOutputRoot = (root) => {
|
|
426
|
+
const posix = root.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
427
|
+
return posix.split("/").some((segment) => BUILD_OUTPUT_SEGMENTS.has(segment));
|
|
428
|
+
};
|
|
429
|
+
var warnIfMissingRepoRoot = () => {
|
|
430
|
+
if (warnedMissing) return;
|
|
431
|
+
warnedMissing = true;
|
|
432
|
+
console.warn(
|
|
433
|
+
`
|
|
434
|
+
|
|
435
|
+
${BANNER}: we don't know where your project is ========
|
|
436
|
+
Rasputin needs to know which folder your app lives in so errors show
|
|
437
|
+
the right file paths in the dashboard.
|
|
438
|
+
|
|
439
|
+
Add this to RasputinInit (from the same file you call it in):
|
|
440
|
+
|
|
441
|
+
moduleUrl: import.meta.url
|
|
442
|
+
|
|
443
|
+
Or set repoRoot to the folder you open in your editor.
|
|
444
|
+
|
|
445
|
+
Nothing will be sent until this is set.
|
|
446
|
+
======================================================================
|
|
447
|
+
`
|
|
448
|
+
);
|
|
449
|
+
};
|
|
450
|
+
var warnIfSuspiciousRepoRoot = (root) => {
|
|
451
|
+
if (warnedSuspicious || !looksLikeBuildOutputRoot(root)) return;
|
|
452
|
+
warnedSuspicious = true;
|
|
453
|
+
console.warn(
|
|
454
|
+
`
|
|
455
|
+
|
|
456
|
+
${BANNER}: this looks like a build folder, not your project ========
|
|
457
|
+
Rasputin thinks your project is here:
|
|
458
|
+
${root}
|
|
459
|
+
|
|
460
|
+
That's usually compiled output (dist, build, .next, \u2026), not the folder
|
|
461
|
+
you write code in. Error locations in the dashboard will be wrong.
|
|
462
|
+
|
|
463
|
+
Point Rasputin at the folder you open in your editor.
|
|
464
|
+
|
|
465
|
+
Easiest fix \u2014 from the file where you call RasputinInit:
|
|
466
|
+
|
|
467
|
+
moduleUrl: import.meta.url
|
|
468
|
+
|
|
469
|
+
Or set repoRoot yourself.
|
|
470
|
+
======================================================================
|
|
471
|
+
`
|
|
472
|
+
);
|
|
473
|
+
};
|
|
474
|
+
|
|
253
475
|
// src/resolve-source-maps/resolve-source-maps.ts
|
|
254
476
|
import { readFile } from "node:fs/promises";
|
|
255
|
-
import { dirname, isAbsolute as isAbsolute2, join } from "node:path";
|
|
256
|
-
import { fileURLToPath as
|
|
477
|
+
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
478
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
257
479
|
import { LEAST_UPPER_BOUND, originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
|
|
258
480
|
var SOURCE_EXT2 = /\.(ts|tsx|mts|cts)$/i;
|
|
259
481
|
var SOURCEMAP_COMMENT = /(?:\/\/[#@][ \t]*sourceMappingURL=([^\s'"]+)|\/\*[#@][ \t]*sourceMappingURL=([^\s*'"]+)[ \t]*\*\/)\s*$/;
|
|
@@ -261,7 +483,7 @@ var mapCache = /* @__PURE__ */ new Map();
|
|
|
261
483
|
var toPath = (file) => {
|
|
262
484
|
if (file.startsWith("file://")) {
|
|
263
485
|
try {
|
|
264
|
-
return
|
|
486
|
+
return fileURLToPath3(file);
|
|
265
487
|
} catch {
|
|
266
488
|
return file;
|
|
267
489
|
}
|
|
@@ -302,7 +524,7 @@ var readMapPayload = async (generatedPath) => {
|
|
|
302
524
|
if (comment?.startsWith("data:")) return decodeDataUrl(comment);
|
|
303
525
|
if (comment) {
|
|
304
526
|
try {
|
|
305
|
-
return await readFile(
|
|
527
|
+
return await readFile(join3(dirname2(generatedPath), comment), "utf8");
|
|
306
528
|
} catch {
|
|
307
529
|
}
|
|
308
530
|
}
|
|
@@ -349,7 +571,7 @@ var resolveFrame = async (raw) => {
|
|
|
349
571
|
return {
|
|
350
572
|
...base,
|
|
351
573
|
original: {
|
|
352
|
-
file: isAbsolute2(pos.source) ? pos.source :
|
|
574
|
+
file: isAbsolute2(pos.source) ? pos.source : join3(dirname2(generatedPath), pos.source),
|
|
353
575
|
line: pos.line,
|
|
354
576
|
column: pos.column ?? void 0,
|
|
355
577
|
name: pos.name ?? void 0
|
|
@@ -369,7 +591,7 @@ var resolveSourceMaps = async (frames) => {
|
|
|
369
591
|
|
|
370
592
|
// src/sdk-meta.ts
|
|
371
593
|
var SDK_NAME = "@rasputin-ai/core";
|
|
372
|
-
var SDK_VERSION = "0.
|
|
594
|
+
var SDK_VERSION = "0.3.0";
|
|
373
595
|
|
|
374
596
|
// src/suppression/identity-needs-message-template.ts
|
|
375
597
|
var identityNeedsMessageTemplate = (type, code) => {
|
|
@@ -431,14 +653,22 @@ var RULES = [
|
|
|
431
653
|
pattern: /\b(?=[a-z0-9]*[a-z])(?=[a-z0-9]*\d)[a-z0-9]{8,}\b/giu
|
|
432
654
|
}
|
|
433
655
|
];
|
|
434
|
-
var
|
|
435
|
-
|
|
436
|
-
|
|
656
|
+
var normalizeForIdentity = (normalized) => {
|
|
657
|
+
try {
|
|
658
|
+
return JSON.stringify(JSON.parse(normalized));
|
|
659
|
+
} catch {
|
|
660
|
+
}
|
|
437
661
|
const lines = normalized.split("\n").filter((line) => line.trim());
|
|
438
662
|
let result = lines.slice(0, 2).join("\n");
|
|
439
663
|
if (result !== normalized) {
|
|
440
664
|
result += "...";
|
|
441
665
|
}
|
|
666
|
+
return result;
|
|
667
|
+
};
|
|
668
|
+
var parameterizeMessage = (rawMessage) => {
|
|
669
|
+
if (rawMessage.length > MAX_INPUT_LENGTH) return rawMessage;
|
|
670
|
+
const normalized = rawMessage.normalize("NFKC").replace(/\r\n?/g, "\n").trim();
|
|
671
|
+
let result = normalizeForIdentity(normalized);
|
|
442
672
|
result = result.replace(/\b([A-Za-z_][\w.-]*)=(["'])(?:\\.|(?!\2).)*\2/gu, "$1=<string>");
|
|
443
673
|
result = result.replace(/\b([A-Za-z_][\w.-]*)=(?:true|false)\b/giu, "$1=<bool>");
|
|
444
674
|
for (const rule of RULES) {
|
|
@@ -454,21 +684,43 @@ var parameterizeMessage = (rawMessage) => {
|
|
|
454
684
|
};
|
|
455
685
|
|
|
456
686
|
// src/suppression/compute-burst-key.ts
|
|
687
|
+
var FRAME_COUNT = 4;
|
|
457
688
|
var preferredFile = (frame) => (frame.original ?? frame.generated).file;
|
|
458
|
-
var
|
|
459
|
-
|
|
689
|
+
var normalizeFunction = (raw) => {
|
|
690
|
+
if (!raw) return "";
|
|
691
|
+
const name = raw.trim();
|
|
692
|
+
if (name === "<anonymous>" || name === "anonymous") return "";
|
|
693
|
+
return name.replace(/^(async|new|get|set|bound)\s+/, "").replace(/^Object\./, "").replace(/\s+\[as .+\]$/, "");
|
|
694
|
+
};
|
|
695
|
+
var collapseConsecutive = (frames) => {
|
|
696
|
+
const out = [];
|
|
697
|
+
let previousKey = null;
|
|
698
|
+
for (const frame of frames) {
|
|
460
699
|
const file = preferredFile(frame);
|
|
461
|
-
const fn = frame.function ?? frame.original?.name
|
|
462
|
-
|
|
463
|
-
|
|
700
|
+
const fn = normalizeFunction(frame.function ?? frame.original?.name);
|
|
701
|
+
const key = `${file}#${fn}`;
|
|
702
|
+
if (key !== previousKey) {
|
|
703
|
+
out.push(frame);
|
|
704
|
+
previousKey = key;
|
|
464
705
|
}
|
|
706
|
+
}
|
|
707
|
+
return out;
|
|
708
|
+
};
|
|
709
|
+
var frameBurstParts = (frames) => {
|
|
710
|
+
const collapsed = collapseConsecutive(frames);
|
|
711
|
+
const inApp = collapsed.filter((frame) => frame.in_app);
|
|
712
|
+
const selected = (inApp.length > 0 ? inApp : collapsed).slice(0, FRAME_COUNT);
|
|
713
|
+
return selected.map((frame) => {
|
|
714
|
+
const file = preferredFile(frame);
|
|
715
|
+
const fn = normalizeFunction(frame.function ?? frame.original?.name);
|
|
465
716
|
return `${file}:${fn}`;
|
|
466
717
|
});
|
|
467
718
|
};
|
|
468
719
|
var computeBurstKey = (errorType, message, code, frames) => {
|
|
469
720
|
const parts = frameBurstParts(frames);
|
|
470
|
-
|
|
471
|
-
|
|
721
|
+
const codePart = code ?? "";
|
|
722
|
+
let key = `${errorType}\0${codePart}\0${parts.join("\0")}`;
|
|
723
|
+
if (parts.length === 0 || identityNeedsMessageTemplate(errorType, code)) {
|
|
472
724
|
key += `\0${parameterizeMessage(message)}`;
|
|
473
725
|
}
|
|
474
726
|
return key;
|
|
@@ -537,7 +789,7 @@ var createSuppression = (options = {}) => {
|
|
|
537
789
|
const identity = captureIdentity(input);
|
|
538
790
|
const captureRawFrames = (input.rawFrames ?? []).slice(0, MAX_STACK_FRAMES);
|
|
539
791
|
const burstKey = computeBurstKey(input.errorType, input.message, input.code, input.frames);
|
|
540
|
-
const release = input.release
|
|
792
|
+
const release = input.release;
|
|
541
793
|
const route = input.route ?? null;
|
|
542
794
|
let bucket = burstBuckets.get(burstKey);
|
|
543
795
|
if (!bucket) {
|
|
@@ -579,7 +831,7 @@ var createSuppression = (options = {}) => {
|
|
|
579
831
|
bucket.suppressed += 1;
|
|
580
832
|
return { action: "suppress" };
|
|
581
833
|
};
|
|
582
|
-
const
|
|
834
|
+
const drainSummaries = (now = Date.now()) => {
|
|
583
835
|
const drafts = pending.splice(0, pending.length);
|
|
584
836
|
for (const bucket of burstBuckets.values()) {
|
|
585
837
|
if (bucket.suppressed > 0) {
|
|
@@ -587,22 +839,32 @@ var createSuppression = (options = {}) => {
|
|
|
587
839
|
bucket.suppressed = 0;
|
|
588
840
|
}
|
|
589
841
|
}
|
|
842
|
+
return drafts.map((draft) => ({
|
|
843
|
+
summary: toSummary(draft, draft.identity),
|
|
844
|
+
rawFrames: draft.captureRawFrames
|
|
845
|
+
}));
|
|
846
|
+
};
|
|
847
|
+
const takeSummaries = async (now = Date.now(), prepareSummary) => {
|
|
848
|
+
const drafts = drainSummaries(now);
|
|
849
|
+
if (!prepareSummary) return drafts.map((draft) => draft.summary);
|
|
590
850
|
const out = [];
|
|
591
851
|
for (const draft of drafts) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
852
|
+
if (draft.rawFrames.length === 0) {
|
|
853
|
+
out.push(draft.summary);
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
try {
|
|
857
|
+
const resolved = await prepareSummary(draft.rawFrames);
|
|
858
|
+
out.push({ ...draft.summary, ...resolved });
|
|
859
|
+
} catch {
|
|
860
|
+
out.push(draft.summary);
|
|
599
861
|
}
|
|
600
|
-
out.push(toSummary(draft, identity));
|
|
601
862
|
}
|
|
602
863
|
return out;
|
|
603
864
|
};
|
|
604
865
|
return {
|
|
605
866
|
decide,
|
|
867
|
+
drainSummaries,
|
|
606
868
|
takeSummaries,
|
|
607
869
|
/** Test helper — number of active burst buckets. */
|
|
608
870
|
size: () => burstBuckets.size
|
|
@@ -611,7 +873,21 @@ var createSuppression = (options = {}) => {
|
|
|
611
873
|
|
|
612
874
|
// src/transport/send-batch.ts
|
|
613
875
|
import { gzipSync } from "node:zlib";
|
|
614
|
-
var sleep = (ms) => new Promise((
|
|
876
|
+
var sleep = (ms, signal) => new Promise((resolve3) => {
|
|
877
|
+
if (signal?.aborted) {
|
|
878
|
+
resolve3();
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
const timer = setTimeout(resolve3, ms);
|
|
882
|
+
signal?.addEventListener(
|
|
883
|
+
"abort",
|
|
884
|
+
() => {
|
|
885
|
+
clearTimeout(timer);
|
|
886
|
+
resolve3();
|
|
887
|
+
},
|
|
888
|
+
{ once: true }
|
|
889
|
+
);
|
|
890
|
+
});
|
|
615
891
|
var backoffMs = (attempt) => {
|
|
616
892
|
const base = Math.min(3e4, 500 * 2 ** attempt);
|
|
617
893
|
return Math.floor(base * (0.5 + Math.random()));
|
|
@@ -634,6 +910,7 @@ var sendBatch = async (options) => {
|
|
|
634
910
|
}
|
|
635
911
|
if (options.sdkVersion) payload.sdk_version = options.sdkVersion;
|
|
636
912
|
if (options.sdkName) payload.sdk_name = options.sdkName;
|
|
913
|
+
if (options.idempotencyKey) payload.idempotency_key = options.idempotencyKey;
|
|
637
914
|
const json = JSON.stringify(payload);
|
|
638
915
|
const headers = {
|
|
639
916
|
Authorization: `Bearer ${options.projectApiKey}`,
|
|
@@ -661,16 +938,16 @@ var sendBatch = async (options) => {
|
|
|
661
938
|
if (response.status === 429) {
|
|
662
939
|
if (attempt === options.maxRetries) return "failed";
|
|
663
940
|
const wait = retryAfterMs(response) ?? backoffMs(attempt);
|
|
664
|
-
await sleep(wait);
|
|
941
|
+
await sleep(wait, options.signal);
|
|
665
942
|
continue;
|
|
666
943
|
}
|
|
667
944
|
if (response.status >= 400 && response.status < 500) return "dropped";
|
|
668
945
|
if (attempt === options.maxRetries) return "failed";
|
|
669
|
-
await sleep(backoffMs(attempt));
|
|
946
|
+
await sleep(backoffMs(attempt), options.signal);
|
|
670
947
|
} catch {
|
|
671
948
|
if (options.signal?.aborted) return "failed";
|
|
672
949
|
if (attempt === options.maxRetries) return "failed";
|
|
673
|
-
await sleep(backoffMs(attempt));
|
|
950
|
+
await sleep(backoffMs(attempt), options.signal);
|
|
674
951
|
}
|
|
675
952
|
}
|
|
676
953
|
return "failed";
|
|
@@ -684,6 +961,9 @@ var RANK = {
|
|
|
684
961
|
};
|
|
685
962
|
var createQueue = (maxSize) => {
|
|
686
963
|
const items = [];
|
|
964
|
+
let enqueued = 0;
|
|
965
|
+
let droppedIncoming = 0;
|
|
966
|
+
let droppedQueued = 0;
|
|
687
967
|
const oldestLowestIndex = () => {
|
|
688
968
|
let best = 0;
|
|
689
969
|
for (let i = 1; i < items.length; i++) {
|
|
@@ -699,16 +979,28 @@ var createQueue = (maxSize) => {
|
|
|
699
979
|
const victimIndex = oldestLowestIndex();
|
|
700
980
|
const victim = items[victimIndex];
|
|
701
981
|
if (!victim) break;
|
|
702
|
-
if (RANK[item.priority] < RANK[victim.priority])
|
|
982
|
+
if (RANK[item.priority] < RANK[victim.priority]) {
|
|
983
|
+
droppedIncoming++;
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
703
986
|
items.splice(victimIndex, 1);
|
|
987
|
+
droppedQueued++;
|
|
704
988
|
}
|
|
705
989
|
items.push(item);
|
|
990
|
+
enqueued++;
|
|
706
991
|
};
|
|
707
992
|
const drain = (count) => items.splice(0, count);
|
|
708
993
|
return {
|
|
709
994
|
enqueue,
|
|
710
995
|
drain,
|
|
711
996
|
size: () => items.length,
|
|
997
|
+
getStats: () => ({
|
|
998
|
+
queued: items.length,
|
|
999
|
+
enqueued,
|
|
1000
|
+
droppedIncoming,
|
|
1001
|
+
droppedQueued,
|
|
1002
|
+
droppedTotal: droppedIncoming + droppedQueued
|
|
1003
|
+
}),
|
|
712
1004
|
clear: () => {
|
|
713
1005
|
items.length = 0;
|
|
714
1006
|
}
|
|
@@ -725,6 +1017,20 @@ var DEFAULTS2 = {
|
|
|
725
1017
|
maxRetries: 5,
|
|
726
1018
|
installSignalHandlers: true
|
|
727
1019
|
};
|
|
1020
|
+
var removeProcessListener = process.off.bind(process);
|
|
1021
|
+
var holdProcessExitUntil = (done) => {
|
|
1022
|
+
const nativeExit = process.exit.bind(process);
|
|
1023
|
+
let heldCode;
|
|
1024
|
+
const restore = () => {
|
|
1025
|
+
process.exit = nativeExit;
|
|
1026
|
+
if (heldCode !== void 0) nativeExit(heldCode);
|
|
1027
|
+
};
|
|
1028
|
+
const holdExit = ((code) => {
|
|
1029
|
+
heldCode = code ?? 0;
|
|
1030
|
+
});
|
|
1031
|
+
process.exit = holdExit;
|
|
1032
|
+
void done.finally(restore);
|
|
1033
|
+
};
|
|
728
1034
|
var createTransport = (options) => {
|
|
729
1035
|
const enabled = options.enabled ?? DEFAULTS2.enabled;
|
|
730
1036
|
const batchSize = options.batchSize ?? DEFAULTS2.batchSize;
|
|
@@ -736,6 +1042,8 @@ var createTransport = (options) => {
|
|
|
736
1042
|
let timer;
|
|
737
1043
|
let flushing;
|
|
738
1044
|
let closed = false;
|
|
1045
|
+
let shuttingDown = false;
|
|
1046
|
+
let retryBatch = null;
|
|
739
1047
|
const schedule = () => {
|
|
740
1048
|
if (timer || !enabled || closed) return;
|
|
741
1049
|
timer = setInterval(() => {
|
|
@@ -748,9 +1056,19 @@ var createTransport = (options) => {
|
|
|
748
1056
|
clearInterval(timer);
|
|
749
1057
|
timer = void 0;
|
|
750
1058
|
};
|
|
1059
|
+
const prepareSummary = async (item) => {
|
|
1060
|
+
if (!options.prepareSummary || item.rawFrames.length === 0) return item.summary;
|
|
1061
|
+
try {
|
|
1062
|
+
const resolved = await options.prepareSummary(item.rawFrames);
|
|
1063
|
+
return { ...item.summary, ...resolved };
|
|
1064
|
+
} catch {
|
|
1065
|
+
return item.summary;
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
751
1068
|
const flush = async (timeoutMs = 1e4) => {
|
|
752
1069
|
if (!enabled || closed) {
|
|
753
1070
|
queue.clear();
|
|
1071
|
+
retryBatch = null;
|
|
754
1072
|
return;
|
|
755
1073
|
}
|
|
756
1074
|
if (flushing) return flushing;
|
|
@@ -758,15 +1076,17 @@ var createTransport = (options) => {
|
|
|
758
1076
|
const controller = new AbortController();
|
|
759
1077
|
const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
|
|
760
1078
|
try {
|
|
761
|
-
while (queue.size() > 0) {
|
|
1079
|
+
while (queue.size() > 0 || retryBatch) {
|
|
762
1080
|
if (controller.signal.aborted) break;
|
|
763
|
-
const batch = queue.drain(batchSize);
|
|
1081
|
+
const batch = retryBatch?.items ?? queue.drain(batchSize);
|
|
1082
|
+
const idempotencyKey = retryBatch?.idempotencyKey ?? crypto.randomUUID();
|
|
1083
|
+
retryBatch = null;
|
|
764
1084
|
if (batch.length === 0) break;
|
|
765
1085
|
const events = [];
|
|
766
1086
|
const summaries = [];
|
|
767
1087
|
for (const item of batch) {
|
|
768
1088
|
if (item.kind === "summary") {
|
|
769
|
-
summaries.push(item
|
|
1089
|
+
summaries.push(await prepareSummary(item));
|
|
770
1090
|
continue;
|
|
771
1091
|
}
|
|
772
1092
|
if (options.prepareEvent && item.rawFrames.length > 0) {
|
|
@@ -789,6 +1109,7 @@ var createTransport = (options) => {
|
|
|
789
1109
|
projectApiKey: options.projectApiKey,
|
|
790
1110
|
events,
|
|
791
1111
|
summaries,
|
|
1112
|
+
idempotencyKey,
|
|
792
1113
|
sdkVersion: options.sdkVersion,
|
|
793
1114
|
sdkName: options.sdkName,
|
|
794
1115
|
gzipThresholdBytes,
|
|
@@ -797,13 +1118,13 @@ var createTransport = (options) => {
|
|
|
797
1118
|
signal: controller.signal
|
|
798
1119
|
});
|
|
799
1120
|
if (result === "failed") {
|
|
800
|
-
|
|
1121
|
+
retryBatch = { items: batch, idempotencyKey };
|
|
801
1122
|
break;
|
|
802
1123
|
}
|
|
803
1124
|
}
|
|
804
1125
|
} finally {
|
|
805
1126
|
if (timeout) clearTimeout(timeout);
|
|
806
|
-
if (queue.size() === 0) stopTimer();
|
|
1127
|
+
if (queue.size() === 0 && !retryBatch) stopTimer();
|
|
807
1128
|
flushing = void 0;
|
|
808
1129
|
}
|
|
809
1130
|
})();
|
|
@@ -815,38 +1136,53 @@ var createTransport = (options) => {
|
|
|
815
1136
|
schedule();
|
|
816
1137
|
if (queue.size() >= batchSize) void flush();
|
|
817
1138
|
};
|
|
818
|
-
const enqueueSummary = (summary) => {
|
|
1139
|
+
const enqueueSummary = (summary, rawFrames = []) => {
|
|
819
1140
|
if (!enabled || closed) return;
|
|
820
|
-
queue.enqueue({ kind: "summary", summary, priority: "summary" });
|
|
1141
|
+
queue.enqueue({ kind: "summary", summary, priority: "summary", rawFrames });
|
|
821
1142
|
schedule();
|
|
822
1143
|
if (queue.size() >= batchSize) void flush();
|
|
823
1144
|
};
|
|
1145
|
+
const onBeforeExit = () => {
|
|
1146
|
+
if (closed || shuttingDown) return;
|
|
1147
|
+
if (queue.size() === 0 && !retryBatch && !flushing) return;
|
|
1148
|
+
void flush(2e3).catch(() => {
|
|
1149
|
+
});
|
|
1150
|
+
};
|
|
824
1151
|
const onSignal = () => {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1152
|
+
if (shuttingDown) return;
|
|
1153
|
+
shuttingDown = true;
|
|
1154
|
+
stopTimer();
|
|
1155
|
+
holdProcessExitUntil(flush(2e3).catch(() => {
|
|
1156
|
+
}));
|
|
829
1157
|
};
|
|
830
1158
|
if (enabled && (options.installSignalHandlers ?? DEFAULTS2.installSignalHandlers)) {
|
|
831
|
-
process.on("beforeExit",
|
|
1159
|
+
process.on("beforeExit", onBeforeExit);
|
|
832
1160
|
process.on("SIGTERM", onSignal);
|
|
833
1161
|
process.on("SIGINT", onSignal);
|
|
834
1162
|
}
|
|
835
1163
|
const close = async () => {
|
|
836
|
-
closed = true;
|
|
837
1164
|
stopTimer();
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1165
|
+
removeProcessListener("beforeExit", onBeforeExit);
|
|
1166
|
+
removeProcessListener("SIGTERM", onSignal);
|
|
1167
|
+
removeProcessListener("SIGINT", onSignal);
|
|
841
1168
|
await flush(2e3);
|
|
1169
|
+
closed = true;
|
|
842
1170
|
queue.clear();
|
|
1171
|
+
retryBatch = null;
|
|
843
1172
|
};
|
|
844
1173
|
return {
|
|
845
1174
|
enqueue,
|
|
846
1175
|
enqueueSummary,
|
|
847
1176
|
flush,
|
|
848
1177
|
close,
|
|
849
|
-
size: () => queue.size()
|
|
1178
|
+
size: () => queue.size() + (retryBatch?.items.length ?? 0),
|
|
1179
|
+
getStats: () => {
|
|
1180
|
+
const queueStats = queue.getStats();
|
|
1181
|
+
return {
|
|
1182
|
+
...queueStats,
|
|
1183
|
+
queued: queueStats.queued + (retryBatch?.items.length ?? 0)
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
850
1186
|
};
|
|
851
1187
|
};
|
|
852
1188
|
|
|
@@ -858,7 +1194,16 @@ var noopClient = () => ({
|
|
|
858
1194
|
flush: async () => {
|
|
859
1195
|
},
|
|
860
1196
|
close: async () => {
|
|
861
|
-
}
|
|
1197
|
+
},
|
|
1198
|
+
getStats: () => ({
|
|
1199
|
+
transport: {
|
|
1200
|
+
queued: 0,
|
|
1201
|
+
enqueued: 0,
|
|
1202
|
+
droppedIncoming: 0,
|
|
1203
|
+
droppedQueued: 0,
|
|
1204
|
+
droppedTotal: 0
|
|
1205
|
+
}
|
|
1206
|
+
})
|
|
862
1207
|
});
|
|
863
1208
|
|
|
864
1209
|
// src/create-client/resolve-endpoint-urls.ts
|
|
@@ -867,7 +1212,8 @@ var resolveEndpointUrls = (apiUrl = DEFAULT_API_URL) => {
|
|
|
867
1212
|
const base = apiUrl.replace(/\/+$/, "");
|
|
868
1213
|
return {
|
|
869
1214
|
ingestUrl: `${base}/ingest`,
|
|
870
|
-
deployUrl: `${base}/deploy
|
|
1215
|
+
deployUrl: `${base}/deploy`,
|
|
1216
|
+
instrumentationManifestUrl: `${base}/instrumentationManifest`
|
|
871
1217
|
};
|
|
872
1218
|
};
|
|
873
1219
|
|
|
@@ -888,12 +1234,6 @@ var warnPartialResolution = () => {
|
|
|
888
1234
|
"[rasputin] Source map paths could not be normalized to the repo root; fingerprinting may be degraded"
|
|
889
1235
|
);
|
|
890
1236
|
};
|
|
891
|
-
var prepareResolvedEvent = (event, normalized, resolution) => {
|
|
892
|
-
const stackFrames = framesToStackFrames(normalized);
|
|
893
|
-
const finalResolution = finalizeResolution(resolution, stackFrames);
|
|
894
|
-
if (finalResolution === "partial") warnPartialResolution();
|
|
895
|
-
return withResolvedFrames(event, normalized, finalResolution);
|
|
896
|
-
};
|
|
897
1237
|
var toResolvedFrames = (raw) => raw.map((frame) => ({
|
|
898
1238
|
generated: { file: frame.file, line: frame.line, column: frame.column },
|
|
899
1239
|
...frame.function ? { function: frame.function } : {}
|
|
@@ -909,7 +1249,46 @@ var createClient = (options, hooks = {}) => {
|
|
|
909
1249
|
const release = detectRelease({ release: options.release });
|
|
910
1250
|
const environment = options.environment;
|
|
911
1251
|
const { ingestUrl, deployUrl } = resolveEndpointUrls(options.apiUrl);
|
|
912
|
-
|
|
1252
|
+
if (!release) {
|
|
1253
|
+
if (hooks.announceDeploy ?? true) {
|
|
1254
|
+
void announceDeploy({
|
|
1255
|
+
deployUrl,
|
|
1256
|
+
projectApiKey,
|
|
1257
|
+
environment,
|
|
1258
|
+
fetch: hooks.fetch
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
return noopClient();
|
|
1262
|
+
}
|
|
1263
|
+
const repoRoot = resolveRepoRoot(options);
|
|
1264
|
+
if (!repoRoot) {
|
|
1265
|
+
warnIfMissingRepoRoot();
|
|
1266
|
+
return noopClient();
|
|
1267
|
+
}
|
|
1268
|
+
warnIfSuspiciousRepoRoot(repoRoot);
|
|
1269
|
+
const MAX_STACK_RESOLVE_CACHE = 32;
|
|
1270
|
+
const stackResolveCache = /* @__PURE__ */ new Map();
|
|
1271
|
+
const stackCacheKey = (rawFrames) => rawFrames.map((frame) => `${frame.file}\0${frame.line}\0${frame.column}\0${frame.function ?? ""}`).join("\n");
|
|
1272
|
+
const resolveStack = async (rawFrames) => {
|
|
1273
|
+
const key = stackCacheKey(rawFrames);
|
|
1274
|
+
const cached = stackResolveCache.get(key);
|
|
1275
|
+
if (cached) return cached;
|
|
1276
|
+
const pending = (async () => {
|
|
1277
|
+
const { frames, resolution, unresolvedGenerated } = await resolveSourceMaps(rawFrames);
|
|
1278
|
+
warnUnresolvedMaps(unresolvedGenerated);
|
|
1279
|
+
const normalized = normalizeFrames(frames, { appRoot: repoRoot });
|
|
1280
|
+
const stack_frames = framesToStackFrames(normalized);
|
|
1281
|
+
const finalResolution = finalizeResolution(resolution, stack_frames);
|
|
1282
|
+
if (finalResolution === "partial") warnPartialResolution();
|
|
1283
|
+
return { frames: normalized, stack_frames, resolution: finalResolution };
|
|
1284
|
+
})();
|
|
1285
|
+
stackResolveCache.set(key, pending);
|
|
1286
|
+
if (stackResolveCache.size > MAX_STACK_RESOLVE_CACHE) {
|
|
1287
|
+
const oldest = stackResolveCache.keys().next().value;
|
|
1288
|
+
if (oldest !== void 0) stackResolveCache.delete(oldest);
|
|
1289
|
+
}
|
|
1290
|
+
return pending;
|
|
1291
|
+
};
|
|
913
1292
|
const transport = createTransport({
|
|
914
1293
|
ingestUrl,
|
|
915
1294
|
projectApiKey,
|
|
@@ -919,10 +1298,12 @@ var createClient = (options, hooks = {}) => {
|
|
|
919
1298
|
sdkVersion: hooks.sdkVersion ?? SDK_VERSION,
|
|
920
1299
|
sdkName: hooks.sdkName ?? SDK_NAME,
|
|
921
1300
|
prepareEvent: async ({ event, rawFrames }) => {
|
|
922
|
-
const
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1301
|
+
const prepared = await resolveStack(rawFrames);
|
|
1302
|
+
return withResolvedFrames(event, prepared.frames, prepared.resolution);
|
|
1303
|
+
},
|
|
1304
|
+
prepareSummary: async (rawFrames) => {
|
|
1305
|
+
const prepared = await resolveStack(rawFrames);
|
|
1306
|
+
return { stack_frames: prepared.stack_frames, resolution: prepared.resolution };
|
|
926
1307
|
}
|
|
927
1308
|
});
|
|
928
1309
|
const suppression = createSuppression();
|
|
@@ -947,7 +1328,8 @@ var createClient = (options, hooks = {}) => {
|
|
|
947
1328
|
release,
|
|
948
1329
|
origin: "server",
|
|
949
1330
|
resolution: "none",
|
|
950
|
-
request: context?.request
|
|
1331
|
+
request: context?.request,
|
|
1332
|
+
runtimeState: context?.runtimeState
|
|
951
1333
|
});
|
|
952
1334
|
const decision = suppression.decide({
|
|
953
1335
|
errorType: event.type,
|
|
@@ -966,40 +1348,62 @@ var createClient = (options, hooks = {}) => {
|
|
|
966
1348
|
} catch {
|
|
967
1349
|
}
|
|
968
1350
|
};
|
|
1351
|
+
const enqueuePendingSummaries = () => {
|
|
1352
|
+
for (const { summary, rawFrames } of suppression.drainSummaries()) {
|
|
1353
|
+
transport.enqueueSummary(summary, rawFrames);
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
969
1356
|
const flush = async (timeoutMs) => {
|
|
970
1357
|
try {
|
|
971
|
-
|
|
972
|
-
const { frames, resolution, unresolvedGenerated } = await resolveSourceMaps(raw);
|
|
973
|
-
warnUnresolvedMaps(unresolvedGenerated);
|
|
974
|
-
const normalized = normalizeFrames(frames, { appRoot: repoRoot });
|
|
975
|
-
const stack_frames = framesToStackFrames(normalized);
|
|
976
|
-
const finalResolution = finalizeResolution(resolution, stack_frames);
|
|
977
|
-
if (finalResolution === "partial") warnPartialResolution();
|
|
978
|
-
return {
|
|
979
|
-
stack_frames,
|
|
980
|
-
resolution: finalResolution
|
|
981
|
-
};
|
|
982
|
-
});
|
|
983
|
-
for (const summary of summaries) {
|
|
984
|
-
transport.enqueueSummary(summary);
|
|
985
|
-
}
|
|
1358
|
+
enqueuePendingSummaries();
|
|
986
1359
|
await transport.flush(timeoutMs);
|
|
987
1360
|
} catch {
|
|
988
1361
|
}
|
|
989
1362
|
};
|
|
990
1363
|
const close = async () => {
|
|
991
1364
|
try {
|
|
992
|
-
|
|
1365
|
+
enqueuePendingSummaries();
|
|
993
1366
|
await transport.close();
|
|
994
1367
|
} catch {
|
|
995
1368
|
}
|
|
996
1369
|
};
|
|
997
|
-
return {
|
|
1370
|
+
return {
|
|
1371
|
+
captureException,
|
|
1372
|
+
flush,
|
|
1373
|
+
close,
|
|
1374
|
+
getStats: () => ({ transport: transport.getStats() })
|
|
1375
|
+
};
|
|
998
1376
|
} catch {
|
|
999
1377
|
return noopClient();
|
|
1000
1378
|
}
|
|
1001
1379
|
};
|
|
1380
|
+
|
|
1381
|
+
// src/instrumentation-manifest/instrumentation-manifest-types.ts
|
|
1382
|
+
var INSTRUMENTATION_MANIFEST_SCHEMA_VERSION = 1;
|
|
1383
|
+
|
|
1384
|
+
// src/instrumentation-manifest/upload-instrumentation-manifest.ts
|
|
1385
|
+
var uploadInstrumentationManifest = async (options) => {
|
|
1386
|
+
const release = options.release.trim();
|
|
1387
|
+
if (!release) return "failed";
|
|
1388
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
1389
|
+
try {
|
|
1390
|
+
const response = await fetchFn(options.url, {
|
|
1391
|
+
method: "POST",
|
|
1392
|
+
headers: {
|
|
1393
|
+
Authorization: `Bearer ${options.projectApiKey}`,
|
|
1394
|
+
"Content-Type": "application/json",
|
|
1395
|
+
Accept: "application/json"
|
|
1396
|
+
},
|
|
1397
|
+
body: JSON.stringify({ release, manifest: options.manifest })
|
|
1398
|
+
});
|
|
1399
|
+
if (response.ok) return "ok";
|
|
1400
|
+
return "failed";
|
|
1401
|
+
} catch {
|
|
1402
|
+
return "failed";
|
|
1403
|
+
}
|
|
1404
|
+
};
|
|
1002
1405
|
export {
|
|
1406
|
+
INSTRUMENTATION_MANIFEST_SCHEMA_VERSION,
|
|
1003
1407
|
announceDeploy,
|
|
1004
1408
|
createClient,
|
|
1005
1409
|
createIngestEvent,
|
|
@@ -1009,5 +1413,9 @@ export {
|
|
|
1009
1413
|
isClientEnabled,
|
|
1010
1414
|
normalizeFrames,
|
|
1011
1415
|
parseStack,
|
|
1012
|
-
|
|
1416
|
+
repoRootFrom,
|
|
1417
|
+
resolveEndpointUrls,
|
|
1418
|
+
resolveRepoRoot,
|
|
1419
|
+
resolveSourceMaps,
|
|
1420
|
+
uploadInstrumentationManifest
|
|
1013
1421
|
};
|