refactor(code-executor): migrate user scripts to single context argument

- replace (page, context, helpers) signature with async (context) => {}
- add ScriptContext interface exposing page, browser, env, getEnv,
  getCredential, getStepOutput, runSnippet, dumpDom, log/warn/error
- rename getEnvUrl to getEnv throughout service and docs
- fix step ordering: normalizeStepOrder and run step rows are now 1-based
- migrate existing DB rows (scenario_steps, scenario_run_steps) +1
- update all tests and stored snippet/step code in DB to new API
This commit is contained in:
2026-04-14 17:44:59 +03:00
parent 1a2e786ca8
commit 90be5a5490
10 changed files with 166 additions and 91 deletions
+15 -13
View File
@@ -187,16 +187,15 @@ export class BrowserService {
this.logger.log(`[${label}] exec: running user code`);
const scriptLogger: ScriptLogger = (level, msg) =>
this.logger[level](`[${label}] script: ${msg}`);
const result = await this.codeExecutor.execute(
const result = await this.codeExecutor.execute({
page,
context,
browser: context,
code,
scriptLogger,
undefined,
log: scriptLogger,
credentials,
environment,
snippets,
);
});
this.logger.log(`[${label}] exec: done`);
return result;
} catch (err) {
@@ -208,13 +207,17 @@ export class BrowserService {
const snippetMap = await this.snippetService
.buildSnippetMap()
.catch(() => ({}) as Record<string, string>);
// TODO: instantiate one browser per server instance and reuse contexts for anonymous sessions,
// instead of launching a new browser for each request.
const browser = await chromium.launch({
headless: true,
executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH,
});
try {
const context = await browser.newContext();
const page = await context.newPage();
const browserContext = await browser.newContext();
const page = await browserContext.newPage();
if (url) {
this.logger.log(`[${label}] exec: navigating to ${url}`);
@@ -224,16 +227,15 @@ export class BrowserService {
this.logger.log(`[${label}] exec: running user code`);
const scriptLogger: ScriptLogger = (level, msg) =>
this.logger[level](`[${label}] script: ${msg}`);
const result = await this.codeExecutor.execute(
const result = await this.codeExecutor.execute({
page,
context,
browser: browserContext,
code,
scriptLogger,
undefined,
log: scriptLogger,
credentials,
environment,
snippetMap,
);
snippets: snippetMap,
});
this.logger.log(`[${label}] exec: done`);
return result;
} catch (err) {
+1 -1
View File
@@ -31,7 +31,7 @@ export class ExecDto {
@ApiPropertyOptional({
description:
"Key/value map of environment variables available via `helpers.env` and `helpers.getEnvUrl()` in the script.",
"Key/value map of environment variables available via `helpers.env` and `helpers.getEnv()` in the script.",
example: { BASE_URL: "https://example.com" },
})
@IsOptional()