@superblocksteam/sdk 2.0.146 → 2.0.147

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.
Files changed (36) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/dev-utils/dev-server-metrics.d.mts +9 -0
  3. package/dist/dev-utils/dev-server-metrics.d.mts.map +1 -1
  4. package/dist/dev-utils/dev-server-metrics.mjs +14 -0
  5. package/dist/dev-utils/dev-server-metrics.mjs.map +1 -1
  6. package/dist/dev-utils/dev-server.d.mts +28 -0
  7. package/dist/dev-utils/dev-server.d.mts.map +1 -1
  8. package/dist/dev-utils/dev-server.mjs +55 -20
  9. package/dist/dev-utils/dev-server.mjs.map +1 -1
  10. package/dist/dev-utils/dev-server.process-errors.test.d.mts +2 -0
  11. package/dist/dev-utils/dev-server.process-errors.test.d.mts.map +1 -0
  12. package/dist/dev-utils/dev-server.process-errors.test.mjs +68 -0
  13. package/dist/dev-utils/dev-server.process-errors.test.mjs.map +1 -0
  14. package/dist/dev-utils/fatal-exit.d.mts +13 -0
  15. package/dist/dev-utils/fatal-exit.d.mts.map +1 -1
  16. package/dist/dev-utils/fatal-exit.mjs +103 -0
  17. package/dist/dev-utils/fatal-exit.mjs.map +1 -1
  18. package/dist/dev-utils/fatal-exit.test.mjs +138 -1
  19. package/dist/dev-utils/fatal-exit.test.mjs.map +1 -1
  20. package/dist/dev-utils/recoverable-build-error.d.mts +31 -0
  21. package/dist/dev-utils/recoverable-build-error.d.mts.map +1 -0
  22. package/dist/dev-utils/recoverable-build-error.mjs +92 -0
  23. package/dist/dev-utils/recoverable-build-error.mjs.map +1 -0
  24. package/dist/dev-utils/recoverable-build-error.test.d.mts +2 -0
  25. package/dist/dev-utils/recoverable-build-error.test.d.mts.map +1 -0
  26. package/dist/dev-utils/recoverable-build-error.test.mjs +176 -0
  27. package/dist/dev-utils/recoverable-build-error.test.mjs.map +1 -0
  28. package/package.json +6 -6
  29. package/src/dev-utils/dev-server-metrics.mts +19 -0
  30. package/src/dev-utils/dev-server.mts +85 -31
  31. package/src/dev-utils/dev-server.process-errors.test.mts +103 -0
  32. package/src/dev-utils/fatal-exit.mts +128 -0
  33. package/src/dev-utils/fatal-exit.test.mts +202 -0
  34. package/src/dev-utils/recoverable-build-error.mts +151 -0
  35. package/src/dev-utils/recoverable-build-error.test.mts +236 -0
  36. package/tsconfig.tsbuildinfo +1 -1
@@ -124,4 +124,107 @@ export function isRecoverableShutdownError(reason) {
124
124
  }
125
125
  return RECOVERABLE_SHUTDOWN_CODES.has(code);
126
126
  }
127
+ // Every alternative here has to be specific to Vite/esbuild output. A false
128
+ // positive marks a genuine crash recoverable, which keeps a broken pod alive
129
+ // and suppresses the `dev_server_fatal_exit` signal we alert on. That is why
130
+ // `Could not resolve` requires esbuild's quoted specifier
131
+ // (`Could not resolve "buffer/"`): the bare phrase also appears in package
132
+ // manager output ("Could not resolve dependency:") and DNS failures
133
+ // ("could not resolve host").
134
+ const VITE_BUILD_MESSAGE_MARKERS = /Could not resolve ["']|Build failed with \d+ error|Pre-transform error|Failed to resolve import|Transform failed/i;
135
+ /**
136
+ * An esbuild `BuildFailure` carries `errors: Message[]`, where each `Message`
137
+ * has a `text` string. Requiring that inner shape keeps the check specific:
138
+ * a bare `errors` array is far too common to treat as proof of a build failure.
139
+ */
140
+ function hasEsbuildBuildFailureShape(reason) {
141
+ const errors = reason.errors;
142
+ return (Array.isArray(errors) &&
143
+ errors.length > 0 &&
144
+ errors.every((entry) => typeof entry === "object" &&
145
+ entry !== null &&
146
+ typeof entry.text === "string"));
147
+ }
148
+ /**
149
+ * Vite/esbuild build failures that should keep the dev-server process alive and
150
+ * surface through `devServerStatus.serverErrors` instead of `process.exit(1)`.
151
+ *
152
+ * Deliberately narrow: anything not positively identified as a build failure
153
+ * must stay fatal, so genuine crashes still exit and emit
154
+ * `dev_server_fatal_exit`. Rollup/Vite plugin errors are matched by their
155
+ * message rather than by structural fields like `id`/`plugin`/`loc`, which are
156
+ * far too common to be evidence of a build failure on their own.
157
+ */
158
+ export function isViteBuildError(reason) {
159
+ if (typeof reason !== "object" || reason === null) {
160
+ if (typeof reason === "string") {
161
+ return VITE_BUILD_MESSAGE_MARKERS.test(reason);
162
+ }
163
+ return false;
164
+ }
165
+ if (hasEsbuildBuildFailureShape(reason)) {
166
+ return true;
167
+ }
168
+ const message = reason instanceof Error
169
+ ? reason.message
170
+ : typeof reason.message === "string"
171
+ ? reason.message
172
+ : undefined;
173
+ if (message && VITE_BUILD_MESSAGE_MARKERS.test(message)) {
174
+ return true;
175
+ }
176
+ return false;
177
+ }
178
+ /** Pull usable esbuild `Message.text` strings off any object-shaped reason. */
179
+ function extractEsbuildErrorTexts(reason) {
180
+ if (typeof reason !== "object" || reason === null) {
181
+ return [];
182
+ }
183
+ const errors = reason.errors;
184
+ if (!Array.isArray(errors)) {
185
+ return [];
186
+ }
187
+ // Read defensively. `isViteBuildError` can classify on a message marker alone
188
+ // without re-validating the array, so entries here may be anything. This
189
+ // function runs inside the `uncaughtException` / `unhandledRejection`
190
+ // listeners before the fatal-exit line is written, and a throw there tears the
191
+ // process down immediately.
192
+ return errors
193
+ .map((entry) => typeof entry === "object" &&
194
+ entry !== null &&
195
+ typeof entry.text === "string"
196
+ ? entry.text.trim()
197
+ : "")
198
+ .filter((text) => text.length > 0);
199
+ }
200
+ /** Best-effort text extraction for Vite/esbuild rejection reasons. */
201
+ export function extractViteBuildErrorMessage(reason) {
202
+ if (typeof reason === "string") {
203
+ return reason;
204
+ }
205
+ // Real optimizeDeps rejections are esbuild `BuildFailure` values — Error
206
+ // subclasses with `errors: Message[]`. Prefer those structured texts over the
207
+ // stack so the overlay and Clark see `Could not resolve "buffer/"`, not a
208
+ // noisy stack trace.
209
+ const esbuildTexts = extractEsbuildErrorTexts(reason);
210
+ if (esbuildTexts.length > 0) {
211
+ return esbuildTexts.join("\n");
212
+ }
213
+ if (reason instanceof Error) {
214
+ return reason.stack ?? reason.message;
215
+ }
216
+ if (typeof reason === "object" && reason !== null) {
217
+ const candidate = reason;
218
+ if (typeof candidate.message === "string" && candidate.message.trim()) {
219
+ return candidate.message;
220
+ }
221
+ try {
222
+ return JSON.stringify(reason);
223
+ }
224
+ catch {
225
+ return String(reason);
226
+ }
227
+ }
228
+ return String(reason);
229
+ }
127
230
  //# sourceMappingURL=fatal-exit.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"fatal-exit.mjs","sourceRoot":"","sources":["../../src/dev-utils/fatal-exit.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAoBH,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAiBxD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAyB;IACnD,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC;QACZ,KAAK,GAAG,CAAC;QACT,KAAK,KAAK,CAAC;QACX,KAAK,IAAI;YACP,OAAO,IAAI,CAAC;QACd;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAuB;IACvD,OAAO;QACL,uBAAuB;QACvB,SAAS,gBAAgB,EAAE;QAC3B,WAAW,MAAM,CAAC,OAAO,EAAE;QAC3B,aAAa,MAAM,CAAC,QAAQ,IAAI,MAAM,EAAE;QACxC,UAAU,MAAM,CAAC,MAAM,IAAI,MAAM,EAAE;QACnC,WAAW,MAAM,CAAC,MAAM,EAAE;KAC3B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAkB;IAC1D,CAAC;IACD,EAAE;IACF,mBAAmB;CACpB,CAAC,CAAC;AAEH,MAAM,UAAU,0BAA0B,CAAC,MAAe;IACxD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,EAAE,CAAC;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,GAAG,MAA2B,CAAC;IAC7C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACzD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,0BAA0B,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"fatal-exit.mjs","sourceRoot":"","sources":["../../src/dev-utils/fatal-exit.mts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { buildFatalExitLog, FATAL_EXIT_EVENT, isRecoverableShutdownError, parseIsWarm, } from "./fatal-exit.mjs";
2
+ import { buildFatalExitLog, FATAL_EXIT_EVENT, extractViteBuildErrorMessage, isRecoverableShutdownError, isViteBuildError, parseIsWarm, } from "./fatal-exit.mjs";
3
3
  describe("parseIsWarm", () => {
4
4
  it("treats the same truthy spellings as the entrypoint shell", () => {
5
5
  for (const value of ["true", "1", "yes", "on", "TRUE", "Yes", " on "]) {
@@ -102,4 +102,141 @@ describe("isRecoverableShutdownError", () => {
102
102
  expect(isRecoverableShutdownError({ code: "ERR_OTHER" })).toBe(false);
103
103
  });
104
104
  });
105
+ describe("isViteBuildError", () => {
106
+ it("matches esbuild BuildFailure shapes", () => {
107
+ expect(isViteBuildError({
108
+ errors: [
109
+ {
110
+ text: 'Could not resolve "buffer/"',
111
+ },
112
+ ],
113
+ warnings: [],
114
+ })).toBe(true);
115
+ });
116
+ it("matches Rollup/Vite plugin errors via their message", () => {
117
+ expect(isViteBuildError(Object.assign(new Error('Failed to resolve import "buffer/" from "helpers.js"'), {
118
+ frame: "5: import buffer from 'buffer/'",
119
+ id: "/app/node_modules/plotly.js/src/traces/image/helpers.js",
120
+ loc: { column: 0, file: "helpers.js", line: 5 },
121
+ plugin: "vite:import-analysis",
122
+ }))).toBe(true);
123
+ });
124
+ it("does not match non-build rejections that merely carry Rollup-ish fields", () => {
125
+ // `id` is ubiquitous in this process (RuntimeErrorData, ConsoleLogEntry,
126
+ // socket payloads), and `typeof null === "object"` makes a bare `loc` check
127
+ // match too. Structural fields alone must never mark a rejection
128
+ // recoverable: that would swallow genuine crashes and suppress the
129
+ // dev_server_fatal_exit signal we alert on.
130
+ expect(isViteBuildError({ id: "abc123", message: "boom" })).toBe(false);
131
+ expect(isViteBuildError({ loc: null })).toBe(false);
132
+ expect(isViteBuildError({ plugin: "some-plugin" })).toBe(false);
133
+ expect(isViteBuildError({ frame: "..." })).toBe(false);
134
+ });
135
+ it("ignores esbuild-ish objects whose errors array is not esbuild messages", () => {
136
+ expect(isViteBuildError({ errors: [] })).toBe(false);
137
+ expect(isViteBuildError({ errors: ["nope"] })).toBe(false);
138
+ });
139
+ it("keeps AggregateError fatal even though it carries an errors array", () => {
140
+ // The only thing separating an AggregateError from an esbuild BuildFailure
141
+ // is the `typeof entry.text === "string"` requirement. Without this test,
142
+ // simplifying that shape check would silently reclassify every
143
+ // `Promise.any` rejection and undici `AggregateError [ECONNREFUSED]` as a
144
+ // recoverable build error — keeping a genuinely broken pod alive and
145
+ // dropping it out of the fatal-exit population we alert on.
146
+ expect(isViteBuildError(new AggregateError([
147
+ new Error("connect ECONNREFUSED"),
148
+ new Error("connect ECONNREFUSED"),
149
+ ], "All promises were rejected"))).toBe(false);
150
+ expect(isViteBuildError({
151
+ errors: [new Error("connect ECONNREFUSED 127.0.0.1:5432")],
152
+ })).toBe(false);
153
+ });
154
+ it("matches known Vite/esbuild message markers", () => {
155
+ expect(isViteBuildError('Build failed with 1 error:\nCould not resolve "buffer/"')).toBe(true);
156
+ expect(isViteBuildError("Pre-transform error: Failed to resolve import")).toBe(true);
157
+ });
158
+ it("does not match plain runtime errors", () => {
159
+ expect(isViteBuildError(new TypeError("Cannot read property 'x'"))).toBe(false);
160
+ expect(isViteBuildError(undefined)).toBe(false);
161
+ });
162
+ it("requires esbuild's quoted specifier before trusting 'Could not resolve'", () => {
163
+ // esbuild always quotes the specifier (`Could not resolve "buffer/"`).
164
+ // Matching the bare phrase pulls in package-manager and DNS failures, and
165
+ // calling those recoverable would keep a genuinely broken pod alive while
166
+ // suppressing the dev_server_fatal_exit signal we alert on.
167
+ expect(isViteBuildError(new Error("Could not resolve dependency: peer react@^18.0.0"))).toBe(false);
168
+ expect(isViteBuildError("getaddrinfo ENOTFOUND: could not resolve host cdn.example.com")).toBe(false);
169
+ expect(isViteBuildError('Could not resolve "buffer/"')).toBe(true);
170
+ });
171
+ });
172
+ describe("extractViteBuildErrorMessage", () => {
173
+ it("joins esbuild error texts", () => {
174
+ expect(extractViteBuildErrorMessage({
175
+ errors: [{ text: 'Could not resolve "buffer/"' }],
176
+ })).toBe('Could not resolve "buffer/"');
177
+ });
178
+ it("prefers errors[].text over stack for esbuild BuildFailure-shaped Errors", () => {
179
+ // Real optimizeDeps rejections are Error subclasses carrying `errors:
180
+ // Message[]`. The old `instanceof Error` early return sent the stack to the
181
+ // overlay instead of the resolve/transform line Clark needs.
182
+ class FakeBuildFailure extends Error {
183
+ errors;
184
+ constructor(message, errors) {
185
+ super(message);
186
+ this.errors = errors;
187
+ }
188
+ }
189
+ const reason = new FakeBuildFailure("Build failed with 1 error", [
190
+ { text: 'Could not resolve "buffer/"' },
191
+ ]);
192
+ expect(extractViteBuildErrorMessage(reason)).toBe('Could not resolve "buffer/"');
193
+ expect(extractViteBuildErrorMessage(reason)).not.toMatch(/^\s*at /m);
194
+ });
195
+ it("still uses stack for plain Errors without structured texts", () => {
196
+ const err = new Error("something broke");
197
+ expect(extractViteBuildErrorMessage(err)).toContain("something broke");
198
+ });
199
+ // This function runs inside the `uncaughtException` / `unhandledRejection`
200
+ // listeners, before the fatal-exit line is written. A throw here does not
201
+ // degrade to the fatal path — Node tears the process down immediately, so the
202
+ // real error never reaches the logs and the misleading TypeError shows up in
203
+ // its place. `isViteBuildError` accepts a message-marker match without
204
+ // re-validating `errors`, so a malformed `errors` array does reach us.
205
+ it("survives an errors array that is not esbuild messages", () => {
206
+ for (const errors of [
207
+ [{ text: 42 }],
208
+ [null],
209
+ [undefined],
210
+ ["plain string"],
211
+ [{ text: { nested: true } }],
212
+ ]) {
213
+ expect(() => extractViteBuildErrorMessage({
214
+ errors,
215
+ message: "Transform failed with 1 error",
216
+ })).not.toThrow();
217
+ }
218
+ });
219
+ it("falls back to the message when no entry carries usable text", () => {
220
+ // Returning "" here would be classified, counted, and then rendered
221
+ // nowhere: the UI dispatches `loadError(rawError)` and an empty string is
222
+ // falsy, so the overlay never appears.
223
+ expect(extractViteBuildErrorMessage({
224
+ errors: [{}],
225
+ message: "Build failed with 1 error",
226
+ })).toBe("Build failed with 1 error");
227
+ expect(extractViteBuildErrorMessage({
228
+ errors: [{ text: 42 }],
229
+ message: "Transform failed with 1 error",
230
+ })).toBe("Transform failed with 1 error");
231
+ });
232
+ it("still prefers usable entry text over the outer message", () => {
233
+ expect(extractViteBuildErrorMessage({
234
+ errors: [{ text: 'Could not resolve "buffer/"' }, { text: 42 }],
235
+ message: "Build failed with 1 error",
236
+ })).toBe('Could not resolve "buffer/"');
237
+ });
238
+ it("never returns an empty string for an object with no usable text at all", () => {
239
+ expect(extractViteBuildErrorMessage({ errors: [{}] })).not.toBe("");
240
+ });
241
+ });
105
242
  //# sourceMappingURL=fatal-exit.test.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"fatal-exit.test.mjs","sourceRoot":"","sources":["../../src/dev-utils/fatal-exit.test.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fatal-exit.test.mjs","sourceRoot":"","sources":["../../src/dev-utils/fatal-exit.test.mts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { DevServerBuildError, ServerError } from "@superblocksteam/library-shared/types";
2
+ /** Maximum build errors retained on `devServerStatus.serverErrors`. */
3
+ export declare const MAX_DEV_SERVER_BUILD_ERRORS = 5;
4
+ type DevServerStatus = {
5
+ serverErrors: ServerError[];
6
+ };
7
+ export declare function buildDevServerBuildErrorDedupeKey(entry: Pick<DevServerBuildError, "category" | "unresolvedSpecifier">): string;
8
+ export declare function buildDevServerBuildError(reason: unknown): DevServerBuildError;
9
+ export declare function upsertDevServerBuildError(devServerStatus: DevServerStatus, entry: DevServerBuildError): void;
10
+ type RecoverableBuildErrorHandler = "uncaughtException" | "unhandledRejection";
11
+ type RecoverableBuildErrorMetrics = {
12
+ recordRecoverableBuildError: (labels: {
13
+ category: string;
14
+ handler: RecoverableBuildErrorHandler;
15
+ }) => void;
16
+ };
17
+ /**
18
+ * When a Vite/esbuild build error reaches the process-level handler, keep the
19
+ * pod alive and record it for the editor's `/_sb_connect` readiness path.
20
+ */
21
+ export declare function handleRecoverableBuildError(params: {
22
+ devServerStatus?: DevServerStatus;
23
+ devServerMetrics: RecoverableBuildErrorMetrics;
24
+ handler: RecoverableBuildErrorHandler;
25
+ logger: {
26
+ warn: (message: string, meta?: unknown) => void;
27
+ };
28
+ reason: unknown;
29
+ }): boolean;
30
+ export {};
31
+ //# sourceMappingURL=recoverable-build-error.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recoverable-build-error.d.mts","sourceRoot":"","sources":["../../src/dev-utils/recoverable-build-error.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EACZ,MAAM,uCAAuC,CAAC;AAW/C,uEAAuE;AACvE,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,KAAK,eAAe,GAAG;IAAE,YAAY,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC;AAEvD,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,qBAAqB,CAAC,GACnE,MAAM,CAER;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,OAAO,GAAG,mBAAmB,CAwB7E;AAED,wBAAgB,yBAAyB,CACvC,eAAe,EAAE,eAAe,EAChC,KAAK,EAAE,mBAAmB,GACzB,IAAI,CA4BN;AAED,KAAK,4BAA4B,GAAG,mBAAmB,GAAG,oBAAoB,CAAC;AAE/E,KAAK,4BAA4B,GAAG;IAClC,2BAA2B,EAAE,CAAC,MAAM,EAAE;QACpC,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,4BAA4B,CAAC;KACvC,KAAK,IAAI,CAAC;CACZ,CAAC;AAEF;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,EAAE,4BAA4B,CAAC;IAC/C,OAAO,EAAE,4BAA4B,CAAC;IACtC,MAAM,EAAE;QACN,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;KACjD,CAAC;IACF,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CA6CV"}
@@ -0,0 +1,92 @@
1
+ import { extractViteBuildErrorMessage, isViteBuildError, } from "./fatal-exit.mjs";
2
+ import { diagnoseViteBuildLogMessage, truncateViteRawLog, } from "./vite-dev-server-diagnostics.mjs";
3
+ /** Maximum build errors retained on `devServerStatus.serverErrors`. */
4
+ export const MAX_DEV_SERVER_BUILD_ERRORS = 5;
5
+ export function buildDevServerBuildErrorDedupeKey(entry) {
6
+ return `${entry.category}:${entry.unresolvedSpecifier ?? ""}`;
7
+ }
8
+ export function buildDevServerBuildError(reason) {
9
+ const fullMessage = extractViteBuildErrorMessage(reason);
10
+ // Classify against the full text so a specifier past the cap is still found,
11
+ // but store the capped copy: this string is persisted on `serverErrors`,
12
+ // logged, sent over `/_sb_connect`, and rendered in the editor overlay.
13
+ const diagnostic = diagnoseViteBuildLogMessage(fullMessage);
14
+ const rawError = truncateViteRawLog(fullMessage);
15
+ if (diagnostic.category === "module_resolve") {
16
+ return {
17
+ type: "dev-server/build",
18
+ timestamp: new Date().toISOString(),
19
+ category: diagnostic.category,
20
+ unresolvedSpecifier: diagnostic.unresolvedSpecifier,
21
+ rawError,
22
+ };
23
+ }
24
+ return {
25
+ type: "dev-server/build",
26
+ timestamp: new Date().toISOString(),
27
+ category: diagnostic.category,
28
+ rawError,
29
+ };
30
+ }
31
+ export function upsertDevServerBuildError(devServerStatus, entry) {
32
+ const dedupeKey = buildDevServerBuildErrorDedupeKey(entry);
33
+ devServerStatus.serverErrors = devServerStatus.serverErrors.filter((existing) => existing.type !== "dev-server/build" ||
34
+ buildDevServerBuildErrorDedupeKey(existing) !== dedupeKey);
35
+ devServerStatus.serverErrors.push(entry);
36
+ // Cap build errors only. `serverErrors` is shared with the startup path,
37
+ // which pushes a dependency-install/upgrade entry once per pod before Vite
38
+ // serves anything; those sit at the front and can never be re-reported, so a
39
+ // whole-array cap keeping the newest entries would evict them first.
40
+ let excess = devServerStatus.serverErrors.filter((existing) => existing.type === "dev-server/build").length - MAX_DEV_SERVER_BUILD_ERRORS;
41
+ if (excess > 0) {
42
+ devServerStatus.serverErrors = devServerStatus.serverErrors.filter((existing) => {
43
+ if (excess > 0 && existing.type === "dev-server/build") {
44
+ excess -= 1;
45
+ return false;
46
+ }
47
+ return true;
48
+ });
49
+ }
50
+ }
51
+ /**
52
+ * When a Vite/esbuild build error reaches the process-level handler, keep the
53
+ * pod alive and record it for the editor's `/_sb_connect` readiness path.
54
+ */
55
+ export function handleRecoverableBuildError(params) {
56
+ // Never throw. This runs inside the process-level `uncaughtException` /
57
+ // `unhandledRejection` listeners, ahead of the caller's `fatalExitHandled`
58
+ // latch and its fatal-exit log line. Node does not degrade a throw from those
59
+ // listeners to the normal fatal path — it tears the process down immediately,
60
+ // losing the real error, the metric, and `gracefulShutdown`, and leaving a
61
+ // misleading TypeError in their place. Returning `false` instead routes the
62
+ // caller to its fatal path, which logs the original reason.
63
+ //
64
+ // `isViteBuildError` classifies on a message marker without re-validating
65
+ // `errors`, so a malformed array reaches the extraction below; that specific
66
+ // hazard is handled in `extractViteBuildErrorMessage`, and this guard covers
67
+ // whatever else the diagnostic, upsert, and metric steps can raise.
68
+ try {
69
+ if (!isViteBuildError(params.reason)) {
70
+ return false;
71
+ }
72
+ const entry = buildDevServerBuildError(params.reason);
73
+ if (params.devServerStatus) {
74
+ upsertDevServerBuildError(params.devServerStatus, entry);
75
+ }
76
+ params.devServerMetrics.recordRecoverableBuildError({
77
+ category: entry.category,
78
+ handler: params.handler,
79
+ });
80
+ params.logger.warn(`Ignoring recoverable Vite build error (${params.handler})`, { error: entry });
81
+ return true;
82
+ }
83
+ catch (recordError) {
84
+ // Falling through to the fatal path is the safe direction: the pod exits
85
+ // and the caller logs the original reason. Note the bookkeeping failure
86
+ // separately so a malformed-reason bug stays diagnosable instead of looking
87
+ // like a plain crash.
88
+ params.logger.warn(`Failed to record recoverable Vite build error (${params.handler}); treating as fatal`, { recordError });
89
+ return false;
90
+ }
91
+ }
92
+ //# sourceMappingURL=recoverable-build-error.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recoverable-build-error.mjs","sourceRoot":"","sources":["../../src/dev-utils/recoverable-build-error.mts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=recoverable-build-error.test.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recoverable-build-error.test.d.mts","sourceRoot":"","sources":["../../src/dev-utils/recoverable-build-error.test.mts"],"names":[],"mappings":""}
@@ -0,0 +1,176 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { buildDevServerBuildError, buildDevServerBuildErrorDedupeKey, handleRecoverableBuildError, MAX_DEV_SERVER_BUILD_ERRORS, upsertDevServerBuildError, } from "./recoverable-build-error.mjs";
3
+ import { VITE_ERROR_RAW_LOG_MAX_CHARS } from "./vite-dev-server-diagnostics.mjs";
4
+ describe("buildDevServerBuildError", () => {
5
+ it("classifies module resolve failures with the unresolved specifier", () => {
6
+ const entry = buildDevServerBuildError({
7
+ errors: [{ text: 'Could not resolve "buffer/"' }],
8
+ });
9
+ expect(entry.type).toBe("dev-server/build");
10
+ expect(entry.category).toBe("module_resolve");
11
+ expect(entry.unresolvedSpecifier).toBe("buffer/");
12
+ expect(entry.rawError).toContain("buffer/");
13
+ });
14
+ it("truncates rawError to the same cap the logging pipeline uses", () => {
15
+ // rawError is persisted on serverErrors, logged, shipped over
16
+ // /_sb_connect, and rendered in the editor overlay. A pathological stack or
17
+ // a failure listing hundreds of specifiers must not reach any of those
18
+ // unbounded.
19
+ const entry = buildDevServerBuildError({
20
+ errors: [
21
+ {
22
+ text: `Could not resolve "buffer/"\n${"x".repeat(VITE_ERROR_RAW_LOG_MAX_CHARS * 2)}`,
23
+ },
24
+ ],
25
+ });
26
+ expect(entry.rawError.length).toBeLessThan(VITE_ERROR_RAW_LOG_MAX_CHARS + 100);
27
+ expect(entry.rawError).toContain("(truncated)");
28
+ // Classification still reads the full text, so the specifier survives even
29
+ // when it sits past the cap.
30
+ expect(entry.category).toBe("module_resolve");
31
+ expect(entry.unresolvedSpecifier).toBe("buffer/");
32
+ });
33
+ });
34
+ describe("upsertDevServerBuildError", () => {
35
+ it("dedupes by category and unresolved specifier and caps the list", () => {
36
+ const devServerStatus = { serverErrors: [] };
37
+ const first = buildDevServerBuildError({
38
+ errors: [{ text: 'Could not resolve "buffer/"' }],
39
+ });
40
+ const second = buildDevServerBuildError({
41
+ errors: [{ text: 'Could not resolve "buffer/"' }],
42
+ });
43
+ upsertDevServerBuildError(devServerStatus, first);
44
+ upsertDevServerBuildError(devServerStatus, second);
45
+ expect(devServerStatus.serverErrors).toHaveLength(1);
46
+ expect(devServerStatus.serverErrors[0]?.timestamp).toBe(second.timestamp);
47
+ for (let index = 0; index < MAX_DEV_SERVER_BUILD_ERRORS + 2; index++) {
48
+ upsertDevServerBuildError(devServerStatus, buildDevServerBuildError({
49
+ errors: [{ text: `Could not resolve "pkg-${index}/"` }],
50
+ }));
51
+ }
52
+ expect(devServerStatus.serverErrors.length).toBeLessThanOrEqual(MAX_DEV_SERVER_BUILD_ERRORS);
53
+ });
54
+ it("uses a stable dedupe key", () => {
55
+ expect(buildDevServerBuildErrorDedupeKey({
56
+ category: "module_resolve",
57
+ unresolvedSpecifier: "buffer/",
58
+ })).toBe("module_resolve:buffer/");
59
+ });
60
+ it("keeps startup errors when a burst of build errors exceeds the cap", () => {
61
+ // `serverErrors` is shared with the startup path, which pushes a
62
+ // `dependency-install` / `dependency-upgrade` entry once per pod before
63
+ // Vite ever serves a request. Those sit at the front of the array, so a
64
+ // cap that counted every entry and kept only the newest would evict the
65
+ // install failure first — the one entry the user most needs to act on, and
66
+ // one that can never be re-reported.
67
+ const installError = {
68
+ type: "dev-server/dependency-install",
69
+ timestamp: new Date("2026-01-01T00:00:00.000Z").toISOString(),
70
+ category: "not_in_registry",
71
+ message: "npm ERR! 404 Not Found - GET https://registry/plotly.js",
72
+ };
73
+ const devServerStatus = { serverErrors: [installError] };
74
+ for (let index = 0; index < MAX_DEV_SERVER_BUILD_ERRORS + 3; index++) {
75
+ upsertDevServerBuildError(devServerStatus, buildDevServerBuildError({
76
+ errors: [{ text: `Could not resolve "pkg-${index}/"` }],
77
+ }));
78
+ }
79
+ expect(devServerStatus.serverErrors).toContain(installError);
80
+ expect(devServerStatus.serverErrors.filter((entry) => entry.type === "dev-server/build")).toHaveLength(MAX_DEV_SERVER_BUILD_ERRORS);
81
+ });
82
+ it("evicts the oldest build error rather than an arbitrary entry", () => {
83
+ const devServerStatus = { serverErrors: [] };
84
+ for (let index = 0; index < MAX_DEV_SERVER_BUILD_ERRORS + 1; index++) {
85
+ upsertDevServerBuildError(devServerStatus, buildDevServerBuildError({
86
+ errors: [{ text: `Could not resolve "pkg-${index}/"` }],
87
+ }));
88
+ }
89
+ const specifiers = devServerStatus.serverErrors.map((entry) => entry.unresolvedSpecifier);
90
+ expect(specifiers).not.toContain("pkg-0/");
91
+ expect(specifiers).toContain(`pkg-${MAX_DEV_SERVER_BUILD_ERRORS}/`);
92
+ });
93
+ });
94
+ describe("handleRecoverableBuildError", () => {
95
+ it("records a build error and returns true for esbuild failures", () => {
96
+ const devServerStatus = { serverErrors: [] };
97
+ const recordRecoverableBuildError = vi.fn();
98
+ const metrics = {
99
+ recordRecoverableBuildError,
100
+ };
101
+ const handled = handleRecoverableBuildError({
102
+ devServerStatus,
103
+ devServerMetrics: metrics,
104
+ handler: "unhandledRejection",
105
+ logger: { warn: vi.fn() },
106
+ reason: {
107
+ errors: [{ text: 'Could not resolve "buffer/"' }],
108
+ },
109
+ });
110
+ expect(handled).toBe(true);
111
+ expect(devServerStatus.serverErrors).toHaveLength(1);
112
+ expect(recordRecoverableBuildError).toHaveBeenCalledWith({
113
+ category: "module_resolve",
114
+ handler: "unhandledRejection",
115
+ });
116
+ });
117
+ // This runs inside the `uncaughtException` / `unhandledRejection` listeners
118
+ // before the caller writes its fatal-exit line. An escaping throw there does
119
+ // not degrade to the fatal path: Node tears the process down immediately, so
120
+ // the real error never reaches the logs, no metric is emitted, and
121
+ // `gracefulShutdown` never runs. Returning false instead routes the caller to
122
+ // its normal fatal path, which logs the original reason.
123
+ it("falls back to the fatal path instead of throwing when recording fails", () => {
124
+ const logger = { warn: vi.fn() };
125
+ const handled = handleRecoverableBuildError({
126
+ devServerStatus: { serverErrors: [] },
127
+ devServerMetrics: {
128
+ recordRecoverableBuildError: () => {
129
+ throw new Error("meter unavailable");
130
+ },
131
+ },
132
+ handler: "uncaughtException",
133
+ logger,
134
+ reason: { errors: [{ text: 'Could not resolve "buffer/"' }] },
135
+ });
136
+ expect(handled).toBe(false);
137
+ // The bookkeeping failure is noted separately so a malformed reason is
138
+ // diagnosable rather than looking like a plain crash.
139
+ expect(logger.warn).toHaveBeenCalled();
140
+ });
141
+ it("keeps the recoverable path for a reason whose errors array is malformed", () => {
142
+ // `isViteBuildError` accepts this via the message marker without
143
+ // re-validating `errors`, which is how a malformed array reaches the
144
+ // extraction step.
145
+ const devServerStatus = { serverErrors: [] };
146
+ const logger = { warn: vi.fn() };
147
+ const handled = handleRecoverableBuildError({
148
+ devServerStatus,
149
+ devServerMetrics: { recordRecoverableBuildError: vi.fn() },
150
+ handler: "unhandledRejection",
151
+ logger,
152
+ reason: {
153
+ errors: [{ text: 42 }],
154
+ message: "Transform failed with 1 error",
155
+ },
156
+ });
157
+ expect(handled).toBe(true);
158
+ expect(devServerStatus.serverErrors).toHaveLength(1);
159
+ expect(devServerStatus.serverErrors[0].rawError).toContain("Transform failed");
160
+ });
161
+ it("returns false for non-build errors", () => {
162
+ const devServerStatus = { serverErrors: [] };
163
+ const metrics = {
164
+ recordRecoverableBuildError: vi.fn(),
165
+ };
166
+ expect(handleRecoverableBuildError({
167
+ devServerStatus,
168
+ devServerMetrics: metrics,
169
+ handler: "uncaughtException",
170
+ logger: { warn: vi.fn() },
171
+ reason: new TypeError("boom"),
172
+ })).toBe(false);
173
+ expect(devServerStatus.serverErrors).toHaveLength(0);
174
+ });
175
+ });
176
+ //# sourceMappingURL=recoverable-build-error.test.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recoverable-build-error.test.mjs","sourceRoot":"","sources":["../../src/dev-utils/recoverable-build-error.test.mts"],"names":[],"mappings":"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"}