Environment Variables
@@ -768,3 +753,30 @@ function PreflightScriptModal({
);
}
+
+const LOG_COLORS = {
+ error: 'text-red-400',
+ info: 'text-emerald-400',
+ warn: 'text-yellow-400',
+ log: 'text-gray-400',
+};
+
+export function LogLine({ log }: { log: LogRecord }) {
+ if ('type' in log && log.type === 'separator') {
+ return
;
+ }
+
+ if ('level' in log && log.level in LOG_COLORS) {
+ return (
+
+ {log.level}: {log.message}
+ {log.line && log.column ? ` (${log.line}:${log.column})` : ''}
+
+ );
+ }
+
+ captureException(new Error('Unexpected log type in Preflight Script output'), {
+ extra: { log },
+ });
+ return null;
+}
diff --git a/packages/web/app/src/lib/preflight-sandbox/preflight-script-worker.ts b/packages/web/app/src/lib/preflight-sandbox/preflight-script-worker.ts
index 95b358f33..63c441655 100644
--- a/packages/web/app/src/lib/preflight-sandbox/preflight-script-worker.ts
+++ b/packages/web/app/src/lib/preflight-sandbox/preflight-script-worker.ts
@@ -2,9 +2,7 @@ import CryptoJS from 'crypto-js';
import CryptoJSPackageJson from 'crypto-js/package.json';
import { ALLOWED_GLOBALS } from './allowed-globals';
import { isJSONPrimitive } from './json';
-import { WorkerEvents } from './shared-types';
-
-export type LogMessage = string | Error;
+import { LogMessage, WorkerEvents } from './shared-types';
/**
* Unique id for each prompt request.
@@ -79,13 +77,22 @@ async function execute(args: WorkerEvents.Incoming.EventData): Promise {
(level: 'log' | 'warn' | 'error' | 'info') =>
(...args: unknown[]) => {
console[level](...args);
- let message = `${level.charAt(0).toUpperCase()}${level.slice(1)}: ${args.map(String).join(' ')}`;
- message += appendLineAndColumn(new Error(), {
+ const message = args.map(String).join(' ');
+ const { line, column } = readLineAndColumn(new Error(), {
columnOffset: 'console.'.length,
});
// The messages should be streamed to the main thread as they occur not gathered and send to
// the main thread at the end of the execution of the preflight script
- postMessage({ type: 'log', message });
+ // const message: LogMessage = { level, message };
+ postMessage({
+ type: 'log',
+ message: {
+ level,
+ message,
+ line,
+ column,
+ } satisfies LogMessage,
+ });
};
function getValidEnvVariable(value: unknown) {
@@ -161,10 +168,15 @@ ${script}})()`;
'undefined',
)(labApi, consoleApi);
} catch (error) {
- if (error instanceof Error) {
- error.message += appendLineAndColumn(error);
- }
- sendMessage({ type: WorkerEvents.Outgoing.Event.error, error: error as Error });
+ const { line, column } = error instanceof Error ? readLineAndColumn(error) : {};
+ sendMessage({
+ type: WorkerEvents.Outgoing.Event.error,
+ error: {
+ message: error instanceof Error ? error.message : String(error),
+ line,
+ column,
+ },
+ });
return;
}
sendMessage({
@@ -173,11 +185,14 @@ ${script}})()`;
});
}
-function appendLineAndColumn(error: Error, { columnOffset = 0 } = {}): string {
+function readLineAndColumn(error: Error, { columnOffset = 0 } = {}) {
const regex = /:(?\d+):(?\d+)/; // Regex to match the line and column numbers
const { line, column } = error.stack?.match(regex)?.groups || {};
- return ` (Line: ${Number(line) - 3}, Column: ${Number(column) - columnOffset})`;
+ return {
+ line: Number(line) - 3,
+ column: Number(column) - columnOffset,
+ };
}
sendMessage({ type: WorkerEvents.Outgoing.Event.ready });
diff --git a/packages/web/app/src/lib/preflight-sandbox/preflight-worker-embed.ts b/packages/web/app/src/lib/preflight-sandbox/preflight-worker-embed.ts
index b1aa78664..538e58100 100644
--- a/packages/web/app/src/lib/preflight-sandbox/preflight-worker-embed.ts
+++ b/packages/web/app/src/lib/preflight-sandbox/preflight-worker-embed.ts
@@ -71,9 +71,9 @@ function handleEvent(data: IFrameEvents.Incoming.EventData) {
postMessage({
type: IFrameEvents.Outgoing.Event.error,
runId,
- error: new Error(
- `Preflight script execution timed out after ${PREFLIGHT_TIMEOUT / 1000} seconds`,
- ),
+ error: {
+ message: `Preflight script execution timed out after ${PREFLIGHT_TIMEOUT / 1000} seconds`,
+ },
});
terminate();
}, PREFLIGHT_TIMEOUT);
@@ -141,7 +141,9 @@ function handleEvent(data: IFrameEvents.Incoming.EventData) {
postMessage({
type: IFrameEvents.Outgoing.Event.error,
runId,
- error: error as Error,
+ error: {
+ message: error instanceof Error ? error.message : String(error),
+ },
});
terminate();
}
diff --git a/packages/web/app/src/lib/preflight-sandbox/shared-types.ts b/packages/web/app/src/lib/preflight-sandbox/shared-types.ts
index b96386e4b..fcbf75cff 100644
--- a/packages/web/app/src/lib/preflight-sandbox/shared-types.ts
+++ b/packages/web/app/src/lib/preflight-sandbox/shared-types.ts
@@ -2,6 +2,19 @@
type _MessageEvent = MessageEvent;
+export type LogMessage = {
+ level: 'log' | 'warn' | 'error' | 'info';
+ message: string;
+ line?: number;
+ column?: number;
+};
+
+export type ErrorMessage = {
+ message: string;
+ line?: number;
+ column?: number;
+};
+
export namespace IFrameEvents {
export namespace Outgoing {
export const enum Event {
@@ -25,7 +38,7 @@ export namespace IFrameEvents {
type LogEventData = {
type: Event.log;
runId: string;
- log: string | Error;
+ log: LogMessage;
};
type ResultEventData = {
@@ -37,7 +50,7 @@ export namespace IFrameEvents {
type ErrorEventData = {
type: Event.error;
runId: string;
- error: Error;
+ error: ErrorMessage;
};
type PromptEventData = {
@@ -100,8 +113,8 @@ export namespace WorkerEvents {
prompt = 'prompt',
}
- type LogEventData = { type: Event.log; message: string };
- type ErrorEventData = { type: Event.error; error: Error };
+ type LogEventData = { type: Event.log; message: LogMessage };
+ type ErrorEventData = { type: Event.error; error: ErrorMessage };
type PromptEventData = {
type: Event.prompt;
promptId: number;
diff --git a/packages/web/app/src/pages/target-laboratory.tsx b/packages/web/app/src/pages/target-laboratory.tsx
index 4278c9d23..5ccdf5f64 100644
--- a/packages/web/app/src/pages/target-laboratory.tsx
+++ b/packages/web/app/src/pages/target-laboratory.tsx
@@ -36,6 +36,7 @@ import { useSyncOperationState } from '@/lib/hooks/laboratory/use-sync-operation
import { useOperationFromQueryString } from '@/lib/hooks/laboratory/useOperationFromQueryString';
import { useResetState } from '@/lib/hooks/use-reset-state';
import {
+ LogLine,
LogRecord,
preflightScriptPlugin,
PreflightScriptProvider,
@@ -57,7 +58,6 @@ import 'graphiql/style.css';
import '@graphiql/plugin-explorer/style.css';
import { PromptManager, PromptProvider } from '@/components/ui/prompt';
import { useRedirect } from '@/lib/access/common';
-import { captureException } from '@sentry/react';
const explorer = explorerPlugin();
@@ -702,13 +702,6 @@ function PreflightScriptLogs(props: { logs: LogRecord[]; onClear: () => void })
consoleEl?.scroll({ top: consoleEl.scrollHeight, behavior: 'smooth' });
}, [props.logs, isOpen]);
- const logColor = {
- error: 'text-red-400',
- info: 'text-emerald-400',
- warn: 'text-yellow-400',
- log: '', // default
- };
-
return (
void })
) : (
<>
- {props.logs.map((log, index) => {
- if (typeof log !== 'string' && 'type' in log && log.type === 'separator') {
- return
;
- }
-
- let logType: 'error' | 'warn' | 'info' | 'log' = 'log';
- let logMessage = '';
-
- if (log instanceof Error) {
- logType = 'error';
- logMessage = `${log.name}: ${log.message}`;
- } else if (typeof log === 'string') {
- logType = log.split(':')[0].toLowerCase() as 'error' | 'warn' | 'info' | 'log';
- logMessage = log.substring(log.indexOf(':') + 1).trim();
- } else {
- captureException(new Error('Unexpected log type in Preflight Script Logs'), {
- extra: { log },
- });
- return null;
- }
-
- return (
-
- {logType}: {logMessage}
-
- );
- })}
+ {props.logs.map((log, index) => (
+
+ ))}
>
)}