fix(editor): Restore WASM file paths for cURL import in HTTP Request node (#28610)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Matsuuu <huhta.matias@gmail.com>
This commit is contained in:
Jon 2026-04-17 13:41:56 +01:00 committed by GitHub
parent 3b248eedc2
commit 51bc71e897
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 82 additions and 21 deletions

View file

@ -1198,7 +1198,7 @@
"experiments.resourceCenter.seeMore": "See more",
"experiments.resourceCenter.sidebar": "Resources",
"experiments.resourceCenter.sidebar.inspiration": "Inspiration",
"experiments.resourceCenter.templateCard.useNow": "▶ Run workflow",
"experiments.resourceCenter.templateCard.useNow": "▶ Run workflow",
"experiments.resourceCenter.templatePreviews.title": "Popular Templates",
"experiments.resourceCenter.title": "Resources",
"experiments.resourceCenter.viewAllTemplates": "View all templates",
@ -3461,7 +3461,7 @@
"templates.collection": "Collection",
"templates.collections": "Collections",
"templates.collectionsNotFound": "Collection could not be found",
"templates.connectionWarning": "⚠️ There was a problem fetching workflow templates. Check your internet connection.",
"templates.connectionWarning": "! There was a problem fetching workflow templates. Check your internet connection.",
"templates.heading": "Workflow templates",
"templates.shareWorkflow": "Share template",
"templates.noSearchResults": "Nothing found. Try adjusting your search to see more.",
@ -3994,6 +3994,8 @@
"ImportCurlModal.notice.content": "This will overwrite any changes you have already made to the current node",
"importCurlModal.button.label": "Import",
"importCurlParameter.label": "Import cURL",
"importCurlParameter.showError.failedToLoad.title": "Failed to load dependencies",
"importCurlParameter.showError.failedToLoad.message": "There was an error loading required dependencies.",
"importCurlParameter.showError.invalidCurlCommand.title": "Couldnt import cURL command",
"importCurlParameter.showError.invalidCurlCommand.message": "This command is in an unsupported format",
"importCurlParameter.showError.invalidProtocol1.title": "Use the {node} node",

View file

@ -15,16 +15,23 @@ vi.mock('@/app/composables/useTelemetry', () => ({
}),
}));
const mockShowToast = vi.fn();
vi.mock('@/app/composables/useToast', () => ({
useToast: () => ({ showToast: mockShowToast }),
}));
const mockImportCurlCommand = vi.fn();
let mockImportOptions: {
onImportSuccess: () => void;
onImportFailure: (data: { invalidProtocol: boolean; protocol?: string }) => void;
onAfterImport: () => void;
};
vi.mock('@/app/composables/useImportCurlCommand', () => ({
useImportCurlCommand: (options: {
onImportSuccess: () => void;
onAfterImport: () => void;
}) => ({
importCurlCommand: () => {
options.onImportSuccess();
options.onAfterImport();
},
}),
useImportCurlCommand: (options: typeof mockImportOptions) => {
mockImportOptions = options;
return { importCurlCommand: mockImportCurlCommand };
},
}));
const renderModal = createComponentRenderer(ImportCurlModal, {
@ -43,6 +50,10 @@ const testNode = {
describe('ImportCurlModal', () => {
beforeEach(() => {
vi.clearAllMocks();
mockImportCurlCommand.mockImplementation(() => {
mockImportOptions.onImportSuccess();
mockImportOptions.onAfterImport();
});
});
it('should show empty input when no curl command exists for active node', async () => {
@ -149,4 +160,35 @@ describe('ImportCurlModal', () => {
'node-1': 'curl -X GET https://api.example.com/other',
});
});
it('should show error toast and track failure telemetry when import throws (e.g. WASM load failure)', async () => {
mockImportCurlCommand.mockImplementation(() => {
throw new Error('WASM failed to load');
});
const uiStore = mockedStore(useUIStore);
uiStore.modalsById = {
[IMPORT_CURL_MODAL_KEY]: {
open: true,
data: { curlCommands: {} },
},
};
uiStore.modalStack = [IMPORT_CURL_MODAL_KEY];
const ndvStore = mockedStore(useNDVStore);
ndvStore.activeNode = testNode;
const { getByTestId } = renderModal();
await nextTick();
const input = getByTestId('import-curl-modal-input');
await userEvent.type(input, 'curl -X GET https://api.example.com/data');
const button = getByTestId('import-curl-modal-button');
await userEvent.click(button);
expect(mockShowToast).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }));
expect(mockTelemetryTrack).toHaveBeenCalledWith(
'User imported curl command',
expect.objectContaining({ success: false }),
);
});
});

View file

@ -5,11 +5,13 @@ import { onMounted, ref } from 'vue';
import { useUIStore } from '@/app/stores/ui.store';
import { createEventBus } from '@n8n/utils/event-bus';
import { useTelemetry } from '@/app/composables/useTelemetry';
import { useToast } from '@/app/composables/useToast';
import { useI18n } from '@n8n/i18n';
import { useNDVStore } from '@/features/ndv/shared/ndv.store';
import { N8nButton, N8nInput, N8nInputLabel, N8nNotice } from '@n8n/design-system';
const telemetry = useTelemetry();
const toast = useToast();
const i18n = useI18n();
const uiStore = useUIStore();
@ -80,13 +82,24 @@ function sendTelemetry(
}
async function onImport() {
const { useImportCurlCommand } = await import('@/app/composables/useImportCurlCommand');
const { importCurlCommand } = useImportCurlCommand({
onImportSuccess,
onImportFailure,
onAfterImport,
});
importCurlCommand(curlCommand);
try {
const { useImportCurlCommand } = await import('@/app/composables/useImportCurlCommand');
const { importCurlCommand } = useImportCurlCommand({
onImportSuccess,
onImportFailure,
onAfterImport,
});
importCurlCommand(curlCommand);
} catch {
// Handles WASM loading failures (e.g. wrong MIME type for tree-sitter.wasm)
toast.showToast({
title: i18n.baseText('importCurlParameter.showError.failedToLoad.title'),
message: i18n.baseText('importCurlParameter.showError.failedToLoad.message'),
type: 'error',
duration: 0,
});
onImportFailure({ invalidProtocol: false });
}
}
</script>

View file

@ -460,7 +460,9 @@ const setHttpNodeParameters = (parameters: CurlToJSONResponse) => {
name: 'parameters',
value: parameters as unknown as INodeParameters,
});
} catch {}
} catch (error) {
console.error('Failed to apply cURL parameters to node:', error);
}
};
const onSwitchSelectedNode = (node: string) => {

View file

@ -110,11 +110,13 @@ const plugins: UserConfig['plugins'] = [
targets: [
{
src: 'node_modules/web-tree-sitter/tree-sitter.wasm',
dest: 'dist',
dest: '.',
rename: { stripBase: true },
},
{
src: 'node_modules/curlconverter/dist/tree-sitter-bash.wasm',
dest: 'dist',
dest: '.',
rename: { stripBase: true },
},
// wa-sqlite WASM files for OPFS database support (no cross-origin isolation needed)
{