@blaxel/core 0.2.55-preview.17 → 0.2.56-dev.23
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/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/client/sdk.gen.js +2 -36
- package/dist/cjs/common/sentry.js +227 -131
- package/dist/cjs/common/settings.js +3 -3
- package/dist/cjs/common/version.js +6 -0
- package/dist/cjs/types/client/sdk.gen.d.ts +1 -11
- package/dist/cjs/types/client/types.gen.d.ts +0 -48
- package/dist/cjs/types/common/sentry.d.ts +1 -3
- package/dist/cjs/types/common/version.d.ts +2 -0
- package/dist/cjs/types/sandbox/client/sdk.gen.d.ts +3 -3
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/client/sdk.gen.js +2 -36
- package/dist/cjs-browser/common/sentry-browser.js +27 -0
- package/dist/cjs-browser/common/sentry.js +306 -11
- package/dist/cjs-browser/common/settings.js +3 -3
- package/dist/cjs-browser/common/version.js +6 -0
- package/dist/cjs-browser/types/client/sdk.gen.d.ts +1 -11
- package/dist/cjs-browser/types/client/types.gen.d.ts +0 -48
- package/dist/cjs-browser/types/common/sentry.d.ts +1 -3
- package/dist/cjs-browser/types/common/version.d.ts +2 -0
- package/dist/cjs-browser/types/sandbox/client/sdk.gen.d.ts +3 -3
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/client/sdk.gen.js +0 -32
- package/dist/esm/common/sentry.js +227 -98
- package/dist/esm/common/settings.js +3 -3
- package/dist/esm/common/version.js +3 -0
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/client/sdk.gen.js +0 -32
- package/dist/esm-browser/common/sentry-browser.js +22 -0
- package/dist/esm-browser/common/sentry.js +306 -11
- package/dist/esm-browser/common/settings.js +3 -3
- package/dist/esm-browser/common/version.js +3 -0
- package/package.json +2 -4
|
@@ -1206,38 +1206,6 @@ export const deleteSandboxPreviewToken = (options) => {
|
|
|
1206
1206
|
...options
|
|
1207
1207
|
});
|
|
1208
1208
|
};
|
|
1209
|
-
/**
|
|
1210
|
-
* Start Sandbox
|
|
1211
|
-
* Starts a Sandbox by name.
|
|
1212
|
-
*/
|
|
1213
|
-
export const startSandbox = (options) => {
|
|
1214
|
-
return (options.client ?? _heyApiClient).put({
|
|
1215
|
-
security: [
|
|
1216
|
-
{
|
|
1217
|
-
scheme: 'bearer',
|
|
1218
|
-
type: 'http'
|
|
1219
|
-
}
|
|
1220
|
-
],
|
|
1221
|
-
url: '/sandboxes/{sandboxName}/start',
|
|
1222
|
-
...options
|
|
1223
|
-
});
|
|
1224
|
-
};
|
|
1225
|
-
/**
|
|
1226
|
-
* Stop Sandbox
|
|
1227
|
-
* Stops a Sandbox by name.
|
|
1228
|
-
*/
|
|
1229
|
-
export const stopSandbox = (options) => {
|
|
1230
|
-
return (options.client ?? _heyApiClient).put({
|
|
1231
|
-
security: [
|
|
1232
|
-
{
|
|
1233
|
-
scheme: 'bearer',
|
|
1234
|
-
type: 'http'
|
|
1235
|
-
}
|
|
1236
|
-
],
|
|
1237
|
-
url: '/sandboxes/{sandboxName}/stop',
|
|
1238
|
-
...options
|
|
1239
|
-
});
|
|
1240
|
-
};
|
|
1241
1209
|
/**
|
|
1242
1210
|
* Get workspace service accounts
|
|
1243
1211
|
* Returns a list of all service accounts in the workspace.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { settings } from "./settings.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// Isolated Sentry client for SDK-only error tracking (doesn't interfere with user's Sentry)
|
|
5
|
-
let sentryClient = null;
|
|
2
|
+
// Lightweight Sentry client using fetch - only captures SDK errors
|
|
3
|
+
let sentryInitialized = false;
|
|
6
4
|
const capturedExceptions = new Set();
|
|
7
5
|
let handlersRegistered = false;
|
|
6
|
+
// Parsed DSN components
|
|
7
|
+
let sentryConfig = null;
|
|
8
8
|
// SDK path patterns to identify errors originating from our SDK
|
|
9
9
|
const SDK_PATTERNS = [
|
|
10
10
|
"@blaxel/",
|
|
@@ -22,9 +22,146 @@ function isFromSDK(error) {
|
|
|
22
22
|
return SDK_PATTERNS.some((pattern) => stack.includes(pattern));
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
|
|
25
|
+
* Parse a Sentry DSN into its components.
|
|
26
|
+
* DSN format: https://{public_key}@{host}/{project_id}
|
|
27
|
+
*/
|
|
28
|
+
function parseDsn(dsn) {
|
|
29
|
+
try {
|
|
30
|
+
const url = new URL(dsn);
|
|
31
|
+
const publicKey = url.username;
|
|
32
|
+
const host = url.host;
|
|
33
|
+
const projectId = url.pathname.slice(1); // Remove leading slash
|
|
34
|
+
if (!publicKey || !host || !projectId) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return { publicKey, host, projectId };
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Generate a UUID v4
|
|
45
|
+
*/
|
|
46
|
+
function generateEventId() {
|
|
47
|
+
return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
48
|
+
const r = (Math.random() * 16) | 0;
|
|
49
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
50
|
+
return v.toString(16);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Convert an Error to a Sentry event payload.
|
|
55
|
+
*/
|
|
56
|
+
function errorToSentryEvent(error) {
|
|
57
|
+
const frames = parseStackTrace(error.stack || "");
|
|
58
|
+
return {
|
|
59
|
+
event_id: generateEventId(),
|
|
60
|
+
timestamp: Date.now() / 1000,
|
|
61
|
+
platform: "javascript",
|
|
62
|
+
level: "error",
|
|
63
|
+
environment: settings.env,
|
|
64
|
+
release: `sdk-typescript@${settings.version}`,
|
|
65
|
+
tags: {
|
|
66
|
+
"blaxel.workspace": settings.workspace,
|
|
67
|
+
"blaxel.version": settings.version,
|
|
68
|
+
"blaxel.commit": settings.commit,
|
|
69
|
+
},
|
|
70
|
+
exception: {
|
|
71
|
+
values: [
|
|
72
|
+
{
|
|
73
|
+
type: error.name,
|
|
74
|
+
value: error.message,
|
|
75
|
+
stacktrace: {
|
|
76
|
+
frames,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Parse a stack trace string into Sentry-compatible frames.
|
|
85
|
+
*/
|
|
86
|
+
function parseStackTrace(stack) {
|
|
87
|
+
const lines = stack.split("\n").slice(1); // Skip first line (error message)
|
|
88
|
+
const frames = [];
|
|
89
|
+
for (const line of lines) {
|
|
90
|
+
// Match patterns like "at functionName (filename:line:col)" or "at filename:line:col"
|
|
91
|
+
const match = line.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/);
|
|
92
|
+
if (match) {
|
|
93
|
+
frames.unshift({
|
|
94
|
+
function: match[1] || "<anonymous>",
|
|
95
|
+
filename: match[2],
|
|
96
|
+
lineno: parseInt(match[3], 10),
|
|
97
|
+
colno: parseInt(match[4], 10),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return frames;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Send an event to Sentry using fetch.
|
|
105
|
+
*/
|
|
106
|
+
async function sendToSentry(event) {
|
|
107
|
+
if (!sentryConfig)
|
|
108
|
+
return;
|
|
109
|
+
const { publicKey, host, projectId } = sentryConfig;
|
|
110
|
+
const envelopeUrl = `https://${host}/api/${projectId}/envelope/`;
|
|
111
|
+
// Create envelope header
|
|
112
|
+
const envelopeHeader = JSON.stringify({
|
|
113
|
+
event_id: event.event_id,
|
|
114
|
+
sent_at: new Date().toISOString(),
|
|
115
|
+
dsn: `https://${publicKey}@${host}/${projectId}`,
|
|
116
|
+
});
|
|
117
|
+
// Create item header
|
|
118
|
+
const itemHeader = JSON.stringify({
|
|
119
|
+
type: "event",
|
|
120
|
+
content_type: "application/json",
|
|
121
|
+
});
|
|
122
|
+
// Create envelope body
|
|
123
|
+
const envelope = `${envelopeHeader}\n${itemHeader}\n${JSON.stringify(event)}`;
|
|
124
|
+
try {
|
|
125
|
+
await fetch(envelopeUrl, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
headers: {
|
|
128
|
+
"Content-Type": "application/x-sentry-envelope",
|
|
129
|
+
"X-Sentry-Auth": `Sentry sentry_version=7, sentry_client=blaxel-sdk/${settings.version}, sentry_key=${publicKey}`,
|
|
130
|
+
},
|
|
131
|
+
body: envelope,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Silently fail - error reporting should never break the SDK
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Queue for pending events
|
|
139
|
+
const pendingEvents = [];
|
|
140
|
+
let flushPromise = null;
|
|
141
|
+
/**
|
|
142
|
+
* Register browser/edge environment error handlers.
|
|
143
|
+
* Separated to isolate dynamic globalThis access.
|
|
144
|
+
*/
|
|
145
|
+
function registerBrowserHandlers() {
|
|
146
|
+
const g = globalThis;
|
|
147
|
+
if (g && typeof g.addEventListener === "function") {
|
|
148
|
+
g.addEventListener("error", (event) => {
|
|
149
|
+
const e = event;
|
|
150
|
+
if (e.error instanceof Error && isFromSDK(e.error)) {
|
|
151
|
+
captureException(e.error);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
g.addEventListener("unhandledrejection", (event) => {
|
|
155
|
+
const e = event;
|
|
156
|
+
const error = e.reason instanceof Error ? e.reason : new Error(String(e.reason));
|
|
157
|
+
if (isFromSDK(error)) {
|
|
158
|
+
captureException(error);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Initialize the lightweight Sentry client for SDK error tracking.
|
|
28
165
|
*/
|
|
29
166
|
export function initSentry() {
|
|
30
167
|
try {
|
|
@@ -36,87 +173,64 @@ export function initSentry() {
|
|
|
36
173
|
if (!dsn) {
|
|
37
174
|
return;
|
|
38
175
|
}
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Filter errors before sending - only send SDK errors
|
|
52
|
-
beforeSend(event, hint) {
|
|
53
|
-
if (event.environment !== 'dev' && event.environment !== 'prod') {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
const error = hint.originalException;
|
|
57
|
-
if (error instanceof Error) {
|
|
58
|
-
if (!isFromSDK(error)) {
|
|
59
|
-
// Drop errors that don't originate from SDK
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return event;
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
sentryClient.init();
|
|
67
|
-
// Set SDK-specific tags
|
|
68
|
-
const scope = new Sentry.Scope();
|
|
69
|
-
scope.setTag("blaxel.workspace", settings.workspace);
|
|
70
|
-
scope.setTag("blaxel.version", settings.version);
|
|
71
|
-
scope.setTag("blaxel.commit", settings.commit);
|
|
72
|
-
scope.setClient(sentryClient);
|
|
73
|
-
// Register process handlers for uncaught errors (Node.js only)
|
|
74
|
-
// Only register once to prevent memory leaks
|
|
75
|
-
if (typeof process !== "undefined" &&
|
|
76
|
-
typeof process.on === "function" &&
|
|
77
|
-
!handlersRegistered) {
|
|
176
|
+
// Parse DSN
|
|
177
|
+
sentryConfig = parseDsn(dsn);
|
|
178
|
+
if (!sentryConfig) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
// Only allow dev/prod environments
|
|
182
|
+
if (settings.env !== "dev" && settings.env !== "prod") {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
sentryInitialized = true;
|
|
186
|
+
// Register error handlers only once
|
|
187
|
+
if (!handlersRegistered) {
|
|
78
188
|
handlersRegistered = true;
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
};
|
|
96
|
-
// Unhandled rejection handler - only capture SDK errors
|
|
97
|
-
const unhandledRejectionHandler = (reason) => {
|
|
98
|
-
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
99
|
-
if (isFromSDK(error)) {
|
|
100
|
-
captureException(error);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
process.on("SIGTERM", () => signalHandler("SIGTERM"));
|
|
104
|
-
process.on("SIGINT", () => signalHandler("SIGINT"));
|
|
105
|
-
process.on("uncaughtException", uncaughtExceptionHandler);
|
|
106
|
-
process.on("unhandledRejection", unhandledRejectionHandler);
|
|
107
|
-
// Intercept console.error to capture SDK errors that are caught and logged
|
|
108
|
-
const originalConsoleError = console.error;
|
|
109
|
-
console.error = function (...args) {
|
|
110
|
-
// Call the original console.error first
|
|
111
|
-
originalConsoleError.apply(console, args);
|
|
112
|
-
// Check if any argument is an Error from SDK and capture it
|
|
113
|
-
for (const arg of args) {
|
|
114
|
-
if (arg instanceof Error && isFromSDK(arg)) {
|
|
115
|
-
captureException(arg);
|
|
116
|
-
break; // Only capture the first SDK error to avoid duplicates
|
|
189
|
+
// Node.js specific handlers
|
|
190
|
+
if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
191
|
+
// For SIGTERM/SIGINT, flush before exit
|
|
192
|
+
const signalHandler = (signal) => {
|
|
193
|
+
flushSentry(500)
|
|
194
|
+
.catch(() => {
|
|
195
|
+
// Silently fail
|
|
196
|
+
})
|
|
197
|
+
.finally(() => {
|
|
198
|
+
process.exit(signal === "SIGTERM" ? 143 : 130);
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
// Uncaught exception handler - only capture SDK errors
|
|
202
|
+
const uncaughtExceptionHandler = (error) => {
|
|
203
|
+
if (isFromSDK(error)) {
|
|
204
|
+
captureException(error);
|
|
117
205
|
}
|
|
118
|
-
}
|
|
119
|
-
|
|
206
|
+
};
|
|
207
|
+
// Unhandled rejection handler - only capture SDK errors
|
|
208
|
+
const unhandledRejectionHandler = (reason) => {
|
|
209
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
210
|
+
if (isFromSDK(error)) {
|
|
211
|
+
captureException(error);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
process.on("SIGTERM", () => signalHandler("SIGTERM"));
|
|
215
|
+
process.on("SIGINT", () => signalHandler("SIGINT"));
|
|
216
|
+
process.on("uncaughtException", uncaughtExceptionHandler);
|
|
217
|
+
process.on("unhandledRejection", unhandledRejectionHandler);
|
|
218
|
+
// Intercept console.error to capture SDK errors that are caught and logged
|
|
219
|
+
const originalConsoleError = console.error;
|
|
220
|
+
console.error = function (...args) {
|
|
221
|
+
originalConsoleError.apply(console, args);
|
|
222
|
+
for (const arg of args) {
|
|
223
|
+
if (arg instanceof Error && isFromSDK(arg)) {
|
|
224
|
+
captureException(arg);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
// Browser/Edge environment handlers
|
|
232
|
+
registerBrowserHandlers();
|
|
233
|
+
}
|
|
120
234
|
}
|
|
121
235
|
}
|
|
122
236
|
catch (error) {
|
|
@@ -127,13 +241,13 @@ export function initSentry() {
|
|
|
127
241
|
}
|
|
128
242
|
}
|
|
129
243
|
/**
|
|
130
|
-
* Capture an exception to
|
|
244
|
+
* Capture an exception to Sentry.
|
|
131
245
|
* Only errors originating from SDK code will be captured.
|
|
132
246
|
*
|
|
133
247
|
* @param error - The error to capture
|
|
134
248
|
*/
|
|
135
249
|
function captureException(error) {
|
|
136
|
-
if (
|
|
250
|
+
if (!sentryInitialized || !sentryConfig) {
|
|
137
251
|
return;
|
|
138
252
|
}
|
|
139
253
|
// Double-check that error is from SDK (defense in depth)
|
|
@@ -151,13 +265,13 @@ function captureException(error) {
|
|
|
151
265
|
if (capturedExceptions.size > 1000) {
|
|
152
266
|
capturedExceptions.clear();
|
|
153
267
|
}
|
|
154
|
-
//
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
268
|
+
// Convert error to Sentry event and queue it
|
|
269
|
+
const event = errorToSentryEvent(error);
|
|
270
|
+
pendingEvents.push(event);
|
|
271
|
+
// Send immediately (fire and forget)
|
|
272
|
+
sendToSentry(event).catch(() => {
|
|
273
|
+
// Silently fail
|
|
274
|
+
});
|
|
161
275
|
}
|
|
162
276
|
catch {
|
|
163
277
|
// Silently fail - error capturing should never break the SDK
|
|
@@ -170,19 +284,34 @@ function captureException(error) {
|
|
|
170
284
|
* @param timeout - Maximum time in milliseconds to wait for flush (default: 2000)
|
|
171
285
|
*/
|
|
172
286
|
export async function flushSentry(timeout = 2000) {
|
|
173
|
-
if (
|
|
287
|
+
if (!sentryInitialized || pendingEvents.length === 0) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// If already flushing, wait for it
|
|
291
|
+
if (flushPromise) {
|
|
292
|
+
await flushPromise;
|
|
174
293
|
return;
|
|
175
294
|
}
|
|
176
295
|
try {
|
|
177
|
-
|
|
296
|
+
// Send all pending events
|
|
297
|
+
const eventsToSend = [...pendingEvents];
|
|
298
|
+
pendingEvents.length = 0;
|
|
299
|
+
flushPromise = Promise.race([
|
|
300
|
+
Promise.all(eventsToSend.map((event) => sendToSentry(event))).then(() => { }),
|
|
301
|
+
new Promise((resolve) => setTimeout(resolve, timeout)),
|
|
302
|
+
]);
|
|
303
|
+
await flushPromise;
|
|
178
304
|
}
|
|
179
305
|
catch {
|
|
180
306
|
// Silently fail
|
|
181
307
|
}
|
|
308
|
+
finally {
|
|
309
|
+
flushPromise = null;
|
|
310
|
+
}
|
|
182
311
|
}
|
|
183
312
|
/**
|
|
184
313
|
* Check if Sentry is initialized and available.
|
|
185
314
|
*/
|
|
186
315
|
export function isSentryInitialized() {
|
|
187
|
-
return
|
|
316
|
+
return sentryInitialized;
|
|
188
317
|
}
|
|
@@ -3,9 +3,9 @@ import { authentication } from "../authentication/index.js";
|
|
|
3
3
|
import { env } from "../common/env.js";
|
|
4
4
|
import { fs, os, path } from "../common/node.js";
|
|
5
5
|
// Build info - these placeholders are replaced at build time by build:replace-imports
|
|
6
|
-
const BUILD_VERSION = "0.2.
|
|
7
|
-
const BUILD_COMMIT = "
|
|
8
|
-
const BUILD_SENTRY_DSN = "
|
|
6
|
+
const BUILD_VERSION = "0.2.56-dev.23";
|
|
7
|
+
const BUILD_COMMIT = "d54548dc9e764e57728f9eda7721d5e50f11573a";
|
|
8
|
+
const BUILD_SENTRY_DSN = "";
|
|
9
9
|
// Cache for config.yaml tracking value
|
|
10
10
|
let configTrackingValue = null;
|
|
11
11
|
let configTrackingLoaded = false;
|