@rasputin-ai/core 0.2.0 → 0.4.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 +50 -19
- 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 +2 -1
- package/dist/create-client/resolve-endpoint-urls.d.ts.map +1 -1
- package/dist/create-ingest-event/create-ingest-event-types.d.ts +8 -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 +91 -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 +1 -1
- package/dist/detect-release/detect-release.d.ts.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +331 -80
- package/dist/instrumentation-manifest/instrumentation-manifest-types.d.ts +98 -0
- package/dist/instrumentation-manifest/instrumentation-manifest-types.d.ts.map +1 -0
- package/dist/instrumentation-manifest/upload-instrumentation-manifest.d.ts +21 -0
- package/dist/instrumentation-manifest/upload-instrumentation-manifest.d.ts.map +1 -0
- package/dist/normalize-frames/normalize-frames.d.ts +4 -2
- package/dist/normalize-frames/normalize-frames.d.ts.map +1 -1
- package/dist/repo-root/warn-repo-root-setup.d.ts +0 -1
- package/dist/repo-root/warn-repo-root-setup.d.ts.map +1 -1
- package/dist/sdk-meta.d.ts +2 -2
- package/dist/sdk-meta.d.ts.map +1 -1
- package/dist/source-identity/source-identity.d.ts +25 -0
- package/dist/source-identity/source-identity.d.ts.map +1 -0
- 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/transport-queue.d.ts +7 -0
- package/dist/transport/transport-queue.d.ts.map +1 -1
- package/dist/transport/transport-types.d.ts +11 -2
- package/dist/transport/transport-types.d.ts.map +1 -1
- package/package.json +1 -2
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;
|
|
@@ -52,6 +153,7 @@ var toStackFrame = (frame) => {
|
|
|
52
153
|
const fn = frame.function ?? frame.original?.name;
|
|
53
154
|
return {
|
|
54
155
|
filename,
|
|
156
|
+
...frame.source ? { source: frame.source } : {},
|
|
55
157
|
...fn ? { function: fn } : {},
|
|
56
158
|
...preferred.line !== void 0 ? { lineno: preferred.line } : {},
|
|
57
159
|
...preferred.column !== void 0 ? { colno: preferred.column } : {},
|
|
@@ -79,18 +181,19 @@ var createIngestEvent = (input) => {
|
|
|
79
181
|
const occurred = input.occurredAt === void 0 ? (/* @__PURE__ */ new Date()).toISOString() : typeof input.occurredAt === "string" ? input.occurredAt : input.occurredAt.toISOString();
|
|
80
182
|
const event = {
|
|
81
183
|
type: extracted.type,
|
|
82
|
-
message:
|
|
184
|
+
message: truncateIngestMessage(extracted.message),
|
|
83
185
|
stack_frames: framesToStackFrames(input.frames),
|
|
84
186
|
occurred_at: occurred,
|
|
85
187
|
environment: input.environment,
|
|
86
188
|
origin: input.origin,
|
|
87
|
-
resolution: input.resolution
|
|
189
|
+
resolution: input.resolution,
|
|
190
|
+
release: input.release
|
|
88
191
|
};
|
|
89
192
|
const code = input.code !== void 0 ? input.code : extracted.code;
|
|
90
193
|
if (code !== void 0) event.code = code;
|
|
91
|
-
if (input.release !== void 0) event.release = input.release;
|
|
92
194
|
const request = sanitizeRequest(input.request);
|
|
93
195
|
if (request) event.request = request;
|
|
196
|
+
if (input.runtimeState) event.runtime_state = input.runtimeState;
|
|
94
197
|
return event;
|
|
95
198
|
};
|
|
96
199
|
|
|
@@ -166,20 +269,98 @@ var detectRelease = (options = {}) => {
|
|
|
166
269
|
const fromGit = readGitHead(options.cwd ?? process.cwd());
|
|
167
270
|
if (fromGit) return fromGit;
|
|
168
271
|
console.warn(
|
|
169
|
-
"[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).
|
|
272
|
+
"[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."
|
|
170
273
|
);
|
|
171
274
|
return void 0;
|
|
172
275
|
};
|
|
173
276
|
|
|
174
277
|
// src/normalize-frames/normalize-frames.ts
|
|
175
|
-
import { isAbsolute, resolve } from "node:path";
|
|
278
|
+
import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
279
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
280
|
+
|
|
281
|
+
// src/source-identity/source-identity.ts
|
|
282
|
+
import { createHash } from "node:crypto";
|
|
283
|
+
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
284
|
+
import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
176
285
|
import { fileURLToPath } from "node:url";
|
|
177
|
-
var
|
|
286
|
+
var packageBoundaryByDirectory = /* @__PURE__ */ new Map();
|
|
178
287
|
var toPosix = (path) => path.replaceAll("\\", "/");
|
|
288
|
+
var physicalPath = (file) => {
|
|
289
|
+
if (!file.startsWith("file://")) return resolve(file);
|
|
290
|
+
try {
|
|
291
|
+
return fileURLToPath(file);
|
|
292
|
+
} catch {
|
|
293
|
+
return resolve(file.replace(/^file:\/\//, ""));
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
var readPackageBoundary = (directory) => {
|
|
297
|
+
const visited = [];
|
|
298
|
+
let current = resolve(directory);
|
|
299
|
+
while (true) {
|
|
300
|
+
const cached = packageBoundaryByDirectory.get(current);
|
|
301
|
+
if (cached !== void 0) {
|
|
302
|
+
for (const path of visited) packageBoundaryByDirectory.set(path, cached);
|
|
303
|
+
return cached;
|
|
304
|
+
}
|
|
305
|
+
visited.push(current);
|
|
306
|
+
const packageJson = resolve(current, "package.json");
|
|
307
|
+
if (existsSync(packageJson)) {
|
|
308
|
+
let name;
|
|
309
|
+
try {
|
|
310
|
+
const parsed = JSON.parse(readFileSync2(packageJson, "utf8"));
|
|
311
|
+
if (typeof parsed.name === "string" && parsed.name.trim()) name = parsed.name.trim();
|
|
312
|
+
} catch {
|
|
313
|
+
}
|
|
314
|
+
const boundary = { root: current, ...name ? { name } : {} };
|
|
315
|
+
for (const path of visited) packageBoundaryByDirectory.set(path, boundary);
|
|
316
|
+
return boundary;
|
|
317
|
+
}
|
|
318
|
+
const parent = dirname(current);
|
|
319
|
+
if (parent === current) {
|
|
320
|
+
for (const path of visited) packageBoundaryByDirectory.set(path, null);
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
current = parent;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
var sourceLocatorFromFile = (file) => {
|
|
327
|
+
if (!file || /^(node:|bun:|https?:|webpack:|blob:|data:|native$)/i.test(file)) return void 0;
|
|
328
|
+
const absolute = physicalPath(file.split("?")[0] ?? file);
|
|
329
|
+
const boundary = readPackageBoundary(dirname(absolute));
|
|
330
|
+
if (!boundary) return void 0;
|
|
331
|
+
const fromPackage = relative(boundary.root, absolute);
|
|
332
|
+
if (!fromPackage || fromPackage === ".." || fromPackage.startsWith(`..${sep}`) || isAbsolute(fromPackage)) {
|
|
333
|
+
return { packageRelativePath: basename(absolute) };
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
...boundary.name ? { packageName: boundary.name } : {},
|
|
337
|
+
packageRelativePath: toPosix(fromPackage)
|
|
338
|
+
};
|
|
339
|
+
};
|
|
340
|
+
var sourceLocatorKey = (source) => `${source.packageName ?? ""}\0${source.packageRelativePath}`;
|
|
341
|
+
var runtimeFunctionId = (definition) => {
|
|
342
|
+
const source = definition.source;
|
|
343
|
+
const identity = [
|
|
344
|
+
definition.name,
|
|
345
|
+
source?.packageName ?? "",
|
|
346
|
+
source?.packageRelativePath ?? "",
|
|
347
|
+
String(source?.line ?? ""),
|
|
348
|
+
String(source?.column ?? "")
|
|
349
|
+
].join("\0");
|
|
350
|
+
return `rf_${createHash("sha256").update(identity).digest("base64url").slice(0, 24)}`;
|
|
351
|
+
};
|
|
352
|
+
var sourceDisplayPath = (source) => source.repositoryPath ?? source.packageRelativePath;
|
|
353
|
+
var resetSourceIdentityCache = () => {
|
|
354
|
+
packageBoundaryByDirectory.clear();
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// src/normalize-frames/normalize-frames.ts
|
|
358
|
+
var NON_SOURCE = /* @__PURE__ */ new Set(["", "native", "<anonymous>", "unknown location"]);
|
|
359
|
+
var toPosix2 = (path) => path.replaceAll("\\", "/");
|
|
179
360
|
var stripFileUrl = (file) => {
|
|
180
361
|
if (!file.startsWith("file://")) return file;
|
|
181
362
|
try {
|
|
182
|
-
return
|
|
363
|
+
return fileURLToPath2(file);
|
|
183
364
|
} catch {
|
|
184
365
|
return file.replace(/^file:\/\//, "");
|
|
185
366
|
}
|
|
@@ -187,44 +368,55 @@ var stripFileUrl = (file) => {
|
|
|
187
368
|
var isSchemePath = (file) => NON_SOURCE.has(file) || /^(node:|bun:|https?:|webpack:|blob:|data:|eval\b)/i.test(file);
|
|
188
369
|
var normalizeFile = (file, appRoot) => {
|
|
189
370
|
if (!file || isSchemePath(file)) return { file, underRoot: false };
|
|
190
|
-
const cleaned =
|
|
371
|
+
const cleaned = toPosix2(stripFileUrl(file)).split("?")[0]?.trim() ?? "";
|
|
191
372
|
if (!cleaned || isSchemePath(cleaned)) return { file: cleaned, underRoot: false };
|
|
192
|
-
const root =
|
|
193
|
-
const absolute =
|
|
194
|
-
|
|
373
|
+
const root = appRoot ? toPosix2(resolve2(appRoot)) : void 0;
|
|
374
|
+
const absolute = toPosix2(
|
|
375
|
+
isAbsolute2(cleaned) || /^[A-Za-z]:\//.test(cleaned) ? resolve2(cleaned) : resolve2(root ?? process.cwd(), cleaned)
|
|
195
376
|
);
|
|
196
|
-
if (
|
|
377
|
+
if (!root) return { file: cleaned, absolute, underRoot: false };
|
|
378
|
+
if (absolute === root) return { file: ".", absolute, underRoot: true };
|
|
197
379
|
const prefix = root.endsWith("/") ? root : `${root}/`;
|
|
198
380
|
if (absolute.startsWith(prefix)) {
|
|
199
|
-
return { file: absolute.slice(prefix.length), underRoot: true };
|
|
381
|
+
return { file: absolute.slice(prefix.length), absolute, underRoot: true };
|
|
200
382
|
}
|
|
201
|
-
return { file: absolute, underRoot: false };
|
|
383
|
+
return { file: absolute, absolute, underRoot: false };
|
|
202
384
|
};
|
|
203
|
-
var isInApp = (file, underRoot) => {
|
|
204
|
-
if (
|
|
205
|
-
if (/(^|\/)node_modules\//.test(file)) return false;
|
|
206
|
-
return
|
|
385
|
+
var isInApp = (file, absolute, underRoot, source) => {
|
|
386
|
+
if (isSchemePath(file)) return false;
|
|
387
|
+
if (/(^|\/)node_modules\//.test(absolute ?? file)) return false;
|
|
388
|
+
if (/^@rasputin-ai\/(?:core|node|elysia)$/.test(source?.packageName ?? "")) return false;
|
|
389
|
+
return underRoot || source !== void 0;
|
|
207
390
|
};
|
|
208
391
|
var normalizeFrames = (frames, options) => {
|
|
209
392
|
const appRoot = options.appRoot;
|
|
210
393
|
return frames.map((frame) => {
|
|
211
394
|
const generated = normalizeFile(frame.generated.file, appRoot);
|
|
212
395
|
const original = frame.original ? normalizeFile(frame.original.file, appRoot) : void 0;
|
|
396
|
+
const generatedSource = generated.absolute ? sourceLocatorFromFile(generated.absolute) : void 0;
|
|
397
|
+
const originalSource = original?.absolute ? sourceLocatorFromFile(original.absolute) : void 0;
|
|
213
398
|
const preferred = original ?? generated;
|
|
399
|
+
const locator = originalSource ?? generatedSource;
|
|
400
|
+
const source = locator ? {
|
|
401
|
+
...locator,
|
|
402
|
+
...(frame.original?.line ?? frame.generated.line) !== void 0 ? { line: frame.original?.line ?? frame.generated.line } : {},
|
|
403
|
+
...(frame.original?.column ?? frame.generated.column) !== void 0 ? { column: frame.original?.column ?? frame.generated.column } : {}
|
|
404
|
+
} : void 0;
|
|
214
405
|
return {
|
|
215
406
|
generated: {
|
|
216
|
-
file: generated.file,
|
|
407
|
+
file: generatedSource && !generated.underRoot ? sourceDisplayPath(generatedSource) : generated.file,
|
|
217
408
|
line: frame.generated.line,
|
|
218
409
|
column: frame.generated.column
|
|
219
410
|
},
|
|
220
411
|
original: frame.original ? {
|
|
221
|
-
file: original?.file ?? frame.original.file,
|
|
412
|
+
file: originalSource && !original?.underRoot ? sourceDisplayPath(originalSource) : original?.file ?? frame.original.file,
|
|
222
413
|
line: frame.original.line,
|
|
223
414
|
column: frame.original.column,
|
|
224
415
|
name: frame.original.name
|
|
225
416
|
} : void 0,
|
|
226
417
|
function: frame.function,
|
|
227
|
-
in_app: isInApp(preferred.file, preferred.underRoot)
|
|
418
|
+
in_app: isInApp(preferred.file, preferred.absolute, preferred.underRoot, source),
|
|
419
|
+
...source ? { source } : {}
|
|
228
420
|
};
|
|
229
421
|
});
|
|
230
422
|
};
|
|
@@ -288,22 +480,22 @@ var parseStack = (stack) => {
|
|
|
288
480
|
};
|
|
289
481
|
|
|
290
482
|
// src/repo-root/repo-root-from.ts
|
|
291
|
-
import { existsSync } from "node:fs";
|
|
292
|
-
import { dirname, join as join2, resolve as
|
|
293
|
-
import { fileURLToPath as
|
|
294
|
-
var isGitRoot = (dir) =>
|
|
295
|
-
var hasPackageJson = (dir) =>
|
|
483
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
484
|
+
import { dirname as dirname2, join as join2, resolve as resolve3 } from "node:path";
|
|
485
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
486
|
+
var isGitRoot = (dir) => existsSync2(join2(dir, ".git"));
|
|
487
|
+
var hasPackageJson = (dir) => existsSync2(join2(dir, "package.json"));
|
|
296
488
|
var repoRootFrom = (moduleUrl) => {
|
|
297
|
-
let current =
|
|
489
|
+
let current = resolve3(dirname2(fileURLToPath3(moduleUrl)));
|
|
298
490
|
let packageRoot;
|
|
299
491
|
while (true) {
|
|
300
492
|
if (isGitRoot(current)) return current;
|
|
301
493
|
if (hasPackageJson(current)) packageRoot = current;
|
|
302
|
-
const parent =
|
|
494
|
+
const parent = dirname2(current);
|
|
303
495
|
if (parent === current) break;
|
|
304
496
|
current = parent;
|
|
305
497
|
}
|
|
306
|
-
return packageRoot ??
|
|
498
|
+
return packageRoot ?? resolve3(dirname2(fileURLToPath3(moduleUrl)));
|
|
307
499
|
};
|
|
308
500
|
|
|
309
501
|
// src/repo-root/resolve-repo-root.ts
|
|
@@ -317,34 +509,12 @@ var resolveRepoRoot = (options) => {
|
|
|
317
509
|
|
|
318
510
|
// src/repo-root/warn-repo-root-setup.ts
|
|
319
511
|
var BUILD_OUTPUT_SEGMENTS = /* @__PURE__ */ new Set(["dist", "build", "out", ".next", "coverage"]);
|
|
320
|
-
var warnedMissing = false;
|
|
321
512
|
var warnedSuspicious = false;
|
|
322
513
|
var BANNER = "======== [rasputin] WARNING";
|
|
323
514
|
var looksLikeBuildOutputRoot = (root) => {
|
|
324
515
|
const posix = root.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
325
516
|
return posix.split("/").some((segment) => BUILD_OUTPUT_SEGMENTS.has(segment));
|
|
326
517
|
};
|
|
327
|
-
var warnIfMissingRepoRoot = () => {
|
|
328
|
-
if (warnedMissing) return;
|
|
329
|
-
warnedMissing = true;
|
|
330
|
-
console.warn(
|
|
331
|
-
`
|
|
332
|
-
|
|
333
|
-
${BANNER}: we don't know where your project is ========
|
|
334
|
-
Rasputin needs to know which folder your app lives in so errors show
|
|
335
|
-
the right file paths in the dashboard.
|
|
336
|
-
|
|
337
|
-
Add this to RasputinInit (from the same file you call it in):
|
|
338
|
-
|
|
339
|
-
moduleUrl: import.meta.url
|
|
340
|
-
|
|
341
|
-
Or set repoRoot to the folder you open in your editor.
|
|
342
|
-
|
|
343
|
-
Nothing will be sent until this is set.
|
|
344
|
-
======================================================================
|
|
345
|
-
`
|
|
346
|
-
);
|
|
347
|
-
};
|
|
348
518
|
var warnIfSuspiciousRepoRoot = (root) => {
|
|
349
519
|
if (warnedSuspicious || !looksLikeBuildOutputRoot(root)) return;
|
|
350
520
|
warnedSuspicious = true;
|
|
@@ -372,8 +542,8 @@ Or set repoRoot yourself.
|
|
|
372
542
|
|
|
373
543
|
// src/resolve-source-maps/resolve-source-maps.ts
|
|
374
544
|
import { readFile } from "node:fs/promises";
|
|
375
|
-
import { dirname as
|
|
376
|
-
import { fileURLToPath as
|
|
545
|
+
import { dirname as dirname3, isAbsolute as isAbsolute3, join as join3 } from "node:path";
|
|
546
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
377
547
|
import { LEAST_UPPER_BOUND, originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
|
|
378
548
|
var SOURCE_EXT2 = /\.(ts|tsx|mts|cts)$/i;
|
|
379
549
|
var SOURCEMAP_COMMENT = /(?:\/\/[#@][ \t]*sourceMappingURL=([^\s'"]+)|\/\*[#@][ \t]*sourceMappingURL=([^\s*'"]+)[ \t]*\*\/)\s*$/;
|
|
@@ -381,7 +551,7 @@ var mapCache = /* @__PURE__ */ new Map();
|
|
|
381
551
|
var toPath = (file) => {
|
|
382
552
|
if (file.startsWith("file://")) {
|
|
383
553
|
try {
|
|
384
|
-
return
|
|
554
|
+
return fileURLToPath4(file);
|
|
385
555
|
} catch {
|
|
386
556
|
return file;
|
|
387
557
|
}
|
|
@@ -422,7 +592,7 @@ var readMapPayload = async (generatedPath) => {
|
|
|
422
592
|
if (comment?.startsWith("data:")) return decodeDataUrl(comment);
|
|
423
593
|
if (comment) {
|
|
424
594
|
try {
|
|
425
|
-
return await readFile(join3(
|
|
595
|
+
return await readFile(join3(dirname3(generatedPath), comment), "utf8");
|
|
426
596
|
} catch {
|
|
427
597
|
}
|
|
428
598
|
}
|
|
@@ -469,7 +639,7 @@ var resolveFrame = async (raw) => {
|
|
|
469
639
|
return {
|
|
470
640
|
...base,
|
|
471
641
|
original: {
|
|
472
|
-
file:
|
|
642
|
+
file: isAbsolute3(pos.source) ? pos.source : join3(dirname3(generatedPath), pos.source),
|
|
473
643
|
line: pos.line,
|
|
474
644
|
column: pos.column ?? void 0,
|
|
475
645
|
name: pos.name ?? void 0
|
|
@@ -489,7 +659,7 @@ var resolveSourceMaps = async (frames) => {
|
|
|
489
659
|
|
|
490
660
|
// src/sdk-meta.ts
|
|
491
661
|
var SDK_NAME = "@rasputin-ai/core";
|
|
492
|
-
var SDK_VERSION = "0.
|
|
662
|
+
var SDK_VERSION = "0.4.0";
|
|
493
663
|
|
|
494
664
|
// src/suppression/identity-needs-message-template.ts
|
|
495
665
|
var identityNeedsMessageTemplate = (type, code) => {
|
|
@@ -687,7 +857,7 @@ var createSuppression = (options = {}) => {
|
|
|
687
857
|
const identity = captureIdentity(input);
|
|
688
858
|
const captureRawFrames = (input.rawFrames ?? []).slice(0, MAX_STACK_FRAMES);
|
|
689
859
|
const burstKey = computeBurstKey(input.errorType, input.message, input.code, input.frames);
|
|
690
|
-
const release = input.release
|
|
860
|
+
const release = input.release;
|
|
691
861
|
const route = input.route ?? null;
|
|
692
862
|
let bucket = burstBuckets.get(burstKey);
|
|
693
863
|
if (!bucket) {
|
|
@@ -771,17 +941,17 @@ var createSuppression = (options = {}) => {
|
|
|
771
941
|
|
|
772
942
|
// src/transport/send-batch.ts
|
|
773
943
|
import { gzipSync } from "node:zlib";
|
|
774
|
-
var sleep = (ms, signal) => new Promise((
|
|
944
|
+
var sleep = (ms, signal) => new Promise((resolve4) => {
|
|
775
945
|
if (signal?.aborted) {
|
|
776
|
-
|
|
946
|
+
resolve4();
|
|
777
947
|
return;
|
|
778
948
|
}
|
|
779
|
-
const timer = setTimeout(
|
|
949
|
+
const timer = setTimeout(resolve4, ms);
|
|
780
950
|
signal?.addEventListener(
|
|
781
951
|
"abort",
|
|
782
952
|
() => {
|
|
783
953
|
clearTimeout(timer);
|
|
784
|
-
|
|
954
|
+
resolve4();
|
|
785
955
|
},
|
|
786
956
|
{ once: true }
|
|
787
957
|
);
|
|
@@ -859,6 +1029,9 @@ var RANK = {
|
|
|
859
1029
|
};
|
|
860
1030
|
var createQueue = (maxSize) => {
|
|
861
1031
|
const items = [];
|
|
1032
|
+
let enqueued = 0;
|
|
1033
|
+
let droppedIncoming = 0;
|
|
1034
|
+
let droppedQueued = 0;
|
|
862
1035
|
const oldestLowestIndex = () => {
|
|
863
1036
|
let best = 0;
|
|
864
1037
|
for (let i = 1; i < items.length; i++) {
|
|
@@ -874,16 +1047,28 @@ var createQueue = (maxSize) => {
|
|
|
874
1047
|
const victimIndex = oldestLowestIndex();
|
|
875
1048
|
const victim = items[victimIndex];
|
|
876
1049
|
if (!victim) break;
|
|
877
|
-
if (RANK[item.priority] < RANK[victim.priority])
|
|
1050
|
+
if (RANK[item.priority] < RANK[victim.priority]) {
|
|
1051
|
+
droppedIncoming++;
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
878
1054
|
items.splice(victimIndex, 1);
|
|
1055
|
+
droppedQueued++;
|
|
879
1056
|
}
|
|
880
1057
|
items.push(item);
|
|
1058
|
+
enqueued++;
|
|
881
1059
|
};
|
|
882
1060
|
const drain = (count) => items.splice(0, count);
|
|
883
1061
|
return {
|
|
884
1062
|
enqueue,
|
|
885
1063
|
drain,
|
|
886
1064
|
size: () => items.length,
|
|
1065
|
+
getStats: () => ({
|
|
1066
|
+
queued: items.length,
|
|
1067
|
+
enqueued,
|
|
1068
|
+
droppedIncoming,
|
|
1069
|
+
droppedQueued,
|
|
1070
|
+
droppedTotal: droppedIncoming + droppedQueued
|
|
1071
|
+
}),
|
|
887
1072
|
clear: () => {
|
|
888
1073
|
items.length = 0;
|
|
889
1074
|
}
|
|
@@ -1058,7 +1243,14 @@ var createTransport = (options) => {
|
|
|
1058
1243
|
enqueueSummary,
|
|
1059
1244
|
flush,
|
|
1060
1245
|
close,
|
|
1061
|
-
size: () => queue.size() + (retryBatch?.items.length ?? 0)
|
|
1246
|
+
size: () => queue.size() + (retryBatch?.items.length ?? 0),
|
|
1247
|
+
getStats: () => {
|
|
1248
|
+
const queueStats = queue.getStats();
|
|
1249
|
+
return {
|
|
1250
|
+
...queueStats,
|
|
1251
|
+
queued: queueStats.queued + (retryBatch?.items.length ?? 0)
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1062
1254
|
};
|
|
1063
1255
|
};
|
|
1064
1256
|
|
|
@@ -1070,16 +1262,26 @@ var noopClient = () => ({
|
|
|
1070
1262
|
flush: async () => {
|
|
1071
1263
|
},
|
|
1072
1264
|
close: async () => {
|
|
1073
|
-
}
|
|
1265
|
+
},
|
|
1266
|
+
getStats: () => ({
|
|
1267
|
+
transport: {
|
|
1268
|
+
queued: 0,
|
|
1269
|
+
enqueued: 0,
|
|
1270
|
+
droppedIncoming: 0,
|
|
1271
|
+
droppedQueued: 0,
|
|
1272
|
+
droppedTotal: 0
|
|
1273
|
+
}
|
|
1274
|
+
})
|
|
1074
1275
|
});
|
|
1075
1276
|
|
|
1076
1277
|
// src/create-client/resolve-endpoint-urls.ts
|
|
1077
|
-
var DEFAULT_API_URL = "https://
|
|
1278
|
+
var DEFAULT_API_URL = "https://api.rasputinai.dev";
|
|
1078
1279
|
var resolveEndpointUrls = (apiUrl = DEFAULT_API_URL) => {
|
|
1079
1280
|
const base = apiUrl.replace(/\/+$/, "");
|
|
1080
1281
|
return {
|
|
1081
1282
|
ingestUrl: `${base}/ingest`,
|
|
1082
|
-
deployUrl: `${base}/deploy
|
|
1283
|
+
deployUrl: `${base}/deploy`,
|
|
1284
|
+
instrumentationManifestUrl: `${base}/instrumentationManifest`
|
|
1083
1285
|
};
|
|
1084
1286
|
};
|
|
1085
1287
|
|
|
@@ -1115,12 +1317,19 @@ var createClient = (options, hooks = {}) => {
|
|
|
1115
1317
|
const release = detectRelease({ release: options.release });
|
|
1116
1318
|
const environment = options.environment;
|
|
1117
1319
|
const { ingestUrl, deployUrl } = resolveEndpointUrls(options.apiUrl);
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1320
|
+
if (!release) {
|
|
1321
|
+
if (hooks.announceDeploy ?? true) {
|
|
1322
|
+
void announceDeploy({
|
|
1323
|
+
deployUrl,
|
|
1324
|
+
projectApiKey,
|
|
1325
|
+
environment,
|
|
1326
|
+
fetch: hooks.fetch
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1121
1329
|
return noopClient();
|
|
1122
1330
|
}
|
|
1123
|
-
|
|
1331
|
+
const repoRoot = resolveRepoRoot(options);
|
|
1332
|
+
if (repoRoot) warnIfSuspiciousRepoRoot(repoRoot);
|
|
1124
1333
|
const MAX_STACK_RESOLVE_CACHE = 32;
|
|
1125
1334
|
const stackResolveCache = /* @__PURE__ */ new Map();
|
|
1126
1335
|
const stackCacheKey = (rawFrames) => rawFrames.map((frame) => `${frame.file}\0${frame.line}\0${frame.column}\0${frame.function ?? ""}`).join("\n");
|
|
@@ -1183,7 +1392,8 @@ var createClient = (options, hooks = {}) => {
|
|
|
1183
1392
|
release,
|
|
1184
1393
|
origin: "server",
|
|
1185
1394
|
resolution: "none",
|
|
1186
|
-
request: context?.request
|
|
1395
|
+
request: context?.request,
|
|
1396
|
+
runtimeState: context?.runtimeState
|
|
1187
1397
|
});
|
|
1188
1398
|
const decision = suppression.decide({
|
|
1189
1399
|
errorType: event.type,
|
|
@@ -1221,12 +1431,45 @@ var createClient = (options, hooks = {}) => {
|
|
|
1221
1431
|
} catch {
|
|
1222
1432
|
}
|
|
1223
1433
|
};
|
|
1224
|
-
return {
|
|
1434
|
+
return {
|
|
1435
|
+
captureException,
|
|
1436
|
+
flush,
|
|
1437
|
+
close,
|
|
1438
|
+
getStats: () => ({ transport: transport.getStats() })
|
|
1439
|
+
};
|
|
1225
1440
|
} catch {
|
|
1226
1441
|
return noopClient();
|
|
1227
1442
|
}
|
|
1228
1443
|
};
|
|
1444
|
+
|
|
1445
|
+
// src/instrumentation-manifest/instrumentation-manifest-types.ts
|
|
1446
|
+
var INSTRUMENTATION_MANIFEST_SCHEMA_VERSION = 2;
|
|
1447
|
+
|
|
1448
|
+
// src/instrumentation-manifest/upload-instrumentation-manifest.ts
|
|
1449
|
+
var uploadInstrumentationManifest = async (options) => {
|
|
1450
|
+
const release = options.release.trim();
|
|
1451
|
+
if (!release) return { ok: false };
|
|
1452
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
1453
|
+
try {
|
|
1454
|
+
const response = await fetchFn(options.url, {
|
|
1455
|
+
method: "POST",
|
|
1456
|
+
headers: {
|
|
1457
|
+
Authorization: `Bearer ${options.projectApiKey}`,
|
|
1458
|
+
"Content-Type": "application/json",
|
|
1459
|
+
Accept: "application/json"
|
|
1460
|
+
},
|
|
1461
|
+
body: JSON.stringify({ release, manifest: options.manifest })
|
|
1462
|
+
});
|
|
1463
|
+
if (!response.ok) return { ok: false };
|
|
1464
|
+
const body = await response.json();
|
|
1465
|
+
const verification = body.payload?.verification;
|
|
1466
|
+
return verification ? { ok: true, verification } : { ok: false };
|
|
1467
|
+
} catch {
|
|
1468
|
+
return { ok: false };
|
|
1469
|
+
}
|
|
1470
|
+
};
|
|
1229
1471
|
export {
|
|
1472
|
+
INSTRUMENTATION_MANIFEST_SCHEMA_VERSION,
|
|
1230
1473
|
announceDeploy,
|
|
1231
1474
|
createClient,
|
|
1232
1475
|
createIngestEvent,
|
|
@@ -1237,5 +1480,13 @@ export {
|
|
|
1237
1480
|
normalizeFrames,
|
|
1238
1481
|
parseStack,
|
|
1239
1482
|
repoRootFrom,
|
|
1240
|
-
|
|
1483
|
+
resetSourceIdentityCache,
|
|
1484
|
+
resolveEndpointUrls,
|
|
1485
|
+
resolveRepoRoot,
|
|
1486
|
+
resolveSourceMaps,
|
|
1487
|
+
runtimeFunctionId,
|
|
1488
|
+
sourceDisplayPath,
|
|
1489
|
+
sourceLocatorFromFile,
|
|
1490
|
+
sourceLocatorKey,
|
|
1491
|
+
uploadInstrumentationManifest
|
|
1241
1492
|
};
|