refactor(laboratory): rename preflight-script to just preflight (#6470)

This commit is contained in:
Jason Kuhrt 2025-02-05 12:45:08 -05:00 committed by GitHub
parent 9eeaf7a9a6
commit 4c2550551e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 165 additions and 168 deletions

View file

@ -2,7 +2,10 @@
"search.exclude": {
"**/dist": true,
"**/pnpm-lock.yaml": true,
"**/*.tsbuildinfo": true
"**/*.tsbuildinfo": true,
"**/*.next": true,
"**/*.turbo": true,
"**/web/docs/out": true
},
"commands.commands": [
{

View file

@ -1,9 +1,9 @@
import { dedent } from '../support/testkit';
const selectors = {
buttonPreflightScript: '[aria-label*="Preflight Script"]',
buttonModalCy: 'preflight-script-modal-button',
buttonToggleCy: 'toggle-preflight-script',
buttonGraphiQLPreflight: '[aria-label*="Preflight Script"]',
buttonModalCy: 'preflight-modal-button',
buttonToggleCy: 'toggle-preflight',
buttonHeaders: '[data-name="headers"]',
headersEditor: {
textArea: '.graphiql-editor-tool .graphiql-editor:last-child textarea',
@ -13,7 +13,7 @@ const selectors = {
},
modal: {
buttonSubmitCy: 'preflight-script-modal-submit',
buttonSubmitCy: 'preflight-modal-submit',
},
};
@ -27,7 +27,7 @@ beforeEach(() => {
cy.setCookie('sRefreshToken', refreshToken);
data.slug = slug;
cy.visit(`/${slug}/laboratory`);
cy.get(selectors.buttonPreflightScript).click();
cy.get(selectors.buttonGraphiQLPreflight).click();
});
});
});
@ -52,7 +52,7 @@ function setMonacoEditorContents(editorCyName: string, text: string) {
}
function setEditorScript(script: string) {
setMonacoEditorContents('preflight-script-editor', script);
setMonacoEditorContents('preflight-editor', script);
}
describe('Laboratory > Preflight Script', () => {
@ -60,22 +60,19 @@ describe('Laboratory > Preflight Script', () => {
it('regression: loads even if local storage is set to {}', () => {
window.localStorage.setItem('hive:laboratory:environment', '{}');
cy.visit(`/${data.slug}/laboratory`);
cy.get(selectors.buttonPreflightScript).click();
cy.get(selectors.buttonGraphiQLPreflight).click();
});
it('mini script editor is read only', () => {
cy.dataCy('toggle-preflight-script').click();
cy.dataCy('toggle-preflight').click();
// Wait loading disappears
cy.dataCy('preflight-script-editor-mini').should('not.contain', 'Loading');
cy.dataCy('preflight-editor-mini').should('not.contain', 'Loading');
// Click
cy.dataCy('preflight-script-editor-mini').click();
cy.dataCy('preflight-editor-mini').click();
// And type
cy.dataCy('preflight-script-editor-mini').within(() => {
cy.dataCy('preflight-editor-mini').within(() => {
cy.get('textarea').type('🐝', { force: true });
});
cy.dataCy('preflight-script-editor-mini').should(
'have.text',
'Cannot edit in read-only editor',
);
cy.dataCy('preflight-editor-mini').should('have.text', 'Cannot edit in read-only editor');
});
});
@ -84,25 +81,25 @@ describe('Preflight Script Modal', () => {
const env = '{"foo":123}';
beforeEach(() => {
cy.dataCy('preflight-script-modal-button').click();
cy.dataCy('preflight-modal-button').click();
setMonacoEditorContents('env-editor', env);
});
it('save script and environment variables when submitting', () => {
setEditorScript(script);
cy.dataCy('preflight-script-modal-submit').click();
cy.dataCy('preflight-modal-submit').click();
cy.dataCy('env-editor-mini').should('have.text', env);
cy.dataCy('toggle-preflight-script').click();
cy.dataCy('preflight-script-editor-mini').should('have.text', script);
cy.dataCy('toggle-preflight').click();
cy.dataCy('preflight-editor-mini').should('have.text', script);
cy.reload();
cy.get('[aria-label*="Preflight Script"]').click();
cy.dataCy('env-editor-mini').should('have.text', env);
cy.dataCy('preflight-script-editor-mini').should('have.text', script);
cy.dataCy('preflight-editor-mini').should('have.text', script);
});
it('logs show console/error information', () => {
setEditorScript(script);
cy.dataCy('run-preflight-script').click();
cy.dataCy('run-preflight').click();
cy.dataCy('console-output').should('contain', 'log: Hello_world (1:1)');
setEditorScript(
@ -112,7 +109,7 @@ console.error('Fatal')
throw new TypeError('Test')`,
);
cy.dataCy('run-preflight-script').click();
cy.dataCy('run-preflight').click();
// First log previous log message
cy.dataCy('console-output').should('contain', 'log: Hello_world (1:1)');
// After the new logs
@ -125,7 +122,7 @@ throw new TypeError('Test')`,
it('prompt and pass the awaited response', () => {
setEditorScript(script);
cy.dataCy('run-preflight-script').click();
cy.dataCy('run-preflight').click();
cy.dataCy('console-output').should('contain', 'log: Hello_world (1:1)');
setEditorScript(
@ -135,7 +132,7 @@ throw new TypeError('Test')`,
`,
);
cy.dataCy('run-preflight-script').click();
cy.dataCy('run-preflight').click();
cy.dataCy('prompt').get('input').type('test-username');
cy.dataCy('prompt').get('form').submit();
@ -153,7 +150,7 @@ throw new TypeError('Test')`,
it('prompt and cancel', () => {
setEditorScript(script);
cy.dataCy('run-preflight-script').click();
cy.dataCy('run-preflight').click();
cy.dataCy('console-output').should('contain', 'log: Hello_world (1:1)');
setEditorScript(
@ -163,7 +160,7 @@ throw new TypeError('Test')`,
`,
);
cy.dataCy('run-preflight-script').click();
cy.dataCy('run-preflight').click();
cy.dataCy('prompt').get('input').type('test-username');
cy.dataCy('prompt').get('[data-cy="prompt-cancel"]').click();
@ -181,7 +178,7 @@ throw new TypeError('Test')`,
it('script execution updates environment variables', () => {
setEditorScript(`lab.environment.set('my-test', "TROLOLOL")`);
cy.dataCy('run-preflight-script').click();
cy.dataCy('run-preflight').click();
cy.dataCy('env-editor').should(
'include.text',
// replace space with  
@ -191,7 +188,7 @@ throw new TypeError('Test')`,
it('`crypto-js` can be used for generating hashes', () => {
setEditorScript('console.log(lab.CryptoJS.SHA256("🐝"))');
cy.dataCy('run-preflight-script').click();
cy.dataCy('run-preflight').click();
cy.dataCy('console-output').should('contain', 'info: Using crypto-js version:');
cy.dataCy('console-output').should(
'contain',
@ -201,13 +198,13 @@ throw new TypeError('Test')`,
it('scripts can not use `eval`', () => {
setEditorScript('eval()');
cy.dataCy('preflight-script-modal-submit').click();
cy.dataCy('preflight-modal-submit').click();
cy.get('body').contains('Usage of dangerous statement like eval() or Function("").');
});
it('invalid code is rejected and can not be saved', () => {
setEditorScript('🐝');
cy.dataCy('preflight-script-modal-submit').click();
cy.dataCy('preflight-modal-submit').click();
cy.get('body').contains("[1:1]: Illegal character '}");
});
});
@ -289,7 +286,7 @@ describe('Execution', () => {
});
it('header placeholders are substituted with environment variables', () => {
cy.dataCy('toggle-preflight-script').click();
cy.dataCy('toggle-preflight').click();
cy.get('[data-name="headers"]').click();
cy.get('.graphiql-editor-tool .graphiql-editor:last-child textarea').type(
'{ "__test": "{{foo}} bar {{nonExist}}" }',
@ -316,7 +313,7 @@ describe('Execution', () => {
});
it('executed script updates update env editor and substitute headers', () => {
cy.dataCy('toggle-preflight-script').click();
cy.dataCy('toggle-preflight').click();
cy.get('[data-name="headers"]').click();
cy.get('.graphiql-editor-tool .graphiql-editor:last-child textarea').type(
'{ "__test": "{{foo}}" }',
@ -325,9 +322,9 @@ describe('Execution', () => {
parseSpecialCharSequences: false,
},
);
cy.dataCy('preflight-script-modal-button').click();
setMonacoEditorContents('preflight-script-editor', `lab.environment.set('foo', '92')`);
cy.dataCy('preflight-script-modal-submit').click();
cy.dataCy('preflight-modal-button').click();
setMonacoEditorContents('preflight-editor', `lab.environment.set('foo', '92')`);
cy.dataCy('preflight-modal-submit').click();
cy.intercept({
method: 'POST',
@ -340,7 +337,7 @@ describe('Execution', () => {
});
it('execute, prompt and use it in headers', () => {
cy.dataCy('toggle-preflight-script').click();
cy.dataCy('toggle-preflight').click();
cy.get('[data-name="headers"]').click();
cy.get('[data-name="headers"]').click();
@ -352,15 +349,15 @@ describe('Execution', () => {
},
);
cy.dataCy('preflight-script-modal-button').click();
cy.dataCy('preflight-modal-button').click();
setMonacoEditorContents(
'preflight-script-editor',
'preflight-editor',
dedent`
const username = await lab.prompt('Enter your username');
lab.environment.set('username', username);
`,
);
cy.dataCy('preflight-script-modal-submit').click();
cy.dataCy('preflight-modal-submit').click();
cy.intercept({
method: 'POST',
@ -385,11 +382,11 @@ describe('Execution', () => {
parseSpecialCharSequences: false,
},
);
cy.dataCy('preflight-script-modal-button').click();
setMonacoEditorContents('preflight-script-editor', `lab.environment.set('foo', 92)`);
cy.dataCy('preflight-modal-button').click();
setMonacoEditorContents('preflight-editor', `lab.environment.set('foo', 92)`);
setMonacoEditorContents('env-editor', `{"foo":10}`);
cy.dataCy('preflight-script-modal-submit').click();
cy.dataCy('preflight-modal-submit').click();
cy.intercept({
method: 'POST',
@ -402,11 +399,11 @@ describe('Execution', () => {
});
it('logs are visible when opened', () => {
cy.dataCy('toggle-preflight-script').click();
cy.dataCy('toggle-preflight').click();
cy.dataCy('preflight-script-modal-button').click();
cy.dataCy('preflight-modal-button').click();
setMonacoEditorContents(
'preflight-script-editor',
'preflight-editor',
dedent`
console.info(1)
console.warn(true)
@ -414,18 +411,18 @@ describe('Execution', () => {
throw new TypeError('Test')
`,
);
cy.dataCy('preflight-script-modal-submit').click();
cy.dataCy('preflight-modal-submit').click();
cy.intercept({
method: 'POST',
}).as('post');
// shows no logs before executing
cy.get('#preflight-script-logs button[data-cy="trigger"]').click({
cy.get('#preflight-logs button[data-cy="trigger"]').click({
// it's because the button is not fully visible on the screen
force: true,
});
cy.get('#preflight-script-logs [data-cy="logs"]').should(
cy.get('#preflight-logs [data-cy="logs"]').should(
'contain',
['No logs available', 'Execute a query to see logs'].join(''),
);
@ -433,7 +430,7 @@ describe('Execution', () => {
cy.get('.graphiql-execute-button').click();
cy.wait('@post');
cy.get('#preflight-script-logs [data-cy="logs"]').should(
cy.get('#preflight-logs [data-cy="logs"]').should(
'contain',
[
'log: Running script...',
@ -447,11 +444,11 @@ describe('Execution', () => {
});
it('logs are cleared when requested', () => {
cy.dataCy('toggle-preflight-script').click();
cy.dataCy('toggle-preflight').click();
cy.dataCy('preflight-script-modal-button').click();
cy.dataCy('preflight-modal-button').click();
setMonacoEditorContents(
'preflight-script-editor',
'preflight-editor',
dedent`
console.info(1)
console.warn(true)
@ -459,7 +456,7 @@ describe('Execution', () => {
throw new TypeError('Test')
`,
);
cy.dataCy('preflight-script-modal-submit').click();
cy.dataCy('preflight-modal-submit').click();
cy.intercept({
method: 'POST',
@ -468,12 +465,12 @@ describe('Execution', () => {
cy.wait('@post');
// open logs
cy.get('#preflight-script-logs button[data-cy="trigger"]').click({
cy.get('#preflight-logs button[data-cy="trigger"]').click({
// it's because the button is not fully visible on the screen
force: true,
});
cy.get('#preflight-script-logs [data-cy="logs"]').should(
cy.get('#preflight-logs [data-cy="logs"]').should(
'contain',
[
'log: Running script...',
@ -485,8 +482,8 @@ describe('Execution', () => {
].join(''),
);
cy.get('#preflight-script-logs button[data-cy="erase-logs"]').click();
cy.get('#preflight-script-logs [data-cy="logs"]').should(
cy.get('#preflight-logs button[data-cy="erase-logs"]').click();
cy.get('#preflight-logs [data-cy="logs"]').should(
'contain',
['No logs available', 'Execute a query to see logs'].join(''),
);

View file

@ -8,6 +8,6 @@
<a href="https://www.youtube.com/watch?v=CMNry4PE93Y" rel="nofollow">I like turtles</a>
<a href="https://www.youtube.com/watch?v=XOi2jFIhZhA" rel="nofollow">Wheatherboi</a>
<script type="module" src="./src/lib/preflight-sandbox/preflight-worker-embed.ts"></script>
<script type="module" src="./src/lib/preflight/preflight-worker-embed.ts"></script>
</body>
</html>

View file

@ -38,12 +38,9 @@ import { cn } from '../utils';
import labApiDefinitionRaw from './lab-api-declaration?raw';
import { IFrameEvents, LogMessage } from './shared-types';
export type PreflightScriptResultData = Omit<
IFrameEvents.Outgoing.EventData.Result,
'type' | 'runId'
>;
export type PreflightResultData = Omit<IFrameEvents.Outgoing.EventData.Result, 'type' | 'runId'>;
export const preflightScriptPlugin: GraphiQLPlugin = {
export const preflightPlugin: GraphiQLPlugin = {
icon: () => (
<svg
viewBox="0 0 256 256"
@ -59,7 +56,7 @@ export const preflightScriptPlugin: GraphiQLPlugin = {
</svg>
),
title: 'Preflight Script',
content: PreflightScriptContent,
content: PreflightContent,
};
const classes = {
@ -147,14 +144,16 @@ export const enum PreflightWorkerState {
ready,
}
export function usePreflightScript(args: {
export function usePreflight(args: {
target: FragmentType<typeof PreflightScript_TargetFragment> | null;
}) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const prompt = usePromptManager();
const target = useFragment(PreflightScript_TargetFragment, args.target);
const [isPreflightScriptEnabled, setIsPreflightScriptEnabled] = useLocalStorageJson(
const [isEnabled, setIsEnabled] = useLocalStorageJson(
// todo: ability to pass historical keys for seamless gradual migration to new key names.
// 'hive:laboratory:isPreflightEnabled',
'hive:laboratory:isPreflightScriptEnabled',
z.boolean().default(false),
);
@ -170,13 +169,13 @@ export function usePreflightScript(args: {
const [state, setState] = useState<PreflightWorkerState>(PreflightWorkerState.ready);
const [logs, setLogs] = useState<LogRecord[]>([]);
const currentRun = useRef<null | Function>(null);
const abortExecutionRef = useRef<null | (() => void)>(null);
async function execute(
script = target?.preflightScript?.sourceCode ?? '',
isPreview = false,
): Promise<PreflightScriptResultData> {
const resultEnvironmentVariablesDecoded: PreflightScriptResultData['environmentVariables'] =
): Promise<PreflightResultData> {
const resultEnvironmentVariablesDecoded: PreflightResultData['environmentVariables'] =
Kit.tryOr(
() => JSON.parse(latestEnvironmentVariablesRef.current),
// todo: find a better solution than blowing away the user's
@ -192,14 +191,14 @@ export function usePreflightScript(args: {
//
() => ({}),
);
const result: PreflightScriptResultData = {
const result: PreflightResultData = {
request: {
headers: [],
},
environmentVariables: resultEnvironmentVariablesDecoded,
};
if (isPreview === false && !isPreflightScriptEnabled) {
if (isPreview === false && !isEnabled) {
return result;
}
@ -226,7 +225,7 @@ export function usePreflightScript(args: {
type: IFrameEvents.Incoming.Event.run,
id,
script,
// Preflight Script has read/write relationship with environment variables.
// Preflight has read/write relationship with environment variables.
environmentVariables: result.environmentVariables,
} satisfies IFrameEvents.Incoming.EventData,
'*',
@ -356,7 +355,7 @@ export function usePreflightScript(args: {
// Window message events can be emitted from unknowable sources.
// For example when our e2e tests runs within Cypress GUI, we see a `MessageEvent` with `.data` of `{ vscodeScheduleAsyncWork: 3 }`.
// Since we cannot know if the event source is Preflight Script, we cannot perform an exhaustive check.
// Since we cannot know if the event source is Preflight, we cannot perform an exhaustive check.
//
// Kit.neverCase(ev.data);
//
@ -367,7 +366,7 @@ export function usePreflightScript(args: {
}
window.addEventListener('message', eventHandler);
currentRun.current = () => {
abortExecutionRef.current = () => {
contentWindow.postMessage({
type: IFrameEvents.Incoming.Event.abort,
id,
@ -375,7 +374,7 @@ export function usePreflightScript(args: {
closedOpenedPrompts();
currentRun.current = null;
abortExecutionRef.current = null;
};
await isFinishedD.promise;
@ -407,24 +406,24 @@ export function usePreflightScript(args: {
}
}
function abort() {
currentRun.current?.();
function abortExecution() {
abortExecutionRef.current?.();
}
// terminate worker when leaving laboratory
useEffect(
() => () => {
currentRun.current?.();
abortExecutionRef.current?.();
},
[],
);
return {
execute,
abort,
isPreflightScriptEnabled,
setIsPreflightScriptEnabled,
script: target?.preflightScript?.sourceCode ?? '',
abortExecution,
isEnabled,
setIsEnabled,
content: target?.preflightScript?.sourceCode ?? '',
environmentVariables,
setEnvironmentVariables,
state,
@ -447,15 +446,15 @@ export function usePreflightScript(args: {
} as const;
}
type PreflightScriptObject = ReturnType<typeof usePreflightScript>;
type PreflightObject = ReturnType<typeof usePreflight>;
const PreflightScriptContext = createContext<PreflightScriptObject | null>(null);
export const PreflightScriptProvider = PreflightScriptContext.Provider;
const PreflightContext = createContext<PreflightObject | null>(null);
export const PreflightProvider = PreflightContext.Provider;
function PreflightScriptContent() {
const preflightScript = useContext(PreflightScriptContext);
if (preflightScript === null) {
throw new Error('PreflightScriptContent used outside PreflightScriptContext.Provider');
function PreflightContent() {
const preflight = useContext(PreflightContext);
if (preflight === null) {
throw new Error('PreflightContent used outside PreflightContext.Provider');
}
const [showModal, toggleShowModal] = useToggle();
@ -467,7 +466,7 @@ function PreflightScriptContent() {
const { toast } = useToast();
const handleScriptChange = useCallback(async (newValue = '') => {
const handleContentChange = useCallback(async (newValue = '') => {
const { data, error } = await mutate({
input: {
selector: params,
@ -494,24 +493,24 @@ function PreflightScriptContent() {
return (
<>
<PreflightScriptModal
<PreflightModal
// to unmount on submit/close
key={String(showModal)}
isOpen={showModal}
toggle={toggleShowModal}
scriptValue={preflightScript.script}
executeScript={value =>
preflightScript.execute(value, true).catch(err => {
execute={value =>
preflight.execute(value, true).catch(err => {
console.error(err);
})
}
state={preflightScript.state}
abortScriptRun={preflightScript.abort}
logs={preflightScript.logs}
clearLogs={preflightScript.clearLogs}
onScriptValueChange={handleScriptChange}
envValue={preflightScript.environmentVariables}
onEnvValueChange={preflightScript.setEnvironmentVariables}
state={preflight.state}
abortExecution={preflight.abortExecution}
logs={preflight.logs}
clearLogs={preflight.clearLogs}
content={preflight.content}
onContentChange={handleContentChange}
envValue={preflight.environmentVariables}
onEnvValueChange={preflight.setEnvironmentVariables}
/>
<div className="graphiql-doc-explorer-title flex items-center justify-between gap-4">
Preflight Script
@ -520,7 +519,7 @@ function PreflightScriptContent() {
size="icon-sm"
className="size-auto gap-1"
onClick={toggleShowModal}
data-cy="preflight-script-modal-button"
data-cy="preflight-modal-button"
>
<Pencil1Icon className="shrink-0" />
Edit
@ -536,13 +535,11 @@ function PreflightScriptContent() {
size="sm"
variant="outline"
className="mt-3"
onClick={() =>
preflightScript.setIsPreflightScriptEnabled(!preflightScript.isPreflightScriptEnabled)
}
data-cy="toggle-preflight-script"
onClick={() => preflight.setIsEnabled(!preflight.isEnabled)}
data-cy="toggle-preflight"
>
<PowerIcon className="mr-2 size-4" />
{preflightScript.isPreflightScriptEnabled ? 'On' : 'Off'}
{preflight.isEnabled ? 'On' : 'Off'}
</Button>
</div>
@ -554,7 +551,7 @@ function PreflightScriptContent() {
</EditorTitle>
<Subtitle className="mb-3 cursor-not-allowed">Read-only view of the script</Subtitle>
<div className="relative">
{preflightScript.isPreflightScriptEnabled ? null : (
{preflight.isEnabled ? null : (
<div className="absolute inset-0 z-20 flex items-center justify-center bg-[#030711]/90 p-4 text-white">
<div className="rounded-md bg-[#0f1520] p-4 text-sm">
Preflight Script is disabled and will not be executed
@ -563,11 +560,11 @@ function PreflightScriptContent() {
)}
<MonacoEditor
height={128}
value={preflightScript.script}
value={preflight.content}
{...monacoProps.script}
className={cn(classes.monacoMini, 'z-10')}
wrapperProps={{
['data-cy']: 'preflight-script-editor-mini',
['data-cy']: 'preflight-editor-mini',
}}
options={{
...monacoProps.script.options,
@ -592,8 +589,8 @@ function PreflightScriptContent() {
</Subtitle>
<MonacoEditor
height={128}
value={preflightScript.environmentVariables}
onChange={value => preflightScript.setEnvironmentVariables(value ?? '')}
value={preflight.environmentVariables}
onChange={value => preflight.setEnvironmentVariables(value ?? '')}
{...monacoProps.env}
className={classes.monacoMini}
wrapperProps={{
@ -604,28 +601,28 @@ function PreflightScriptContent() {
);
}
function PreflightScriptModal({
function PreflightModal({
isOpen,
toggle,
scriptValue,
executeScript,
content,
state,
abortScriptRun,
execute,
abortExecution,
logs,
clearLogs,
onScriptValueChange,
onContentChange,
envValue,
onEnvValueChange,
}: {
isOpen: boolean;
toggle: () => void;
scriptValue?: string;
executeScript: (script: string) => void;
content?: string;
state: PreflightWorkerState;
abortScriptRun: () => void;
execute: (script: string) => void;
abortExecution: () => void;
logs: Array<LogRecord>;
clearLogs: () => void;
onScriptValueChange: (value: string) => void;
onContentChange: (value: string) => void;
envValue: string;
onEnvValueChange: (value: string) => void;
}) {
@ -653,7 +650,7 @@ function PreflightScriptModal({
}, []);
const handleSubmit = useCallback(() => {
onScriptValueChange(scriptEditorRef.current?.getValue() ?? '');
onContentChange(scriptEditorRef.current?.getValue() ?? '');
onEnvValueChange(envEditorRef.current?.getValue() ?? '');
toggle();
}, []);
@ -668,7 +665,7 @@ function PreflightScriptModal({
open={isOpen}
onOpenChange={open => {
if (!open) {
abortScriptRun();
abortExecution();
}
toggle();
}}
@ -706,13 +703,13 @@ function PreflightScriptModal({
className="size-auto gap-1"
onClick={() => {
if (state === PreflightWorkerState.running) {
abortScriptRun();
abortExecution();
return;
}
executeScript(scriptEditorRef.current?.getValue() ?? '');
execute(scriptEditorRef.current?.getValue() ?? '');
}}
data-cy="run-preflight-script"
data-cy="run-preflight"
>
{state === PreflightWorkerState.running && (
<>
@ -729,7 +726,7 @@ function PreflightScriptModal({
</Button>
</div>
<MonacoEditor
value={scriptValue}
value={content}
beforeMount={handleMonacoEditorBeforeMount}
onMount={handleScriptEditorDidMount}
{...monacoProps.script}
@ -738,7 +735,7 @@ function PreflightScriptModal({
wordWrap: 'wordWrapColumn',
}}
wrapperProps={{
['data-cy']: 'preflight-script-editor',
['data-cy']: 'preflight-editor',
}}
/>
</div>
@ -792,14 +789,14 @@ function PreflightScriptModal({
Changes made to this Preflight Script will apply to all users on your team using this
target.
</p>
<Button type="button" onClick={toggle} data-cy="preflight-script-modal-cancel">
<Button type="button" onClick={toggle} data-cy="preflight-modal-cancel">
Close
</Button>
<Button
type="button"
variant="primary"
onClick={handleSubmit}
data-cy="preflight-script-modal-submit"
data-cy="preflight-modal-submit"
>
Save
</Button>

View file

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
// The content of this file is used as a string
// and feed into the context of the Monaco Editor and the Preflight Script.
// and feed into the context of the Monaco Editor and Preflight.
// The lack of `declare const lab: LabAPI` is intentional, to avoid messing up the global scope
// of the web app codebase.
// This could be a string in `graphiql-plugin.tsx`, but it's better to keep it in a separate file,

View file

@ -1,5 +1,5 @@
import { Kit } from '../kit';
import PreflightWorker from './preflight-script-worker?worker&inline';
import PreflightWorker from './preflight-worker?worker&inline';
import { IFrameEvents, WorkerEvents } from './shared-types';
function postMessage(data: IFrameEvents.Outgoing.EventData) {
@ -73,7 +73,7 @@ function handleEvent(data: IFrameEvents.Incoming.EventData) {
type: IFrameEvents.Outgoing.Event.error,
runId,
error: {
message: `Preflight script execution timed out after ${PREFLIGHT_TIMEOUT / 1000} seconds`,
message: `Preflight execution timed out after ${PREFLIGHT_TIMEOUT / 1000} seconds`,
},
});
terminate();

View file

@ -94,7 +94,7 @@ async function execute(args: WorkerEvents.Incoming.EventData): Promise<void> {
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
// the main thread at the end of preflight execution
// const message: LogMessage = { level, message };
postMessage({
type: 'log',

View file

@ -38,11 +38,11 @@ import { useResetState } from '@/lib/hooks/use-reset-state';
import {
LogLine,
LogRecord,
preflightScriptPlugin,
PreflightScriptProvider,
PreflightScriptResultData,
usePreflightScript,
} from '@/lib/preflight-sandbox/graphiql-plugin';
preflightPlugin,
PreflightProvider,
PreflightResultData,
usePreflight,
} from '@/lib/preflight/graphiql-plugin';
import { cn } from '@/lib/utils';
import { explorerPlugin } from '@graphiql/plugin-explorer';
import {
@ -64,7 +64,7 @@ import { Kit } from '@/lib/kit';
const explorer = explorerPlugin();
// Declare outside components, otherwise while clicking on field in explorer operationCollectionsPlugin will be open
const plugins = [explorer, operationCollectionsPlugin, preflightScriptPlugin];
const plugins = [explorer, operationCollectionsPlugin, preflightPlugin];
function Share(): ReactElement | null {
const label = 'Share query';
@ -315,7 +315,7 @@ function LaboratoryPageContent(props: {
const mockEndpoint = `${location.origin}/api/lab/${props.organizationSlug}/${props.projectSlug}/${props.targetSlug}`;
const target = query.data?.target;
const preflightScript = usePreflightScript({ target: target ?? null });
const preflight = usePreflight({ target: target ?? null });
const fetcher = useMemo<Fetcher>(() => {
return async (params, opts) => {
@ -324,17 +324,17 @@ function LaboratoryPageContent(props: {
mockEndpoint;
return new Repeater(async (push, stop) => {
let hasFinishedPreflightScript = false;
let isPreflightExecutionDone = false;
// eslint-disable-next-line @typescript-eslint/no-floating-promises
stop.then(() => {
if (!hasFinishedPreflightScript) {
preflightScript.abort();
if (!isPreflightExecutionDone) {
preflight.abortExecution();
}
});
let preflightData: PreflightScriptResultData;
let preflightResultData: PreflightResultData;
try {
preflightData = await preflightScript.execute();
preflightResultData = await preflight.execute();
} catch (err: unknown) {
if (err instanceof Error === false) {
throw err;
@ -347,22 +347,25 @@ function LaboratoryPageContent(props: {
null,
2,
);
const error = new Error(`Error during preflight script execution:\n\n${formatError}`);
const error = new Error(`Error during preflight execution:\n\n${formatError}`);
// We only want to expose the error message, not the whole stack trace.
delete error.stack;
stop(error);
return;
} finally {
hasFinishedPreflightScript = true;
isPreflightExecutionDone = true;
}
const headers = {
// We want to prevent users from interpolating environment variables into
// their preflight script headers. So, apply substitution BEFORE merging
// their preflight headers. So, apply substitution BEFORE merging
// in preflight headers.
//
...substituteVariablesInHeaders(opts?.headers ?? {}, preflightData.environmentVariables),
...Object.fromEntries(preflightData.request.headers),
...substituteVariablesInHeaders(
opts?.headers ?? {},
preflightResultData.environmentVariables,
),
...Object.fromEntries(preflightResultData.request.headers),
};
const graphiqlFetcher = createGraphiQLFetcher({ url, fetch });
@ -396,8 +399,8 @@ function LaboratoryPageContent(props: {
}, [
target?.graphqlEndpointUrl,
actualSelectedApiEndpoint,
preflightScript.execute,
preflightScript.isPreflightScriptEnabled,
preflight.execute,
preflight.isEnabled,
]);
const FullScreenIcon = isFullScreen ? ExitFullScreenIcon : EnterFullScreenIcon;
@ -450,7 +453,7 @@ function LaboratoryPageContent(props: {
return (
<>
{preflightScript.iframeElement}
{preflight.iframeElement}
<div className="flex py-6">
<div className="flex-1">
<Title>Laboratory</Title>
@ -568,28 +571,28 @@ function LaboratoryPageContent(props: {
margin-top: 15px;
}
#preflight-script-logs {
#preflight-logs {
background-color: hsl(var(--color-base));
border-radius: var(--border-radius-12);
box-shadow: var(--popover-box-shadow);
color: hsla(var(--color-neutral), var(--alpha-tertiary));
}
#preflight-script-logs h2 {
#preflight-logs h2 {
color: hsla(var(--color-neutral), var(--alpha-secondary));
}
#preflight-script-logs button[data-state="open"] > h2 {
#preflight-logs button[data-state="open"] > h2 {
color: hsl(var(--color-neutral));
}
#preflight-script-logs > div {
#preflight-logs > div {
border-color: hsl(var(--border));
}
`}</style>
</Helmet>
{!query.fetching && !query.stale && (
<PreflightScriptProvider value={preflightScript}>
<PreflightProvider value={preflight}>
<GraphiQL
fetcher={fetcher}
shouldPersistHeaders
@ -631,16 +634,13 @@ function LaboratoryPageContent(props: {
</GraphiQL.Toolbar>
<GraphiQL.Footer>
<div>
{preflightScript.isPreflightScriptEnabled ? (
<PreflightScriptLogs
logs={preflightScript.logs}
onClear={preflightScript.clearLogs}
/>
{preflight.isEnabled ? (
<PreflightLogs logs={preflight.logs} onClear={preflight.clearLogs} />
) : null}
</div>
</GraphiQL.Footer>
</GraphiQL>
</PreflightScriptProvider>
</PreflightProvider>
)}
<ConnectLabModal
endpoint={mockEndpoint}
@ -703,7 +703,7 @@ function useApiTabValueState(graphqlEndpointUrl: string | null) {
] as const;
}
function PreflightScriptLogs(props: { logs: LogRecord[]; onClear: () => void }) {
function PreflightLogs(props: { logs: LogRecord[]; onClear: () => void }) {
const [isOpen, setIsOpen] = useState(false);
const consoleRef = useRef<HTMLDivElement>(null);
useEffect(() => {
@ -716,7 +716,7 @@ function PreflightScriptLogs(props: { logs: LogRecord[]; onClear: () => void })
open={isOpen}
onOpenChange={setIsOpen}
className={cn('flex max-h-[200px] w-full flex-col overflow-hidden bg-[#030711]')}
id="preflight-script-logs"
id="preflight-logs"
>
<div
className={cn(