Browse Source

refactor: service worker file

Ahmad Kholid 2 years ago
parent
commit
cc946e1425
63 changed files with 233 additions and 4582 deletions
  1. 132 0
      src/background/BackgroundEventsListeners.js
  2. 13 0
      src/background/BackgroundRecordWorkflow.js
  3. 50 1
      src/background/BackgroundWorkflowTriggers.js
  4. 5 4
      src/background/BackgroundWorkflowUtils.js
  5. 25 384
      src/background/index.js
  6. 0 18
      src/background/workflowEngine/blocksHandler.js
  7. 0 58
      src/background/workflowEngine/blocksHandler/handlerActiveTab.js
  8. 0 80
      src/background/workflowEngine/blocksHandler/handlerBlockPackage.js
  9. 0 48
      src/background/workflowEngine/blocksHandler/handlerBlocksGroup.js
  10. 0 123
      src/background/workflowEngine/blocksHandler/handlerBrowserEvent.js
  11. 0 66
      src/background/workflowEngine/blocksHandler/handlerClipboard.js
  12. 0 56
      src/background/workflowEngine/blocksHandler/handlerCloseTab.js
  13. 0 109
      src/background/workflowEngine/blocksHandler/handlerConditions.js
  14. 0 83
      src/background/workflowEngine/blocksHandler/handlerCookie.js
  15. 0 38
      src/background/workflowEngine/blocksHandler/handlerCreateElement.js
  16. 0 61
      src/background/workflowEngine/blocksHandler/handlerDataMapping.js
  17. 0 14
      src/background/workflowEngine/blocksHandler/handlerDelay.js
  18. 0 37
      src/background/workflowEngine/blocksHandler/handlerDeleteData.js
  19. 0 24
      src/background/workflowEngine/blocksHandler/handlerElementExists.js
  20. 0 143
      src/background/workflowEngine/blocksHandler/handlerExecuteWorkflow.js
  21. 0 51
      src/background/workflowEngine/blocksHandler/handlerExportData.js
  22. 0 14
      src/background/workflowEngine/blocksHandler/handlerForwardPage.js
  23. 0 14
      src/background/workflowEngine/blocksHandler/handlerGoBack.js
  24. 0 144
      src/background/workflowEngine/blocksHandler/handlerGoogleSheets.js
  25. 0 62
      src/background/workflowEngine/blocksHandler/handlerHandleDialog.js
  26. 0 137
      src/background/workflowEngine/blocksHandler/handlerHandleDownload.js
  27. 0 36
      src/background/workflowEngine/blocksHandler/handlerHoverElement.js
  28. 0 30
      src/background/workflowEngine/blocksHandler/handlerIncreaseVariable.js
  29. 0 57
      src/background/workflowEngine/blocksHandler/handlerInsertData.js
  30. 0 97
      src/background/workflowEngine/blocksHandler/handlerInteractionBlock.js
  31. 0 58
      src/background/workflowEngine/blocksHandler/handlerJavascriptCode.js
  32. 0 34
      src/background/workflowEngine/blocksHandler/handlerLogData.js
  33. 0 78
      src/background/workflowEngine/blocksHandler/handlerLoopBreakpoint.js
  34. 0 125
      src/background/workflowEngine/blocksHandler/handlerLoopData.js
  35. 0 79
      src/background/workflowEngine/blocksHandler/handlerLoopElements.js
  36. 0 106
      src/background/workflowEngine/blocksHandler/handlerNewTab.js
  37. 0 39
      src/background/workflowEngine/blocksHandler/handlerNewWindow.js
  38. 0 38
      src/background/workflowEngine/blocksHandler/handlerNotification.js
  39. 0 80
      src/background/workflowEngine/blocksHandler/handlerParameterPrompt.js
  40. 0 77
      src/background/workflowEngine/blocksHandler/handlerProxy.js
  41. 0 42
      src/background/workflowEngine/blocksHandler/handlerRegexVariable.js
  42. 0 14
      src/background/workflowEngine/blocksHandler/handlerReloadTab.js
  43. 0 23
      src/background/workflowEngine/blocksHandler/handlerRepeatTask.js
  44. 0 63
      src/background/workflowEngine/blocksHandler/handlerSaveAssets.js
  45. 0 36
      src/background/workflowEngine/blocksHandler/handlerSliceVariable.js
  46. 0 69
      src/background/workflowEngine/blocksHandler/handlerSortData.js
  47. 0 92
      src/background/workflowEngine/blocksHandler/handlerSwitchTab.js
  48. 0 54
      src/background/workflowEngine/blocksHandler/handlerSwitchTo.js
  49. 0 29
      src/background/workflowEngine/blocksHandler/handlerTabUrl.js
  50. 0 120
      src/background/workflowEngine/blocksHandler/handlerTakeScreenshot.js
  51. 0 10
      src/background/workflowEngine/blocksHandler/handlerTrigger.js
  52. 0 76
      src/background/workflowEngine/blocksHandler/handlerWaitConnections.js
  53. 0 85
      src/background/workflowEngine/blocksHandler/handlerWebhook.js
  54. 0 23
      src/background/workflowEngine/blocksHandler/handlerWhileLoop.js
  55. 0 33
      src/background/workflowEngine/blocksHandler/handlerWorkflowState.js
  56. 0 515
      src/background/workflowEngine/engine.js
  57. 0 126
      src/background/workflowEngine/helper.js
  58. 0 49
      src/background/workflowEngine/injectContentScript.js
  59. 0 408
      src/background/workflowEngine/worker.js
  60. 2 2
      src/components/newtab/shared/SharedLogsTable.vue
  61. 2 6
      src/components/newtab/shared/SharedWorkflowState.vue
  62. 2 2
      src/components/newtab/workflow/WorkflowRunning.vue
  63. 2 2
      src/newtab/pages/logs/Running.vue

+ 132 - 0
src/background/BackgroundEventsListeners.js

@@ -1,12 +1,19 @@
+import browser from 'webextension-polyfill';
 import BackgroundUtils from './BackgroundUtils';
 import BackgroundRecordWorkflow from './BackgroundRecordWorkflow';
 import BackgroundWorkflowTriggers from './BackgroundWorkflowTriggers';
 
+const validateUrl = (str) => str?.startsWith('http');
+
 class BackgroundEventsListeners {
   static onActionClicked() {
     BackgroundUtils.openDashboard();
   }
 
+  static onCommand(name) {
+    if (name === 'open-dashboard') BackgroundUtils.openDashboard();
+  }
+
   static onAlarms(event) {
     BackgroundWorkflowTriggers.scheduleWorkflow(event);
   }
@@ -21,6 +28,131 @@ class BackgroundEventsListeners {
   static onContextMenuClicked(event, tab) {
     BackgroundWorkflowTriggers.contextMenu(event, tab);
   }
+
+  static async onTabCreated(tab) {
+    const { isRecording, recording } = await browser.storage.local.get([
+      'isRecording',
+      'recording',
+    ]);
+
+    if (!isRecording || !recording) return;
+
+    const url = tab.url || tab.pendingUrl;
+    const lastFlow = recording.flows[recording.flows.length - 1];
+    const invalidPrevFlow =
+      lastFlow && lastFlow.id === 'new-tab' && !validateUrl(lastFlow.data.url);
+
+    if (!invalidPrevFlow) {
+      const validUrl = validateUrl(url) ? url : '';
+
+      recording.flows.push({
+        id: 'new-tab',
+        data: {
+          url: validUrl,
+          description: tab.title || validUrl,
+        },
+      });
+    }
+
+    recording.activeTab = {
+      url,
+      id: tab.id,
+    };
+
+    await browser.storage.local.set({ recording });
+  }
+
+  static onNotificationClicked(notificationId) {
+    if (notificationId.startsWith('logs')) {
+      const { 1: logId } = notificationId.split(':');
+      BackgroundUtils.openDashboard(`/logs/${logId}`);
+    }
+  }
+
+  static onRuntimeStartup() {
+    BackgroundWorkflowTriggers.reRegisterTriggers(true);
+  }
+
+  static async onRuntimeInstalled({ reason }) {
+    try {
+      if (reason === 'install') {
+        await browser.storage.local.set({
+          logs: [],
+          shortcuts: {},
+          workflows: [],
+          collections: [],
+          workflowState: {},
+          isFirstTime: true,
+          visitWebTriggers: [],
+        });
+        await browser.tabs.create({
+          active: true,
+          url: browser.runtime.getURL('newtab.html#/welcome'),
+        });
+
+        return;
+      }
+
+      if (reason === 'update') {
+        await BackgroundWorkflowTriggers.reRegisterTriggers();
+      }
+    } catch (error) {
+      console.error(error);
+    }
+  }
+
+  static async onTabsActivated({ tabId }) {
+    const { url, id, title } = await browser.tabs.get(tabId);
+
+    if (!validateUrl(url)) return;
+
+    BackgroundRecordWorkflow.updateRecording((recording) => {
+      recording.activeTab = { id, url };
+      recording.flows.push({
+        id: 'switch-tab',
+        description: title,
+        data: {
+          url,
+          matchPattern: url,
+          createIfNoMatch: true,
+        },
+      });
+    });
+  }
+
+  static onWebNavigationCommited({ frameId, tabId, url, transitionType }) {
+    const allowedType = ['link', 'typed'];
+    if (frameId !== 0 || !allowedType.includes(transitionType)) return;
+
+    BackgroundRecordWorkflow.updateRecording((recording) => {
+      if (tabId !== recording.activeTab.id) return;
+
+      const lastFlow = recording.flows.at(-1) ?? {};
+      const isInvalidNewtabFlow =
+        lastFlow &&
+        lastFlow.id === 'new-tab' &&
+        !validateUrl(lastFlow.data.url);
+
+      if (isInvalidNewtabFlow) {
+        lastFlow.data.url = url;
+        lastFlow.description = url;
+      } else if (validateUrl(url)) {
+        if (lastFlow?.id !== 'link' || !lastFlow.isClickLink) {
+          recording.flows.push({
+            id: 'new-tab',
+            description: url,
+            data: {
+              url,
+              updatePrevTab: recording.activeTab.id === tabId,
+            },
+          });
+        }
+
+        recording.activeTab.id = tabId;
+        recording.activeTab.url = url;
+      }
+    });
+  }
 }
 
 export default BackgroundEventsListeners;

+ 13 - 0
src/background/BackgroundRecordWorkflow.js

@@ -15,6 +15,19 @@ class BackgroundRecordWorkflow {
       files: ['recordWorkflow.bundle.js'],
     });
   }
+
+  static async updateRecording(callback) {
+    const { isRecording, recording } = await browser.storage.local.get([
+      'isRecording',
+      'recording',
+    ]);
+
+    if (!isRecording || !recording) return;
+
+    callback(recording);
+
+    await browser.storage.local.set({ recording });
+  }
 }
 
 export default BackgroundRecordWorkflow;

+ 50 - 1
src/background/BackgroundWorkflowTriggers.js

@@ -1,7 +1,11 @@
 import browser from 'webextension-polyfill';
 import dayjs from 'dayjs';
 import { findTriggerBlock, parseJSON } from '@/utils/helper';
-import { registerCronJob, registerSpecificDay } from '@/utils/workflowTrigger';
+import {
+  registerCronJob,
+  registerSpecificDay,
+  registerWorkflowTrigger,
+} from '@/utils/workflowTrigger';
 import BackgroundWorkflowUtils from './BackgroundWorkflowUtils';
 
 class BackgroundWorkflowTriggers {
@@ -140,6 +144,51 @@ class BackgroundWorkflowTriggers {
       console.error(error);
     }
   }
+
+  static async reRegisterTriggers(isStartup = false) {
+    const { workflows, workflowHosts, teamWorkflows } =
+      await browser.storage.local.get([
+        'workflows',
+        'workflowHosts',
+        'teamWorkflows',
+      ]);
+    const convertToArr = (value) =>
+      Array.isArray(value) ? value : Object.values(value);
+
+    const workflowsArr = convertToArr(workflows);
+
+    if (workflowHosts) {
+      workflowsArr.push(...convertToArr(workflowHosts));
+    }
+    if (teamWorkflows) {
+      workflowsArr.push(
+        ...BackgroundWorkflowUtils.flattenTeamWorkflows(teamWorkflows)
+      );
+    }
+
+    for (const currWorkflow of workflowsArr) {
+      let triggerBlock = currWorkflow.trigger;
+
+      if (!triggerBlock) {
+        const flow =
+          typeof currWorkflow.drawflow === 'string'
+            ? parseJSON(currWorkflow.drawflow, {})
+            : currWorkflow.drawflow;
+
+        triggerBlock = findTriggerBlock(flow)?.data;
+      }
+
+      if (triggerBlock) {
+        if (isStartup && triggerBlock.type === 'on-startup') {
+          BackgroundWorkflowUtils.executeWorkflow(currWorkflow);
+        } else {
+          await registerWorkflowTrigger(currWorkflow.id, {
+            data: triggerBlock,
+          });
+        }
+      }
+    }
+  }
 }
 
 export default BackgroundWorkflowTriggers;

+ 5 - 4
src/background/BackgroundWorkflowUtils.js

@@ -1,10 +1,11 @@
 import browser from 'webextension-polyfill';
 import BackgroundUtils from './BackgroundUtils';
 
-const flattenTeamWorkflows = (workflows) =>
-  Object.values(Object.values(workflows)[0]);
-
 class BackgroundWorkflowUtils {
+  static flattenTeamWorkflows(workflows) {
+    return Object.values(Object.values(workflows || {})[0] || {});
+  }
+
   static async getWorkflow(workflowId) {
     if (!workflowId) return null;
 
@@ -14,7 +15,7 @@ class BackgroundWorkflowUtils {
       );
       if (!teamWorkflows) return null;
 
-      const workflows = flattenTeamWorkflows(teamWorkflows);
+      const workflows = this.flattenTeamWorkflows(teamWorkflows);
 
       return workflows.find((item) => item.id === workflowId);
     }

+ 25 - 384
src/background/index.js

@@ -1,303 +1,32 @@
 import browser from 'webextension-polyfill';
-import dayjs from '@/lib/dayjs';
 import { MessageListener } from '@/utils/message';
-import { parseJSON, findTriggerBlock, sleep } from '@/utils/helper';
-import { fetchApi } from '@/utils/api';
+import { sleep } from '@/utils/helper';
 import getFile from '@/utils/getFile';
-import decryptFlow, { getWorkflowPass } from '@/utils/decryptFlow';
-import convertWorkflowData from '@/utils/convertWorkflowData';
-import getBlockMessage from '@/utils/getBlockMessage';
 import automa from '@business';
-import {
-  registerContextMenu,
-  registerWorkflowTrigger,
-} from '../utils/workflowTrigger';
-import WorkflowState from './WorkflowState';
-import WorkflowEngine from './workflowEngine/engine';
-import blocksHandler from './workflowEngine/blocksHandler';
-import WorkflowLogger from './WorkflowLogger';
+import { registerWorkflowTrigger } from '../utils/workflowTrigger';
 import BackgroundUtils from './BackgroundUtils';
+import BackgroundWorkflowUtils from './BackgroundWorkflowUtils';
 import BackgroundEventsListeners from './BackgroundEventsListeners';
 
-const validateUrl = (str) => str?.startsWith('http');
-const flattenTeamWorkflows = (workflows) =>
-  Object.values(Object.values(workflows)[0]);
-
-const browserStorage = {
-  async get(key) {
-    try {
-      const result = await browser.storage.local.get(key);
-
-      return result[key];
-    } catch (error) {
-      console.error(error);
-      return [];
-    }
-  },
-  async set(key, value) {
-    await browser.storage.local.set({ [key]: value });
-
-    if (key === 'workflowState') {
-      sessionStorage.setItem(key, JSON.stringify(value));
-    }
-  },
-};
-const localStateStorage = {
-  get(key) {
-    const data = parseJSON(localStorage.getItem(key), null);
-
-    return data;
-  },
-  set(key, value) {
-    const data = typeof value === 'object' ? JSON.stringify(value) : value;
-
-    return localStorage.setItem(key, data);
-  },
-};
-const workflow = {
-  states: new WorkflowState({ storage: localStateStorage }),
-  logger: new WorkflowLogger({ storage: browserStorage }),
-  async get(workflowId) {
-    if (!workflowId) return null;
-
-    if (workflowId.startsWith('team')) {
-      const { teamWorkflows } = await browser.storage.local.get(
-        'teamWorkflows'
-      );
-      if (!teamWorkflows) return null;
-
-      const workflows = flattenTeamWorkflows(teamWorkflows);
-
-      return workflows.find((item) => item.id === workflowId);
-    }
-
-    const { workflows, workflowHosts } = await browser.storage.local.get([
-      'workflows',
-      'workflowHosts',
-    ]);
-    let findWorkflow = Array.isArray(workflows)
-      ? workflows.find(({ id }) => id === workflowId)
-      : workflows[workflowId];
-
-    if (!findWorkflow) {
-      findWorkflow = Object.values(workflowHosts || {}).find(
-        ({ hostId }) => hostId === workflowId
-      );
-
-      if (findWorkflow) findWorkflow.id = findWorkflow.hostId;
-    }
-
-    return findWorkflow;
-  },
-  execute(workflowData, options) {
-    if (workflowData.isDisabled) return null;
-    if (workflowData.isProtected) {
-      const flow = parseJSON(workflowData.drawflow, null);
-
-      if (!flow) {
-        const pass = getWorkflowPass(workflowData.pass);
-
-        workflowData.drawflow = decryptFlow(workflowData, pass);
-      }
-    }
-
-    const convertedWorkflow = convertWorkflowData(workflowData);
-    const engine = new WorkflowEngine(convertedWorkflow, {
-      options,
-      logger: this.logger,
-      states: this.states,
-      blocksHandler: blocksHandler(),
-    });
-
-    engine.init();
-    engine.on(
-      'destroyed',
-      ({
-        id,
-        status,
-        history,
-        startedTimestamp,
-        endedTimestamp,
-        blockDetail,
-      }) => {
-        if (workflowData.id.startsWith('team') && workflowData.teamId) {
-          const payload = {
-            status,
-            workflowId: workflowData.id,
-            workflowLog: {
-              status,
-              endedTimestamp,
-              startedTimestamp,
-            },
-          };
-
-          if (status === 'error') {
-            const message = getBlockMessage(blockDetail);
-            const workflowHistory = history.map((item) => {
-              delete item.logId;
-              delete item.prevBlockData;
-              delete item.workerId;
-
-              item.description = item.description || '';
-
-              return item;
-            });
-            payload.workflowLog = {
-              status,
-              message,
-              endedTimestamp,
-              startedTimestamp,
-              history: workflowHistory,
-              blockId: blockDetail.blockId,
-            };
-          }
-
-          fetchApi(`/teams/${workflowData.teamId}/workflows/logs`, {
-            method: 'POST',
-            body: JSON.stringify(payload),
-          }).catch((error) => {
-            console.error(error);
-          });
-        }
-
-        if (status !== 'stopped') {
-          browser.permissions
-            .contains({ permissions: ['notifications'] })
-            .then((hasPermission) => {
-              if (!hasPermission || !workflowData.settings.notification) return;
-
-              const name = workflowData.name.slice(0, 32);
-
-              browser.notifications.create(`logs:${id}`, {
-                type: 'basic',
-                iconUrl: browser.runtime.getURL('icon-128.png'),
-                title: status === 'success' ? 'Success' : 'Error',
-                message: `${
-                  status === 'success' ? 'Successfully' : 'Failed'
-                } to run the "${name}" workflow`,
-              });
-            });
-        }
-      }
-    );
-
-    const lastCheckStatus = localStorage.getItem('check-status');
-    const isSameDay = dayjs().isSame(lastCheckStatus, 'day');
-    if (!isSameDay) {
-      fetchApi('/status')
-        .then((response) => response.json())
-        .then(() => {
-          localStorage.setItem('check-status', new Date());
-        });
-    }
+browser.alarms.onAlarm.addListener(BackgroundEventsListeners.onAlarms);
 
-    return engine;
-  },
-};
+browser.tabs.onCreated.addListener(BackgroundEventsListeners.onTabCreated);
+browser.tabs.onActivated.addListener(BackgroundEventsListeners.onTabsActivated);
 
-async function updateRecording(callback) {
-  const { isRecording, recording } = await browser.storage.local.get([
-    'isRecording',
-    'recording',
-  ]);
+browser.commands.onCommand.addListener(BackgroundEventsListeners.onCommand);
 
-  if (!isRecording || !recording) return;
+browser.action.onClicked.addListener(BackgroundEventsListeners.onActionClicked);
 
-  callback(recording);
+browser.runtime.onStartup.addListener(
+  BackgroundEventsListeners.onRuntimeStartup
+);
+browser.runtime.onInstalled.addListener(
+  BackgroundEventsListeners.onRuntimeInstalled
+);
 
-  await browser.storage.local.set({ recording });
-}
-browser.commands.onCommand.addListener((name) => {
-  if (name === 'open-dashboard') BackgroundUtils.openDashboard();
-});
 browser.webNavigation.onCommitted.addListener(
-  ({ frameId, tabId, url, transitionType }) => {
-    const allowedType = ['link', 'typed'];
-    if (frameId !== 0 || !allowedType.includes(transitionType)) return;
-
-    updateRecording((recording) => {
-      if (tabId !== recording.activeTab.id) return;
-
-      const lastFlow = recording.flows.at(-1) ?? {};
-      const isInvalidNewtabFlow =
-        lastFlow &&
-        lastFlow.id === 'new-tab' &&
-        !validateUrl(lastFlow.data.url);
-
-      if (isInvalidNewtabFlow) {
-        lastFlow.data.url = url;
-        lastFlow.description = url;
-      } else if (validateUrl(url)) {
-        if (lastFlow?.id !== 'link' || !lastFlow.isClickLink) {
-          recording.flows.push({
-            id: 'new-tab',
-            description: url,
-            data: {
-              url,
-              updatePrevTab: recording.activeTab.id === tabId,
-            },
-          });
-        }
-
-        recording.activeTab.id = tabId;
-        recording.activeTab.url = url;
-      }
-    });
-  }
+  BackgroundEventsListeners.onWebNavigationCommited
 );
-browser.tabs.onActivated.addListener(async ({ tabId }) => {
-  const { url, id, title } = await browser.tabs.get(tabId);
-
-  if (!validateUrl(url)) return;
-
-  updateRecording((recording) => {
-    recording.activeTab = { id, url };
-    recording.flows.push({
-      id: 'switch-tab',
-      description: title,
-      data: {
-        url,
-        matchPattern: url,
-        createIfNoMatch: true,
-      },
-    });
-  });
-});
-browser.tabs.onCreated.addListener(async (tab) => {
-  const { isRecording, recording } = await browser.storage.local.get([
-    'isRecording',
-    'recording',
-  ]);
-
-  if (!isRecording || !recording) return;
-
-  const url = tab.url || tab.pendingUrl;
-  const lastFlow = recording.flows[recording.flows.length - 1];
-  const invalidPrevFlow =
-    lastFlow && lastFlow.id === 'new-tab' && !validateUrl(lastFlow.data.url);
-
-  if (!invalidPrevFlow) {
-    const validUrl = validateUrl(url) ? url : '';
-
-    recording.flows.push({
-      id: 'new-tab',
-      data: {
-        url: validUrl,
-        description: tab.title || validUrl,
-      },
-    });
-  }
-
-  recording.activeTab = {
-    url,
-    id: tab.id,
-  };
-
-  await browser.storage.local.set({ recording });
-});
-
-browser.alarms.onAlarm.addListener(BackgroundEventsListeners.onAlarms);
-browser.action.onClicked.addListener(BackgroundEventsListeners.onActionClicked);
 browser.webNavigation.onCompleted.addListener(
   BackgroundEventsListeners.onWebNavigationCompleted
 );
@@ -311,103 +40,11 @@ if (contextMenu && contextMenu.onClicked) {
 }
 
 if (browser.notifications && browser.notifications.onClicked) {
-  browser.notifications.onClicked.addListener((notificationId) => {
-    if (notificationId.startsWith('logs')) {
-      const { 1: logId } = notificationId.split(':');
-      BackgroundUtils.openDashboard(`/logs/${logId}`);
-    }
-  });
+  browser.notifications.onClicked.addListener(
+    BackgroundEventsListeners.onNotificationClicked
+  );
 }
 
-browser.runtime.onInstalled.addListener(async ({ reason }) => {
-  try {
-    if (reason === 'install') {
-      await browser.storage.local.set({
-        logs: [],
-        shortcuts: {},
-        workflows: [],
-        collections: [],
-        workflowState: {},
-        isFirstTime: true,
-        visitWebTriggers: [],
-      });
-      await browser.tabs.create({
-        active: true,
-        url: browser.runtime.getURL('newtab.html#/welcome'),
-      });
-
-      return;
-    }
-
-    if (reason === 'update') {
-      let { workflows } = await browser.storage.local.get('workflows');
-      const alarmTypes = ['specific-day', 'date', 'interval'];
-
-      workflows = Array.isArray(workflows)
-        ? workflows
-        : Object.values(workflows);
-      workflows.forEach(({ trigger, drawflow, id }) => {
-        let workflowTrigger = trigger?.data || trigger;
-
-        if (!trigger) {
-          const flows = parseJSON(drawflow, drawflow);
-          workflowTrigger = findTriggerBlock(flows)?.data;
-        }
-
-        const triggerType = workflowTrigger?.type;
-
-        if (alarmTypes.includes(triggerType)) {
-          registerWorkflowTrigger(id, { data: workflowTrigger });
-        } else if (triggerType === 'context-menu') {
-          registerContextMenu(id, workflowTrigger);
-        }
-      });
-    }
-  } catch (error) {
-    console.error(error);
-  }
-});
-browser.runtime.onStartup.addListener(async () => {
-  const { workflows, workflowHosts, teamWorkflows } =
-    await browser.storage.local.get([
-      'workflows',
-      'workflowHosts',
-      'teamWorkflows',
-    ]);
-  const convertToArr = (value) =>
-    Array.isArray(value) ? value : Object.values(value);
-
-  const workflowsArr = convertToArr(workflows);
-
-  if (workflowHosts) {
-    workflowsArr.push(...convertToArr(workflowHosts));
-  }
-  if (teamWorkflows) {
-    workflowsArr.push(...flattenTeamWorkflows(teamWorkflows));
-  }
-
-  for (const currWorkflow of workflowsArr) {
-    let triggerBlock = currWorkflow.trigger;
-
-    if (!triggerBlock) {
-      const flow =
-        typeof currWorkflow.drawflow === 'string'
-          ? parseJSON(currWorkflow.drawflow, {})
-          : currWorkflow.drawflow;
-
-      triggerBlock = findTriggerBlock(flow)?.data;
-    }
-
-    if (triggerBlock) {
-      if (triggerBlock.type === 'on-startup') {
-        workflow.execute(currWorkflow);
-      } else {
-        await registerWorkflowTrigger(currWorkflow.id, { data: triggerBlock });
-      }
-    }
-  }
-});
-
 const message = new MessageListener('background');
 
 message.on('fetch:text', (url) => {
@@ -460,7 +97,9 @@ message.on('get:tab-screenshot', (options) =>
 );
 
 message.on('dashboard:refresh-packages', async () => {
-  const tabs = await browser.tabs.query({ url: chrome.runtime.getURL('/*') });
+  const tabs = await browser.tabs.query({
+    url: chrome.runtime.getURL('/newtab.html'),
+  });
 
   tabs.forEach((tab) => {
     browser.tabs.sendMessage(tab.id, {
@@ -476,9 +115,11 @@ message.on('workflow:execute', (workflowData, sender) => {
     workflowData.options.tabId = sender.tab.id;
   }
 
-  workflow.execute(workflowData, workflowData?.options || {});
+  BackgroundWorkflowUtils.executeWorkflow(
+    workflowData,
+    workflowData?.options || {}
+  );
 });
-message.on('workflow:stop', (id) => workflow.states.stop(id));
 message.on('workflow:added', ({ workflowId, teamId, source = 'community' }) => {
   let path = `/workflows/${workflowId}`;
 

+ 0 - 18
src/background/workflowEngine/blocksHandler.js

@@ -1,18 +0,0 @@
-import customHandlers from '@business/blocks/backgroundHandler';
-import { toCamelCase } from '@/utils/helper';
-
-const blocksHandler = require.context('./blocksHandler', false, /\.js$/);
-const handlers = blocksHandler.keys().reduce((acc, key) => {
-  const name = key.replace(/^\.\/handler|\.js/g, '');
-
-  acc[toCamelCase(name)] = blocksHandler(key).default;
-
-  return acc;
-}, {});
-
-export default function () {
-  return {
-    ...handlers,
-    ...customHandlers(),
-  };
-}

+ 0 - 58
src/background/workflowEngine/blocksHandler/handlerActiveTab.js

@@ -1,58 +0,0 @@
-import browser from 'webextension-polyfill';
-import { attachDebugger } from '../helper';
-
-async function activeTab(block) {
-  try {
-    const data = {
-      data: '',
-      nextBlockId: this.getBlockConnections(block.id),
-    };
-
-    if (this.activeTab.id) {
-      await browser.tabs.update(this.activeTab.id, { active: true });
-
-      return data;
-    }
-
-    const [tab] = await browser.tabs.query({
-      active: true,
-      url: '*://*/*',
-    });
-
-    if (!tab?.url.startsWith('http')) {
-      const error = new Error('invalid-active-tab');
-      error.data = { url: tab.url };
-
-      throw error;
-    }
-
-    this.activeTab = {
-      ...this.activeTab,
-      frameId: 0,
-      id: tab.id,
-      url: tab.url,
-    };
-    this.windowId = tab.windowId;
-
-    if (this.settings.debugMode) {
-      await attachDebugger(tab.id, this.activeTab.id);
-      this.debugAttached = true;
-    }
-
-    if (this.preloadScripts.length > 0) {
-      const preloadScripts = this.preloadScripts.map((script) =>
-        this._sendMessageToTab(script)
-      );
-      await Promise.allSettled(preloadScripts);
-    }
-
-    return data;
-  } catch (error) {
-    console.error(error);
-    error.data = error.data || {};
-
-    throw error;
-  }
-}
-
-export default activeTab;

+ 0 - 80
src/background/workflowEngine/blocksHandler/handlerBlockPackage.js

@@ -1,80 +0,0 @@
-export default async function (
-  { data, id },
-  { targetHandle: prevTarget, prevBlockData }
-) {
-  if (!this.engine.packagesCache[id]) {
-    this.engine.packagesCache[id] = { extracted: false, nodes: {} };
-  }
-
-  const pkgCache = this.engine.packagesCache[id];
-
-  const { 1: targetId } = prevTarget.split('input-');
-  const addBlockPrefix = (itemId) => `${id}__${itemId}`;
-  const hasCache = pkgCache.nodes[targetId];
-  if (hasCache)
-    return {
-      data: prevBlockData,
-      nextBlockId: [{ id: hasCache }],
-    };
-
-  const input = data.inputs.find((item) => item.id === targetId);
-  if (!input) {
-    throw new Error('Input not found');
-  }
-  const block = data.data.nodes.find((node) => node.id === input.blockId);
-  pkgCache.nodes[targetId] = addBlockPrefix(block.id);
-
-  const connections = {};
-
-  if (!pkgCache.extracted) {
-    const outputsMap = new Set();
-
-    data.inputs.forEach((item) => {
-      connections[addBlockPrefix(item.id)] = [
-        {
-          id: addBlockPrefix(item.blockId),
-          targetId: `${addBlockPrefix(block.id)}-input-1`,
-        },
-      ];
-    });
-    data.outputs.forEach((output) => {
-      outputsMap.add(output.handleId);
-
-      const connection =
-        this.engine.connectionsMap[`${id}-output-${output.id}`];
-      if (!connection) return;
-
-      connections[addBlockPrefix(output.handleId)] = [...connection];
-    });
-
-    data.data.nodes.forEach((node) => {
-      const newNodeId = addBlockPrefix(node.id);
-      this.engine.blocks[newNodeId] = { ...node, id: newNodeId };
-    });
-
-    if (!block) {
-      throw new Error(`Can't find block for this input`);
-    }
-
-    data.data.edges.forEach(({ sourceHandle, target, targetHandle }) => {
-      if (outputsMap.has(sourceHandle)) return;
-
-      const nodeSourceHandle = addBlockPrefix(sourceHandle);
-      if (!connections[nodeSourceHandle]) connections[nodeSourceHandle] = [];
-      connections[nodeSourceHandle].push({
-        id: addBlockPrefix(target),
-        sourceHandle: nodeSourceHandle,
-        targetHandle: addBlockPrefix(targetHandle),
-      });
-    });
-
-    pkgCache.extracted = true;
-  }
-
-  Object.assign(this.engine.connectionsMap, connections);
-
-  return {
-    data: prevBlockData,
-    nextBlockId: [{ id: addBlockPrefix(block.id) }],
-  };
-}

+ 0 - 48
src/background/workflowEngine/blocksHandler/handlerBlocksGroup.js

@@ -1,48 +0,0 @@
-function blocksGroup({ data, id }, { prevBlockData }) {
-  return new Promise((resolve) => {
-    const nextBlockId = this.getBlockConnections(id);
-
-    if (data.blocks.length === 0) {
-      resolve({
-        nextBlockId,
-        data: prevBlockData,
-      });
-
-      return;
-    }
-
-    const { blocks, connections } = data.blocks.reduce(
-      (acc, block, index) => {
-        const nextBlock = data.blocks[index + 1]?.itemId;
-
-        acc.blocks[block.itemId] = {
-          label: block.id,
-          data: block.data,
-          id: nextBlock ? block.itemId : id,
-        };
-
-        if (nextBlock) {
-          const outputId = `${block.itemId}-output-1`;
-
-          if (!acc.connections[outputId]) {
-            acc.connections[outputId] = [];
-          }
-          acc.connections[outputId].push({ id: nextBlock });
-        }
-
-        return acc;
-      },
-      { blocks: {}, connections: {} }
-    );
-
-    Object.assign(this.engine.blocks, blocks);
-    Object.assign(this.engine.connectionsMap, connections);
-
-    resolve({
-      data: prevBlockData,
-      nextBlockId: [{ id: data.blocks[0].itemId }],
-    });
-  });
-}
-
-export default blocksGroup;

+ 0 - 123
src/background/workflowEngine/blocksHandler/handlerBrowserEvent.js

@@ -1,123 +0,0 @@
-import browser from 'webextension-polyfill';
-import { isWhitespace } from '@/utils/helper';
-
-function handleEventListener(target, validate) {
-  return (data, activeTab) => {
-    return new Promise((resolve) => {
-      let resolved = false;
-      const eventListener = (event) => {
-        if (resolved) return;
-        if (validate && !validate(event, { data, activeTab })) return;
-
-        target.removeListener(eventListener);
-        resolve(event);
-      };
-
-      setTimeout(() => {
-        resolved = true;
-        target.removeListener(eventListener);
-        resolve('');
-      }, data.timeout || 10000);
-
-      target.addListener(eventListener);
-    });
-  };
-}
-
-function onTabLoaded({ tabLoadedUrl, activeTabLoaded, timeout }, { id }) {
-  return new Promise((resolve, reject) => {
-    let resolved = false;
-
-    const checkActiveTabStatus = () => {
-      if (resolved) return;
-      if (!id) {
-        reject(new Error('no-tab'));
-        return;
-      }
-
-      browser.tabs
-        .get(id)
-        .then((tab) => {
-          if (tab.status === 'complete') {
-            resolve();
-            return;
-          }
-
-          setTimeout(checkActiveTabStatus, 1000);
-        })
-        .catch(reject);
-    };
-
-    const url = isWhitespace(tabLoadedUrl)
-      ? '<all_urls>'
-      : tabLoadedUrl.replace(/\s/g, '').split(',');
-    const checkTabsStatus = () => {
-      browser.tabs
-        .query({
-          url,
-          status: 'loading',
-        })
-        .then((tabs) => {
-          if (resolved) return;
-          if (tabs.length === 0) {
-            resolve();
-            return;
-          }
-
-          setTimeout(checkTabsStatus, 1000);
-        })
-        .catch(reject);
-    };
-
-    if (activeTabLoaded) checkActiveTabStatus();
-    else checkTabsStatus();
-
-    setTimeout(() => {
-      resolved = true;
-      reject(new Error('timeout'));
-    }, timeout || 10000);
-  });
-}
-
-const validateCreatedTab = ({ url }, { data }) => {
-  if (!isWhitespace(data.tabUrl)) {
-    const regex = new RegExp(data.tabUrl, 'gi');
-
-    if (!regex.test(url)) return false;
-  }
-
-  return true;
-};
-const events = {
-  'tab:loaded': onTabLoaded,
-  'tab:close': handleEventListener(browser.tabs.onRemoved),
-  'tab:create': handleEventListener(
-    browser.webNavigation.onCreatedNavigationTarget,
-    validateCreatedTab
-  ),
-  'window:create': handleEventListener(
-    browser.webNavigation.onCreatedNavigationTarget,
-    validateCreatedTab
-  ),
-  'window:close': handleEventListener(browser.windows.onRemoved),
-};
-
-export default async function ({ data, id }) {
-  const currentEvent = events[data.eventName];
-
-  if (!currentEvent) {
-    throw new Error(`Can't find ${data.eventName} event`);
-  }
-
-  const result = await currentEvent(data, this.activeTab);
-
-  if (data.eventName === 'tab:create' && data.setAsActiveTab) {
-    this.activeTab.id = result.tabId;
-    this.activeTab.url = result.url;
-  }
-
-  return {
-    data: result || '',
-    nextBlockId: this.getBlockConnections(id),
-  };
-}

+ 0 - 66
src/background/workflowEngine/blocksHandler/handlerClipboard.js

@@ -1,66 +0,0 @@
-import browser from 'webextension-polyfill';
-
-function doCommand(command, value) {
-  const textarea = document.createElement('textarea');
-  document.body.appendChild(textarea);
-
-  if (command === 'paste') {
-    textarea.focus();
-    document.execCommand('paste');
-    value = textarea.value;
-  } else if (command === 'copy') {
-    textarea.value = value;
-    textarea.select();
-    document.execCommand('copy');
-    textarea.blur();
-  }
-
-  textarea.remove();
-
-  return value;
-}
-
-export default async function ({ data, id, label }) {
-  const hasPermission = await browser.permissions.contains({
-    permissions: ['clipboardRead'],
-  });
-
-  if (!hasPermission) {
-    throw new Error('no-clipboard-acces');
-  }
-
-  let valueToReturn = '';
-
-  if (!data.type || data.type === 'get') {
-    const copiedText = doCommand('paste');
-    valueToReturn = copiedText;
-
-    if (data.assignVariable) {
-      this.setVariable(data.variableName, copiedText);
-    }
-    if (data.saveData) {
-      this.addDataToColumn(data.dataColumn, copiedText);
-    }
-  } else if (data.type === 'insert') {
-    let text = '';
-
-    if (data.copySelectedText) {
-      if (!this.activeTab.id) throw new Error('no-tab');
-
-      text = await this._sendMessageToTab({
-        id,
-        label,
-      });
-    } else {
-      text = data.dataToCopy;
-    }
-
-    valueToReturn = text;
-    doCommand('copy', text);
-  }
-
-  return {
-    data: valueToReturn,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}

+ 0 - 56
src/background/workflowEngine/blocksHandler/handlerCloseTab.js

@@ -1,56 +0,0 @@
-import browser from 'webextension-polyfill';
-
-async function closeWindow(data, windowId) {
-  const windowIds = [];
-
-  if (data.allWindows) {
-    const windows = await browser.windows.getAll();
-
-    windows.forEach(({ id }) => {
-      windowIds.push(id);
-    });
-  } else {
-    let currentWindowId;
-
-    if (windowId && typeof windowId === 'number') {
-      currentWindowId = windowId;
-    } else {
-      currentWindowId = (await browser.windows.getCurrent()).id;
-    }
-
-    windowIds.push(currentWindowId);
-  }
-
-  await Promise.allSettled(windowIds.map((id) => browser.windows.remove(id)));
-}
-
-async function closeTab(data, tabId) {
-  let tabIds;
-
-  if (data.activeTab && tabId) {
-    tabIds = tabId;
-  } else if (data.url) {
-    tabIds = (await browser.tabs.query({ url: data.url })).map((tab) => tab.id);
-  }
-
-  if (tabIds) await browser.tabs.remove(tabIds);
-}
-
-export default async function ({ data, id }) {
-  if (data.closeType === 'window') {
-    await closeWindow(data, this.windowId);
-
-    this.windowId = null;
-  } else {
-    await closeTab(data, this.activeTab.id);
-
-    if (data.activeTab) {
-      this.activeTab.id = null;
-    }
-  }
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(id),
-  };
-}

+ 0 - 109
src/background/workflowEngine/blocksHandler/handlerConditions.js

@@ -1,109 +0,0 @@
-import compareBlockValue from '@/utils/compareBlockValue';
-import mustacheReplacer from '@/utils/referenceData/mustacheReplacer';
-import testConditions from '@/utils/testConditions';
-
-function checkConditions(data, conditionOptions) {
-  return new Promise((resolve, reject) => {
-    let retryCount = 1;
-    const replacedValue = {};
-
-    const testAllConditions = async () => {
-      try {
-        for (let index = 0; index < data.conditions.length; index += 1) {
-          const result = await testConditions(
-            data.conditions[index].conditions,
-            conditionOptions
-          );
-
-          Object.assign(replacedValue, result?.replacedValue || {});
-
-          if (result.isMatch) {
-            resolve({ match: true, index, replacedValue });
-            return;
-          }
-        }
-
-        if (data.retryConditions && retryCount <= data.retryCount) {
-          retryCount += 1;
-
-          setTimeout(() => {
-            testAllConditions();
-          }, data.retryTimeout);
-        } else {
-          resolve({ match: false, replacedValue });
-        }
-      } catch (error) {
-        reject(error);
-      }
-    };
-
-    testAllConditions();
-  });
-}
-
-async function conditions({ data, id }, { prevBlockData, refData }) {
-  if (data.conditions.length === 0) {
-    throw new Error('conditions-empty');
-  }
-
-  let resultData = '';
-  let isConditionMet = false;
-  let outputId = 'fallback';
-
-  const replacedValue = {};
-  const condition = data.conditions[0];
-  const prevData = Array.isArray(prevBlockData)
-    ? prevBlockData[0]
-    : prevBlockData;
-
-  if (condition && condition.conditions) {
-    const conditionPayload = {
-      refData,
-      activeTab: this.activeTab.id,
-      sendMessage: (payload) =>
-        this._sendMessageToTab({ ...payload.data, label: 'conditions', id }),
-    };
-
-    const conditionsResult = await checkConditions(data, conditionPayload);
-
-    if (conditionsResult.replacedValue) {
-      Object.assign(replacedValue, conditionsResult.replacedValue);
-    }
-    if (conditionsResult.match) {
-      isConditionMet = true;
-      outputId = data.conditions[conditionsResult.index].id;
-    }
-  } else {
-    data.conditions.forEach(({ type, value, compareValue, id: itemId }) => {
-      if (isConditionMet) return;
-
-      const firstValue = mustacheReplacer(
-        compareValue ?? prevData,
-        refData
-      ).value;
-      const secondValue = mustacheReplacer(value, refData).value;
-
-      Object.assign(replacedValue, firstValue.list, secondValue.list);
-
-      const isMatch = compareBlockValue(
-        type,
-        firstValue.value,
-        secondValue.value
-      );
-
-      if (isMatch) {
-        outputId = itemId;
-        resultData = value;
-        isConditionMet = true;
-      }
-    });
-  }
-
-  return {
-    replacedValue,
-    data: resultData,
-    nextBlockId: this.getBlockConnections(id, outputId),
-  };
-}
-
-export default conditions;

+ 0 - 83
src/background/workflowEngine/blocksHandler/handlerCookie.js

@@ -1,83 +0,0 @@
-import browser from 'webextension-polyfill';
-
-function getValues(data, keys) {
-  const values = {};
-  keys.forEach((key) => {
-    const value = data[key];
-
-    if (!value) return;
-
-    values[key] = value;
-  });
-
-  return values;
-}
-
-const keys = {
-  get: ['name', 'url'],
-  remove: ['name', 'url'],
-  getAll: ['domain', 'name', 'path', 'secure', 'url'],
-  set: [
-    'name',
-    'url',
-    'expirationDate',
-    'domain',
-    'path',
-    'sameSite',
-    'secure',
-    'url',
-    'value',
-    'httpOnly',
-  ],
-};
-
-async function cookie({ data, id }) {
-  const hasPermission = await browser.permissions.contains({
-    permissions: ['cookies'],
-  });
-
-  if (!hasPermission) {
-    const error = new Error('no-permission');
-    error.data = { permission: 'cookies' };
-
-    throw error;
-  }
-
-  let key = data.type;
-  if (key === 'get' && data.getAll) key = 'getAll';
-
-  const values = getValues(data, keys[key]);
-  if (values.expirationDate) {
-    values.expirationDate = Date.now() / 1000 + +values.expirationDate;
-  }
-
-  let result = null;
-
-  if (data.type === 'remove' && !data.name) {
-    const cookies = await browser.cookies.getAll({ url: data.url });
-    const removePromise = cookies.map(({ name }) =>
-      browser.cookies.remove({ name, url: data.url })
-    );
-    await Promise.allSettled(removePromise);
-
-    result = cookies;
-  } else {
-    result = await browser.cookies[key](values);
-  }
-
-  if (data.type === 'get') {
-    if (data.assignVariable) {
-      this.setVariable(data.variableName, result);
-    }
-    if (data.saveData) {
-      this.addDataToColumn(data.dataColumn, result);
-    }
-  }
-
-  return {
-    data: result,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default cookie;

+ 0 - 38
src/background/workflowEngine/blocksHandler/handlerCreateElement.js

@@ -1,38 +0,0 @@
-async function handleCreateElement(block, { refData }) {
-  if (!this.activeTab.id) throw new Error('no-tab');
-
-  const { data } = block;
-  const preloadScriptsPromise = await Promise.allSettled(
-    data.preloadScripts.map((item) => {
-      if (!item.src.startsWith('http'))
-        return Promise.reject(new Error('Invalid URL'));
-
-      return fetch(item.src)
-        .then((response) => response.text())
-        .then((result) => ({ type: item.type, script: result }));
-    })
-  );
-  const preloadScripts = preloadScriptsPromise.reduce((acc, item) => {
-    if (item.status === 'rejected') return acc;
-
-    acc.push(item.value);
-
-    return acc;
-  }, []);
-
-  data.preloadScripts = preloadScripts;
-
-  const payload = { ...block, data, refData: { variables: {} } };
-  if (data.javascript.includes('automaRefData')) {
-    payload.refData = { ...refData, secrets: {} };
-  }
-
-  await this._sendMessageToTab(payload, {}, data.runBeforeLoad ?? false);
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(block.id),
-  };
-}
-
-export default handleCreateElement;

+ 0 - 61
src/background/workflowEngine/blocksHandler/handlerDataMapping.js

@@ -1,61 +0,0 @@
-import objectPath from 'object-path';
-import { objectHasKey, isObject } from '@/utils/helper';
-
-function mapData(data, sources) {
-  const mappedData = {};
-
-  sources.forEach((source) => {
-    const dataExist = objectPath.has(data, source.name);
-    if (!dataExist) return;
-
-    const value = objectPath.get(data, source.name);
-
-    source.destinations.forEach(({ name }) => {
-      objectPath.set(mappedData, name, value);
-    });
-  });
-
-  return mappedData;
-}
-
-export async function dataMapping({ id, data }) {
-  let dataToMap = null;
-
-  if (data.dataSource === 'table') {
-    dataToMap = this.engine.referenceData.table;
-  } else if (data.dataSource === 'variable') {
-    const { variables } = this.engine.referenceData;
-
-    if (!objectHasKey(variables, data.varSourceName)) {
-      throw new Error(`Cant find "${data.varSourceName}" variable`);
-    }
-
-    dataToMap = variables[data.varSourceName];
-  }
-
-  if (!isObject(dataToMap) && !Array.isArray(dataToMap)) {
-    const dataType = dataToMap === null ? 'null' : typeof dataToMap;
-
-    throw new Error(`Can't map data with "${dataType}" data type`);
-  }
-
-  if (isObject(dataToMap)) {
-    dataToMap = mapData(dataToMap, data.sources);
-  } else {
-    dataToMap = dataToMap.map((item) => mapData(item, data.sources));
-  }
-
-  if (data.assignVariable) {
-    this.setVariable(data.variableName, dataToMap);
-  }
-  if (data.saveData) {
-    this.addDataToColumn(data.dataColumn, dataToMap);
-  }
-
-  return {
-    data: dataToMap,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default dataMapping;

+ 0 - 14
src/background/workflowEngine/blocksHandler/handlerDelay.js

@@ -1,14 +0,0 @@
-function delay(block) {
-  return new Promise((resolve) => {
-    const delayTime = +block.data.time || 500;
-
-    setTimeout(() => {
-      resolve({
-        data: '',
-        nextBlockId: this.getBlockConnections(block.id),
-      });
-    }, delayTime);
-  });
-}
-
-export default delay;

+ 0 - 37
src/background/workflowEngine/blocksHandler/handlerDeleteData.js

@@ -1,37 +0,0 @@
-function deleteData({ data, id }) {
-  return new Promise((resolve) => {
-    data.deleteList.forEach((item) => {
-      if (item.type === 'table') {
-        if (item.columnId === '[all]') {
-          this.engine.referenceData.table = [];
-
-          Object.keys(this.engine.columns).forEach((key) => {
-            this.engine.columns[key].index = 0;
-          });
-        } else {
-          const columnName = this.engine.columns[item.columnId].name;
-
-          this.engine.referenceData.table.forEach((_, index) => {
-            const row = this.engine.referenceData.table[index];
-            delete row[columnName];
-
-            if (!row || Object.keys(row).length === 0) {
-              this.engine.referenceData.table[index] = {};
-            }
-          });
-
-          this.engine.columns[item.columnId].index = 0;
-        }
-      } else if (item.variableName) {
-        delete this.engine.referenceData.variables[item.variableName];
-      }
-    });
-
-    resolve({
-      data: '',
-      nextBlockId: this.getBlockConnections(id),
-    });
-  });
-}
-
-export default deleteData;

+ 0 - 24
src/background/workflowEngine/blocksHandler/handlerElementExists.js

@@ -1,24 +0,0 @@
-function elementExists(block) {
-  return new Promise((resolve, reject) => {
-    this._sendMessageToTab(block)
-      .then((data) => {
-        if (!data && block.data.throwError) {
-          const error = new Error('element-not-found');
-          error.data = { selector: block.data.selector };
-
-          reject(error);
-          return;
-        }
-
-        resolve({
-          data,
-          nextBlockId: this.getBlockConnections(block.id, data ? 1 : 2),
-        });
-      })
-      .catch((error) => {
-        reject(error);
-      });
-  });
-}
-
-export default elementExists;

+ 0 - 143
src/background/workflowEngine/blocksHandler/handlerExecuteWorkflow.js

@@ -1,143 +0,0 @@
-import browser from 'webextension-polyfill';
-import { isWhitespace, parseJSON } from '@/utils/helper';
-import decryptFlow, { getWorkflowPass } from '@/utils/decryptFlow';
-import convertWorkflowData from '@/utils/convertWorkflowData';
-import WorkflowEngine from '../engine';
-
-function workflowListener(workflow, options) {
-  return new Promise((resolve, reject) => {
-    if (workflow.isProtected) {
-      const flow = parseJSON(workflow.drawflow, null);
-
-      if (!flow) {
-        const pass = getWorkflowPass(workflow.pass);
-
-        workflow.drawflow = decryptFlow(workflow, pass);
-      }
-    }
-
-    const engine = new WorkflowEngine(workflow, options);
-    engine.init();
-    engine.on('destroyed', ({ id, status, message }) => {
-      options.events.onDestroyed(engine);
-
-      if (status === 'error') {
-        const error = new Error(message);
-        error.data = { logId: id };
-
-        reject(error);
-        return;
-      }
-
-      resolve({ id, status, message });
-    });
-
-    options.events.onInit(engine);
-  });
-}
-
-function findWorkflow(workflows, workflowId) {
-  const workflow = Array.isArray(workflows)
-    ? workflows.find(({ id }) => id === workflowId)
-    : workflows[workflowId];
-
-  return workflow;
-}
-
-async function executeWorkflow({ id: blockId, data }) {
-  if (data.workflowId === '') throw new Error('empty-workflow');
-
-  const { workflows, teamWorkflows } = await browser.storage.local.get([
-    'workflows',
-    'teamWorkflows',
-  ]);
-  let workflow = null;
-
-  if (data.workflowId.startsWith('team')) {
-    const teamWorkflowsArr = Object.values(
-      Object.values(teamWorkflows || {})[0] ?? {}
-    );
-    workflow = findWorkflow(teamWorkflowsArr, data.workflowId);
-  } else {
-    workflow = findWorkflow(workflows, data.workflowId);
-  }
-
-  if (!workflow) {
-    const errorInstance = new Error('no-workflow');
-    errorInstance.data = { workflowId: data.workflowId };
-
-    throw errorInstance;
-  }
-
-  workflow = convertWorkflowData(workflow);
-  const optionsParams = { variables: {} };
-
-  if (!isWhitespace(data.globalData))
-    optionsParams.globalData = data.globalData;
-
-  if (data.insertAllVars) {
-    optionsParams.variables = JSON.parse(
-      JSON.stringify(this.engine.referenceData.variables)
-    );
-  } else if (data.insertVars) {
-    const varsName = data.insertVars.split(',');
-    varsName.forEach((name) => {
-      const varName = name.trim();
-      const value = this.engine.referenceData.variables[varName];
-
-      if (!value && typeof value !== 'boolean') return;
-
-      optionsParams.variables[varName] = value;
-    });
-  }
-
-  const options = {
-    options: {
-      data: optionsParams,
-      parentWorkflow: {
-        id: this.engine.id,
-        name: this.engine.workflow.name,
-      },
-    },
-    events: {
-      onInit: (engine) => {
-        this.childWorkflowId = engine.id;
-      },
-      onDestroyed: (engine) => {
-        if (data.executeId) {
-          const { dataColumns, globalData, googleSheets, table } =
-            engine.referenceData;
-
-          this.engine.referenceData.workflow[data.executeId] = {
-            globalData,
-            dataColumns,
-            googleSheets,
-            table: table || dataColumns,
-          };
-        }
-      },
-    },
-    states: this.engine.states,
-    logger: this.engine.logger,
-    blocksHandler: this.engine.blocksHandler,
-  };
-
-  const isWorkflowIncluded = workflow.drawflow.nodes.some(
-    (node) =>
-      node.label === 'execute-workflow' &&
-      node.data.workflowId === this.engine.workflow.id
-  );
-  if (isWorkflowIncluded) {
-    throw new Error('workflow-infinite-loop');
-  }
-
-  const result = await workflowListener(workflow, options);
-
-  return {
-    data: '',
-    logId: result.id,
-    nextBlockId: this.getBlockConnections(blockId),
-  };
-}
-
-export default executeWorkflow;

+ 0 - 51
src/background/workflowEngine/blocksHandler/handlerExportData.js

@@ -1,51 +0,0 @@
-import browser from 'webextension-polyfill';
-import { default as dataExporter, files } from '@/utils/dataExporter';
-
-async function exportData({ data, id }, { refData }) {
-  const dataToExport = data.dataToExport || 'data-columns';
-  let payload = refData.table;
-
-  if (dataToExport === 'google-sheets') {
-    payload = refData.googleSheets[data.refKey] || [];
-  } else if (dataToExport === 'variable') {
-    payload = refData.variables[data.variableName] || [];
-
-    if (!Array.isArray(payload)) {
-      payload = [payload];
-
-      if (data.type === 'csv' && typeof payload[0] !== 'object')
-        payload = [payload];
-    }
-  }
-
-  const hasDownloadAccess = await browser.permissions.contains({
-    permissions: ['downloads'],
-  });
-  const blobUrl = dataExporter(payload, {
-    ...data,
-    csvOptions: {
-      delimiter: data.csvDelimiter || ',',
-    },
-    returnUrl: hasDownloadAccess,
-  });
-
-  if (hasDownloadAccess) {
-    const filename = `${data.name || 'unnamed'}${files[data.type].ext}`;
-    const options = {
-      filename,
-      conflictAction: data.onConflict || 'uniquify',
-    };
-
-    await browser.downloads.download({
-      ...options,
-      url: blobUrl,
-    });
-  }
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default exportData;

+ 0 - 14
src/background/workflowEngine/blocksHandler/handlerForwardPage.js

@@ -1,14 +0,0 @@
-import browser from 'webextension-polyfill';
-
-export async function goBack({ id }) {
-  if (!this.activeTab.id) throw new Error('no-tab');
-
-  await browser.tabs.goForward(this.activeTab.id);
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default goBack;

+ 0 - 14
src/background/workflowEngine/blocksHandler/handlerGoBack.js

@@ -1,14 +0,0 @@
-import browser from 'webextension-polyfill';
-
-export async function goBack({ id }) {
-  if (!this.activeTab.id) throw new Error('no-tab');
-
-  await browser.tabs.goBack(this.activeTab.id);
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default goBack;

+ 0 - 144
src/background/workflowEngine/blocksHandler/handlerGoogleSheets.js

@@ -1,144 +0,0 @@
-import { googleSheets } from '@/utils/api';
-import {
-  convert2DArrayToArrayObj,
-  convertArrObjTo2DArr,
-  isWhitespace,
-  parseJSON,
-} from '@/utils/helper';
-
-async function getSpreadsheetValues({ spreadsheetId, range, firstRowAsKey }) {
-  const response = await googleSheets.getValues({ spreadsheetId, range });
-  const result = await response.json();
-
-  if (!response.ok) {
-    throw new Error(result.message);
-  }
-
-  const sheetsData = firstRowAsKey
-    ? convert2DArrayToArrayObj(result.values)
-    : result.values;
-
-  return sheetsData;
-}
-async function getSpreadsheetRange({ spreadsheetId, range }) {
-  const response = await googleSheets.getRange({ spreadsheetId, range });
-  const result = await response.json();
-
-  if (!response.ok) {
-    throw new Error(result.message);
-  }
-
-  const data = {
-    tableRange: result.tableRange || null,
-    lastRange: result.updates.updatedRange,
-  };
-
-  return data;
-}
-async function clearSpreadsheetValues({ spreadsheetId, range }) {
-  const response = await googleSheets.clearValues({ spreadsheetId, range });
-  const result = await response.json();
-
-  if (!response.ok) {
-    throw new Error(result.message);
-  }
-
-  return result;
-}
-async function updateSpreadsheetValues(
-  {
-    range,
-    append,
-    dataFrom,
-    customData,
-    spreadsheetId,
-    keysAsFirstRow,
-    insertDataOption,
-    valueInputOption,
-  },
-  columns
-) {
-  let values = [];
-
-  if (['data-columns', 'table'].includes(dataFrom)) {
-    if (keysAsFirstRow) {
-      values = convertArrObjTo2DArr(columns);
-    } else {
-      values = columns.map((item) =>
-        Object.values(item).map((value) =>
-          typeof value === 'object' ? JSON.stringify(value) : value
-        )
-      );
-    }
-  } else if (dataFrom === 'custom') {
-    values = parseJSON(customData, customData);
-  }
-
-  const queries = {
-    valueInputOption: valueInputOption || 'RAW',
-  };
-
-  if (append) {
-    Object.assign(queries, {
-      includeValuesInResponse: false,
-      insertDataOption: insertDataOption || 'INSERT_ROWS',
-    });
-  }
-
-  const response = await googleSheets.updateValues({
-    range,
-    append,
-    spreadsheetId,
-    options: {
-      queries,
-      body: JSON.stringify({ values }),
-    },
-  });
-
-  if (!response.ok) {
-    const error = await response.json();
-
-    throw new Error(error.message);
-  }
-}
-
-export default async function ({ data, id }, { refData }) {
-  if (isWhitespace(data.spreadsheetId)) throw new Error('empty-spreadsheet-id');
-  if (isWhitespace(data.range)) throw new Error('empty-spreadsheet-range');
-
-  let result = [];
-
-  if (data.type === 'get') {
-    const spreadsheetValues = await getSpreadsheetValues(data);
-
-    result = spreadsheetValues;
-
-    if (data.refKey && !isWhitespace(data.refKey)) {
-      refData.googleSheets[data.refKey] = spreadsheetValues;
-    }
-  } else if (data.type === 'getRange') {
-    result = await getSpreadsheetRange(data);
-
-    if (data.assignVariable) {
-      this.setVariable(data.variableName, result);
-    }
-    if (data.saveData) {
-      this.addDataToColumn(data.dataColumn, result);
-    }
-  } else if (['update', 'append'].includes(data.type)) {
-    result = await updateSpreadsheetValues(
-      {
-        ...data,
-        append: data.type === 'append',
-      },
-      refData.table
-    );
-  } else if (data.type === 'clear') {
-    result = await clearSpreadsheetValues(data);
-  }
-
-  return {
-    data: result,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}

+ 0 - 62
src/background/workflowEngine/blocksHandler/handlerHandleDialog.js

@@ -1,62 +0,0 @@
-import { sendDebugCommand } from '../helper';
-
-const overwriteDialog = (accept, promptText) => `
-  const realConfirm = window.confirm;
-  window.confirm = function() {
-    return ${accept};
-  };
-
-  const realAlert = window.alert;
-  window.alert = function() {
-    return ${accept};
-  };
-
-  const realPrompt = window.prompt;
-  window.prompt = function() {
-    return ${accept} ? "${promptText}" : null;
-  }
-`;
-
-async function handleDialog({ data, id: blockId }) {
-  if (!this.settings.debugMode || BROWSER_TYPE !== 'chrome') {
-    const isScriptExist = this.preloadScripts.some(({ id }) => id === blockId);
-
-    if (!isScriptExist) {
-      const payload = {
-        id: blockId,
-        isBlock: true,
-        name: 'javascript-code',
-        data: {
-          everyNewTab: true,
-          code: overwriteDialog(data.accept, data.promptText),
-        },
-      };
-
-      this.preloadScripts.push(payload);
-      await this._sendMessageToTab(payload, {}, true);
-    }
-  } else {
-    this.dialogParams = {
-      accept: data.accept,
-      promptText: data.promptText,
-    };
-
-    const methodName = 'Page.javascriptDialogOpening';
-    if (!this.engine.eventListeners[methodName]) {
-      this.engine.on(methodName, () => {
-        sendDebugCommand(
-          this.activeTab.id,
-          'Page.handleJavaScriptDialog',
-          this.dialogParams
-        );
-      });
-    }
-  }
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(blockId),
-  };
-}
-
-export default handleDialog;

+ 0 - 137
src/background/workflowEngine/blocksHandler/handlerHandleDownload.js

@@ -1,137 +0,0 @@
-import browser from 'webextension-polyfill';
-
-const getFileExtension = (str) => /(?:\.([^.]+))?$/.exec(str)[1];
-function determineFilenameListener(item, suggest) {
-  const filesname =
-    JSON.parse(sessionStorage.getItem('rename-downloaded-files')) || {};
-  const suggestion = filesname[item.id];
-
-  if (!suggestion) return true;
-
-  const hasFileExt = getFileExtension(suggestion.filename);
-
-  if (!hasFileExt) {
-    const filExtension = getFileExtension(item.filename);
-    suggestion.filename += `.${filExtension}`;
-  }
-
-  if (!suggestion.waitForDownload) delete filesname[item.id];
-
-  sessionStorage.setItem('rename-downloaded-files', JSON.stringify(filesname));
-
-  suggest({
-    filename: suggestion.filename,
-    conflictAction: suggestion.onConflict,
-  });
-
-  return false;
-}
-
-function handleDownload({ data, id: blockId }) {
-  const nextBlockId = this.getBlockConnections(blockId);
-  const getFilesname = () =>
-    JSON.parse(sessionStorage.getItem('rename-downloaded-files')) || {};
-
-  return new Promise((resolve) => {
-    if (!this.activeTab.id) throw new Error('no-tab');
-
-    const hasListener =
-      BROWSER_TYPE === 'chrome' &&
-      chrome.downloads.onDeterminingFilename.hasListeners(() => {});
-    if (!hasListener) {
-      chrome.downloads.onDeterminingFilename.addListener(
-        determineFilenameListener
-      );
-    }
-
-    let downloadId = null;
-    const handleCreated = ({ id }) => {
-      if (downloadId) return;
-
-      const names = getFilesname();
-
-      downloadId = id;
-      names[id] = data;
-      sessionStorage.setItem('rename-downloaded-files', JSON.stringify(names));
-
-      browser.downloads.onCreated.removeListener(handleCreated);
-    };
-    browser.downloads.onCreated.addListener(handleCreated);
-
-    if (!data.waitForDownload) {
-      resolve({
-        nextBlockId,
-        data: data.filename,
-      });
-
-      return;
-    }
-
-    let isResolved = false;
-    let currentFilename = data.filename;
-
-    const timeout = setTimeout(() => {
-      if (isResolved) return;
-
-      isResolved = true;
-
-      resolve({
-        nextBlockId,
-        data: currentFilename,
-      });
-    }, data.timeout);
-
-    const resolvePromise = (id) => {
-      if (data.saveData) {
-        this.addDataToColumn(data.dataColumn, currentFilename);
-      }
-      if (data.assignVariable) {
-        this.setVariable(data.variableName, currentFilename);
-      }
-
-      clearTimeout(timeout);
-      isResolved = true;
-
-      const filesname = getFilesname();
-      delete filesname[id];
-      sessionStorage.setItem(
-        'rename-downloaded-files',
-        JSON.stringify(filesname)
-      );
-
-      chrome.downloads.onDeterminingFilename.removeListener(
-        determineFilenameListener
-      );
-
-      resolve({
-        nextBlockId,
-        data: currentFilename,
-      });
-    };
-
-    const handleChanged = ({ state, id, filename }) => {
-      if (this.engine.isDestroyed || isResolved) {
-        browser.downloads.onChanged.removeListener(handleChanged);
-        return;
-      }
-
-      if (downloadId !== id) return;
-
-      if (filename) currentFilename = filename.current;
-
-      if (state && state.current === 'complete') {
-        resolvePromise(id);
-      } else {
-        browser.downloads.search({ id }).then(([download]) => {
-          if (!download || !download.endTime) return;
-
-          resolvePromise(id);
-        });
-      }
-    };
-
-    browser.downloads.onChanged.addListener(handleChanged);
-  });
-}
-
-export default handleDownload;

+ 0 - 36
src/background/workflowEngine/blocksHandler/handlerHoverElement.js

@@ -1,36 +0,0 @@
-import { attachDebugger } from '../helper';
-
-export async function hoverElement(block) {
-  if (!this.activeTab.id) throw new Error('no-tab');
-  if (BROWSER_TYPE !== 'chrome') {
-    const error = new Error('browser-not-supported');
-    error.data = { browser: BROWSER_TYPE };
-
-    throw error;
-  }
-
-  const { debugMode, executedBlockOnWeb } = this.settings;
-
-  if (!debugMode) {
-    await attachDebugger(this.activeTab.id);
-  }
-
-  await this._sendMessageToTab({
-    ...block,
-    debugMode,
-    executedBlockOnWeb,
-    activeTabId: this.activeTab.id,
-    frameSelector: this.frameSelector,
-  });
-
-  if (!debugMode) {
-    chrome.debugger.detach({ tabId: this.activeTab.id });
-  }
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(block.id),
-  };
-}
-
-export default hoverElement;

+ 0 - 30
src/background/workflowEngine/blocksHandler/handlerIncreaseVariable.js

@@ -1,30 +0,0 @@
-import objectPath from 'object-path';
-
-export async function increaseVariable({ id, data }) {
-  const refVariables = this.engine.referenceData.variables;
-  const variableExist = objectPath.has(refVariables, data.variableName);
-
-  if (!variableExist) {
-    throw new Error(`Cant find "${data.variableName}" variable`);
-  }
-
-  const currentVar = +objectPath.get(refVariables, data.variableName);
-  if (Number.isNaN(currentVar)) {
-    throw new Error(
-      `The "${data.variableName}" variable value is not a number`
-    );
-  }
-
-  objectPath.set(
-    this.engine.referenceData.variables,
-    data.variableName,
-    currentVar + data.increaseBy
-  );
-
-  return {
-    data: refVariables[data.variableName],
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default increaseVariable;

+ 0 - 57
src/background/workflowEngine/blocksHandler/handlerInsertData.js

@@ -1,57 +0,0 @@
-import Papa from 'papaparse';
-import { parseJSON } from '@/utils/helper';
-import getFile from '@/utils/getFile';
-import mustacheReplacer from '@/utils/referenceData/mustacheReplacer';
-
-async function insertData({ id, data }, { refData }) {
-  const replacedValueList = {};
-
-  for (const item of data.dataList) {
-    let value = '';
-
-    if (item.isFile) {
-      const replacedPath = mustacheReplacer(item.filePath || '', refData);
-      const path = replacedPath.value;
-      const isJSON = path.endsWith('.json');
-      const isCSV = path.endsWith('.csv');
-
-      let result = await getFile(path, {
-        returnValue: true,
-        responseType: isJSON ? 'json' : 'text',
-      });
-
-      if (
-        result &&
-        isCSV &&
-        item.csvAction &&
-        item.csvAction.includes('json')
-      ) {
-        const parsedCSV = Papa.parse(result, {
-          header: item.csvAction.includes('header'),
-        });
-        result = parsedCSV.data || [];
-      }
-
-      value = result;
-      Object.assign(replacedValueList, replacedPath.list);
-    } else {
-      const replacedValue = mustacheReplacer(item.value, refData);
-      value = parseJSON(replacedValue.value, replacedValue.value);
-      Object.assign(replacedValueList, replacedValue.list);
-    }
-
-    if (item.type === 'table') {
-      this.addDataToColumn(item.name, value);
-    } else {
-      this.setVariable(item.name, value);
-    }
-  }
-
-  return {
-    data: '',
-    replacedValue: replacedValueList,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default insertData;

+ 0 - 97
src/background/workflowEngine/blocksHandler/handlerInteractionBlock.js

@@ -1,97 +0,0 @@
-import browser from 'webextension-polyfill';
-import { objectHasKey } from '@/utils/helper';
-import { attachDebugger } from '../helper';
-
-async function checkAccess(blockName) {
-  if (blockName === 'upload-file') {
-    const hasFileAccess = await browser.extension.isAllowedFileSchemeAccess();
-
-    if (hasFileAccess) return true;
-
-    throw new Error('no-file-access');
-  } else if (blockName === 'clipboard') {
-    const hasPermission = await browser.permissions.contains({
-      permissions: ['clipboardRead'],
-    });
-
-    if (!hasPermission) {
-      throw new Error('no-clipboard-acces');
-    }
-  }
-
-  return true;
-}
-
-async function interactionHandler(block) {
-  await checkAccess(block.label);
-
-  const debugMode =
-    (block.data.settings?.debugMode ?? false) && !this.settings.debugMode;
-  const isChrome = BROWSER_TYPE === 'chrome';
-
-  try {
-    if (debugMode && isChrome) {
-      await attachDebugger(this.activeTab.id);
-      block.debugMode = true;
-    }
-
-    const data = await this._sendMessageToTab(block, {
-      frameId: this.activeTab.frameId || 0,
-    });
-
-    if (
-      (block.data.saveData && block.label !== 'forms') ||
-      (block.data.getValue && block.data.saveData)
-    ) {
-      const currentColumnType =
-        this.engine.columns[block.data.dataColumn]?.type || 'any';
-      const insertDataToColumn = (value) => {
-        this.addDataToColumn(block.data.dataColumn, value);
-
-        const addExtraRow =
-          objectHasKey(block.data, 'extraRowDataColumn') &&
-          block.data.addExtraRow;
-        if (addExtraRow) {
-          this.addDataToColumn(
-            block.data.extraRowDataColumn,
-            block.data.extraRowValue
-          );
-        }
-      };
-
-      if (Array.isArray(data) && currentColumnType !== 'array') {
-        data.forEach((value) => {
-          insertDataToColumn(value);
-        });
-      } else {
-        insertDataToColumn(data);
-      }
-    }
-
-    if (block.data.assignVariable) {
-      this.setVariable(block.data.variableName, data);
-    }
-
-    if (debugMode && isChrome) {
-      chrome.debugger.detach({ tabId: this.activeTab.id });
-    }
-
-    return {
-      data,
-      nextBlockId: this.getBlockConnections(block.id),
-    };
-  } catch (error) {
-    if (debugMode && isChrome) {
-      chrome.debugger.detach({ tabId: this.activeTab.id });
-    }
-
-    error.data = {
-      name: block.label,
-      selector: block.data.selector,
-    };
-
-    throw error;
-  }
-}
-
-export default interactionHandler;

+ 0 - 58
src/background/workflowEngine/blocksHandler/handlerJavascriptCode.js

@@ -1,58 +0,0 @@
-export async function javascriptCode({ outputs, data, ...block }, { refData }) {
-  const nextBlockId = this.getBlockConnections(block.id);
-
-  if (data.everyNewTab) {
-    const isScriptExist = this.preloadScripts.find(({ id }) => id === block.id);
-
-    if (!isScriptExist) {
-      this.preloadScripts.push({ ...block, data });
-    }
-  }
-  if (!this.activeTab.id) {
-    if (!data.everyNewTab) {
-      throw new Error('no-tab');
-    } else {
-      return { data: '', nextBlockId };
-    }
-  }
-
-  const payload = { ...block, data, refData: { variables: {} } };
-  if (data.code.includes('automaRefData')) {
-    payload.refData = { ...refData, secrets: {} };
-  }
-
-  if (!data.code.includes('automaNextBlock'))
-    payload.data.code += `\nautomaNextBlock()`;
-
-  const result = await this._sendMessageToTab(
-    payload,
-    {},
-    data.runBeforeLoad ?? false
-  );
-  if (result) {
-    if (result.columns.data?.$error) {
-      throw new Error(result.columns.data.message);
-    }
-
-    if (result.variables) {
-      Object.keys(result.variables).forEach((varName) => {
-        this.setVariable(varName, result.variables[varName]);
-      });
-    }
-
-    if (result.columns.insert && result.columns.data) {
-      const params = Array.isArray(result.columns.data)
-        ? result.columns.data
-        : [result.columns.data];
-
-      this.addDataToColumn(params);
-    }
-  }
-
-  return {
-    nextBlockId,
-    data: result?.columns.data || {},
-  };
-}
-
-export default javascriptCode;

+ 0 - 34
src/background/workflowEngine/blocksHandler/handlerLogData.js

@@ -1,34 +0,0 @@
-import dbLogs from '@/db/logs';
-
-export async function logData({ id, data }) {
-  if (!data.workflowId) {
-    throw new Error('No workflow is selected');
-  }
-
-  const [workflowLog] = await dbLogs.items
-    .where('workflowId')
-    .equals(data.workflowId)
-    .reverse()
-    .sortBy('endedAt');
-  let workflowLogData = null;
-
-  if (workflowLog) {
-    workflowLogData = (
-      await dbLogs.logsData.where('logId').equals(workflowLog.id).first()
-    ).data;
-
-    if (data.assignVariable) {
-      this.setVariable(data.variableName, workflowLogData);
-    }
-    if (data.saveData) {
-      this.addDataToColumn(data.dataColumn, workflowLogData);
-    }
-  }
-
-  return {
-    data: workflowLogData,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default logData;

+ 0 - 78
src/background/workflowEngine/blocksHandler/handlerLoopBreakpoint.js

@@ -1,78 +0,0 @@
-import { waitTabLoaded } from '../helper';
-
-async function loopBreakpoint(block, { prevBlockData }) {
-  const currentLoop = this.loopList[block.data.loopId];
-
-  let validLoopData = false;
-
-  if (currentLoop) {
-    validLoopData =
-      currentLoop.type === 'numbers'
-        ? true
-        : currentLoop.index < currentLoop.data.length - 1;
-  }
-
-  const notReachMaxLoop =
-    currentLoop && currentLoop.maxLoop > 0
-      ? currentLoop.index < currentLoop.maxLoop - 1
-      : true;
-  if (!block.data.clearLoop && validLoopData && notReachMaxLoop) {
-    return {
-      data: '',
-      nextBlockId: [{ id: currentLoop.blockId }],
-    };
-  }
-  if (currentLoop.type === 'elements') {
-    if (currentLoop.loadMoreAction && notReachMaxLoop) {
-      const isClickLink = currentLoop.loadMoreAction.type === 'click-link';
-      let result = await this._sendMessageToTab({
-        id: currentLoop.blockId,
-        label: 'loop-elements',
-        data: {
-          ...currentLoop.loadMoreAction,
-          index: currentLoop.index,
-          onlyClickLink: isClickLink,
-        },
-      });
-
-      if (!result.continue && isClickLink) {
-        await waitTabLoaded({
-          tabId: this.activeTab.id,
-          ms: currentLoop.loadMoreAction.actionPageMaxWaitTime * 1000,
-        });
-        result = await this._sendMessageToTab({
-          id: currentLoop.blockId,
-          label: 'loop-elements',
-          data: {
-            ...currentLoop.loadMoreAction,
-            index: currentLoop.index,
-          },
-        });
-      }
-
-      if (!result.continue && result.length > 0) {
-        this.loopList[block.data.loopId].data.push(...result);
-        return {
-          data: '',
-          nextBlockId: [{ id: currentLoop.blockId }],
-        };
-      }
-    }
-
-    const loopElsIndex = this.loopEls.findIndex(
-      ({ blockId }) => blockId === currentLoop.blockId
-    );
-
-    if (loopElsIndex !== -1) this.loopEls.splice(loopElsIndex, 1);
-  }
-
-  delete this.loopList[block.data.loopId];
-  delete this.engine.referenceData.loopData[block.data.loopId];
-
-  return {
-    data: prevBlockData,
-    nextBlockId: this.getBlockConnections(block.id),
-  };
-}
-
-export default loopBreakpoint;

+ 0 - 125
src/background/workflowEngine/blocksHandler/handlerLoopData.js

@@ -1,125 +0,0 @@
-import objectPath from 'object-path';
-import { parseJSON, isXPath } from '@/utils/helper';
-
-async function loopData({ data, id }, { refData }) {
-  try {
-    if (this.loopList[data.loopId]) {
-      const index = this.loopList[data.loopId].index + 1;
-
-      this.loopList[data.loopId].index = index;
-
-      let currentLoopData;
-
-      if (data.loopThrough === 'numbers') {
-        currentLoopData = refData.loopData[data.loopId].data + 1;
-      } else {
-        currentLoopData = this.loopList[data.loopId].data[index];
-      }
-
-      refData.loopData[data.loopId] = {
-        data: currentLoopData,
-        $index: index,
-      };
-    } else {
-      const maxLoop = +data.maxLoop || 0;
-      const getLoopData = {
-        numbers: () => data.fromNumber,
-        table: () => refData.table,
-        'custom-data': () => JSON.parse(data.loopData),
-        'data-columns': () => refData.table,
-        'google-sheets': () => refData.googleSheets[data.referenceKey],
-        variable: () => {
-          const variableVal = objectPath.get(
-            refData.variables,
-            data.variableName
-          );
-
-          if (Array.isArray(variableVal)) return variableVal;
-
-          return parseJSON(variableVal, variableVal);
-        },
-        elements: async () => {
-          const findBy = isXPath(data.elementSelector)
-            ? 'xpath'
-            : 'cssSelector';
-          const { elements, url, loopId } = await this._sendMessageToTab({
-            id,
-            label: 'loop-data',
-            data: {
-              findBy,
-              max: maxLoop,
-              multiple: true,
-              selector: data.elementSelector,
-              waitForSelector: data.waitForSelector ?? false,
-              waitSelectorTimeout: data.waitSelectorTimeout ?? 5000,
-            },
-          });
-          this.loopEls.push({
-            url,
-            loopId,
-            findBy,
-            max: maxLoop,
-            blockId: id,
-            selector: data.elementSelector,
-          });
-
-          return elements;
-        },
-      };
-
-      const currLoopData = await getLoopData[data.loopThrough]();
-      let index = 0;
-
-      if (data.loopThrough !== 'numbers') {
-        if (!Array.isArray(currLoopData)) {
-          throw new Error('invalid-loop-data');
-        }
-
-        if (data.resumeLastWorkflow) {
-          index = JSON.parse(localStorage.getItem(`index:${id}`)) || 0;
-        } else if (data.startIndex > 0) {
-          index = data.startIndex;
-        }
-
-        if (data.reverseLoop) {
-          currLoopData.reverse();
-        }
-      }
-
-      this.loopList[data.loopId] = {
-        index,
-        blockId: id,
-        id: data.loopId,
-        data: currLoopData,
-        type: data.loopThrough,
-        maxLoop:
-          data.loopThrough === 'numbers'
-            ? data.toNumber + 1 - data.fromNumber
-            : maxLoop,
-      };
-      /* eslint-disable-next-line */
-      refData.loopData[data.loopId] = {
-        data:
-          data.loopThrough === 'numbers'
-            ? data.fromNumber
-            : currLoopData[index],
-        $index: index,
-      };
-    }
-
-    localStorage.setItem(`index:${id}`, this.loopList[data.loopId].index);
-
-    return {
-      data: refData.loopData[data.loopId],
-      nextBlockId: this.getBlockConnections(id),
-    };
-  } catch (error) {
-    if (data.loopThrough === 'elements') {
-      error.data = { selector: data.elementSelector };
-    }
-
-    throw error;
-  }
-}
-
-export default loopData;

+ 0 - 79
src/background/workflowEngine/blocksHandler/handlerLoopElements.js

@@ -1,79 +0,0 @@
-async function loopElements({ data, id }, { refData }) {
-  try {
-    if (!this.activeTab.id) throw new Error('no-tab');
-
-    if (this.loopList[data.loopId]) {
-      const index = this.loopList[data.loopId].index + 1;
-
-      this.loopList[data.loopId].index = index;
-
-      refData.loopData[data.loopId] = {
-        $index: index,
-        data: this.loopList[data.loopId].data[index],
-      };
-    } else {
-      const maxLoop = +data.maxLoop || 0;
-      const { elements, url, loopId } = await this._sendMessageToTab({
-        id,
-        label: 'loop-data',
-        data: {
-          max: maxLoop,
-          multiple: true,
-          ...data,
-        },
-      });
-      this.loopEls.push({
-        url,
-        loopId,
-        max: maxLoop,
-        blockId: id,
-        findBy: data.findBy,
-        selector: data.selector,
-      });
-
-      const loopPayload = {
-        maxLoop,
-        index: 0,
-        blockId: id,
-        data: elements,
-        id: data.loopId,
-        type: 'elements',
-      };
-
-      if (data.loadMoreAction !== 'none') {
-        loopPayload.loadMoreAction = {
-          maxLoop,
-          loopAttrId: loopId,
-          loopId: data.loopId,
-          findBy: data.findBy,
-          type: data.loadMoreAction,
-          selector: data.selector.trim(),
-          scrollToBottom: data.scrollToBottom,
-          actionElMaxWaitTime: data.actionElMaxWaitTime,
-          actionElSelector: data.actionElSelector.trim(),
-          actionPageMaxWaitTime: data.actionPageMaxWaitTime,
-        };
-      }
-
-      this.loopList[data.loopId] = loopPayload;
-      /* eslint-disable-next-line */
-      refData.loopData[data.loopId] = {
-        $index: 0,
-        data: elements[0],
-      };
-    }
-
-    return {
-      data: refData.loopData[data.loopId],
-      nextBlockId: this.getBlockConnections(id),
-    };
-  } catch (error) {
-    if (error?.message === 'element-not-found') {
-      error.data = { selector: data.selector };
-    }
-
-    throw error;
-  }
-}
-
-export default loopElements;

+ 0 - 106
src/background/workflowEngine/blocksHandler/handlerNewTab.js

@@ -1,106 +0,0 @@
-import browser from 'webextension-polyfill';
-import { isWhitespace, sleep } from '@/utils/helper';
-import { waitTabLoaded, attachDebugger, sendDebugCommand } from '../helper';
-
-async function newTab({ id, data }) {
-  if (this.windowId) {
-    try {
-      await browser.windows.get(this.windowId);
-    } catch (error) {
-      this.windowId = null;
-    }
-  }
-
-  const isInvalidUrl = !/^https?/.test(data.url);
-
-  if (isInvalidUrl) {
-    const error = new Error(
-      isWhitespace(data.url) ? 'url-empty' : 'invalid-active-tab'
-    );
-    error.data = { url: data.url };
-
-    throw error;
-  }
-
-  let tab = null;
-  const isChrome = BROWSER_TYPE === 'chrome';
-
-  if (data.updatePrevTab && this.activeTab.id) {
-    tab = await browser.tabs.update(this.activeTab.id, {
-      url: data.url,
-      active: data.active,
-    });
-  } else {
-    tab = await browser.tabs.create({
-      url: data.url,
-      active: data.active,
-      windowId: this.windowId,
-    });
-  }
-
-  this.activeTab.url = data.url;
-  if (tab) {
-    if (this.settings.debugMode || data.customUserAgent) {
-      await attachDebugger(tab.id, this.activeTab.id);
-      this.debugAttached = true;
-
-      if (data.customUserAgent && isChrome) {
-        await sendDebugCommand(tab.id, 'Network.setUserAgentOverride', {
-          userAgent: data.userAgent,
-        });
-        await browser.tabs.reload(tab.id);
-        await sleep(1000);
-      }
-    }
-
-    this.activeTab.id = tab.id;
-    this.windowId = tab.windowId;
-  }
-
-  if (data.inGroup && !data.updatePrevTab) {
-    const options = {
-      groupId: this.activeTab.groupId,
-      tabIds: this.activeTab.id,
-    };
-
-    if (!this.activeTab.groupId) {
-      options.createProperties = {
-        windowId: this.windowId,
-      };
-    }
-
-    if (isChrome) {
-      chrome.tabs.group(options, (tabGroupId) => {
-        this.activeTab.groupId = tabGroupId;
-      });
-    }
-  }
-
-  this.activeTab.frameId = 0;
-
-  if (isChrome && !this.settings.debugMode && data.customUserAgent) {
-    chrome.debugger.detach({ tabId: tab.id });
-  }
-
-  if (this.preloadScripts.length > 0) {
-    const preloadScripts = this.preloadScripts.map((script) =>
-      this._sendMessageToTab(script, {}, true)
-    );
-    await Promise.allSettled(preloadScripts);
-  }
-
-  if (data.waitTabLoaded) {
-    await waitTabLoaded({
-      listenError: true,
-      tabId: this.activeTab.id,
-      ms: this.settings?.tabLoadTimeout ?? 30000,
-    });
-  }
-
-  return {
-    data: data.url,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default newTab;

+ 0 - 39
src/background/workflowEngine/blocksHandler/handlerNewWindow.js

@@ -1,39 +0,0 @@
-import browser from 'webextension-polyfill';
-import { attachDebugger } from '../helper';
-
-export async function newWindow({ data, id }) {
-  const windowOptions = {
-    state: data.windowState,
-    incognito: data.incognito,
-    type: data.type || 'normal',
-  };
-
-  if (data.windowState === 'normal') {
-    ['top', 'left', 'height', 'width'].forEach((key) => {
-      if (data[key] <= 0) return;
-
-      windowOptions[key] = data[key];
-    });
-  }
-  if (data.url) windowOptions.url = data.url;
-
-  const newWindowInstance = await browser.windows.create(windowOptions);
-  this.windowId = newWindowInstance.id;
-
-  if (data.url) {
-    const [tab] = newWindowInstance.tabs;
-
-    if (this.settings.debugMode)
-      await attachDebugger(tab.id, this.activeTab.id);
-
-    this.activeTab.id = tab.id;
-    this.activeTab.url = tab.url;
-  }
-
-  return {
-    data: newWindowInstance.id,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default newWindow;

+ 0 - 38
src/background/workflowEngine/blocksHandler/handlerNotification.js

@@ -1,38 +0,0 @@
-import { nanoid } from 'nanoid';
-import browser from 'webextension-polyfill';
-
-export default async function ({ data, id }) {
-  const hasPermission = await browser.permissions.contains({
-    permissions: ['notifications'],
-  });
-
-  if (!hasPermission) {
-    const error = new Error('no-permission');
-    error.data = { permission: 'notifications' };
-
-    throw error;
-  }
-
-  const options = {
-    title: data.title,
-    message: data.message,
-    iconUrl: browser.runtime.getURL('icon-128.png'),
-  };
-
-  ['iconUrl', 'imageUrl'].forEach((key) => {
-    const url = data[key];
-    if (!url || !url.startsWith('http')) return;
-
-    options[key] = url;
-  });
-
-  await browser.notifications.create(nanoid(), {
-    ...options,
-    type: options.imageUrl ? 'image' : 'basic',
-  });
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(id),
-  };
-}

+ 0 - 80
src/background/workflowEngine/blocksHandler/handlerParameterPrompt.js

@@ -1,80 +0,0 @@
-import browser from 'webextension-polyfill';
-import { sleep } from '@/utils/helper';
-
-function getInputtedParams({ execId, blockId }, ms) {
-  return new Promise((resolve) => {
-    let timeout = null;
-    const key = `params-prompt:${execId}__${blockId}`;
-
-    const storageListener = (event) => {
-      if (!event[key]) return;
-
-      clearTimeout(timeout);
-      browser.storage.onChanged.removeListener(storageListener);
-
-      const { newValue } = event[key];
-      resolve(newValue);
-    };
-
-    timeout = setTimeout(() => {
-      browser.storage.onChanged.removeListener(storageListener);
-      resolve({});
-    }, ms || 10000);
-
-    browser.storage.onChanged.addListener(storageListener);
-  });
-}
-
-export default async function ({ data, id }) {
-  const paramURL = browser.runtime.getURL('/params.html');
-  let tab = (await browser.tabs.query({})).find((item) =>
-    item.url.includes(paramURL)
-  );
-
-  if (!tab) {
-    const { tabs } = await browser.windows.create({
-      type: 'popup',
-      width: 480,
-      height: 600,
-      url: browser.runtime.getURL('/params.html'),
-    });
-    [tab] = tabs;
-    await sleep(1000);
-  } else {
-    await browser.tabs.update(tab.id, {
-      active: true,
-    });
-    await browser.windows.update(tab.windowId, { focused: true });
-  }
-
-  const timeout = data.timeout || 20000;
-
-  await browser.tabs.sendMessage(tab.id, {
-    name: 'workflow:params-block',
-    data: {
-      blockId: id,
-      execId: this.engine.id,
-      params: data.parameters,
-      timeout: Date.now() + timeout,
-      name: this.engine.workflow.name,
-      icon: this.engine.workflow.icon,
-      description: this.engine.workflow.description,
-    },
-  });
-
-  const result = await getInputtedParams(
-    {
-      blockId: id,
-      execId: this.engine.id,
-    },
-    timeout
-  );
-  Object.entries(result).forEach(([varName, varValue]) => {
-    this.setVariable(varName, varValue);
-  });
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(id),
-  };
-}

+ 0 - 77
src/background/workflowEngine/blocksHandler/handlerProxy.js

@@ -1,77 +0,0 @@
-import browser from 'webextension-polyfill';
-import { isWhitespace } from '@/utils/helper';
-
-function setProxy({ data, id }) {
-  const nextBlockId = this.getBlockConnections(id);
-
-  return new Promise((resolve, reject) => {
-    if (data.clearProxy) {
-      browser.proxy.settings.clear({});
-    }
-
-    const config = {
-      mode: 'fixed_servers',
-      rules: {
-        singleProxy: {
-          scheme: data.scheme,
-        },
-        bypassList: isWhitespace(data.bypassList)
-          ? []
-          : data.bypassList.split(','),
-      },
-    };
-
-    let proxyPort = data.port;
-
-    if (!isWhitespace(data.host)) {
-      let proxyHost = data.host;
-
-      const schemeRegex = /^https?|socks4|socks5/i;
-      if (schemeRegex.test(data.host)) {
-        /* eslint-disable-next-line */
-        let [scheme, host] = data.host.split(/:\/\/(.*)/);
-
-        if (host.includes(':')) {
-          [host, proxyPort] = host.split(':');
-        }
-
-        proxyHost = host;
-        config.rules.singleProxy.scheme = scheme;
-      }
-
-      config.rules.singleProxy.host = proxyHost;
-    } else {
-      if (data.clearProxy) {
-        this.engine.isUsingProxy = false;
-
-        resolve({
-          data: '',
-          nextBlockId,
-        });
-
-        return;
-      }
-
-      const error = new Error('invalid-proxy-host');
-      error.nextBlockId = nextBlockId;
-
-      reject(error);
-      return;
-    }
-
-    if (proxyPort && !Number.isNaN(+proxyPort)) {
-      config.rules.singleProxy.port = +proxyPort;
-    }
-
-    chrome.proxy.settings.set({ value: config, scope: 'regular' }, () => {
-      this.engine.isUsingProxy = true;
-
-      resolve({
-        data: data.host,
-        nextBlockId,
-      });
-    });
-  });
-}
-
-export default setProxy;

+ 0 - 42
src/background/workflowEngine/blocksHandler/handlerRegexVariable.js

@@ -1,42 +0,0 @@
-import objectPath from 'object-path';
-
-export async function regexVariable({ id, data }) {
-  const refVariables = this.engine.referenceData.variables;
-  const variableExist = objectPath.has(refVariables, data.variableName);
-
-  if (!variableExist) {
-    throw new Error(`Cant find "${data.variableName}" variable`);
-  }
-
-  const str = objectPath.get(refVariables, data.variableName);
-  if (typeof str !== 'string') {
-    throw new Error(
-      `The value of the "${data.variableName}" variable is not a string/text`
-    );
-  }
-
-  const method = data.method || 'match';
-  const regex = new RegExp(data.expression, data.flag.join(''));
-
-  let newValue = '';
-
-  if (method === 'match') {
-    const matches = str.match(regex);
-    newValue = matches && !data.flag.includes('g') ? matches[0] : matches;
-  } else if (method === 'replace') {
-    newValue = str.replace(regex, data.replaceVal ?? '');
-  }
-
-  objectPath.set(
-    this.engine.referenceData.variables,
-    data.variableName,
-    newValue
-  );
-
-  return {
-    data: newValue,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default regexVariable;

+ 0 - 14
src/background/workflowEngine/blocksHandler/handlerReloadTab.js

@@ -1,14 +0,0 @@
-import browser from 'webextension-polyfill';
-
-export async function reloadTab({ id }) {
-  if (!this.activeTab.id) throw new Error('no-tab');
-
-  await browser.tabs.reload(this.activeTab.id);
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default reloadTab;

+ 0 - 23
src/background/workflowEngine/blocksHandler/handlerRepeatTask.js

@@ -1,23 +0,0 @@
-function repeatTask({ data, id }) {
-  return new Promise((resolve) => {
-    const repeat = Number.isNaN(+data.repeatFor) ? 0 : +data.repeatFor;
-
-    if (this.repeatedTasks[id] > repeat) {
-      delete this.repeatedTasks[id];
-
-      resolve({
-        data: repeat,
-        nextBlockId: this.getBlockConnections(id),
-      });
-    } else {
-      this.repeatedTasks[id] = (this.repeatedTasks[id] || 1) + 1;
-
-      resolve({
-        data: repeat,
-        nextBlockId: this.getBlockConnections(id, 2),
-      });
-    }
-  });
-}
-
-export default repeatTask;

+ 0 - 63
src/background/workflowEngine/blocksHandler/handlerSaveAssets.js

@@ -1,63 +0,0 @@
-import browser from 'webextension-polyfill';
-
-function getFilename(url) {
-  try {
-    const filename = new URL(url).pathname.split('/').pop();
-    const hasExtension = /\.[0-9a-z]+$/i.test(filename);
-
-    if (!hasExtension) return null;
-
-    return filename;
-  } catch (e) {
-    return null;
-  }
-}
-
-export default async function ({ data, id, label }) {
-  const hasPermission = await browser.permissions.contains({
-    permissions: ['downloads'],
-  });
-
-  if (!hasPermission) {
-    const error = new Error('no-permission');
-    error.data = { permission: 'downloads' };
-
-    throw error;
-  }
-
-  let sources = [data.url];
-  let index = 0;
-  const downloadFile = (url) => {
-    const options = { url, conflictAction: data.onConflict };
-    let filename = data.filename || getFilename(url);
-
-    if (filename) {
-      if (data.onConflict === 'overwrite' && index !== 0) {
-        filename = `(${index}) ${filename}`;
-      }
-
-      options.filename = filename;
-      index += 1;
-    }
-
-    return browser.downloads.download(options);
-  };
-
-  if (data.type === 'element') {
-    sources = await this._sendMessageToTab({
-      id,
-      data,
-      label,
-      tabId: this.activeTab.id,
-    });
-
-    await Promise.all(sources.map((url) => downloadFile(url)));
-  } else if (data.type === 'url') {
-    await downloadFile(data.url);
-  }
-
-  return {
-    data: sources,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}

+ 0 - 36
src/background/workflowEngine/blocksHandler/handlerSliceVariable.js

@@ -1,36 +0,0 @@
-import objectPath from 'object-path';
-
-export async function sliceData({ id, data }) {
-  const variable = objectPath.get(
-    this.engine.referenceData.variables,
-    data.variableName
-  );
-  const payload = {
-    data: variable,
-    nextBlockId: this.getBlockConnections(id),
-  };
-
-  if (!variable || !variable?.slice) return payload;
-
-  let startIndex = 0;
-  let endIndex = variable.length;
-
-  if (data.startIdxEnabled) {
-    startIndex = data.startIndex;
-  }
-  if (data.endIdxEnabled) {
-    endIndex = data.endIndex;
-  }
-
-  const slicedVariable = variable.slice(startIndex, endIndex);
-  payload.data = slicedVariable;
-  objectPath.set(
-    this.engine.referenceData.variables,
-    data.variableName,
-    slicedVariable
-  );
-
-  return payload;
-}
-
-export default sliceData;

+ 0 - 69
src/background/workflowEngine/blocksHandler/handlerSortData.js

@@ -1,69 +0,0 @@
-import { objectHasKey } from '@/utils/helper';
-
-export async function sliceData({ id, data }) {
-  let dataToSort = null;
-
-  if (data.dataSource === 'table') {
-    dataToSort = this.engine.referenceData.table;
-  } else if (data.dataSource === 'variable') {
-    const { variables } = this.engine.referenceData;
-
-    if (!objectHasKey(variables, data.varSourceName)) {
-      throw new Error(`Cant find "${data.varSourceName}" variable`);
-    }
-
-    dataToSort = variables[data.varSourceName];
-  }
-
-  if (!Array.isArray(dataToSort)) {
-    const dataType = dataToSort === null ? 'null' : typeof dataToSort;
-
-    throw new Error(`Can't sort data with "${dataType}" data type`);
-  }
-
-  const getComparisonValue = ({ itemA, itemB, order = 'asc' }) => {
-    let comparison = 0;
-
-    if (itemA > itemB) {
-      comparison = 1;
-    } else if (itemA < itemB) {
-      comparison = -1;
-    }
-
-    return order === 'desc' ? comparison * -1 : comparison;
-  };
-  const sortedArray = dataToSort.sort((a, b) => {
-    let comparison = 0;
-
-    if (data.sortByProperty) {
-      data.itemProperties.forEach(({ name, order }) => {
-        comparison = getComparisonValue({
-          order,
-          itemA: a[name] ?? a,
-          itemB: b[name] ?? b,
-        });
-      });
-    } else {
-      comparison = getComparisonValue({
-        itemA: a,
-        itemB: b,
-      });
-    }
-
-    return comparison;
-  });
-
-  if (data.assignVariable) {
-    this.setVariable(data.variableName, sortedArray);
-  }
-  if (data.saveData) {
-    this.addDataToColumn(data.dataColumn, sortedArray);
-  }
-
-  return {
-    data: sortedArray,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default sliceData;

+ 0 - 92
src/background/workflowEngine/blocksHandler/handlerSwitchTab.js

@@ -1,92 +0,0 @@
-import browser from 'webextension-polyfill';
-import { attachDebugger } from '../helper';
-
-export default async function ({ data, id }) {
-  const nextBlockId = this.getBlockConnections(id);
-  const generateError = (message, errorData) => {
-    const error = new Error(message);
-    error.nextBlockId = nextBlockId;
-
-    if (errorData) error.data = errorData;
-
-    return error;
-  };
-  this.windowId = null;
-
-  let tab = null;
-  const activeTab = data.activeTab ?? true;
-  const findTabBy = data.findTabBy || 'match-patterns';
-  const isPrevNext = ['next-tab', 'prev-tab'].includes(findTabBy);
-
-  if (!this.activeTab.id && isPrevNext) {
-    throw new Error('no-tab');
-  }
-
-  const isTabsQuery = ['match-patterns', 'tab-title'];
-  const tabs =
-    findTabBy !== 'match-patterns' ? await browser.tabs.query({}) : [];
-
-  if (isTabsQuery.includes(findTabBy)) {
-    const query = {};
-
-    if (data.findTabBy === 'match-patterns') query.url = data.matchPattern;
-    else if (data.findTabBy === 'tab-title') query.title = data.tabTitle;
-
-    [tab] = await browser.tabs.query(query);
-
-    if (!tab) {
-      if (data.createIfNoMatch) {
-        if (!data.url.startsWith('http')) {
-          throw generateError('invalid-active-tab', { url: data.url });
-        }
-
-        tab = await browser.tabs.create({
-          active: activeTab,
-          url: data.url,
-          windowId: this.windowId,
-        });
-      } else {
-        throw generateError('no-match-tab', { pattern: data.matchPattern });
-      }
-    }
-  } else if (isPrevNext) {
-    const incrementBy = findTabBy.includes('next') ? 1 : -1;
-    let tabIndex = tabs.findIndex((item) => item.id === this.activeTab.id);
-
-    tabIndex += incrementBy;
-
-    if (tabIndex < 0) tabIndex = tabs.length - 1;
-    else if (tabIndex > tabs.length - 1) tabIndex = 0;
-
-    tab = tabs[tabIndex];
-  } else if (findTabBy === 'tab-index') {
-    tab = tabs[data.tabIndex];
-
-    if (!tab)
-      throw generateError(`Can't find a tab with ${data.tabIndex} index`);
-  }
-
-  await browser.tabs.update(tab.id, { active: activeTab });
-
-  this.activeTab.id = tab.id;
-  this.activeTab.frameId = 0;
-  this.activeTab.url = tab.url;
-  this.windowId = tab.windowId;
-
-  if (this.settings.debugMode) {
-    await attachDebugger(tab.id, this.activeTab.id);
-    this.debugAttached = true;
-  }
-
-  if (this.preloadScripts.length > 0) {
-    const preloadScripts = this.preloadScripts.map((script) =>
-      this._sendMessageToTab(script, {}, true)
-    );
-    await Promise.allSettled(preloadScripts);
-  }
-
-  return {
-    nextBlockId,
-    data: tab.url,
-  };
-}

+ 0 - 54
src/background/workflowEngine/blocksHandler/handlerSwitchTo.js

@@ -1,54 +0,0 @@
-import { objectHasKey, sleep } from '@/utils/helper';
-import { getFrames } from '../helper';
-
-async function switchTo(block) {
-  const nextBlockId = this.getBlockConnections(block.id);
-
-  try {
-    if (block.data.windowType === 'main-window') {
-      this.activeTab.frameId = 0;
-
-      delete this.frameSelector;
-
-      return {
-        data: '',
-        nextBlockId,
-      };
-    }
-
-    const { url, isSameOrigin } = await this._sendMessageToTab(block, {
-      frameId: 0,
-    });
-
-    if (isSameOrigin) {
-      this.frameSelector = block.data.selector;
-
-      return {
-        data: block.data.selector,
-        nextBlockId,
-      };
-    }
-
-    const frames = await getFrames(this.activeTab.id);
-
-    if (objectHasKey(frames, url)) {
-      this.activeTab.frameId = frames[url];
-
-      await sleep(1000);
-
-      return {
-        data: this.activeTab.frameId,
-        nextBlockId,
-      };
-    }
-
-    throw new Error('no-iframe-id');
-  } catch (error) {
-    error.data = { selector: block.data.selector };
-    error.nextBlockId = nextBlockId;
-
-    throw error;
-  }
-}
-
-export default switchTo;

+ 0 - 29
src/background/workflowEngine/blocksHandler/handlerTabUrl.js

@@ -1,29 +0,0 @@
-import browser from 'webextension-polyfill';
-
-export async function logData({ id, data }) {
-  let urls = [];
-
-  if (data.type === 'active-tab') {
-    if (!this.activeTab.id) throw new Error('no-tab');
-
-    const tab = await browser.tabs.get(this.activeTab.id);
-    urls = tab.url || tab.pendingUrl || '';
-  } else {
-    const tabs = await browser.tabs.query({});
-    urls = tabs.map((tab) => tab.url);
-  }
-
-  if (data.assignVariable) {
-    this.setVariable(data.variableName, urls);
-  }
-  if (data.saveData) {
-    this.addDataToColumn(data.dataColumn, urls);
-  }
-
-  return {
-    data: urls,
-    nextBlockId: this.getBlockConnections(id),
-  };
-}
-
-export default logData;

+ 0 - 120
src/background/workflowEngine/blocksHandler/handlerTakeScreenshot.js

@@ -1,120 +0,0 @@
-import browser from 'webextension-polyfill';
-import { fileSaver } from '@/utils/helper';
-import { waitTabLoaded } from '../helper';
-
-async function saveImage({ filename, uri, ext }) {
-  const hasDownloadAccess = await browser.permissions.contains({
-    permissions: ['downloads'],
-  });
-  const name = `${filename || 'Screenshot'}.${ext || 'png'}`;
-
-  if (hasDownloadAccess) {
-    await browser.downloads.download({
-      url: uri,
-      filename: name,
-    });
-
-    return;
-  }
-
-  const image = new Image();
-
-  image.onload = () => {
-    const canvas = document.createElement('canvas');
-    canvas.width = image.width;
-    canvas.height = image.height;
-
-    const context = canvas.getContext('2d');
-    context.drawImage(image, 0, 0);
-
-    fileSaver(name, canvas.toDataURL());
-  };
-
-  image.src = uri;
-}
-
-async function takeScreenshot({ data, id, label }) {
-  const saveToComputer =
-    typeof data.saveToComputer === 'undefined' || data.saveToComputer;
-
-  try {
-    let screenshot = null;
-    const options = {
-      quality: data.quality,
-      format: data.ext || 'png',
-    };
-    const saveScreenshot = async (dataUrl) => {
-      if (data.saveToColumn) this.addDataToColumn(data.dataColumn, dataUrl);
-      if (saveToComputer)
-        await saveImage({
-          filename: data.fileName,
-          uri: dataUrl,
-          ext: data.ext,
-        });
-      if (data.assignVariable) this.setVariable(data.variableName, dataUrl);
-    };
-
-    if (data.captureActiveTab) {
-      if (!this.activeTab.id) {
-        throw new Error('no-tab');
-      }
-
-      let tab = null;
-      const isChrome = BROWSER_TYPE === 'chrome';
-      const captureTab = () => {
-        if (isChrome) return browser.tabs.captureVisibleTab(options);
-
-        return browser.tabs.captureTab(this.activeTab.id, options);
-      };
-
-      if (isChrome) {
-        [tab] = await browser.tabs.query({
-          active: true,
-          currentWindow: true,
-        });
-
-        if (this.windowId) {
-          await browser.windows.update(this.windowId, { focused: true });
-        }
-      }
-
-      await browser.tabs.update(this.activeTab.id, { active: true });
-      await waitTabLoaded({ tabId: this.activeTab.id, listenError: true });
-
-      screenshot = await (data.fullPage ||
-      ['element', 'fullpage'].includes(data.type)
-        ? this._sendMessageToTab({
-            label,
-            options,
-            data: {
-              type: data.type,
-              selector: data.selector,
-            },
-            tabId: this.activeTab.id,
-          })
-        : captureTab());
-
-      if (tab) {
-        await browser.windows.update(tab.windowId, { focused: true });
-        await browser.tabs.update(tab.id, { active: true });
-      }
-
-      await saveScreenshot(screenshot);
-    } else {
-      screenshot = await browser.tabs.captureVisibleTab(options);
-
-      await saveScreenshot(screenshot);
-    }
-
-    return {
-      data: screenshot,
-      nextBlockId: this.getBlockConnections(id),
-    };
-  } catch (error) {
-    if (data.type === 'element') error.data = { selector: data.selector };
-
-    throw error;
-  }
-}
-
-export default takeScreenshot;

+ 0 - 10
src/background/workflowEngine/blocksHandler/handlerTrigger.js

@@ -1,10 +0,0 @@
-async function trigger(block) {
-  return new Promise((resolve) => {
-    resolve({
-      data: '',
-      nextBlockId: this.getBlockConnections(block.id),
-    });
-  });
-}
-
-export default trigger;

+ 0 - 76
src/background/workflowEngine/blocksHandler/handlerWaitConnections.js

@@ -1,76 +0,0 @@
-async function waitConnections({ data, id }, { prevBlock }) {
-  return new Promise((resolve) => {
-    let timeout;
-    let resolved = false;
-
-    const nextBlockId = this.getBlockConnections(id);
-    const destroyWorker =
-      data.specificFlow && prevBlock?.id !== data.flowBlockId;
-
-    const registerConnections = () => {
-      const connections = this.engine.connectionsMap;
-      Object.keys(connections).forEach((key) => {
-        const isConnected = connections[key].some(
-          (connection) => connection.id === id
-        );
-        if (!isConnected) return;
-
-        const index = key.indexOf('-output');
-        const prevBlockId = key.slice(0, index === -1 ? key.length : index);
-        this.engine.waitConnections[id][prevBlockId] = {
-          isHere: false,
-          isContinue: false,
-        };
-      });
-    };
-    const checkConnections = () => {
-      if (resolved) return;
-
-      const state = Object.values(this.engine.waitConnections[id]);
-      const isAllHere = state.every((worker) => worker.isHere);
-
-      if (isAllHere) {
-        this.engine.waitConnections[id][prevBlock.id].isContinue = true;
-        const allContinue = state.every((worker) => worker.isContinue);
-
-        if (allContinue) {
-          registerConnections();
-        }
-
-        clearTimeout(timeout);
-
-        resolve({
-          data: '',
-          nextBlockId,
-          destroyWorker,
-        });
-      } else {
-        setTimeout(() => {
-          checkConnections();
-        }, 1000);
-      }
-    };
-
-    if (!this.engine.waitConnections[id]) {
-      this.engine.waitConnections[id] = {};
-
-      registerConnections();
-    }
-
-    this.engine.waitConnections[id][prevBlock.id].isHere = true;
-
-    timeout = setTimeout(() => {
-      resolved = true;
-
-      resolve({
-        data: '',
-        nextBlockId,
-        destroyWorker,
-      });
-    }, data.timeout);
-
-    checkConnections();
-  });
-}
-
-export default waitConnections;

+ 0 - 85
src/background/workflowEngine/blocksHandler/handlerWebhook.js

@@ -1,85 +0,0 @@
-import objectPath from 'object-path';
-import { isWhitespace } from '@/utils/helper';
-import { executeWebhook } from '@/utils/webhookUtil';
-import mustacheReplacer from '@/utils/referenceData/mustacheReplacer';
-
-export async function webhook({ data, id }, { refData }) {
-  const nextBlockId = this.getBlockConnections(id);
-  const fallbackOutput = this.getBlockConnections(id, 'fallback');
-
-  try {
-    if (isWhitespace(data.url)) throw new Error('url-empty');
-    if (!data.url.startsWith('http')) {
-      const error = new Error('invalid-active-tab');
-      error.data = { url: data.url };
-
-      throw error;
-    }
-
-    const newHeaders = [];
-    data.headers.forEach(({ value, name }) => {
-      const newValue = mustacheReplacer(value, refData).value;
-
-      newHeaders.push({ name, value: newValue });
-    });
-
-    const response = await executeWebhook({ ...data, headers: newHeaders });
-
-    if (!response.ok) {
-      if (fallbackOutput && fallbackOutput.length > 0) {
-        return {
-          data: '',
-          nextBlockId: fallbackOutput,
-        };
-      }
-
-      throw new Error(`(${response.status}) ${response.statusText}`);
-    }
-
-    if (!data.assignVariable && !data.saveData) {
-      return {
-        data: '',
-        nextBlockId,
-      };
-    }
-
-    let returnData = '';
-
-    if (data.responseType === 'json') {
-      const jsonRes = await response.json();
-
-      returnData = objectPath.get(jsonRes, data.dataPath);
-    } else {
-      returnData = await response.text();
-    }
-
-    if (data.assignVariable) {
-      this.setVariable(data.variableName, returnData);
-    }
-    if (data.saveData) {
-      if (data.dataColumn === '$assignColumns' && Array.isArray(returnData)) {
-        this.addDataToColumn(returnData);
-      } else {
-        this.addDataToColumn(data.dataColumn, returnData);
-      }
-    }
-
-    return {
-      nextBlockId,
-      data: returnData,
-    };
-  } catch (error) {
-    if (fallbackOutput && error.message === 'Failed to fetch') {
-      return {
-        data: '',
-        nextBlockId: fallbackOutput,
-      };
-    }
-
-    error.nextBlockId = nextBlockId;
-
-    throw error;
-  }
-}
-
-export default webhook;

+ 0 - 23
src/background/workflowEngine/blocksHandler/handlerWhileLoop.js

@@ -1,23 +0,0 @@
-import testConditions from '@/utils/testConditions';
-
-async function whileLoop({ data, id }, { refData }) {
-  const conditionPayload = {
-    refData,
-    activeTab: this.activeTab.id,
-    sendMessage: (payload) =>
-      this._sendMessageToTab({ ...payload.data, label: 'conditions', id }),
-  };
-  const result = await testConditions(data.conditions, conditionPayload);
-  const nextBlockId = this.getBlockConnections(
-    id,
-    result.isMatch ? 1 : 'fallback'
-  );
-
-  return {
-    data: '',
-    nextBlockId,
-    replacedValue: result?.replacedValue || {},
-  };
-}
-
-export default whileLoop;

+ 0 - 33
src/background/workflowEngine/blocksHandler/handlerWorkflowState.js

@@ -1,33 +0,0 @@
-export default async function ({ data, id }) {
-  let stopCurrent = false;
-
-  if (data.type === 'stop-current') {
-    return {};
-  }
-  if (['stop-specific', 'stop-all'].includes(data.type)) {
-    const ids = [];
-    const isSpecific = data.type === 'stop-specific';
-    this.engine.states.getAll.forEach((state) => {
-      const workflowNotIncluded =
-        isSpecific && !data.workflowsToStop.includes(state.workflowId);
-      if (workflowNotIncluded) return;
-
-      ids.push(state.id);
-    });
-
-    for (const stateId of ids) {
-      if (stateId === this.engine.id) {
-        stopCurrent = isSpecific ? true : !data.exceptCurrent;
-      } else {
-        await this.engine.states.stop(stateId);
-      }
-    }
-  }
-
-  if (stopCurrent) return {};
-
-  return {
-    data: '',
-    nextBlockId: this.getBlockConnections(id),
-  };
-}

+ 0 - 515
src/background/workflowEngine/engine.js

@@ -1,515 +0,0 @@
-import browser from 'webextension-polyfill';
-import { nanoid } from 'nanoid';
-import { getBlocks } from '@/utils/getSharedData';
-import { clearCache, sleep, parseJSON, isObject } from '@/utils/helper';
-import dbStorage from '@/db/storage';
-import Worker from './worker';
-
-let blocks = getBlocks();
-
-class WorkflowEngine {
-  constructor(workflow, { states, logger, blocksHandler, options }) {
-    this.id = nanoid();
-    this.states = states;
-    this.logger = logger;
-    this.workflow = workflow;
-    this.blocksHandler = blocksHandler;
-    this.parentWorkflow = options?.parentWorkflow;
-    this.saveLog = workflow.settings?.saveLog ?? true;
-
-    this.workerId = 0;
-    this.workers = new Map();
-
-    this.packagesCache = {};
-    this.extractedGroup = {};
-    this.connectionsMap = {};
-    this.waitConnections = {};
-
-    this.isDestroyed = false;
-    this.isUsingProxy = false;
-
-    this.triggerBlockId = null;
-
-    this.blocks = {};
-    this.history = [];
-    this.columnsId = {};
-    this.historyCtxData = {};
-    this.eventListeners = {};
-    this.preloadScripts = [];
-
-    this.columns = {
-      column: {
-        index: 0,
-        type: 'any',
-        name: this.workflow.settings?.defaultColumnName || 'column',
-      },
-    };
-    this.rowData = {};
-
-    this.logsLimit = 1001;
-    this.logHistoryId = 0;
-
-    let variables = {};
-    let { globalData } = workflow;
-    if (options && options?.data) {
-      globalData = options.data.globalData || globalData;
-      variables = isObject(options.data.variables)
-        ? options?.data.variables
-        : {};
-
-      options.data = { globalData, variables };
-    }
-    this.options = options;
-
-    this.referenceData = {
-      variables,
-      table: [],
-      secrets: {},
-      loopData: {},
-      workflow: {},
-      googleSheets: {},
-      globalData: parseJSON(globalData, globalData),
-    };
-
-    this.onDebugEvent = ({ tabId }, method, params) => {
-      let isActiveTabEvent = false;
-      this.workers.forEach((worker) => {
-        if (isActiveTabEvent) return;
-
-        isActiveTabEvent = worker.activeTab.id === tabId;
-      });
-
-      if (!isActiveTabEvent) return;
-
-      (this.eventListeners[method] || []).forEach((listener) => {
-        listener(params);
-      });
-    };
-    this.onWorkflowStopped = (id) => {
-      if (this.id !== id || this.isDestroyed) return;
-      this.stop();
-    };
-  }
-
-  async init() {
-    try {
-      if (this.workflow.isDisabled) return;
-
-      if (!this.states) {
-        console.error(`"${this.workflow.name}" workflow doesn't have states`);
-        this.destroy('error');
-        return;
-      }
-
-      const { nodes, edges } = this.workflow.drawflow;
-      if (!nodes || nodes.length === 0) {
-        console.error(`${this.workflow.name} doesn't have blocks`);
-        return;
-      }
-
-      const triggerBlock = nodes.find((node) => node.label === 'trigger');
-      if (!triggerBlock) {
-        console.error(`${this.workflow.name} doesn't have a trigger block`);
-        return;
-      }
-
-      blocks = getBlocks();
-
-      const checkParams = this.options?.checkParams ?? true;
-      const hasParams = triggerBlock.data.parameters?.length > 0;
-      if (checkParams && hasParams) {
-        this.eventListeners = {};
-
-        const paramUrl = browser.runtime.getURL('params.html');
-        const tabs = await browser.tabs.query({});
-        const paramTab = tabs.find((tab) => tab.url?.includes(paramUrl));
-
-        if (paramTab) {
-          browser.tabs.sendMessage(paramTab.id, {
-            name: 'workflow:params',
-            data: this.workflow,
-          });
-
-          browser.windows.update(paramTab.windowId, { focused: true });
-        } else {
-          browser.windows.create({
-            type: 'popup',
-            width: 480,
-            height: window.screen.availHeight,
-            url: browser.runtime.getURL(
-              `/params.html?workflowId=${this.workflow.id}`
-            ),
-          });
-        }
-        return;
-      }
-
-      this.triggerBlockId = triggerBlock.id;
-
-      this.blocks = nodes.reduce((acc, node) => {
-        acc[node.id] = node;
-
-        return acc;
-      }, {});
-      this.connectionsMap = edges.reduce(
-        (acc, { sourceHandle, target, targetHandle }) => {
-          if (!acc[sourceHandle]) acc[sourceHandle] = [];
-          acc[sourceHandle].push({ id: target, targetHandle, sourceHandle });
-
-          return acc;
-        },
-        {}
-      );
-
-      const workflowTable = this.workflow.table || this.workflow.dataColumns;
-      let columns = Array.isArray(workflowTable)
-        ? workflowTable
-        : Object.values(workflowTable);
-
-      if (this.workflow.connectedTable) {
-        const connectedTable = await dbStorage.tablesItems
-          .where('id')
-          .equals(this.workflow.connectedTable)
-          .first();
-        const connectedTableData = await dbStorage.tablesData
-          .where('tableId')
-          .equals(connectedTable?.id)
-          .first();
-        if (connectedTable && connectedTableData) {
-          columns = Object.values(connectedTable.columns);
-          Object.assign(this.columns, connectedTableData.columnsIndex);
-          this.referenceData.table = connectedTableData.items || [];
-        } else {
-          this.workflow.connectedTable = null;
-        }
-      }
-
-      columns.forEach(({ name, type, id }) => {
-        const columnId = id || name;
-
-        this.rowData[name] = null;
-
-        this.columnsId[name] = columnId;
-        if (!this.columns[columnId])
-          this.columns[columnId] = { index: 0, name, type };
-      });
-
-      if (BROWSER_TYPE !== 'chrome') {
-        this.workflow.settings.debugMode = false;
-      } else if (this.workflow.settings.debugMode) {
-        chrome.debugger.onEvent.addListener(this.onDebugEvent);
-      }
-      if (
-        this.workflow.settings.reuseLastState &&
-        !this.workflow.connectedTable
-      ) {
-        const lastStateKey = `state:${this.workflow.id}`;
-        const value = await browser.storage.local.get(lastStateKey);
-        const lastState = value[lastStateKey];
-
-        if (lastState) {
-          Object.assign(this.columns, lastState.columns);
-          Object.assign(this.referenceData, lastState.referenceData);
-        }
-      }
-
-      const { settings: userSettings } = await browser.storage.local.get(
-        'settings'
-      );
-      this.logsLimit = userSettings?.logsLimit || 1001;
-
-      this.workflow.table = columns;
-      this.startedTimestamp = Date.now();
-
-      this.states.on('stop', this.onWorkflowStopped);
-
-      const credentials = await dbStorage.credentials.toArray();
-      credentials.forEach(({ name, value }) => {
-        this.referenceData.secrets[name] = value;
-      });
-
-      const variables = await dbStorage.variables.toArray();
-      variables.forEach(({ name, value }) => {
-        this.referenceData.variables[`$$${name}`] = value;
-      });
-
-      await this.states.add(this.id, {
-        id: this.id,
-        state: this.state,
-        workflowId: this.workflow.id,
-        parentState: this.parentWorkflow,
-        teamId: this.workflow.teamId || null,
-      });
-      this.addWorker({ blockId: triggerBlock.id });
-    } catch (error) {
-      console.error(error);
-    }
-  }
-
-  addWorker(detail) {
-    this.workerId += 1;
-
-    const workerId = `worker-${this.workerId}`;
-    const worker = new Worker(workerId, this, { blocksDetail: blocks });
-    worker.init(detail);
-
-    this.workers.set(worker.id, worker);
-  }
-
-  addLogHistory(detail) {
-    if (detail.name === 'blocks-group') return;
-
-    const isLimit = this.history.length >= this.logsLimit;
-    const notErrorLog = detail.type !== 'error';
-
-    if ((isLimit || !this.saveLog) && notErrorLog) return;
-
-    this.logHistoryId += 1;
-    detail.id = this.logHistoryId;
-
-    if (
-      detail.name !== 'delay' ||
-      detail.replacedValue ||
-      detail.name === 'javascript-code' ||
-      (blocks[detail.name]?.refDataKeys && this.saveLog)
-    ) {
-      const { activeTabUrl, variables, loopData } = JSON.parse(
-        JSON.stringify(this.referenceData)
-      );
-
-      this.historyCtxData[this.logHistoryId] = {
-        referenceData: {
-          loopData,
-          variables,
-          activeTabUrl,
-          prevBlockData: detail.prevBlockData || '',
-        },
-        replacedValue: detail.replacedValue,
-      };
-
-      delete detail.replacedValue;
-    }
-
-    this.history.push(detail);
-  }
-
-  async stop() {
-    try {
-      if (this.childWorkflowId) {
-        await this.states.stop(this.childWorkflowId);
-      }
-
-      await this.destroy('stopped');
-    } catch (error) {
-      console.error(error);
-    }
-  }
-
-  async executeQueue() {
-    const { workflowQueue } = await browser.storage.local.get('workflowQueue');
-    const queueIndex = (workflowQueue || []).indexOf(this.workflow.id);
-
-    if (!workflowQueue || queueIndex === -1) return;
-
-    const engine = new WorkflowEngine(this.workflow, {
-      logger: this.logger,
-      states: this.states,
-      blocksHandler: this.blocksHandler,
-    });
-    engine.init();
-
-    workflowQueue.splice(queueIndex, 1);
-
-    await browser.storage.local.set({ workflowQueue });
-  }
-
-  destroyWorker(workerId) {
-    this.workers.delete(workerId);
-
-    if (this.workers.size === 0) {
-      this.addLogHistory({
-        type: 'finish',
-        name: 'finish',
-      });
-      this.dispatchEvent('finish');
-      this.destroy('success');
-    }
-  }
-
-  async destroy(status, message, blockDetail) {
-    try {
-      if (this.isDestroyed) return;
-      if (this.isUsingProxy) browser.proxy.settings.clear({});
-      if (this.workflow.settings.debugMode && BROWSER_TYPE === 'chrome') {
-        chrome.debugger.onEvent.removeListener(this.onDebugEvent);
-
-        await sleep(1000);
-
-        this.workers.forEach((worker) => {
-          if (!worker.debugAttached) return;
-
-          chrome.debugger.detach({ tabId: worker.activeTab.id });
-        });
-      }
-
-      const endedTimestamp = Date.now();
-      this.workers.clear();
-      this.executeQueue();
-
-      if (!this.workflow.isTesting) {
-        const { name, id, teamId } = this.workflow;
-
-        await this.logger.add({
-          detail: {
-            name,
-            status,
-            teamId,
-            message,
-            id: this.id,
-            workflowId: id,
-            endedAt: endedTimestamp,
-            parentLog: this.parentWorkflow,
-            startedAt: this.startedTimestamp,
-          },
-          history: {
-            logId: this.id,
-            data: this.saveLog ? this.history : [],
-          },
-          ctxData: {
-            logId: this.id,
-            data: this.historyCtxData,
-          },
-          data: {
-            logId: this.id,
-            data: {
-              table: [...this.referenceData.table],
-              variables: { ...this.referenceData.variables },
-            },
-          },
-        });
-      }
-
-      this.states.off('stop', this.onWorkflowStopped);
-      await this.states.delete(this.id);
-
-      this.dispatchEvent('destroyed', {
-        status,
-        message,
-        blockDetail,
-        id: this.id,
-        endedTimestamp,
-        history: this.history,
-        startedTimestamp: this.startedTimestamp,
-      });
-
-      if (this.workflow.settings.reuseLastState) {
-        const workflowState = {
-          [`state:${this.workflow.id}`]: {
-            columns: this.columns,
-            referenceData: {
-              table: this.referenceData.table,
-              variables: this.referenceData.variables,
-            },
-          },
-        };
-
-        browser.storage.local.set(workflowState);
-      } else if (status === 'success') {
-        clearCache(this.workflow);
-      }
-
-      const { table, variables } = this.referenceData;
-      const tableId = this.workflow.connectedTable;
-
-      await dbStorage.transaction(
-        'rw',
-        dbStorage.tablesItems,
-        dbStorage.tablesData,
-        dbStorage.variables,
-        async () => {
-          if (tableId) {
-            await dbStorage.tablesItems.update(tableId, {
-              modifiedAt: Date.now(),
-              rowsCount: table.length,
-            });
-            await dbStorage.tablesData.where('tableId').equals(tableId).modify({
-              items: table,
-              columnsIndex: this.columns,
-            });
-          }
-
-          for (const key in variables) {
-            if (key.startsWith('$$')) {
-              const varName = key.slice(2);
-              const varValue = variables[key];
-
-              const variable =
-                (await dbStorage.variables
-                  .where('name')
-                  .equals(varName)
-                  .first()) || {};
-              variable.name = varName;
-              variable.value = varValue;
-
-              await dbStorage.variables.put(variable);
-            }
-          }
-        }
-      );
-
-      this.isDestroyed = true;
-      this.referenceData = null;
-      this.eventListeners = null;
-      this.packagesCache = null;
-      this.extractedGroup = null;
-      this.connectionsMap = null;
-      this.waitConnections = null;
-      this.blocks = null;
-      this.history = null;
-      this.columnsId = null;
-      this.historyCtxData = null;
-      this.preloadScripts = null;
-    } catch (error) {
-      console.error(error);
-    }
-  }
-
-  async updateState(data) {
-    const state = {
-      ...data,
-      tabIds: [],
-      currentBlock: [],
-      name: this.workflow.name,
-      logs: this.history.slice(-5),
-      startedTimestamp: this.startedTimestamp,
-    };
-
-    this.workers.forEach((worker) => {
-      const { id, label, startedAt } = worker.currentBlock;
-
-      state.currentBlock.push({ id, name: label, startedAt });
-      state.tabIds.push(worker.activeTab.id);
-    });
-
-    await this.states.update(this.id, { state });
-    this.dispatchEvent('update', { state });
-  }
-
-  dispatchEvent(name, params) {
-    const listeners = this.eventListeners[name];
-
-    if (!listeners) return;
-
-    listeners.forEach((callback) => {
-      callback(params);
-    });
-  }
-
-  on(name, listener) {
-    (this.eventListeners[name] = this.eventListeners[name] || []).push(
-      listener
-    );
-  }
-}
-
-export default WorkflowEngine;

+ 0 - 126
src/background/workflowEngine/helper.js

@@ -1,126 +0,0 @@
-import browser from 'webextension-polyfill';
-
-export async function getFrames(tabId) {
-  try {
-    const frames = await browser.webNavigation.getAllFrames({ tabId });
-    const framesObj = frames.reduce((acc, { frameId, url }) => {
-      const key = url === 'about:blank' ? '' : url;
-
-      acc[key] = frameId;
-
-      return acc;
-    }, {});
-
-    return framesObj;
-  } catch (error) {
-    console.error(error);
-    return {};
-  }
-}
-
-export function sendDebugCommand(tabId, method, params = {}) {
-  return new Promise((resolve) => {
-    chrome.debugger.sendCommand({ tabId }, method, params, resolve);
-  });
-}
-
-export function attachDebugger(tabId, prevTab) {
-  return new Promise((resolve) => {
-    if (prevTab && tabId !== prevTab)
-      chrome.debugger.detach({ tabId: prevTab });
-
-    chrome.debugger.getTargets((targets) => {
-      targets.forEach((target) => {
-        if (target.attached || target.tabId !== tabId) {
-          resolve();
-          return;
-        }
-
-        chrome.debugger.attach({ tabId }, '1.3', () => {
-          chrome.debugger.sendCommand({ tabId }, 'Page.enable', resolve);
-        });
-      });
-    });
-  });
-}
-
-export function waitTabLoaded({ tabId, listenError = false, ms = 10000 }) {
-  return new Promise((resolve, reject) => {
-    let timeout = null;
-    const excludeErrors = ['net::ERR_BLOCKED_BY_CLIENT', 'net::ERR_ABORTED'];
-
-    const onErrorOccurred = (details) => {
-      if (
-        details.tabId !== tabId ||
-        details.frameId !== 0 ||
-        excludeErrors.includes(details.error)
-      )
-        return;
-
-      clearTimeout(timeout);
-      browser.webNavigation.onErrorOccurred.removeListener(onErrorOccurred);
-      reject(new Error(details.error));
-    };
-
-    if (ms > 0) {
-      timeout = setTimeout(() => {
-        browser.webNavigation.onErrorOccurred.removeListener(onErrorOccurred);
-        reject(new Error('Timeout'));
-      }, ms);
-    }
-    if (listenError && BROWSER_TYPE === 'chrome')
-      browser.webNavigation.onErrorOccurred.addListener(onErrorOccurred);
-
-    const activeTabStatus = () => {
-      browser.tabs.get(tabId).then((tab) => {
-        if (!tab) {
-          reject(new Error('no-tab'));
-          return;
-        }
-
-        if (tab.status === 'loading') {
-          setTimeout(() => {
-            activeTabStatus();
-          }, 1000);
-          return;
-        }
-
-        clearTimeout(timeout);
-
-        browser.webNavigation.onErrorOccurred.removeListener(onErrorOccurred);
-        resolve();
-      });
-    };
-
-    activeTabStatus();
-  });
-}
-
-export function convertData(data, type) {
-  if (type === 'any') return data;
-
-  let result = data;
-
-  switch (type) {
-    case 'integer':
-      /* eslint-disable-next-line */
-      result = typeof data !== 'number' ? +data?.replace(/\D+/g, '') : data;
-      break;
-    case 'boolean':
-      result = Boolean(data);
-      break;
-    case 'array':
-      result = Array.from(data);
-      break;
-    case 'string':
-      result = String(data);
-      break;
-    default:
-  }
-
-  return result;
-}
-
-export function getBlockConnection(blockId, outputId = 1) {
-  return `${blockId}-output-${outputId}`;
-}

+ 0 - 49
src/background/workflowEngine/injectContentScript.js

@@ -1,49 +0,0 @@
-import browser from 'webextension-polyfill';
-
-async function contentScriptExist(tabId, frameId = 0) {
-  try {
-    await browser.tabs.sendMessage(
-      tabId,
-      { type: 'content-script-exists' },
-      { frameId }
-    );
-
-    return true;
-  } catch (error) {
-    return false;
-  }
-}
-
-export default function (tabId, frameId = 0) {
-  return new Promise((resolve) => {
-    const currentFrameId = typeof frameId !== 'number' ? 0 : frameId;
-    let tryCount = 0;
-
-    (async function tryExecute() {
-      try {
-        if (tryCount > 3) {
-          resolve(false);
-          return;
-        }
-
-        tryCount += 1;
-
-        await browser.tabs.executeScript(tabId, {
-          allFrames: true,
-          runAt: 'document_start',
-          file: './contentScript.bundle.js',
-        });
-        const isScriptExists = await contentScriptExist(tabId, currentFrameId);
-
-        if (isScriptExists) {
-          resolve(true);
-        } else {
-          setTimeout(tryExecute, 1000);
-        }
-      } catch (error) {
-        console.error(error);
-        setTimeout(tryExecute, 1000);
-      }
-    })();
-  });
-}

+ 0 - 408
src/background/workflowEngine/worker.js

@@ -1,408 +0,0 @@
-import browser from 'webextension-polyfill';
-import {
-  toCamelCase,
-  sleep,
-  objectHasKey,
-  parseJSON,
-  isObject,
-} from '@/utils/helper';
-import referenceData from '@/utils/referenceData';
-import mustacheReplacer from '@/utils/referenceData/mustacheReplacer';
-import { convertData, waitTabLoaded } from './helper';
-import injectContentScript from './injectContentScript';
-
-class Worker {
-  constructor(id, engine, options = {}) {
-    this.id = id;
-    this.engine = engine;
-    this.settings = engine.workflow.settings;
-    this.blocksDetail = options.blocksDetail || {};
-
-    this.loopEls = [];
-    this.loopList = {};
-    this.repeatedTasks = {};
-    this.preloadScripts = [];
-
-    this.windowId = null;
-    this.currentBlock = null;
-    this.childWorkflowId = null;
-
-    this.debugAttached = false;
-
-    this.activeTab = {
-      url: '',
-      frameId: 0,
-      frames: {},
-      groupId: null,
-      id: engine.options?.tabId,
-    };
-  }
-
-  init({ blockId, execParam, state }) {
-    if (state) {
-      Object.keys(state).forEach((key) => {
-        this[key] = state[key];
-      });
-    }
-
-    const block = this.engine.blocks[blockId];
-    this.executeBlock(block, execParam);
-  }
-
-  addDataToColumn(key, value) {
-    if (Array.isArray(key)) {
-      key.forEach((item) => {
-        if (!isObject(item)) return;
-
-        Object.entries(item).forEach(([itemKey, itemValue]) => {
-          this.addDataToColumn(itemKey, itemValue);
-        });
-      });
-
-      return;
-    }
-
-    const insertDefault = this.settings.insertDefaultColumn ?? true;
-    const columnId =
-      (this.engine.columns[key] ? key : this.engine.columnsId[key]) || 'column';
-
-    if (columnId === 'column' && !insertDefault) return;
-
-    const currentColumn = this.engine.columns[columnId];
-    const columnName = currentColumn.name || 'column';
-    const convertedValue = convertData(value, currentColumn.type);
-
-    if (objectHasKey(this.engine.referenceData.table, currentColumn.index)) {
-      this.engine.referenceData.table[currentColumn.index][columnName] =
-        convertedValue;
-    } else {
-      this.engine.referenceData.table.push({
-        [columnName]: convertedValue,
-      });
-    }
-
-    currentColumn.index += 1;
-  }
-
-  setVariable(name, value) {
-    this.engine.referenceData.variables[name] = value;
-  }
-
-  getBlockConnections(blockId, outputIndex = 1) {
-    if (this.engine.isDestroyed) return null;
-
-    const outputId = `${blockId}-output-${outputIndex}`;
-    return this.engine.connectionsMap[outputId] || null;
-  }
-
-  executeNextBlocks(connections, prevBlockData) {
-    connections.forEach((connection, index) => {
-      const { id, targetHandle, sourceHandle } =
-        typeof connection === 'string'
-          ? { id: connection, targetHandle: '', sourceHandle: '' }
-          : connection;
-      const execParam = { prevBlockData, targetHandle, sourceHandle };
-
-      if (index === 0) {
-        this.executeBlock(this.engine.blocks[id], {
-          prevBlockData,
-          ...execParam,
-        });
-      } else {
-        const state = structuredClone({
-          windowId: this.windowId,
-          loopList: this.loopList,
-          activeTab: this.activeTab,
-          currentBlock: this.currentBlock,
-          repeatedTasks: this.repeatedTasks,
-          preloadScripts: this.preloadScripts,
-        });
-
-        this.engine.addWorker({
-          state,
-          execParam,
-          blockId: id,
-        });
-      }
-    });
-  }
-
-  async executeBlock(block, execParam = {}, isRetry = false) {
-    const currentState = await this.engine.states.get(this.engine.id);
-
-    if (!currentState || currentState.isDestroyed) {
-      if (this.engine.isDestroyed) return;
-
-      await this.engine.destroy('stopped');
-      return;
-    }
-
-    const startExecuteTime = Date.now();
-    const prevBlock = this.currentBlock;
-    this.currentBlock = { ...block, startedAt: startExecuteTime };
-
-    if (!isRetry) {
-      await this.engine.updateState({
-        activeTabUrl: this.activeTab.url,
-        childWorkflowId: this.childWorkflowId,
-      });
-    }
-
-    const blockHandler = this.engine.blocksHandler[toCamelCase(block.label)];
-    const handler =
-      !blockHandler && this.blocksDetail[block.label].category === 'interaction'
-        ? this.engine.blocksHandler.interactionBlock
-        : blockHandler;
-
-    if (!handler) {
-      console.error(`${block.label} doesn't have handler`);
-      this.engine.destroy('stopped');
-      return;
-    }
-
-    const { prevBlockData } = execParam;
-    const refData = {
-      prevBlockData,
-      ...this.engine.referenceData,
-      activeTabUrl: this.activeTab.url,
-    };
-
-    const replacedBlock = referenceData({
-      block,
-      data: refData,
-      refKeys:
-        isRetry || block.data.disableBlock
-          ? null
-          : this.blocksDetail[block.label].refDataKeys,
-    });
-    const blockDelay = this.settings?.blockDelay || 0;
-    const addBlockLog = (status, obj = {}) => {
-      let { description } = block.data;
-
-      if (block.label === 'loop-breakpoint') description = block.data.loopId;
-      else if (block.label === 'block-package') description = block.data.name;
-
-      this.engine.addLogHistory({
-        description,
-        prevBlockData,
-        type: status,
-        name: block.label,
-        blockId: block.id,
-        workerId: this.id,
-        timestamp: startExecuteTime,
-        replacedValue: replacedBlock.replacedValue,
-        duration: Math.round(Date.now() - startExecuteTime),
-        ...obj,
-      });
-    };
-
-    try {
-      let result;
-
-      if (block.data.disableBlock) {
-        result = {
-          data: '',
-          nextBlockId: this.getBlockConnections(block.id),
-        };
-      } else {
-        result = await handler.call(this, replacedBlock, {
-          refData,
-          prevBlock,
-          ...(execParam || {}),
-        });
-
-        if (this.engine.isDestroyed) return;
-
-        if (result.replacedValue) {
-          replacedBlock.replacedValue = result.replacedValue;
-        }
-
-        addBlockLog(result.status || 'success', {
-          logId: result.logId,
-        });
-      }
-
-      if (result.nextBlockId && !result.destroyWorker) {
-        setTimeout(() => {
-          this.executeNextBlocks(result.nextBlockId, result.data);
-        }, blockDelay);
-      } else {
-        this.engine.destroyWorker(this.id);
-      }
-    } catch (error) {
-      console.error(error);
-      const { onError: blockOnError } = replacedBlock.data;
-      if (blockOnError && blockOnError.enable) {
-        if (blockOnError.retry && blockOnError.retryTimes) {
-          await sleep(blockOnError.retryInterval * 1000);
-          blockOnError.retryTimes -= 1;
-          await this.executeBlock(replacedBlock, execParam, true);
-
-          return;
-        }
-
-        if (blockOnError.insertData) {
-          blockOnError.dataToInsert.forEach((item) => {
-            let value = mustacheReplacer(item.value, refData)?.value;
-            value = parseJSON(value, value);
-
-            if (item.type === 'variable') {
-              this.setVariable(item.name, value);
-            } else {
-              this.addDataToColumn(item.name, value);
-            }
-          });
-        }
-
-        const nextBlocks = this.getBlockConnections(
-          block.id,
-          blockOnError.toDo === 'continue' ? 1 : 'fallback'
-        );
-        if (blockOnError.toDo !== 'error' && nextBlocks) {
-          addBlockLog('error', {
-            message: error.message,
-            ...(error.data || {}),
-          });
-
-          this.executeNextBlocks(nextBlocks, prevBlockData);
-
-          return;
-        }
-      }
-
-      const errorLogItem = { message: error.message, ...(error.data || {}) };
-      addBlockLog('error', errorLogItem);
-
-      errorLogItem.blockId = block.id;
-
-      const { onError } = this.settings;
-      const nodeConnections = this.getBlockConnections(block.id);
-
-      if (onError === 'keep-running' && nodeConnections) {
-        setTimeout(() => {
-          this.executeNextBlocks(nodeConnections, error.data || '');
-        }, blockDelay);
-      } else if (onError === 'restart-workflow' && !this.parentWorkflow) {
-        const restartKey = `restart-count:${this.id}`;
-        const restartCount = +localStorage.getItem(restartKey) || 0;
-        const maxRestart = this.settings.restartTimes ?? 3;
-
-        if (restartCount >= maxRestart) {
-          localStorage.removeItem(restartKey);
-          this.engine.destroy('error', error.message, errorLogItem);
-          return;
-        }
-
-        this.reset();
-
-        const triggerBlock = this.engine.blocks[this.engine.triggerBlockId];
-        this.executeBlock(triggerBlock, execParam);
-
-        localStorage.setItem(restartKey, restartCount + 1);
-      } else {
-        this.engine.destroy('error', error.message, errorLogItem);
-      }
-    }
-  }
-
-  reset() {
-    this.loopList = {};
-    this.repeatedTasks = {};
-
-    this.windowId = null;
-    this.currentBlock = null;
-    this.childWorkflowId = null;
-
-    this.engine.history = [];
-    this.engine.preloadScripts = [];
-    this.engine.columns = {
-      column: {
-        index: 0,
-        type: 'any',
-        name: this.settings?.defaultColumnName || 'column',
-      },
-    };
-
-    this.activeTab = {
-      url: '',
-      frameId: 0,
-      frames: {},
-      groupId: null,
-      id: this.options?.tabId,
-    };
-    this.engine.referenceData = {
-      table: [],
-      loopData: {},
-      workflow: {},
-      googleSheets: {},
-      variables: this.engine.options?.variables || {},
-      globalData: this.engine.referenceData.globalData,
-    };
-  }
-
-  async _sendMessageToTab(payload, options = {}, runBeforeLoad = false) {
-    try {
-      if (!this.activeTab.id) {
-        const error = new Error('no-tab');
-        error.workflowId = this.id;
-
-        throw error;
-      }
-
-      if (!runBeforeLoad) {
-        await waitTabLoaded({
-          tabId: this.activeTab.id,
-          ms: this.settings?.tabLoadTimeout ?? 30000,
-        });
-      }
-
-      const { executedBlockOnWeb, debugMode } = this.settings;
-      const messagePayload = {
-        isBlock: true,
-        debugMode,
-        executedBlockOnWeb,
-        loopEls: this.loopEls,
-        activeTabId: this.activeTab.id,
-        frameSelector: this.frameSelector,
-        ...payload,
-      };
-
-      const data = await browser.tabs.sendMessage(
-        this.activeTab.id,
-        messagePayload,
-        { frameId: this.activeTab.frameId, ...options }
-      );
-
-      return data;
-    } catch (error) {
-      console.error(error);
-      const noConnection = error.message?.includes(
-        'Could not establish connection'
-      );
-      const channelClosed = error.message?.includes('message channel closed');
-
-      if (noConnection || channelClosed) {
-        const isScriptInjected = await injectContentScript(
-          this.activeTab.id,
-          this.activeTab.frameId
-        );
-
-        if (isScriptInjected) {
-          const result = await this._sendMessageToTab(
-            payload,
-            options,
-            runBeforeLoad
-          );
-          return result;
-        }
-        error.message = 'Could not establish connection to the active tab';
-      } else if (error.message?.startsWith('No tab')) {
-        error.message = 'active-tab-removed';
-      }
-
-      throw error;
-    }
-  }
-}
-
-export default Worker;

+ 2 - 2
src/components/newtab/shared/SharedLogsTable.vue

@@ -113,8 +113,8 @@
 <script setup>
 import { reactive } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { sendMessage } from '@/utils/message';
 import { countDuration } from '@/utils/helper';
+import { workflowState } from '@/newtab/utils/workflowEngine';
 import dayjs from '@/lib/dayjs';
 
 defineProps({
@@ -144,7 +144,7 @@ function getTranslation(key, defText = '') {
   return te(key) ? t(key) : defText;
 }
 function stopWorkflow(stateId) {
-  sendMessage('workflow:stop', stateId, 'background');
+  workflowState.stop(stateId);
 }
 function toggleSelectedLog(selected, id) {
   if (selected) {

+ 2 - 6
src/components/newtab/shared/SharedWorkflowState.vue

@@ -58,8 +58,8 @@
 <script setup>
 import browser from 'webextension-polyfill';
 import { useI18n } from 'vue-i18n';
-import { sendMessage } from '@/utils/message';
 import { getBlocks } from '@/utils/getSharedData';
+import { workflowState } from '@/newtab/utils/workflowEngine';
 import dayjs from '@/lib/dayjs';
 
 const props = defineProps({
@@ -81,10 +81,6 @@ function openTab() {
   browser.tabs.update(props.data.state.tabId, { active: true });
 }
 function stopWorkflow() {
-  sendMessage(
-    props.data.isCollection ? 'collection:stop' : 'workflow:stop',
-    props.data.id,
-    'background'
-  );
+  workflowState.stop(props.data.id);
 }
 </script>

+ 2 - 2
src/components/newtab/workflow/WorkflowRunning.vue

@@ -42,8 +42,8 @@
 <script setup>
 import browser from 'webextension-polyfill';
 import { useI18n } from 'vue-i18n';
-import { sendMessage } from '@/utils/message';
 import { getBlocks } from '@/utils/getSharedData';
+import { workflowState } from '@/newtab/utils/workflowEngine';
 import dayjs from '@/lib/dayjs';
 
 defineProps({
@@ -70,6 +70,6 @@ function openTab(tabId) {
   browser.tabs.update(tabId, { active: true });
 }
 function stopWorkflow(item) {
-  sendMessage('workflow:stop', item, 'background');
+  workflowState.stop(item);
 }
 </script>

+ 2 - 2
src/newtab/pages/logs/Running.vue

@@ -79,8 +79,8 @@ import { computed, watch, shallowRef, onBeforeUnmount } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import { countDuration } from '@/utils/helper';
-import { sendMessage } from '@/utils/message';
 import { useWorkflowStore } from '@/stores/workflow';
+import { workflowState } from '@/newtab/utils/workflowEngine';
 import dbLogs from '@/db/logs';
 import dayjs from '@/lib/dayjs';
 import LogsHistory from '@/components/newtab/logs/LogsHistory.vue';
@@ -100,7 +100,7 @@ const running = computed(() =>
 );
 
 function stopWorkflow() {
-  sendMessage('workflow:stop', running.value.id, 'background');
+  workflowState.stop(running.value.id);
 }
 function getBlockPath(blockId) {
   const { workflowId, teamId } = running.value;