ci: Fix prettier auto-formatting (no-changelog) (#7063)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-09-01 13:29:31 +02:00 committed by GitHub
parent fa3d7070b0
commit a693b29134
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 285 additions and 234 deletions

View file

@ -2,90 +2,100 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import util from 'util'; import util from 'util';
import { exec } from 'child_process'; import { exec } from 'child_process';
import { glob } from "glob"; import { glob } from 'glob';
import ts from 'typescript'; import ts from 'typescript';
const readFileAsync = util.promisify(fs.readFile); const readFileAsync = util.promisify(fs.readFile);
const execAsync = util.promisify(exec); const execAsync = util.promisify(exec);
const filterAsync = async (asyncPredicate, arr) => { const filterAsync = async (asyncPredicate, arr) => {
const filterResults = await Promise.all(arr.map(async item => ({ const filterResults = await Promise.all(
item, arr.map(async (item) => ({
shouldKeep: await asyncPredicate(item) item,
}))); shouldKeep: await asyncPredicate(item),
})),
);
return filterResults.filter(({shouldKeep}) => shouldKeep).map(({item}) => item); return filterResults.filter(({ shouldKeep }) => shouldKeep).map(({ item }) => item);
} };
const isAbstractClass = (node) => { const isAbstractClass = (node) => {
if (ts.isClassDeclaration(node)) { if (ts.isClassDeclaration(node)) {
return node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword) || false; return (
node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword) || false
);
} }
return false; return false;
} };
const isAbstractMethod = (node) => {
return ts.isMethodDeclaration(node) && Boolean(node.modifiers?.find((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword));
}
const isAbstractMethod = (node) => {
return (
ts.isMethodDeclaration(node) &&
Boolean(node.modifiers?.find((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword))
);
};
// Function to check if a file has a function declaration, function expression, object method or class // Function to check if a file has a function declaration, function expression, object method or class
const hasFunctionOrClass = async filePath => { const hasFunctionOrClass = async (filePath) => {
const fileContent = await readFileAsync(filePath, 'utf-8'); const fileContent = await readFileAsync(filePath, 'utf-8');
const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true); const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true);
let hasFunctionOrClass = false; let hasFunctionOrClass = false;
const visit = node => { const visit = (node) => {
if ( if (
ts.isFunctionDeclaration(node) ts.isFunctionDeclaration(node) ||
|| ts.isFunctionExpression(node) ts.isFunctionExpression(node) ||
|| ts.isArrowFunction(node) ts.isArrowFunction(node) ||
|| (ts.isMethodDeclaration(node) && !isAbstractMethod(node)) (ts.isMethodDeclaration(node) && !isAbstractMethod(node)) ||
|| (ts.isClassDeclaration(node) && !isAbstractClass(node)) (ts.isClassDeclaration(node) && !isAbstractClass(node))
) { ) {
hasFunctionOrClass = true; hasFunctionOrClass = true;
} }
node.forEachChild(visit); node.forEachChild(visit);
} };
visit(sourceFile); visit(sourceFile);
return hasFunctionOrClass; return hasFunctionOrClass;
} };
const main = async () => { const main = async () => {
// Run a git command to get a list of all changed files in the branch (branch has to be up to date with master)
const changedFiles = await execAsync(
'git diff --name-only --diff-filter=d origin/master..HEAD',
).then(({ stdout }) => stdout.trim().split('\n').filter(Boolean));
// Run a git command to get a list of all changed files in the branch (branch has to be up to date with master) // Get all .spec.ts and .test.ts files from the packages
const changedFiles = await execAsync('git diff --name-only --diff-filter=d origin/master..HEAD')
.then(({stdout}) => stdout.trim().split('\n').filter(Boolean));
// Get all .spec.ts and .test.ts files from the packages
const specAndTestTsFiles = await glob('packages/*/**/{test,__tests__}/**/*.{spec,test}.ts'); const specAndTestTsFiles = await glob('packages/*/**/{test,__tests__}/**/*.{spec,test}.ts');
const specAndTestTsFilesNames = specAndTestTsFiles.map(file => path.parse(file).name.replace(/\.(test|spec)/, '')); const specAndTestTsFilesNames = specAndTestTsFiles.map((file) =>
path.parse(file).name.replace(/\.(test|spec)/, ''),
// Filter out the .ts and .vue files from the changed files
const changedVueFiles = changedFiles.filter(file => file.endsWith('.vue'));
// .ts files with any kind of function declaration or class and not in any of the test folders
const changedTsFilesWithFunction = await filterAsync(
async filePath =>
filePath.endsWith('.ts') &&
!(await glob('packages/*/**/{test,__tests__}/*.ts')).includes(filePath) &&
await hasFunctionOrClass(filePath),
changedFiles
); );
// For each .ts or .vue file, check if there's a corresponding .test.ts or .spec.ts file in the repository // Filter out the .ts and .vue files from the changed files
const missingTests = changedVueFiles.concat(changedTsFilesWithFunction).reduce((filesList, nextFile) => { const changedVueFiles = changedFiles.filter((file) => file.endsWith('.vue'));
const fileName = path.parse(nextFile).name; // .ts files with any kind of function declaration or class and not in any of the test folders
const changedTsFilesWithFunction = await filterAsync(
async (filePath) =>
filePath.endsWith('.ts') &&
!(await glob('packages/*/**/{test,__tests__}/*.ts')).includes(filePath) &&
(await hasFunctionOrClass(filePath)),
changedFiles,
);
if (!specAndTestTsFilesNames.includes(fileName)) { // For each .ts or .vue file, check if there's a corresponding .test.ts or .spec.ts file in the repository
filesList.push(nextFile); const missingTests = changedVueFiles
} .concat(changedTsFilesWithFunction)
.reduce((filesList, nextFile) => {
const fileName = path.parse(nextFile).name;
return filesList; if (!specAndTestTsFilesNames.includes(fileName)) {
}, []); filesList.push(nextFile);
}
if(missingTests.length) { return filesList;
}, []);
if (missingTests.length) {
console.error(`Missing tests for:\n${missingTests.join('\n')}`); console.error(`Missing tests for:\n${missingTests.join('\n')}`);
process.exit(1); process.exit(1);
} }

View file

@ -2,6 +2,7 @@ coverage
dist dist
package.json package.json
.pnpm-lock.yml .pnpm-lock.yml
packages/editor-ui/index.html
packages/nodes-base/nodes/**/test packages/nodes-base/nodes/**/test
cypress/fixtures cypress/fixtures
CHANGELOG.md CHANGELOG.md

View file

@ -9,6 +9,7 @@
"typescript.format.enable": false, "typescript.format.enable": false,
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"workspace-default-settings.runOnActivation": true, "workspace-default-settings.runOnActivation": true,
"prettier.prettierPath": "node_modules/prettier/index.cjs",
"eslint.probe": ["javascript", "typescript", "vue"], "eslint.probe": ["javascript", "typescript", "vue"],
"eslint.workingDirectories": [ "eslint.workingDirectories": [
{ {

View file

@ -54,16 +54,22 @@ describe('Resource Locator', () => {
// unlike RMC and remote options, RLC does not support loadOptionDependsOn // unlike RMC and remote options, RLC does not support loadOptionDependsOn
it('should retrieve list options when other params throw errors', () => { it('should retrieve list options when other params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Resource Locator'}); workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Resource Locator' });
ndv.getters.resourceLocatorInput('rlc').click(); ndv.getters.resourceLocatorInput('rlc').click();
getVisiblePopper().should('have.length', 1).findChildByTestId('rlc-item').should('have.length', 5); getVisiblePopper()
.should('have.length', 1)
.findChildByTestId('rlc-item')
.should('have.length', 5);
ndv.actions.setInvalidExpression('fieldId'); ndv.actions.setInvalidExpression('fieldId');
ndv.getters.container().click(); // remove focus from input, hide expression preview ndv.getters.container().click(); // remove focus from input, hide expression preview
ndv.getters.resourceLocatorInput('rlc').click(); ndv.getters.resourceLocatorInput('rlc').click();
getVisiblePopper().should('have.length', 1).findChildByTestId('rlc-item').should('have.length', 5); getVisiblePopper()
.should('have.length', 1)
.findChildByTestId('rlc-item')
.should('have.length', 5);
}); });
}); });

View file

@ -42,7 +42,7 @@ describe('Two-factor authentication', () => {
signinPage.actions.loginWithEmailAndPassword(email, password); signinPage.actions.loginWithEmailAndPassword(email, password);
personalSettingsPage.actions.enableMfa(); personalSettingsPage.actions.enableMfa();
mainSidebar.actions.signout(); mainSidebar.actions.signout();
const token = generateOTPToken(user.mfaSecret) const token = generateOTPToken(user.mfaSecret);
mfaLoginPage.actions.loginWithMfaToken(email, password, token); mfaLoginPage.actions.loginWithMfaToken(email, password, token);
mainSidebar.actions.signout(); mainSidebar.actions.signout();
}); });
@ -61,7 +61,7 @@ describe('Two-factor authentication', () => {
signinPage.actions.loginWithEmailAndPassword(email, password); signinPage.actions.loginWithEmailAndPassword(email, password);
personalSettingsPage.actions.enableMfa(); personalSettingsPage.actions.enableMfa();
mainSidebar.actions.signout(); mainSidebar.actions.signout();
const token = generateOTPToken(user.mfaSecret) const token = generateOTPToken(user.mfaSecret);
mfaLoginPage.actions.loginWithMfaToken(email, password, token); mfaLoginPage.actions.loginWithMfaToken(email, password, token);
personalSettingsPage.actions.disableMfa(); personalSettingsPage.actions.disableMfa();
mainSidebar.actions.signout(); mainSidebar.actions.signout();

View file

@ -1,5 +1,6 @@
import { import {
HTTP_REQUEST_NODE_NAME, IF_NODE_NAME, HTTP_REQUEST_NODE_NAME,
IF_NODE_NAME,
INSTANCE_OWNER, INSTANCE_OWNER,
MANUAL_TRIGGER_NODE_NAME, MANUAL_TRIGGER_NODE_NAME,
SET_NODE_NAME, SET_NODE_NAME,
@ -11,119 +12,121 @@ const ndv = new NDV();
const executionsTab = new WorkflowExecutionsTab(); const executionsTab = new WorkflowExecutionsTab();
describe('Debug', () => { describe('Debug', () => {
it('should be able to debug executions', () => { it('should be able to debug executions', () => {
cy.intercept('GET', '/rest/settings', (req) => { cy.intercept('GET', '/rest/settings', (req) => {
req.on('response', (res) => { req.on('response', (res) => {
res.send({ res.send({
data: { ...res.body.data, enterprise: { debugInEditor: true } }, data: { ...res.body.data, enterprise: { debugInEditor: true } },
}); });
}); });
}).as('loadSettings'); }).as('loadSettings');
cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions'); cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');
cy.intercept('GET', '/rest/executions/*').as('getExecution'); cy.intercept('GET', '/rest/executions/*').as('getExecution');
cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions'); cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions');
cy.intercept('POST', '/rest/workflows/run').as('postWorkflowRun'); cy.intercept('POST', '/rest/workflows/run').as('postWorkflowRun');
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
workflowPage.actions.visit(); workflowPage.actions.visit();
workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME); workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME);
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME); workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME);
ndv.actions.typeIntoParameterInput('url', 'https://foo.bar'); ndv.actions.typeIntoParameterInput('url', 'https://foo.bar');
ndv.actions.close(); ndv.actions.close();
workflowPage.actions.addNodeToCanvas(SET_NODE_NAME, true); workflowPage.actions.addNodeToCanvas(SET_NODE_NAME, true);
workflowPage.actions.saveWorkflowUsingKeyboardShortcut(); workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
workflowPage.actions.executeWorkflow(); workflowPage.actions.executeWorkflow();
cy.wait(['@postWorkflowRun']); cy.wait(['@postWorkflowRun']);
executionsTab.actions.switchToExecutionsTab(); executionsTab.actions.switchToExecutionsTab();
cy.wait(['@getExecutions', '@getCurrentExecutions']); cy.wait(['@getExecutions', '@getCurrentExecutions']);
executionsTab.getters.executionDebugButton().should('have.text', 'Debug in editor').click(); executionsTab.getters.executionDebugButton().should('have.text', 'Debug in editor').click();
cy.get('.el-notification').contains('Execution data imported').should('be.visible'); cy.get('.el-notification').contains('Execution data imported').should('be.visible');
cy.get('.matching-pinned-nodes-confirmation').should('not.exist'); cy.get('.matching-pinned-nodes-confirmation').should('not.exist');
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME);
ndv.actions.clearParameterInput('url');
ndv.actions.typeIntoParameterInput('url', 'https://postman-echo.com/get?foo1=bar1&foo2=bar2');
ndv.actions.close();
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME); workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
ndv.actions.clearParameterInput('url'); workflowPage.actions.executeWorkflow();
ndv.actions.typeIntoParameterInput('url', 'https://postman-echo.com/get?foo1=bar1&foo2=bar2');
ndv.actions.close();
workflowPage.actions.saveWorkflowUsingKeyboardShortcut(); cy.wait(['@postWorkflowRun']);
workflowPage.actions.executeWorkflow();
cy.wait(['@postWorkflowRun']); workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME);
ndv.actions.pinData();
ndv.actions.close();
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME); executionsTab.actions.switchToExecutionsTab();
ndv.actions.pinData();
ndv.actions.close();
executionsTab.actions.switchToExecutionsTab(); cy.wait(['@getExecutions', '@getCurrentExecutions']);
cy.wait(['@getExecutions', '@getCurrentExecutions']); executionsTab.getters.executionListItems().should('have.length', 2).first().click();
cy.wait(['@getExecution']);
executionsTab.getters.executionListItems().should('have.length', 2).first().click(); executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
cy.wait(['@getExecution']);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click(); let confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 2);
confirmDialog.get('.btn--cancel').click();
let confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible'); cy.wait(['@getExecutions', '@getCurrentExecutions']);
confirmDialog.find('li').should('have.length', 2);
confirmDialog.get('.btn--cancel').click();
cy.wait(['@getExecutions', '@getCurrentExecutions']); executionsTab.getters.executionListItems().should('have.length', 2).first().click();
cy.wait(['@getExecution']);
executionsTab.getters.executionListItems().should('have.length', 2).first().click(); executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
cy.wait(['@getExecution']);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click(); confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 2);
confirmDialog.get('.btn--confirm').click();
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible'); workflowPage.getters.canvasNodes().first().should('have.descendants', '.node-pin-data-icon');
confirmDialog.find('li').should('have.length', 2); workflowPage.getters
confirmDialog.get('.btn--confirm').click(); .canvasNodes()
.not(':first')
.should('not.have.descendants', '.node-pin-data-icon');
workflowPage.getters.canvasNodes().first().should('have.descendants', '.node-pin-data-icon'); cy.reload(true);
workflowPage.getters.canvasNodes().not(':first').should('not.have.descendants', '.node-pin-data-icon'); cy.wait(['@getExecution']);
cy.reload(true); confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
cy.wait(['@getExecution']); confirmDialog.find('li').should('have.length', 1);
confirmDialog.get('.btn--confirm').click();
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible'); workflowPage.getters.canvasNodePlusEndpointByName(SET_NODE_NAME).click();
confirmDialog.find('li').should('have.length', 1); workflowPage.actions.addNodeToCanvas(IF_NODE_NAME, false);
confirmDialog.get('.btn--confirm').click(); workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
workflowPage.getters.canvasNodePlusEndpointByName(SET_NODE_NAME).click(); executionsTab.actions.switchToExecutionsTab();
workflowPage.actions.addNodeToCanvas(IF_NODE_NAME, false); cy.wait(['@getExecutions', '@getCurrentExecutions']);
workflowPage.actions.saveWorkflowUsingKeyboardShortcut(); executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
executionsTab.actions.switchToExecutionsTab(); confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
cy.wait(['@getExecutions', '@getCurrentExecutions']); confirmDialog.find('li').should('have.length', 1);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click(); confirmDialog.get('.btn--confirm').click();
workflowPage.getters.canvasNodes().last().find('.node-info-icon').should('be.empty');
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible'); workflowPage.getters.canvasNodes().first().dblclick();
confirmDialog.find('li').should('have.length', 1); ndv.getters.pinDataButton().click();
confirmDialog.get('.btn--confirm').click(); ndv.actions.close();
workflowPage.getters.canvasNodes().last().find('.node-info-icon').should('be.empty');
workflowPage.getters.canvasNodes().first().dblclick(); workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
ndv.getters.pinDataButton().click(); workflowPage.actions.executeWorkflow();
ndv.actions.close(); workflowPage.actions.deleteNode(IF_NODE_NAME);
workflowPage.actions.saveWorkflowUsingKeyboardShortcut(); executionsTab.actions.switchToExecutionsTab();
workflowPage.actions.executeWorkflow(); cy.wait(['@getExecutions', '@getCurrentExecutions']);
workflowPage.actions.deleteNode(IF_NODE_NAME); executionsTab.getters.executionListItems().should('have.length', 3).first().click();
cy.wait(['@getExecution']);
executionsTab.actions.switchToExecutionsTab(); executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
cy.wait(['@getExecutions', '@getCurrentExecutions']); cy.get('.el-notification').contains("Some execution data wasn't imported").should('be.visible');
executionsTab.getters.executionListItems().should('have.length', 3).first().click(); });
cy.wait(['@getExecution']);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
cy.get('.el-notification').contains('Some execution data wasn\'t imported').should('be.visible');
});
}); });

View file

@ -9,9 +9,15 @@ describe('Resource Mapper', () => {
}); });
it('should not retrieve list options when required params throw errors', () => { it('should not retrieve list options when required params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Resource Mapping Component'}); workflowPage.actions.addInitialNodeToCanvas('E2e Test', {
action: 'Resource Mapping Component',
});
ndv.getters.resourceMapperFieldsContainer().should('be.visible').findChildByTestId('parameter-input').should('have.length', 2); ndv.getters
.resourceMapperFieldsContainer()
.should('be.visible')
.findChildByTestId('parameter-input')
.should('have.length', 2);
ndv.actions.setInvalidExpression('fieldId'); ndv.actions.setInvalidExpression('fieldId');
@ -20,13 +26,23 @@ describe('Resource Mapper', () => {
}); });
it('should retrieve list options when optional params throw errors', () => { it('should retrieve list options when optional params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Resource Mapping Component'}); workflowPage.actions.addInitialNodeToCanvas('E2e Test', {
action: 'Resource Mapping Component',
});
ndv.getters.resourceMapperFieldsContainer().should('be.visible').findChildByTestId('parameter-input').should('have.length', 2); ndv.getters
.resourceMapperFieldsContainer()
.should('be.visible')
.findChildByTestId('parameter-input')
.should('have.length', 2);
ndv.actions.setInvalidExpression('otherField'); ndv.actions.setInvalidExpression('otherField');
ndv.actions.refreshResourceMapperColumns(); ndv.actions.refreshResourceMapperColumns();
ndv.getters.resourceMapperFieldsContainer().should('be.visible').findChildByTestId('parameter-input').should('have.length', 2); ndv.getters
.resourceMapperFieldsContainer()
.should('be.visible')
.findChildByTestId('parameter-input')
.should('have.length', 2);
}); });
}); });

View file

@ -291,7 +291,7 @@ describe('NDV', () => {
}); });
it('should not retrieve remote options when required params throw errors', () => { it('should not retrieve remote options when required params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Remote Options'}); workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
ndv.getters.parameterInput('remoteOptions').click(); ndv.getters.parameterInput('remoteOptions').click();
getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3); getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3);
@ -308,7 +308,7 @@ describe('NDV', () => {
}); });
it('should retrieve remote options when non-required params throw errors', () => { it('should retrieve remote options when non-required params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Remote Options'}); workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
ndv.getters.parameterInput('remoteOptions').click(); ndv.getters.parameterInput('remoteOptions').click();
getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3); getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3);
@ -338,7 +338,7 @@ describe('NDV', () => {
workflowPage.getters.nodeIssuesByName('Webhook').should('exist'); workflowPage.getters.nodeIssuesByName('Webhook').should('exist');
workflowPage.getters.canvasNodes().first().dblclick(); workflowPage.getters.canvasNodes().first().dblclick();
ndv.getters.parameterInput('path').type('t') ndv.getters.parameterInput('path').type('t');
ndv.getters.nodeExecuteButton().should('not.be.disabled'); ndv.getters.nodeExecuteButton().should('not.be.disabled');
ndv.getters.triggerPanelExecuteButton().should('exist'); ndv.getters.triggerPanelExecuteButton().should('exist');

View file

@ -13,7 +13,7 @@ describe('Code node', () => {
}); });
it('should show correct placeholders switching modes', () => { it('should show correct placeholders switching modes', () => {
cy.contains("// Loop over input items and add a new field").should('be.visible'); cy.contains('// Loop over input items and add a new field').should('be.visible');
ndv.getters.parameterInput('mode').click(); ndv.getters.parameterInput('mode').click();
ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item'); ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item');
@ -22,8 +22,8 @@ describe('Code node', () => {
ndv.getters.parameterInput('mode').click(); ndv.getters.parameterInput('mode').click();
ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for All Items'); ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for All Items');
cy.contains("// Loop over input items and add a new field").should('be.visible'); cy.contains('// Loop over input items and add a new field').should('be.visible');
}) });
it('should execute the placeholder successfully in both modes', () => { it('should execute the placeholder successfully in both modes', () => {
ndv.actions.execute(); ndv.actions.execute();
@ -36,7 +36,7 @@ describe('Code node', () => {
WorkflowPage.getters.successToast().contains('Node executed successfully'); WorkflowPage.getters.successToast().contains('Node executed successfully');
}); });
}) });
describe('Ask AI', () => { describe('Ask AI', () => {
it('tab should display based on experiment', () => { it('tab should display based on experiment', () => {
@ -53,8 +53,8 @@ describe('Code node', () => {
win.featureFlags.override('011_ask_AI', undefined); win.featureFlags.override('011_ask_AI', undefined);
WorkflowPage.actions.openNode('Code'); WorkflowPage.actions.openNode('Code');
cy.getByTestId('code-node-tab-ai').should('not.exist'); cy.getByTestId('code-node-tab-ai').should('not.exist');
}) });
}) });
describe('Enabled', () => { describe('Enabled', () => {
beforeEach(() => { beforeEach(() => {
@ -63,20 +63,19 @@ describe('Code node', () => {
win.featureFlags.override('011_ask_AI', 'gpt3'); win.featureFlags.override('011_ask_AI', 'gpt3');
WorkflowPage.actions.addInitialNodeToCanvas('Manual'); WorkflowPage.actions.addInitialNodeToCanvas('Manual');
WorkflowPage.actions.addNodeToCanvas('Code', true, true); WorkflowPage.actions.addNodeToCanvas('Code', true, true);
}) });
}) });
it('tab should exist if experiment selected and be selectable', () => { it('tab should exist if experiment selected and be selectable', () => {
cy.getByTestId('code-node-tab-ai').should('exist'); cy.getByTestId('code-node-tab-ai').should('exist');
cy.get('#tab-ask-ai').click(); cy.get('#tab-ask-ai').click();
cy.contains('Hey AI, generate JavaScript').should('exist'); cy.contains('Hey AI, generate JavaScript').should('exist');
}) });
it('generate code button should have correct state & tooltips', () => { it('generate code button should have correct state & tooltips', () => {
cy.getByTestId('code-node-tab-ai').should('exist'); cy.getByTestId('code-node-tab-ai').should('exist');
cy.get('#tab-ask-ai').click(); cy.get('#tab-ask-ai').click();
cy.getByTestId('ask-ai-cta').should('be.disabled'); cy.getByTestId('ask-ai-cta').should('be.disabled');
cy.getByTestId('ask-ai-cta').realHover(); cy.getByTestId('ask-ai-cta').realHover();
cy.getByTestId('ask-ai-cta-tooltip-no-input-data').should('exist'); cy.getByTestId('ask-ai-cta-tooltip-no-input-data').should('exist');
@ -85,7 +84,7 @@ describe('Code node', () => {
cy.getByTestId('ask-ai-cta-tooltip-no-prompt').should('exist'); cy.getByTestId('ask-ai-cta-tooltip-no-prompt').should('exist');
cy.getByTestId('ask-ai-prompt-input') cy.getByTestId('ask-ai-prompt-input')
// Type random 14 character string // Type random 14 character string
.type([...Array(14)].map(() => (Math.random() * 36 | 0).toString(36)).join('')) .type([...Array(14)].map(() => ((Math.random() * 36) | 0).toString(36)).join(''));
cy.getByTestId('ask-ai-cta').realHover(); cy.getByTestId('ask-ai-cta').realHover();
cy.getByTestId('ask-ai-cta-tooltip-prompt-too-short').should('exist'); cy.getByTestId('ask-ai-cta-tooltip-prompt-too-short').should('exist');
@ -93,64 +92,66 @@ describe('Code node', () => {
cy.getByTestId('ask-ai-prompt-input') cy.getByTestId('ask-ai-prompt-input')
.clear() .clear()
// Type random 15 character string // Type random 15 character string
.type([...Array(15)].map(() => (Math.random() * 36 | 0).toString(36)).join('')) .type([...Array(15)].map(() => ((Math.random() * 36) | 0).toString(36)).join(''));
cy.getByTestId('ask-ai-cta').should('be.enabled'); cy.getByTestId('ask-ai-cta').should('be.enabled');
cy.getByTestId('ask-ai-prompt-counter').should('contain.text', '15 / 600'); cy.getByTestId('ask-ai-prompt-counter').should('contain.text', '15 / 600');
}) });
it('should send correct schema and replace code', () => { it('should send correct schema and replace code', () => {
const prompt = [...Array(20)].map(() => (Math.random() * 36 | 0).toString(36)).join(''); const prompt = [...Array(20)].map(() => ((Math.random() * 36) | 0).toString(36)).join('');
cy.get('#tab-ask-ai').click(); cy.get('#tab-ask-ai').click();
ndv.actions.executePrevious(); ndv.actions.executePrevious();
cy.getByTestId('ask-ai-prompt-input').type(prompt) cy.getByTestId('ask-ai-prompt-input').type(prompt);
cy.intercept('POST', '/rest/ask-ai', { cy.intercept('POST', '/rest/ask-ai', {
statusCode: 200, statusCode: 200,
body: { body: {
data: { data: {
code: 'console.log("Hello World")' code: 'console.log("Hello World")',
}, },
} },
}).as('ask-ai'); }).as('ask-ai');
cy.getByTestId('ask-ai-cta').click(); cy.getByTestId('ask-ai-cta').click();
const askAiReq = cy.wait('@ask-ai') const askAiReq = cy.wait('@ask-ai');
askAiReq.its('request.body').should('have.keys', ['question', 'model', 'context', 'n8nVersion']); askAiReq
.its('request.body')
.should('have.keys', ['question', 'model', 'context', 'n8nVersion']);
askAiReq.its('context').should('have.keys', ['schema', 'ndvSessionId', 'sessionId']); askAiReq.its('context').should('have.keys', ['schema', 'ndvSessionId', 'sessionId']);
cy.contains('Code generation completed').should('be.visible') cy.contains('Code generation completed').should('be.visible');
cy.getByTestId('code-node-tab-code').should('contain.text', 'console.log("Hello World")'); cy.getByTestId('code-node-tab-code').should('contain.text', 'console.log("Hello World")');
cy.get('#tab-code').should('have.class', 'is-active'); cy.get('#tab-code').should('have.class', 'is-active');
}) });
it('should show error based on status code', () => { it('should show error based on status code', () => {
const prompt = [...Array(20)].map(() => (Math.random() * 36 | 0).toString(36)).join(''); const prompt = [...Array(20)].map(() => ((Math.random() * 36) | 0).toString(36)).join('');
cy.get('#tab-ask-ai').click(); cy.get('#tab-ask-ai').click();
ndv.actions.executePrevious(); ndv.actions.executePrevious();
cy.getByTestId('ask-ai-prompt-input').type(prompt) cy.getByTestId('ask-ai-prompt-input').type(prompt);
const handledCodes = [ const handledCodes = [
{ code: 400, message: 'Code generation failed due to an unknown reason' }, { code: 400, message: 'Code generation failed due to an unknown reason' },
{ code: 413, message: 'Your workflow data is too large for AI to process' }, { code: 413, message: 'Your workflow data is too large for AI to process' },
{ code: 429, message: 'We\'ve hit our rate limit with our AI partner' }, { code: 429, message: "We've hit our rate limit with our AI partner" },
{ code: 500, message: 'Code generation failed due to an unknown reason' }, { code: 500, message: 'Code generation failed due to an unknown reason' },
] ];
handledCodes.forEach(({ code, message }) => { handledCodes.forEach(({ code, message }) => {
cy.intercept('POST', '/rest/ask-ai', { cy.intercept('POST', '/rest/ask-ai', {
statusCode: code, statusCode: code,
status: code, status: code,
}).as('ask-ai'); }).as('ask-ai');
cy.getByTestId('ask-ai-cta').click(); cy.getByTestId('ask-ai-cta').click();
cy.contains(message).should('be.visible') cy.contains(message).should('be.visible');
}) });
}) });
}) });
}); });
}); });

View file

@ -39,7 +39,11 @@ export class NDV extends BasePage {
inlineExpressionEditorInput: () => cy.getByTestId('inline-expression-editor-input'), inlineExpressionEditorInput: () => cy.getByTestId('inline-expression-editor-input'),
nodeParameters: () => cy.getByTestId('node-parameters'), nodeParameters: () => cy.getByTestId('node-parameters'),
parameterInput: (parameterName: string) => cy.getByTestId(`parameter-input-${parameterName}`), parameterInput: (parameterName: string) => cy.getByTestId(`parameter-input-${parameterName}`),
parameterInputIssues: (parameterName: string) => cy.getByTestId(`parameter-input-${parameterName}`).should('have.length', 1).findChildByTestId('parameter-issues'), parameterInputIssues: (parameterName: string) =>
cy
.getByTestId(`parameter-input-${parameterName}`)
.should('have.length', 1)
.findChildByTestId('parameter-issues'),
parameterExpressionPreview: (parameterName: string) => parameterExpressionPreview: (parameterName: string) =>
this.getters this.getters
.nodeParameters() .nodeParameters()
@ -102,7 +106,11 @@ export class NDV extends BasePage {
clearParameterInput: (parameterName: string) => { clearParameterInput: (parameterName: string) => {
this.getters.parameterInput(parameterName).type(`{selectall}{backspace}`); this.getters.parameterInput(parameterName).type(`{selectall}{backspace}`);
}, },
typeIntoParameterInput: (parameterName: string, content: string, opts?: { parseSpecialCharSequences: boolean }) => { typeIntoParameterInput: (
parameterName: string,
content: string,
opts?: { parseSpecialCharSequences: boolean },
) => {
this.getters.parameterInput(parameterName).type(content, opts); this.getters.parameterInput(parameterName).type(content, opts);
}, },
selectOptionInParameterDropdown: (parameterName: string, content: string) => { selectOptionInParameterDropdown: (parameterName: string, content: string) => {
@ -177,16 +185,22 @@ export class NDV extends BasePage {
refreshResourceMapperColumns: () => { refreshResourceMapperColumns: () => {
this.getters.resourceMapperSelectColumn().realHover(); this.getters.resourceMapperSelectColumn().realHover();
this.getters.resourceMapperSelectColumn().findChildByTestId('action-toggle').should('have.length', 1).click(); this.getters
.resourceMapperSelectColumn()
.findChildByTestId('action-toggle')
.should('have.length', 1)
.click();
getVisiblePopper().find('li').last().click(); getVisiblePopper().find('li').last().click();
}, },
setInvalidExpression: (fieldName: string, invalidExpression?: string) => { setInvalidExpression: (fieldName: string, invalidExpression?: string) => {
this.actions.typeIntoParameterInput(fieldName, "="); this.actions.typeIntoParameterInput(fieldName, '=');
this.actions.typeIntoParameterInput(fieldName, invalidExpression ?? "{{ $('unknown')", { parseSpecialCharSequences: false }); this.actions.typeIntoParameterInput(fieldName, invalidExpression ?? "{{ $('unknown')", {
parseSpecialCharSequences: false,
});
this.actions.validateExpressionPreview(fieldName, `node doesn't exist`); this.actions.validateExpressionPreview(fieldName, `node doesn't exist`);
} },
}; };
} }

View file

@ -62,7 +62,7 @@ export class PersonalSettingsPage extends BasePage {
this.getters.enableMfaButton().click(); this.getters.enableMfaButton().click();
mfaSetupModal.getters.copySecretToClipboardButton().realClick(); mfaSetupModal.getters.copySecretToClipboardButton().realClick();
cy.readClipboard().then((secret) => { cy.readClipboard().then((secret) => {
const token = generateOTPToken(secret) const token = generateOTPToken(secret);
mfaSetupModal.getters.tokenInput().type(token); mfaSetupModal.getters.tokenInput().type(token);
mfaSetupModal.getters.downloadRecoveryCodesButton().click(); mfaSetupModal.getters.downloadRecoveryCodesButton().click();

View file

@ -29,9 +29,11 @@ export class WorkflowPage extends BasePage {
canvasNodeByName: (nodeName: string) => canvasNodeByName: (nodeName: string) =>
this.getters.canvasNodes().filter(`:contains(${nodeName})`), this.getters.canvasNodes().filter(`:contains(${nodeName})`),
nodeIssuesByName: (nodeName: string) => nodeIssuesByName: (nodeName: string) =>
this.getters.canvasNodes().filter(`:contains(${nodeName})`) this.getters
.should('have.length.greaterThan', 0) .canvasNodes()
.findChildByTestId('node-issues'), .filter(`:contains(${nodeName})`)
.should('have.length.greaterThan', 0)
.findChildByTestId('node-issues'),
getEndpointSelector: (type: 'input' | 'output' | 'plus', nodeName: string, index = 0) => { getEndpointSelector: (type: 'input' | 'output' | 'plus', nodeName: string, index = 0) => {
return `[data-endpoint-name='${nodeName}'][data-endpoint-type='${type}'][data-input-index='${index}']`; return `[data-endpoint-name='${nodeName}'][data-endpoint-type='${type}'][data-input-index='${index}']`;
}, },
@ -132,14 +134,16 @@ export class WorkflowPage extends BasePage {
win.preventNodeViewBeforeUnload = preventNodeViewUnload; win.preventNodeViewBeforeUnload = preventNodeViewUnload;
}); });
}, },
addInitialNodeToCanvas: (nodeDisplayName: string, opts?: { keepNdvOpen?: boolean, action?: string }) => { addInitialNodeToCanvas: (
nodeDisplayName: string,
opts?: { keepNdvOpen?: boolean; action?: string },
) => {
this.getters.canvasPlusButton().click(); this.getters.canvasPlusButton().click();
this.getters.nodeCreatorSearchBar().type(nodeDisplayName); this.getters.nodeCreatorSearchBar().type(nodeDisplayName);
this.getters.nodeCreatorSearchBar().type('{enter}'); this.getters.nodeCreatorSearchBar().type('{enter}');
if (opts?.action) { if (opts?.action) {
nodeCreator.getters.getCreatorItem(opts.action).click(); nodeCreator.getters.getCreatorItem(opts.action).click();
} } else if (!opts?.keepNdvOpen) {
else if (!opts?.keepNdvOpen) {
cy.get('body').type('{esc}'); cy.get('body').type('{esc}');
} }
}, },

View file

@ -74,7 +74,7 @@
"@types/node": "^18.16.16", "@types/node": "^18.16.16",
"chokidar": "3.5.2", "chokidar": "3.5.2",
"jsonwebtoken": "9.0.0", "jsonwebtoken": "9.0.0",
"prettier": "^3.0.0", "prettier": "^3.0.3",
"semver": "^7.5.4", "semver": "^7.5.4",
"tough-cookie": "^4.1.3", "tough-cookie": "^4.1.3",
"tslib": "^2.6.1", "tslib": "^2.6.1",

View file

@ -6,7 +6,7 @@ This list shows all the versions which include breaking changes and how to upgra
### What changed? ### What changed?
In the Code node, `console.log` does not output to stdout by default. In the Code node, `console.log` does not output to stdout by default.
### When is action necessary? ### When is action necessary?

View file

@ -56,9 +56,8 @@ export class ActiveExecutions {
fullExecutionData.workflowId = workflowId; fullExecutionData.workflowId = workflowId;
} }
const executionResult = await Container.get(ExecutionRepository).createNewExecution( const executionResult =
fullExecutionData, await Container.get(ExecutionRepository).createNewExecution(fullExecutionData);
);
executionId = executionResult.id; executionId = executionResult.id;
if (executionId === undefined) { if (executionId === undefined) {
throw new Error('There was an issue assigning an execution id to the execution'); throw new Error('There was an issue assigning an execution id to the execution');

View file

@ -1251,9 +1251,8 @@ export class Server extends AbstractServer {
Object.assign(findOptions.where, { workflowId: In(sharedWorkflowIds) }); Object.assign(findOptions.where, { workflowId: In(sharedWorkflowIds) });
} }
const executions = await Container.get(ExecutionRepository).findMultipleExecutions( const executions =
findOptions, await Container.get(ExecutionRepository).findMultipleExecutions(findOptions);
);
if (!executions.length) return []; if (!executions.length) return [];

View file

@ -54,9 +54,8 @@ export class SourceControlController {
await this.sourceControlPreferencesService.validateSourceControlPreferences( await this.sourceControlPreferencesService.validateSourceControlPreferences(
sanitizedPreferences, sanitizedPreferences,
); );
const updatedPreferences = await this.sourceControlPreferencesService.setPreferences( const updatedPreferences =
sanitizedPreferences, await this.sourceControlPreferencesService.setPreferences(sanitizedPreferences);
);
if (sanitizedPreferences.initRepo === true) { if (sanitizedPreferences.initRepo === true) {
try { try {
await this.sourceControlService.initializeRepository( await this.sourceControlService.initializeRepository(

View file

@ -317,9 +317,8 @@ export class ExecutionsService {
const workflowRunner = new WorkflowRunner(); const workflowRunner = new WorkflowRunner();
const retriedExecutionId = await workflowRunner.run(data); const retriedExecutionId = await workflowRunner.run(data);
const executionData = await Container.get(ActiveExecutions).getPostExecutePromise( const executionData =
retriedExecutionId, await Container.get(ActiveExecutions).getPostExecutePromise(retriedExecutionId);
);
if (!executionData) { if (!executionData) {
throw new Error('The retry did not start for an unknown reason.'); throw new Error('The retry did not start for an unknown reason.');

View file

@ -3,7 +3,7 @@ import { createEventBus } from '../event-bus';
// @TODO: Remove when conflicting vitest and jest globals are reconciled // @TODO: Remove when conflicting vitest and jest globals are reconciled
declare global { declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports // eslint-disable-next-line @typescript-eslint/consistent-type-imports
const vi: typeof import('vitest')['vitest']; const vi: (typeof import('vitest'))['vitest'];
} }
describe('createEventBus()', () => { describe('createEventBus()', () => {

View file

@ -65,7 +65,7 @@
"n8n-workflow": "workspace:*", "n8n-workflow": "workspace:*",
"normalize-wheel": "^1.0.1", "normalize-wheel": "^1.0.1",
"pinia": "^2.1.6", "pinia": "^2.1.6",
"prettier": "^3.0.0", "prettier": "^3.0.3",
"stream-browserify": "^3.0.0", "stream-browserify": "^3.0.0",
"qrcode.vue": "^3.3.4", "qrcode.vue": "^3.3.4",
"timeago.js": "^4.0.2", "timeago.js": "^4.0.2",

View file

@ -3891,9 +3891,8 @@ export default defineComponent({
await Promise.all([this.loadCredentials(), this.loadVariables(), this.tagsStore.fetchAll()]); await Promise.all([this.loadCredentials(), this.loadVariables(), this.tagsStore.fetchAll()]);
if (workflowId !== null && !this.uiStore.stateIsDirty) { if (workflowId !== null && !this.uiStore.stateIsDirty) {
const workflow: IWorkflowDb | undefined = await this.workflowsStore.fetchWorkflow( const workflow: IWorkflowDb | undefined =
workflowId, await this.workflowsStore.fetchWorkflow(workflowId);
);
if (workflow) { if (workflow) {
this.titleSet(workflow.name, 'IDLE'); this.titleSet(workflow.name, 'IDLE');
await this.openWorkflow(workflow); await this.openWorkflow(workflow);

View file

@ -11,7 +11,7 @@ overrides:
'@types/node': ^18.16.16 '@types/node': ^18.16.16
chokidar: 3.5.2 chokidar: 3.5.2
jsonwebtoken: 9.0.0 jsonwebtoken: 9.0.0
prettier: ^3.0.0 prettier: ^3.0.3
semver: ^7.5.4 semver: ^7.5.4
tough-cookie: ^4.1.3 tough-cookie: ^4.1.3
tslib: ^2.6.1 tslib: ^2.6.1
@ -178,7 +178,7 @@ importers:
version: 1.0.0 version: 1.0.0
eslint-plugin-prettier: eslint-plugin-prettier:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.0.0(@types/eslint@8.44.1)(eslint-config-prettier@8.9.0)(eslint@8.45.0)(prettier@3.0.0) version: 5.0.0(@types/eslint@8.44.1)(eslint-config-prettier@8.9.0)(eslint@8.45.0)(prettier@3.0.3)
eslint-plugin-unicorn: eslint-plugin-unicorn:
specifier: ^48.0.1 specifier: ^48.0.1
version: 48.0.1(eslint@8.45.0) version: 48.0.1(eslint@8.45.0)
@ -894,8 +894,8 @@ importers:
specifier: ^2.1.6 specifier: ^2.1.6
version: 2.1.6(typescript@5.2.2)(vue@3.3.4) version: 2.1.6(typescript@5.2.2)(vue@3.3.4)
prettier: prettier:
specifier: ^3.0.0 specifier: ^3.0.3
version: 3.0.0 version: 3.0.3
qrcode.vue: qrcode.vue:
specifier: ^3.3.4 specifier: ^3.3.4
version: 3.3.4(vue@3.3.4) version: 3.3.4(vue@3.3.4)
@ -5885,7 +5885,7 @@ packages:
less-loader: 11.1.3(less@4.1.3)(webpack@5.75.0) less-loader: 11.1.3(less@4.1.3)(webpack@5.75.0)
postcss: 8.4.27 postcss: 8.4.27
postcss-loader: 7.3.3(postcss@8.4.27)(webpack@5.75.0) postcss-loader: 7.3.3(postcss@8.4.27)(webpack@5.75.0)
prettier: 3.0.0 prettier: 3.0.3
react: 17.0.2 react: 17.0.2
react-dom: 18.2.0(react@17.0.2) react-dom: 18.2.0(react@17.0.2)
resolve-url-loader: 5.0.0 resolve-url-loader: 5.0.0
@ -6199,7 +6199,7 @@ packages:
jscodeshift: 0.14.0(@babel/preset-env@7.22.9) jscodeshift: 0.14.0(@babel/preset-env@7.22.9)
leven: 3.1.0 leven: 3.1.0
ora: 5.4.1 ora: 5.4.1
prettier: 3.0.0 prettier: 3.0.3
prompts: 2.4.2 prompts: 2.4.2
puppeteer-core: 2.1.1 puppeteer-core: 2.1.1
read-pkg-up: 7.0.1 read-pkg-up: 7.0.1
@ -6250,7 +6250,7 @@ packages:
globby: 11.1.0 globby: 11.1.0
jscodeshift: 0.14.0(@babel/preset-env@7.22.9) jscodeshift: 0.14.0(@babel/preset-env@7.22.9)
lodash: 4.17.21 lodash: 4.17.21
prettier: 3.0.0 prettier: 3.0.3
recast: 0.23.3 recast: 0.23.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -10620,7 +10620,7 @@ packages:
cypress: ^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x cypress: ^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x
dependencies: dependencies:
cypress: 12.17.2 cypress: 12.17.2
prettier: 3.0.0 prettier: 3.0.3
dev: true dev: true
/cypress@12.17.2: /cypress@12.17.2:
@ -11810,14 +11810,14 @@ packages:
- typescript - typescript
dev: true dev: true
/eslint-plugin-prettier@5.0.0(@types/eslint@8.44.1)(eslint-config-prettier@8.9.0)(eslint@8.45.0)(prettier@3.0.0): /eslint-plugin-prettier@5.0.0(@types/eslint@8.44.1)(eslint-config-prettier@8.9.0)(eslint@8.45.0)(prettier@3.0.3):
resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==} resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies: peerDependencies:
'@types/eslint': '>=8.0.0' '@types/eslint': '>=8.0.0'
eslint: '>=8.0.0' eslint: '>=8.0.0'
eslint-config-prettier: '*' eslint-config-prettier: '*'
prettier: ^3.0.0 prettier: ^3.0.3
peerDependenciesMeta: peerDependenciesMeta:
'@types/eslint': '@types/eslint':
optional: true optional: true
@ -11827,7 +11827,7 @@ packages:
'@types/eslint': 8.44.1 '@types/eslint': 8.44.1
eslint: 8.45.0 eslint: 8.45.0
eslint-config-prettier: 8.9.0(eslint@8.45.0) eslint-config-prettier: 8.9.0(eslint@8.45.0)
prettier: 3.0.0 prettier: 3.0.3
prettier-linter-helpers: 1.0.0 prettier-linter-helpers: 1.0.0
synckit: 0.8.5 synckit: 0.8.5
dev: true dev: true
@ -18035,8 +18035,8 @@ packages:
fast-diff: 1.2.0 fast-diff: 1.2.0
dev: true dev: true
/prettier@3.0.0: /prettier@3.0.3:
resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true