@blaxel/core 0.3.7-preview.228 → 0.3.7-preview.230

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.
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const vitest_1 = require("vitest");
37
+ const mockSettings = vitest_1.vi.hoisted(() => ({
38
+ tracking: true,
39
+ sentryDsn: "https://public-key@sentry.example/123",
40
+ env: "prod",
41
+ version: "9.9.9",
42
+ commit: "abcdef0",
43
+ workspace: "private-workspace",
44
+ }));
45
+ vitest_1.vi.mock("./settings.js", () => ({ settings: mockSettings }));
46
+ const appFrameUrl = "file:///Users/customer/private-project/app.ts";
47
+ function makeSdkError(message = "secret response body for resource customer-123") {
48
+ // This helper lives under the exact @blaxel/core source root, so its real
49
+ // runtime frame exercises package-root attribution without test hooks.
50
+ return new TypeError(message);
51
+ }
52
+ function makeApplicationError() {
53
+ const sdkFrame = makeSdkError().stack
54
+ ?.split("\n")
55
+ .find((line) => line.includes("sentry.test"));
56
+ if (!sdkFrame)
57
+ throw new Error("Expected a real SDK test frame");
58
+ const error = new Error("application secret");
59
+ error.stack = [
60
+ "Error: application secret",
61
+ ` at application (${appFrameUrl}:10:20)`,
62
+ sdkFrame,
63
+ ].join("\n");
64
+ return error;
65
+ }
66
+ function makeTraversalStackError() {
67
+ const sdkFrame = makeSdkError().stack
68
+ ?.split("\n")
69
+ .find((line) => line.includes("sentry.test"));
70
+ if (!sdkFrame)
71
+ throw new Error("Expected a real SDK test frame");
72
+ const forgedFrame = sdkFrame.replace(/([\\/])src\1common\1sentry\.test\.ts/, "$1src$1..$1private$1customer-secret.ts");
73
+ if (forgedFrame === sdkFrame)
74
+ throw new Error("Expected to forge the SDK frame path");
75
+ const error = new Error("application secret");
76
+ error.stack = ["Error: application secret", forgedFrame].join("\n");
77
+ return error;
78
+ }
79
+ function eventFromFetch(fetchMock) {
80
+ const request = fetchMock.mock.calls[0]?.[1];
81
+ if (typeof request?.body !== "string")
82
+ throw new Error("Expected a string Sentry envelope");
83
+ return JSON.parse(request.body.split("\n")[2]);
84
+ }
85
+ function emitUncaughtExceptionMonitor(error) {
86
+ const processWithStringEvents = process;
87
+ return processWithStringEvents.emit("uncaughtExceptionMonitor", error, "uncaughtException");
88
+ }
89
+ (0, vitest_1.describe)("SDK Sentry boundary", () => {
90
+ let originalMonitorListeners;
91
+ (0, vitest_1.beforeEach)(() => {
92
+ vitest_1.vi.resetModules();
93
+ mockSettings.tracking = true;
94
+ mockSettings.sentryDsn = "https://public-key@sentry.example/123";
95
+ mockSettings.env = "prod";
96
+ originalMonitorListeners = process.listeners("uncaughtExceptionMonitor");
97
+ });
98
+ (0, vitest_1.afterEach)(() => {
99
+ vitest_1.vi.unstubAllGlobals();
100
+ for (const listener of process.listeners("uncaughtExceptionMonitor")) {
101
+ if (!originalMonitorListeners.includes(listener)) {
102
+ process.removeListener("uncaughtExceptionMonitor", listener);
103
+ }
104
+ }
105
+ vitest_1.vi.restoreAllMocks();
106
+ });
107
+ (0, vitest_1.it)("does not replace console.error or report caught errors", async () => {
108
+ const fetchMock = vitest_1.vi.fn().mockResolvedValue({ ok: true });
109
+ const hostConsoleError = vitest_1.vi.fn();
110
+ vitest_1.vi.stubGlobal("fetch", fetchMock);
111
+ vitest_1.vi.spyOn(console, "error").mockImplementation(hostConsoleError);
112
+ const { initSentry } = await Promise.resolve().then(() => __importStar(require("./sentry.js")));
113
+ initSentry();
114
+ const installedConsoleError = console.error;
115
+ console.error(makeSdkError());
116
+ (0, vitest_1.expect)(console.error).toBe(installedConsoleError);
117
+ (0, vitest_1.expect)(hostConsoleError).toHaveBeenCalledOnce();
118
+ (0, vitest_1.expect)(fetchMock).not.toHaveBeenCalled();
119
+ });
120
+ (0, vitest_1.it)("composes with host handlers and reports one sanitized SDK-owned event", async () => {
121
+ const fetchMock = vitest_1.vi.fn().mockResolvedValue({ ok: true });
122
+ const hostMonitor = vitest_1.vi.fn();
123
+ const hostRejection = vitest_1.vi.fn();
124
+ vitest_1.vi.stubGlobal("fetch", fetchMock);
125
+ process.on("uncaughtExceptionMonitor", hostMonitor);
126
+ process.on("unhandledRejection", hostRejection);
127
+ const rejectionListeners = process.listeners("unhandledRejection");
128
+ try {
129
+ const { flushSentry, initSentry } = await Promise.resolve().then(() => __importStar(require("./sentry.js")));
130
+ initSentry();
131
+ const error = makeSdkError();
132
+ emitUncaughtExceptionMonitor(error);
133
+ emitUncaughtExceptionMonitor(error);
134
+ await flushSentry();
135
+ (0, vitest_1.expect)(hostMonitor).toHaveBeenCalledTimes(2);
136
+ (0, vitest_1.expect)(process.listeners("unhandledRejection")).toEqual(rejectionListeners);
137
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledOnce();
138
+ const event = eventFromFetch(fetchMock);
139
+ (0, vitest_1.expect)(event.exception.values[0]).toMatchObject({
140
+ type: "TypeError",
141
+ value: "Unhandled SDK exception",
142
+ });
143
+ (0, vitest_1.expect)(event.tags).toEqual({
144
+ "blaxel.version": "9.9.9",
145
+ "blaxel.commit": "abcdef0",
146
+ "blaxel.error_source": "unhandled-sdk-exception",
147
+ });
148
+ (0, vitest_1.expect)(event.tags).not.toHaveProperty("blaxel.workspace");
149
+ const frames = event.exception.values[0].stacktrace.frames;
150
+ (0, vitest_1.expect)(frames.length).toBeGreaterThan(0);
151
+ for (const frame of frames) {
152
+ (0, vitest_1.expect)(frame.filename).toBe("@blaxel/core/src/common/sentry.test.ts");
153
+ }
154
+ const serialized = JSON.stringify(event);
155
+ (0, vitest_1.expect)(serialized).not.toContain("secret response body");
156
+ (0, vitest_1.expect)(serialized).not.toContain("customer-123");
157
+ (0, vitest_1.expect)(serialized).not.toContain("private-workspace");
158
+ (0, vitest_1.expect)(serialized).not.toContain("/Users/customer");
159
+ }
160
+ finally {
161
+ process.removeListener("uncaughtExceptionMonitor", hostMonitor);
162
+ process.removeListener("unhandledRejection", hostRejection);
163
+ }
164
+ });
165
+ (0, vitest_1.it)("does not attribute an application-owned exception with a later SDK frame", async () => {
166
+ const fetchMock = vitest_1.vi.fn().mockResolvedValue({ ok: true });
167
+ vitest_1.vi.stubGlobal("fetch", fetchMock);
168
+ const { initSentry } = await Promise.resolve().then(() => __importStar(require("./sentry.js")));
169
+ initSentry();
170
+ emitUncaughtExceptionMonitor(makeApplicationError());
171
+ (0, vitest_1.expect)(fetchMock).not.toHaveBeenCalled();
172
+ });
173
+ (0, vitest_1.it)("rejects a forged owned path containing parent traversal", async () => {
174
+ const fetchMock = vitest_1.vi.fn().mockResolvedValue({ ok: true });
175
+ vitest_1.vi.stubGlobal("fetch", fetchMock);
176
+ const { initSentry } = await Promise.resolve().then(() => __importStar(require("./sentry.js")));
177
+ initSentry();
178
+ emitUncaughtExceptionMonitor(makeTraversalStackError());
179
+ (0, vitest_1.expect)(fetchMock).not.toHaveBeenCalled();
180
+ });
181
+ (0, vitest_1.it)("contains delivery setup failures without creating an unhandled rejection", async () => {
182
+ const fetchMock = vitest_1.vi.fn().mockResolvedValue({ ok: true });
183
+ vitest_1.vi.stubGlobal("fetch", fetchMock);
184
+ vitest_1.vi.stubGlobal("AbortController", class FailingAbortController {
185
+ constructor() {
186
+ throw new Error("host AbortController failure");
187
+ }
188
+ });
189
+ const { flushSentry, initSentry } = await Promise.resolve().then(() => __importStar(require("./sentry.js")));
190
+ initSentry();
191
+ emitUncaughtExceptionMonitor(makeSdkError());
192
+ await flushSentry();
193
+ (0, vitest_1.expect)(fetchMock).not.toHaveBeenCalled();
194
+ });
195
+ (0, vitest_1.it)("composes with browser handlers and ignores primitive rejections", async () => {
196
+ const fetchMock = vitest_1.vi.fn().mockResolvedValue({ ok: true });
197
+ const listeners = new Map();
198
+ const addEventListener = vitest_1.vi.fn((type, listener) => {
199
+ listeners.set(type, listener);
200
+ });
201
+ vitest_1.vi.stubGlobal("fetch", fetchMock);
202
+ vitest_1.vi.stubGlobal("process", undefined);
203
+ vitest_1.vi.stubGlobal("addEventListener", addEventListener);
204
+ const { flushSentry, initSentry } = await Promise.resolve().then(() => __importStar(require("./sentry.js")));
205
+ initSentry();
206
+ (0, vitest_1.expect)(addEventListener).toHaveBeenCalledWith("error", vitest_1.expect.any(Function));
207
+ (0, vitest_1.expect)(addEventListener).toHaveBeenCalledWith("unhandledrejection", vitest_1.expect.any(Function));
208
+ listeners.get("unhandledrejection")?.({ reason: "raw rejection secret" });
209
+ listeners.get("error")?.({ error: makeSdkError() });
210
+ await flushSentry();
211
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledOnce();
212
+ (0, vitest_1.expect)(JSON.stringify(eventFromFetch(fetchMock))).not.toContain("raw rejection secret");
213
+ });
214
+ (0, vitest_1.it)("does not initialize when tracking is disabled", async () => {
215
+ const fetchMock = vitest_1.vi.fn().mockResolvedValue({ ok: true });
216
+ vitest_1.vi.stubGlobal("fetch", fetchMock);
217
+ mockSettings.tracking = false;
218
+ const { initSentry, isSentryInitialized } = await Promise.resolve().then(() => __importStar(require("./sentry.js")));
219
+ initSentry();
220
+ emitUncaughtExceptionMonitor(makeSdkError());
221
+ (0, vitest_1.expect)(isSentryInitialized()).toBe(false);
222
+ (0, vitest_1.expect)(fetchMock).not.toHaveBeenCalled();
223
+ });
224
+ });
@@ -30,8 +30,8 @@ function missingCredentialsMessage() {
30
30
  return "No Blaxel credentials found. Set the BL_API_KEY and BL_WORKSPACE environment variables, or run `bl login`.";
31
31
  }
32
32
  // Build info - these placeholders are replaced at build time by build:replace-imports
33
- const BUILD_VERSION = "0.3.7-preview.228";
34
- const BUILD_COMMIT = "f36e6060f2d58224e93de8487dcc1dcc9a485c28";
33
+ const BUILD_VERSION = "0.3.7-preview.230";
34
+ const BUILD_COMMIT = "110dd5983dfa658153d44e36dc9b02ffe6a90d77";
35
35
  const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
36
36
  const BLAXEL_API_VERSION = "2026-04-28";
37
37
  // Bun < 1.3.11 never sends connection-level WINDOW_UPDATE: the pooled h2
@@ -7,8 +7,8 @@
7
7
  * populates `BL_ENV` if the user has a dev workspace configured).
8
8
  * - Re-apply the control-plane and sandbox clients' `baseUrl` so they
9
9
  * target the now-env-aware endpoint.
10
- * - Initialize the lightweight Sentry client (registers
11
- * `uncaughtExceptionMonitor` and patches `console.error` in Node).
10
+ * - Initialize the lightweight Sentry client (registers a composable
11
+ * `uncaughtExceptionMonitor` handler in Node).
12
12
  * - Pre-warm the edge H2 connection pool for `settings.region`.
13
13
  *
14
14
  * These used to run at module load, but that meant `import "@blaxel/core"`
@@ -1,15 +1,11 @@
1
1
  /**
2
- * Initialize the lightweight Sentry client for SDK error tracking.
2
+ * Initialize opt-in SDK error tracking. Registration composes with host global
3
+ * handlers and never replaces console, process, or browser callbacks.
3
4
  */
4
5
  export declare function initSentry(): void;
5
6
  /**
6
- * Flush pending Sentry events.
7
- * This should be called before the process exits to ensure all events are sent.
8
- *
9
- * @param timeout - Maximum time in milliseconds to wait for flush (default: 2000)
7
+ * Await only deliveries already in flight, bounded by the supplied timeout.
8
+ * Events are sent exactly once; flush never re-enqueues them.
10
9
  */
11
10
  export declare function flushSentry(timeout?: number): Promise<void>;
12
- /**
13
- * Check if Sentry is initialized and available.
14
- */
15
11
  export declare function isSentryInitialized(): boolean;
@@ -0,0 +1 @@
1
+ export {};