Browse Source

feat: add workflow execution context

Ahmad Kholid 2 năm trước cách đây
mục cha
commit
65b371fb2c
84 tập tin đã thay đổi với 174 bổ sung85 xóa
  1. 1 1
      src/background/BackgroundUtils.js
  2. 2 11
      src/background/BackgroundWorkflowUtils.js
  3. 13 2
      src/background/index.js
  4. 12 6
      src/components/newtab/shared/SharedConditionBuilder/ConditionBuilderInputs.vue
  5. 2 2
      src/components/newtab/shared/SharedLogsTable.vue
  6. 2 2
      src/components/newtab/shared/SharedWorkflowState.vue
  7. 2 2
      src/components/newtab/workflow/WorkflowRunning.vue
  8. 8 2
      src/components/newtab/workflow/edit/EditJavascriptCode.vue
  9. 1 1
      src/components/newtab/workflow/editor/EditorLocalActions.vue
  10. 16 0
      src/components/newtab/workflow/settings/SettingsGeneral.vue
  11. 1 1
      src/components/newtab/workflows/WorkflowsHosted.vue
  12. 1 1
      src/components/newtab/workflows/WorkflowsLocal.vue
  13. 1 1
      src/components/newtab/workflows/WorkflowsShared.vue
  14. 1 1
      src/components/newtab/workflows/WorkflowsUserTeam.vue
  15. 3 1
      src/components/popup/home/HomeTeamWorkflows.vue
  16. 1 1
      src/content/blocksHandler/handlerConditions.js
  17. 8 2
      src/newtab/App.vue
  18. 2 2
      src/newtab/pages/logs/Running.vue
  19. 1 1
      src/newtab/pages/workflows/Host.vue
  20. 2 2
      src/newtab/pages/workflows/[id].vue
  21. 1 1
      src/sandbox/utils/handleBlockExpression.js
  22. 1 0
      src/stores/workflow.js
  23. 4 2
      src/utils/testConditions.js
  24. 2 1
      src/workflowEngine/WorkflowEngine.js
  25. 0 0
      src/workflowEngine/WorkflowLogger.js
  26. 0 0
      src/workflowEngine/WorkflowState.js
  27. 0 0
      src/workflowEngine/WorkflowWorker.js
  28. 5 12
      src/workflowEngine/blocksHandler.js
  29. 0 0
      src/workflowEngine/blocksHandler/handlerActiveTab.js
  30. 0 0
      src/workflowEngine/blocksHandler/handlerBlockPackage.js
  31. 0 0
      src/workflowEngine/blocksHandler/handlerBlocksGroup.js
  32. 0 0
      src/workflowEngine/blocksHandler/handlerBrowserEvent.js
  33. 3 0
      src/workflowEngine/blocksHandler/handlerClipboard.js
  34. 0 0
      src/workflowEngine/blocksHandler/handlerCloseTab.js
  35. 16 4
      src/workflowEngine/blocksHandler/handlerConditions.js
  36. 0 0
      src/workflowEngine/blocksHandler/handlerCookie.js
  37. 0 0
      src/workflowEngine/blocksHandler/handlerCreateElement.js
  38. 0 0
      src/workflowEngine/blocksHandler/handlerDataMapping.js
  39. 0 0
      src/workflowEngine/blocksHandler/handlerDelay.js
  40. 0 0
      src/workflowEngine/blocksHandler/handlerDeleteData.js
  41. 0 0
      src/workflowEngine/blocksHandler/handlerElementExists.js
  42. 0 0
      src/workflowEngine/blocksHandler/handlerExecuteWorkflow.js
  43. 0 0
      src/workflowEngine/blocksHandler/handlerExportData.js
  44. 0 0
      src/workflowEngine/blocksHandler/handlerForwardPage.js
  45. 0 0
      src/workflowEngine/blocksHandler/handlerGoBack.js
  46. 0 0
      src/workflowEngine/blocksHandler/handlerGoogleSheets.js
  47. 0 0
      src/workflowEngine/blocksHandler/handlerHandleDialog.js
  48. 0 0
      src/workflowEngine/blocksHandler/handlerHandleDownload.js
  49. 0 0
      src/workflowEngine/blocksHandler/handlerHoverElement.js
  50. 0 0
      src/workflowEngine/blocksHandler/handlerIncreaseVariable.js
  51. 10 2
      src/workflowEngine/blocksHandler/handlerInsertData.js
  52. 0 0
      src/workflowEngine/blocksHandler/handlerInteractionBlock.js
  53. 4 1
      src/workflowEngine/blocksHandler/handlerJavascriptCode.js
  54. 0 0
      src/workflowEngine/blocksHandler/handlerLogData.js
  55. 0 0
      src/workflowEngine/blocksHandler/handlerLoopBreakpoint.js
  56. 0 0
      src/workflowEngine/blocksHandler/handlerLoopData.js
  57. 0 0
      src/workflowEngine/blocksHandler/handlerLoopElements.js
  58. 0 0
      src/workflowEngine/blocksHandler/handlerNewTab.js
  59. 0 0
      src/workflowEngine/blocksHandler/handlerNewWindow.js
  60. 0 0
      src/workflowEngine/blocksHandler/handlerNotification.js
  61. 0 0
      src/workflowEngine/blocksHandler/handlerParameterPrompt.js
  62. 0 0
      src/workflowEngine/blocksHandler/handlerProxy.js
  63. 0 0
      src/workflowEngine/blocksHandler/handlerRegexVariable.js
  64. 0 0
      src/workflowEngine/blocksHandler/handlerReloadTab.js
  65. 0 0
      src/workflowEngine/blocksHandler/handlerRepeatTask.js
  66. 0 0
      src/workflowEngine/blocksHandler/handlerSaveAssets.js
  67. 0 0
      src/workflowEngine/blocksHandler/handlerSliceVariable.js
  68. 0 0
      src/workflowEngine/blocksHandler/handlerSortData.js
  69. 0 0
      src/workflowEngine/blocksHandler/handlerSwitchTab.js
  70. 0 0
      src/workflowEngine/blocksHandler/handlerSwitchTo.js
  71. 0 0
      src/workflowEngine/blocksHandler/handlerTabUrl.js
  72. 0 0
      src/workflowEngine/blocksHandler/handlerTakeScreenshot.js
  73. 0 0
      src/workflowEngine/blocksHandler/handlerTrigger.js
  74. 0 0
      src/workflowEngine/blocksHandler/handlerWaitConnections.js
  75. 2 1
      src/workflowEngine/blocksHandler/handlerWebhook.js
  76. 2 0
      src/workflowEngine/blocksHandler/handlerWhileLoop.js
  77. 0 0
      src/workflowEngine/blocksHandler/handlerWorkflowState.js
  78. 0 0
      src/workflowEngine/helper.js
  79. 42 17
      src/workflowEngine/index.js
  80. 0 0
      src/workflowEngine/injectContentScript.js
  81. 0 0
      src/workflowEngine/templating/index.js
  82. 0 0
      src/workflowEngine/templating/mustacheReplacer.js
  83. 2 2
      src/workflowEngine/templating/renderString.js
  84. 0 0
      src/workflowEngine/templating/templatingFunctions.js

+ 1 - 1
src/background/BackgroundUtils.js

@@ -1,5 +1,5 @@
 import browser from 'webextension-polyfill';
-import { waitTabLoaded } from '@/newtab/workflowEngine/helper';
+import { waitTabLoaded } from '@/workflowEngine/helper';
 
 class BackgroundUtils {
   static async openDashboard(url, updateTab = true) {

+ 2 - 11
src/background/BackgroundWorkflowUtils.js

@@ -1,5 +1,5 @@
 import browser from 'webextension-polyfill';
-import BackgroundUtils from './BackgroundUtils';
+import { startWorkflowExec } from '@/workflowEngine';
 
 class BackgroundWorkflowUtils {
   static flattenTeamWorkflows(workflows) {
@@ -40,16 +40,7 @@ class BackgroundWorkflowUtils {
   }
 
   static async executeWorkflow(workflowData, options) {
-    await BackgroundUtils.openDashboard('', false);
-    const result = await BackgroundUtils.sendMessageToDashboard(
-      'workflow:execute',
-      {
-        data: workflowData,
-        options,
-      }
-    );
-
-    return result;
+    startWorkflowExec(workflowData, options, false);
   }
 }
 

+ 13 - 2
src/background/index.js

@@ -98,7 +98,7 @@ message.on('get:tab-screenshot', (options, sender) =>
 
 message.on('dashboard:refresh-packages', async () => {
   const tabs = await browser.tabs.query({
-    url: chrome.runtime.getURL('/newtab.html'),
+    url: browser.runtime.getURL('/newtab.html'),
   });
 
   tabs.forEach((tab) => {
@@ -108,7 +108,18 @@ message.on('dashboard:refresh-packages', async () => {
   });
 });
 
-message.on('workflow:execute', (workflowData, sender) => {
+message.on('workflow:execute', async (workflowData, sender) => {
+  const context = workflowData.settings.execContext;
+  if (!context || context === 'popup') {
+    await BackgroundUtils.openDashboard('', false);
+    await sleep(1000);
+    await BackgroundUtils.sendMessageToDashboard('workflow:execute', {
+      data: workflowData,
+      options: workflowData.option,
+    });
+    return;
+  }
+
   if (workflowData.includeTabId) {
     if (!workflowData.options) workflowData.options = {};
 

+ 12 - 6
src/components/newtab/shared/SharedConditionBuilder/ConditionBuilderInputs.vue

@@ -34,15 +34,20 @@
             class="mr-2"
           >
             <option
-              v-for="context in ['website', 'background']"
-              :key="context"
-              :disabled="isFirefox && context === 'background'"
-              :value="context"
+              :disabled="
+                isFirefox ||
+                !workflow?.data?.value.settings?.execContext ||
+                workflow?.data?.value.settings?.execContext !== 'popup'
+              "
+              value="background"
             >
               {{
-                t(`workflow.blocks.javascript-code.context.items.${context}`)
+                t(`workflow.blocks.javascript-code.context.items.background`)
               }}
             </option>
+            <option value="website">
+              {{ t(`workflow.blocks.javascript-code.context.items.website`) }}
+            </option>
           </ui-select>
           <v-remixicon
             :title="t('workflow.conditionBuilder.topAwait')"
@@ -94,7 +99,7 @@
   </div>
 </template>
 <script setup>
-import { ref, watch, defineAsyncComponent } from 'vue';
+import { ref, watch, defineAsyncComponent, inject } from 'vue';
 import { nanoid } from 'nanoid';
 import { useI18n } from 'vue-i18n';
 import { autocompletion } from '@codemirror/autocomplete';
@@ -143,6 +148,7 @@ const conditionOperators = conditionBuilder.compareTypes.reduce((acc, type) => {
 }, {});
 
 const excludeData = ['context'];
+const workflow = inject('workflow');
 
 const { t } = useI18n();
 const inputsData = ref(cloneDeep(props.data));

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

@@ -114,7 +114,7 @@
 import { reactive } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { countDuration } from '@/utils/helper';
-import { workflowState } from '@/newtab/workflowEngine';
+import { stopWorkflowExec } from '@/workflowEngine';
 import dayjs from '@/lib/dayjs';
 
 defineProps({
@@ -144,7 +144,7 @@ function getTranslation(key, defText = '') {
   return te(key) ? t(key) : defText;
 }
 function stopWorkflow(stateId) {
-  workflowState.stop(stateId);
+  stopWorkflowExec(stateId);
 }
 function toggleSelectedLog(selected, id) {
   if (selected) {

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

@@ -59,7 +59,7 @@
 import browser from 'webextension-polyfill';
 import { useI18n } from 'vue-i18n';
 import { getBlocks } from '@/utils/getSharedData';
-import { workflowState } from '@/newtab/workflowEngine';
+import { stopWorkflowExec } from '@/workflowEngine';
 import dayjs from '@/lib/dayjs';
 
 const props = defineProps({
@@ -81,6 +81,6 @@ function openTab() {
   browser.tabs.update(props.data.state.tabId, { active: true });
 }
 function stopWorkflow() {
-  workflowState.stop(props.data.id);
+  stopWorkflowExec(props.data.id);
 }
 </script>

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

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

+ 8 - 2
src/components/newtab/workflow/edit/EditJavascriptCode.vue

@@ -17,7 +17,11 @@
         @change="updateData({ timeout: +$event })"
       />
       <ui-select
-        v-if="!isFirefox"
+        v-if="
+          !isFirefox &&
+          (!workflow?.data?.value.settings?.execContext ||
+            workflow?.data?.value.settings?.execContext === 'popup')
+        "
         :model-value="data.context"
         :label="t('workflow.blocks.javascript-code.context.name')"
         class="mb-2 w-full"
@@ -145,7 +149,7 @@
   </div>
 </template>
 <script setup>
-import { watch, reactive, defineAsyncComponent } from 'vue';
+import { watch, reactive, defineAsyncComponent, inject } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { autocompletion } from '@codemirror/autocomplete';
 import {
@@ -193,6 +197,8 @@ const availableFuncs = [
 ];
 const autocompleteList = Object.values(automaFuncsSnippets).slice(0, 4);
 
+const workflow = inject('workflow');
+
 const state = reactive({
   activeTab: 'code',
   code: `${props.data.code}`,

+ 1 - 1
src/components/newtab/workflow/editor/EditorLocalActions.vue

@@ -327,7 +327,7 @@ import { tagColors } from '@/utils/shared';
 import { parseJSON, findTriggerBlock } from '@/utils/helper';
 import { exportWorkflow, convertWorkflow } from '@/utils/workflowData';
 import { registerWorkflowTrigger } from '@/utils/workflowTrigger';
-import { executeWorkflow } from '@/newtab/workflowEngine';
+import { executeWorkflow } from '@/workflowEngine';
 import getTriggerText from '@/utils/triggerText';
 import convertWorkflowData from '@/utils/convertWorkflowData';
 import WorkflowShareTeam from '@/components/newtab/workflow/WorkflowShareTeam.vue';

+ 16 - 0
src/components/newtab/workflow/settings/SettingsGeneral.vue

@@ -32,6 +32,22 @@
       </span>
     </div>
   </div>
+  <div class="flex items-center pt-4">
+    <div class="mr-4 flex-1">
+      <p>Workflow Execution</p>
+      <p class="text-gray-600 dark:text-gray-200 text-sm leading-tight">
+        Workflow execution environment (Use "Popup" if workflow runs more than 5
+        minutes)
+      </p>
+    </div>
+    <ui-select
+      :model-value="settings.execContext || 'popup'"
+      @change="updateSetting('execContext', $event)"
+    >
+      <option value="popup">Popup</option>
+      <option value="background">Background</option>
+    </ui-select>
+  </div>
   <div class="flex items-center pt-4">
     <div class="mr-4 flex-1">
       <p>

+ 1 - 1
src/components/newtab/workflows/WorkflowsHosted.vue

@@ -15,7 +15,7 @@ import { useI18n } from 'vue-i18n';
 import { useDialog } from '@/composable/dialog';
 import { arraySorter } from '@/utils/helper';
 import { useHostedWorkflowStore } from '@/stores/hostedWorkflow';
-import { executeWorkflow } from '@/newtab/workflowEngine';
+import { executeWorkflow } from '@/workflowEngine';
 import SharedCard from '@/components/newtab/shared/SharedCard.vue';
 
 const props = defineProps({

+ 1 - 1
src/components/newtab/workflows/WorkflowsLocal.vue

@@ -112,7 +112,7 @@ import { useDialog } from '@/composable/dialog';
 import { useWorkflowStore } from '@/stores/workflow';
 import { exportWorkflow } from '@/utils/workflowData';
 import { useSharedWorkflowStore } from '@/stores/sharedWorkflow';
-import { executeWorkflow } from '@/newtab/workflowEngine';
+import { executeWorkflow } from '@/workflowEngine';
 import WorkflowsLocalCard from './WorkflowsLocalCard.vue';
 
 const props = defineProps({

+ 1 - 1
src/components/newtab/workflows/WorkflowsShared.vue

@@ -12,7 +12,7 @@
 import { computed } from 'vue';
 import { useSharedWorkflowStore } from '@/stores/sharedWorkflow';
 import { arraySorter } from '@/utils/helper';
-import { executeWorkflow } from '@/newtab/workflowEngine';
+import { executeWorkflow } from '@/workflowEngine';
 import SharedCard from '@/components/newtab/shared/SharedCard.vue';
 
 const props = defineProps({

+ 1 - 1
src/components/newtab/workflows/WorkflowsUserTeam.vue

@@ -61,7 +61,7 @@ import { useTeamWorkflowStore } from '@/stores/teamWorkflow';
 import { arraySorter } from '@/utils/helper';
 import { useDialog } from '@/composable/dialog';
 import { tagColors } from '@/utils/shared';
-import { executeWorkflow } from '@/newtab/workflowEngine';
+import { executeWorkflow } from '@/workflowEngine';
 import SharedCard from '@/components/newtab/shared/SharedCard.vue';
 
 const props = defineProps({

+ 3 - 1
src/components/popup/home/HomeTeamWorkflows.vue

@@ -35,7 +35,6 @@ import { useUserStore } from '@/stores/user';
 import { sendMessage } from '@/utils/message';
 import { useTeamWorkflowStore } from '@/stores/teamWorkflow';
 import { tagColors } from '@/utils/shared';
-import { executeWorkflow } from '@/newtab/workflowEngine';
 import dayjs from '@/lib/dayjs';
 
 const props = defineProps({
@@ -60,6 +59,9 @@ function openWorkflowPage({ teamId, id }) {
   const url = `/teams/${teamId}/workflows/${id}`;
   sendMessage('open:dashboard', url, 'background');
 }
+function executeWorkflow(workflow) {
+  sendMessage('workflow:execute', workflow, 'background');
+}
 
 onMounted(() => {
   if (!userStore.user?.teams) return;

+ 1 - 1
src/content/blocksHandler/handlerConditions.js

@@ -1,6 +1,6 @@
 import { customAlphabet } from 'nanoid/non-secure';
 import { visibleInViewport, isXPath } from '@/utils/helper';
-import { automaRefDataStr } from '@/newtab/workflowEngine/helper';
+import { automaRefDataStr } from '@/workflowEngine/helper';
 import handleSelector from '../handleSelector';
 
 const nanoid = customAlphabet('1234567890abcdef', 5);

+ 8 - 2
src/newtab/App.vue

@@ -84,7 +84,7 @@ import dataMigration from '@/utils/dataMigration';
 import iconFirefox from '@/assets/svg/logoFirefox.svg';
 import iconChrome from '@/assets/svg/logo.svg';
 import SharedPermissionsModal from '@/components/newtab/shared/SharedPermissionsModal.vue';
-import { executeWorkflow } from './workflowEngine';
+import { startWorkflowExec } from '@/workflowEngine';
 
 let icon;
 if (window.location.protocol === 'moz-extension:') {
@@ -231,7 +231,7 @@ const messageEvents = {
     }
   },
   'workflow:execute': function ({ data, options = {} }) {
-    executeWorkflow(data, options);
+    startWorkflowExec(data, options);
   },
   'recording:stop': stopRecording,
   'background--recording:stop': stopRecording,
@@ -243,6 +243,12 @@ browser.runtime.onMessage.addListener(({ type, data }) => {
   messageEvents[type](data);
 });
 
+browser.storage.local.onChanged.addListener(({ workflowStates }) => {
+  if (!workflowStates) return;
+
+  workflowStore.states = Object.values(workflowStates.newValue);
+});
+
 useHead(() => {
   const runningWorkflows = workflowStore.states.length;
 

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

@@ -80,7 +80,7 @@ import { useRoute, useRouter } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import { countDuration } from '@/utils/helper';
 import { useWorkflowStore } from '@/stores/workflow';
-import { workflowState } from '@/newtab/workflowEngine';
+import { stopWorkflowExec } from '@/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() {
-  workflowState.stop(running.value.id);
+  stopWorkflowExec(running.value.id);
 }
 function getBlockPath(blockId) {
   const { workflowId, teamId } = running.value;

+ 1 - 1
src/newtab/pages/workflows/Host.vue

@@ -120,7 +120,7 @@ import { useGroupTooltip } from '@/composable/groupTooltip';
 import { findTriggerBlock } from '@/utils/helper';
 import convertWorkflowData from '@/utils/convertWorkflowData';
 import { useWorkflowStore } from '@/stores/workflow';
-import { executeWorkflow } from '@/newtab/workflowEngine';
+import { executeWorkflow } from '@/workflowEngine';
 import { useHostedWorkflowStore } from '@/stores/hostedWorkflow';
 import getTriggerText from '@/utils/triggerText';
 import EditorLogs from '@/components/newtab/workflow/editor/EditorLogs.vue';

+ 2 - 2
src/newtab/pages/workflows/[id].vue

@@ -320,9 +320,9 @@ import { excludeGroupBlocks } from '@/utils/shared';
 import { useGroupTooltip } from '@/composable/groupTooltip';
 import { useCommandManager } from '@/composable/commandManager';
 import { debounce, parseJSON, throttle } from '@/utils/helper';
-import { executeWorkflow } from '@/newtab/workflowEngine';
+import { executeWorkflow } from '@/workflowEngine';
 import { registerWorkflowTrigger } from '@/utils/workflowTrigger';
-import functions from '@/newtab/workflowEngine/templating/templatingFunctions';
+import functions from '@/workflowEngine/templating/templatingFunctions';
 import browser from 'webextension-polyfill';
 import dbStorage from '@/db/storage';
 import DroppedNode from '@/utils/editor/DroppedNode';

+ 1 - 1
src/sandbox/utils/handleBlockExpression.js

@@ -1,5 +1,5 @@
 import * as tmpl from '@n8n_io/riot-tmpl';
-import functions from '@/newtab/workflowEngine/templating/templatingFunctions';
+import functions from '@/workflowEngine/templating/templatingFunctions';
 
 tmpl.brackets.set('{{ }}');
 

+ 1 - 0
src/stores/workflow.js

@@ -48,6 +48,7 @@ const defaultWorkflow = (data = null, options = {}) => {
       debugMode: false,
       restartTimes: 3,
       notification: true,
+      execContext: 'popup',
       reuseLastState: false,
       inputAutocomplete: true,
       onError: 'stop-workflow',

+ 4 - 2
src/utils/testConditions.js

@@ -1,6 +1,6 @@
 import cloneDeep from 'lodash.clonedeep';
 import objectPath from 'object-path';
-import renderString from '@/newtab/workflowEngine/templating/renderString';
+import renderString from '@/workflowEngine/templating/renderString';
 import { conditionBuilder } from './shared';
 
 const isBoolStr = (str) => {
@@ -59,7 +59,8 @@ export default async function (conditionsArr, workflowData) {
     for (const key of Object.keys(data)) {
       const { value, list } = await renderString(
         copyData[key],
-        workflowData.refData
+        workflowData.refData,
+        workflowData.isPopup
       );
 
       copyData[key] = value ?? '';
@@ -83,6 +84,7 @@ export default async function (conditionsArr, workflowData) {
       } else {
         conditionValue = await workflowData.checkCodeCondition({
           data: copyData,
+          isPopup: workflowData.isPopup,
           refData: workflowData.refData,
         });
       }

+ 2 - 1
src/newtab/workflowEngine/WorkflowEngine.js → src/workflowEngine/WorkflowEngine.js

@@ -9,11 +9,12 @@ import WorkflowWorker from './WorkflowWorker';
 let blocks = getBlocks();
 
 class WorkflowEngine {
-  constructor(workflow, { states, logger, blocksHandler, options }) {
+  constructor(workflow, { states, logger, blocksHandler, isPopup, options }) {
     this.id = nanoid();
     this.states = states;
     this.logger = logger;
     this.workflow = workflow;
+    this.isPopup = isPopup ?? true;
     this.blocksHandler = blocksHandler;
     this.parentWorkflow = options?.parentWorkflow;
     this.saveLog = workflow.settings?.saveLog ?? true;

+ 0 - 0
src/newtab/workflowEngine/WorkflowLogger.js → src/workflowEngine/WorkflowLogger.js


+ 0 - 0
src/newtab/workflowEngine/WorkflowState.js → src/workflowEngine/WorkflowState.js


+ 0 - 0
src/newtab/workflowEngine/WorkflowWorker.js → src/workflowEngine/WorkflowWorker.js


+ 5 - 12
src/newtab/workflowEngine/blocksHandler.js → src/workflowEngine/blocksHandler.js

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

+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerActiveTab.js → src/workflowEngine/blocksHandler/handlerActiveTab.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerBlockPackage.js → src/workflowEngine/blocksHandler/handlerBlockPackage.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerBlocksGroup.js → src/workflowEngine/blocksHandler/handlerBlocksGroup.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerBrowserEvent.js → src/workflowEngine/blocksHandler/handlerBrowserEvent.js


+ 3 - 0
src/newtab/workflowEngine/blocksHandler/handlerClipboard.js → src/workflowEngine/blocksHandler/handlerClipboard.js

@@ -21,6 +21,9 @@ function doCommand(command, value) {
 }
 
 export default async function ({ data, id, label }) {
+  if (!this.engine.isPopup)
+    throw new Error('Clipboard block is not supported in background execution');
+
   const hasPermission = await browser.permissions.contains({
     permissions: ['clipboardRead'],
   });

+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerCloseTab.js → src/workflowEngine/blocksHandler/handlerCloseTab.js


+ 16 - 4
src/newtab/workflowEngine/blocksHandler/handlerConditions.js → src/workflowEngine/blocksHandler/handlerConditions.js

@@ -48,7 +48,11 @@ function checkConditions(data, conditionOptions) {
 async function checkCodeCondition(activeTab, payload) {
   const variableId = nanoid();
 
-  if (!payload.data.context || payload.data.context === 'website') {
+  if (
+    !payload.data.context ||
+    payload.data.context === 'website' ||
+    !payload.isPopup
+  ) {
     if (!activeTab.id) throw new Error('no-tab');
 
     const [{ result }] = await browser.scripting.executeScript({
@@ -132,6 +136,7 @@ async function conditions({ data, id }, { prevBlockData, refData }) {
     const conditionPayload = {
       refData,
       isMV2: this.engine.isMV2,
+      isPopup: this.engine.isPopup,
       checkCodeCondition: (payload) =>
         checkCodeCondition(this.activeTab, payload),
       sendMessage: (payload) =>
@@ -151,9 +156,16 @@ async function conditions({ data, id }, { prevBlockData, refData }) {
     for (const { type, value, compareValue, id: itemId } of data.conditions) {
       if (isConditionMet) break;
 
-      const firstValue = await renderString(compareValue ?? prevData, refData)
-        .value;
-      const secondValue = await renderString(value, refData).value;
+      const firstValue = await renderString(
+        compareValue ?? prevData,
+        refData,
+        this.engine.isPopup
+      ).value;
+      const secondValue = await renderString(
+        value,
+        refData,
+        this.engine.isPopup
+      ).value;
 
       Object.assign(replacedValue, firstValue.list, secondValue.list);
 

+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerCookie.js → src/workflowEngine/blocksHandler/handlerCookie.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerCreateElement.js → src/workflowEngine/blocksHandler/handlerCreateElement.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerDataMapping.js → src/workflowEngine/blocksHandler/handlerDataMapping.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerDelay.js → src/workflowEngine/blocksHandler/handlerDelay.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerDeleteData.js → src/workflowEngine/blocksHandler/handlerDeleteData.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerElementExists.js → src/workflowEngine/blocksHandler/handlerElementExists.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerExecuteWorkflow.js → src/workflowEngine/blocksHandler/handlerExecuteWorkflow.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerExportData.js → src/workflowEngine/blocksHandler/handlerExportData.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerForwardPage.js → src/workflowEngine/blocksHandler/handlerForwardPage.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerGoBack.js → src/workflowEngine/blocksHandler/handlerGoBack.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerGoogleSheets.js → src/workflowEngine/blocksHandler/handlerGoogleSheets.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerHandleDialog.js → src/workflowEngine/blocksHandler/handlerHandleDialog.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerHandleDownload.js → src/workflowEngine/blocksHandler/handlerHandleDownload.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerHoverElement.js → src/workflowEngine/blocksHandler/handlerHoverElement.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerIncreaseVariable.js → src/workflowEngine/blocksHandler/handlerIncreaseVariable.js


+ 10 - 2
src/newtab/workflowEngine/blocksHandler/handlerInsertData.js → src/workflowEngine/blocksHandler/handlerInsertData.js

@@ -10,7 +10,11 @@ async function insertData({ id, data }, { refData }) {
     let value = '';
 
     if (item.isFile) {
-      const replacedPath = await renderString(item.filePath || '', refData);
+      const replacedPath = await renderString(
+        item.filePath || '',
+        refData,
+        this.engine.isPopup
+      );
       const path = replacedPath.value;
       const isJSON = path.endsWith('.json');
       const isCSV = path.endsWith('.csv');
@@ -35,7 +39,11 @@ async function insertData({ id, data }, { refData }) {
       value = result;
       Object.assign(replacedValueList, replacedPath.list);
     } else {
-      const replacedValue = await renderString(item.value, refData);
+      const replacedValue = await renderString(
+        item.value,
+        refData,
+        this.engine.isPopup
+      );
       value = parseJSON(replacedValue.value, replacedValue.value);
       Object.assign(replacedValueList, replacedValue.list);
     }

+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerInteractionBlock.js → src/workflowEngine/blocksHandler/handlerInteractionBlock.js


+ 4 - 1
src/newtab/workflowEngine/blocksHandler/handlerJavascriptCode.js → src/workflowEngine/blocksHandler/handlerJavascriptCode.js

@@ -113,7 +113,10 @@ export async function javascriptCode({ outputs, data, ...block }, { refData }) {
     });
   }
 
-  const inSandbox = BROWSER_TYPE !== 'firefox' && data.context === 'background';
+  const inSandbox =
+    BROWSER_TYPE !== 'firefox' &&
+    data.context === 'background' &&
+    this.engine.isPopup;
   const result = await (inSandbox
     ? messageSandbox('javascriptBlock', {
         instanceId,

+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerLogData.js → src/workflowEngine/blocksHandler/handlerLogData.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerLoopBreakpoint.js → src/workflowEngine/blocksHandler/handlerLoopBreakpoint.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerLoopData.js → src/workflowEngine/blocksHandler/handlerLoopData.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerLoopElements.js → src/workflowEngine/blocksHandler/handlerLoopElements.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerNewTab.js → src/workflowEngine/blocksHandler/handlerNewTab.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerNewWindow.js → src/workflowEngine/blocksHandler/handlerNewWindow.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerNotification.js → src/workflowEngine/blocksHandler/handlerNotification.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerParameterPrompt.js → src/workflowEngine/blocksHandler/handlerParameterPrompt.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerProxy.js → src/workflowEngine/blocksHandler/handlerProxy.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerRegexVariable.js → src/workflowEngine/blocksHandler/handlerRegexVariable.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerReloadTab.js → src/workflowEngine/blocksHandler/handlerReloadTab.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerRepeatTask.js → src/workflowEngine/blocksHandler/handlerRepeatTask.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerSaveAssets.js → src/workflowEngine/blocksHandler/handlerSaveAssets.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerSliceVariable.js → src/workflowEngine/blocksHandler/handlerSliceVariable.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerSortData.js → src/workflowEngine/blocksHandler/handlerSortData.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerSwitchTab.js → src/workflowEngine/blocksHandler/handlerSwitchTab.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerSwitchTo.js → src/workflowEngine/blocksHandler/handlerSwitchTo.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerTabUrl.js → src/workflowEngine/blocksHandler/handlerTabUrl.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerTakeScreenshot.js → src/workflowEngine/blocksHandler/handlerTakeScreenshot.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerTrigger.js → src/workflowEngine/blocksHandler/handlerTrigger.js


+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerWaitConnections.js → src/workflowEngine/blocksHandler/handlerWaitConnections.js


+ 2 - 1
src/newtab/workflowEngine/blocksHandler/handlerWebhook.js → src/workflowEngine/blocksHandler/handlerWebhook.js

@@ -28,7 +28,8 @@ export async function webhook({ data, id }, { refData }) {
 
     const newHeaders = [];
     for (const { value, name } of data.headers) {
-      const newValue = await renderString(value, refData).value;
+      const newValue = await renderString(value, refData, this.engine.isPopup)
+        .value;
 
       newHeaders.push({ name, value: newValue });
     }

+ 2 - 0
src/newtab/workflowEngine/blocksHandler/handlerWhileLoop.js → src/workflowEngine/blocksHandler/handlerWhileLoop.js

@@ -3,6 +3,8 @@ import testConditions from '@/utils/testConditions';
 async function whileLoop({ data, id }, { refData }) {
   const conditionPayload = {
     refData,
+    isMV2: this.engine.isMV2,
+    isPopup: this.engine.isPopup,
     activeTab: this.activeTab.id,
     sendMessage: (payload) =>
       this._sendMessageToTab({ ...payload.data, label: 'conditions', id }),

+ 0 - 0
src/newtab/workflowEngine/blocksHandler/handlerWorkflowState.js → src/workflowEngine/blocksHandler/handlerWorkflowState.js


+ 0 - 0
src/newtab/workflowEngine/helper.js → src/workflowEngine/helper.js


+ 42 - 17
src/newtab/workflowEngine/index.js → src/workflowEngine/index.js

@@ -1,9 +1,9 @@
 import browser from 'webextension-polyfill';
 import dayjs from '@/lib/dayjs';
-import { useWorkflowStore } from '@/stores/workflow';
 import decryptFlow, { getWorkflowPass } from '@/utils/decryptFlow';
 import { parseJSON } from '@/utils/helper';
 import { fetchApi } from '@/utils/api';
+import { sendMessage } from '@/utils/message';
 import getBlockMessage from '@/utils/getBlockMessage';
 import convertWorkflowData from '@/utils/convertWorkflowData';
 import WorkflowState from './WorkflowState';
@@ -13,15 +13,14 @@ import blocksHandler from './blocksHandler';
 
 const workflowStateStorage = {
   get() {
-    const workflowStore = useWorkflowStore();
-    return workflowStore.states;
+    return browser.storage.local
+      .get('states')
+      .then(({ workflowStates }) => workflowStates || []);
   },
   set(key, value) {
-    const workflowStore = useWorkflowStore();
     const states = Object.values(value);
 
-    browser.storage.local.set({ workflowStates: states });
-    workflowStore.updateStates(states);
+    return browser.storage.local.set({ workflowStates: states });
   },
 };
 
@@ -30,8 +29,12 @@ export const workflowState = new WorkflowState({
   storage: workflowStateStorage,
 });
 
-export function executeWorkflow(workflowData, options) {
-  if (workflowData.isDisabled) return null;
+export function stopWorkflowExec(executionId) {
+  workflowState.stop(executionId);
+  sendMessage('workflow:stop', executionId, 'background');
+}
+
+export function startWorkflowExec(workflowData, options, isPopup = true) {
   if (workflowData.isProtected) {
     const flow = parseJSON(workflowData.drawflow, null);
 
@@ -45,6 +48,7 @@ export function executeWorkflow(workflowData, options) {
   const convertedWorkflow = convertWorkflowData(workflowData);
   const engine = new WorkflowEngine(convertedWorkflow, {
     options,
+    isPopup,
     states: workflowState,
     logger: workflowLogger,
     blocksHandler: blocksHandler(),
@@ -122,15 +126,36 @@ export function executeWorkflow(workflowData, options) {
     }
   );
 
-  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.storage.local.get('checkStatus').then(({ checkStatus }) => {
+    const isSameDay = dayjs().isSame(checkStatus, 'day');
+    if (!isSameDay || !checkStatus) {
+      fetchApi('/status')
+        .then((response) => response.json())
+        .then(() => {
+          browser.storage.local.set({ checkStatus: new Date().toString() });
+        });
+    }
+  });
 
   return engine;
 }
+
+export function executeWorkflow(workflowData, options) {
+  if (!workflowData || workflowData.isDisabled) return;
+
+  const context = workflowData.settings.execContext;
+  if (context === 'background') {
+    sendMessage('workflow:execute', { ...workflowData, options }, 'background');
+    return;
+  }
+
+  browser.tabs
+    .query({ active: true, currentWindow: true })
+    .then(async ([tab]) => {
+      if (tab && tab.url.includes(browser.runtime.getURL(''))) {
+        await browser.windows.update(tab.windowId, { focused: false });
+      }
+
+      startWorkflowExec(workflowData, options);
+    });
+}

+ 0 - 0
src/newtab/workflowEngine/injectContentScript.js → src/workflowEngine/injectContentScript.js


+ 0 - 0
src/newtab/workflowEngine/templating/index.js → src/workflowEngine/templating/index.js


+ 0 - 0
src/newtab/workflowEngine/templating/mustacheReplacer.js → src/workflowEngine/templating/mustacheReplacer.js


+ 2 - 2
src/newtab/workflowEngine/templating/renderString.js → src/workflowEngine/templating/renderString.js

@@ -3,7 +3,7 @@ import mustacheReplacer from './mustacheReplacer';
 
 const isFirefox = BROWSER_TYPE === 'firefox';
 
-export default async function (str, data) {
+export default async function (str, data, isPopup = true) {
   if (!str || typeof str !== 'string') return '';
 
   const hasMustacheTag = /\{\{(.*?)\}\}/.test(str);
@@ -15,7 +15,7 @@ export default async function (str, data) {
   }
 
   let renderedValue = {};
-  if (str.startsWith('!!') && !isFirefox) {
+  if (str.startsWith('!!') && !isFirefox && isPopup) {
     const refKeysRegex =
       /(variables|table|secrets|loopData|workflow|googleSheets|globalData)@/g;
     const strToRender = str.replace(refKeysRegex, '$1.');

+ 0 - 0
src/newtab/workflowEngine/templating/templatingFunctions.js → src/workflowEngine/templating/templatingFunctions.js