Browse Source

fix: validation

Ahmad Kholid 2 years ago
parent
commit
48009fb2e4
34 changed files with 170 additions and 427 deletions
  1. 3 0
      business/dev/blocks/backgroundHandler/index.js
  2. 3 0
      business/dev/blocks/contentHandler/index.js
  3. 3 1
      business/dev/blocks/editComponents/index.js
  4. 3 1
      business/dev/blocks/index.js
  5. 1 3
      business/dev/index.js
  6. 3 1
      business/dev/parameters/index.js
  7. 0 78
      src/background/collectionEngine/flowHandler.js
  8. 0 251
      src/background/collectionEngine/index.js
  9. 4 1
      src/background/index.js
  10. 6 4
      src/background/workflowEngine/blocksHandler.js
  11. 18 5
      src/background/workflowEngine/engine.js
  12. 6 5
      src/background/workflowEngine/worker.js
  13. 3 2
      src/components/newtab/logs/LogsHistory.vue
  14. 4 3
      src/components/newtab/package/PackageSettingIOSelect.vue
  15. 4 2
      src/components/newtab/package/PackageSettings.vue
  16. 4 3
      src/components/newtab/shared/SharedWorkflowState.vue
  17. 3 2
      src/components/newtab/workflow/WorkflowBlockList.vue
  18. 14 16
      src/components/newtab/workflow/WorkflowDetailsCard.vue
  19. 1 1
      src/components/newtab/workflow/WorkflowEditBlock.vue
  20. 3 3
      src/components/newtab/workflow/WorkflowEditor.vue
  21. 3 2
      src/components/newtab/workflow/WorkflowRunning.vue
  22. 14 6
      src/components/newtab/workflow/edit/EditWorkflowParameters.vue
  23. 2 2
      src/components/newtab/workflow/edit/Parameter/ParameterInputOptions.vue
  24. 4 2
      src/components/newtab/workflow/editor/EditorUsedCredentials.vue
  25. 3 7
      src/composable/editorBlock.js
  26. 6 4
      src/content/blocksHandler.js
  27. 8 2
      src/content/index.js
  28. 5 2
      src/newtab/App.vue
  29. 4 4
      src/newtab/pages/workflows/[id].vue
  30. 22 9
      src/params/App.vue
  31. 3 0
      src/popup/pages/Home.vue
  32. 4 2
      src/utils/editor/DroppedNode.js
  33. 6 0
      src/utils/getSharedData.js
  34. 0 3
      src/utils/shared.js

+ 3 - 0
business/dev/blocks/backgroundHandler/index.js

@@ -0,0 +1,3 @@
+export default function () {
+  return {};
+}

+ 3 - 0
business/dev/blocks/contentHandler/index.js

@@ -0,0 +1,3 @@
+export default function () {
+  return {};
+}

+ 3 - 1
business/dev/blocks/editComponents/index.js

@@ -1 +1,3 @@
-export default {};
+export default function () {
+  return {};
+}

+ 3 - 1
business/dev/blocks/index.js

@@ -1 +1,3 @@
-export default {};
+export default function () {
+  return {};
+}

+ 1 - 3
business/dev/index.js

@@ -1,3 +1 @@
-export default {
-  workflowParameters: {},
-};
+export default {};

+ 3 - 1
business/dev/parameters/index.js

@@ -1 +1,3 @@
-export default {};
+export default function () {
+  return {};
+}

+ 0 - 78
src/background/collectionEngine/flowHandler.js

@@ -1,78 +0,0 @@
-import dataExporter from '@/utils/dataExporter';
-import WorkflowEngine from '../workflowEngine/engine';
-import blocksHandler from '../workflowEngine/blocksHandler';
-
-export function workflow(flow) {
-  return new Promise((resolve, reject) => {
-    const currentWorkflow = this.workflows.find(({ id }) => id === flow.itemId);
-
-    if (!currentWorkflow) {
-      const error = new Error(`Can't find workflow with ${flow.itemId} ID`);
-      error.name = 'Workflow';
-
-      reject(error);
-      return;
-    }
-
-    if (currentWorkflow.isDisabled) {
-      resolve({
-        type: 'stopped',
-        name: currentWorkflow.name,
-        message: 'workflow-disabled',
-      });
-
-      return;
-    }
-
-    const { globalData } = this.collection;
-
-    const engine = new WorkflowEngine(currentWorkflow, {
-      blocksHandler,
-      states: this.states,
-      logger: this.logger,
-      options: {
-        parentWorkflow: {
-          id: this.id,
-          isCollection: true,
-          name: this.collection.name,
-        },
-        data: {
-          globalData: globalData.trim() === '' ? null : globalData,
-        },
-      },
-    });
-
-    this.executedWorkflow.data = {
-      id: engine.id,
-      name: currentWorkflow.name,
-      icon: currentWorkflow.icon,
-      workflowId: currentWorkflow.id,
-    };
-
-    engine.init();
-    engine.on('destroyed', ({ id, status, message }) => {
-      this.data.push({
-        id,
-        status,
-        errorMessage: message,
-        workflowId: currentWorkflow.id,
-        workflowName: currentWorkflow.name,
-      });
-
-      resolve({
-        id,
-        message,
-        type: status,
-        name: currentWorkflow.name,
-      });
-    });
-  });
-}
-
-export function exportResult() {
-  return new Promise((resolve) => {
-    dataExporter(this.data, { name: this.collection.name, type: 'json' }, true);
-
-    resolve({ name: 'Export result' });
-  });
-}

+ 0 - 251
src/background/collectionEngine/index.js

@@ -1,251 +0,0 @@
-import { nanoid } from 'nanoid';
-import browser from 'webextension-polyfill';
-import { toCamelCase } from '@/utils/helper';
-import * as flowHandler from './flowHandler';
-import blocksHandler from '../workflowEngine/blocksHandler';
-import WorkflowEngine from '../workflowEngine/engine';
-
-const executedWorkflows = (workflows, options) => {
-  if (workflows.length === 0) return;
-
-  const workflow = workflows.shift();
-  const engine = new WorkflowEngine(workflow, options);
-
-  engine.init();
-
-  setTimeout(() => {
-    executedWorkflows(workflows, options);
-  }, 500);
-};
-
-class CollectionEngine {
-  constructor(collection, { states, logger }) {
-    this.id = nanoid();
-    this.states = states;
-    this.logger = logger;
-    this.collection = collection;
-
-    this.data = [];
-    this.history = [];
-    this.workflows = [];
-
-    this.isDestroyed = false;
-    this.currentFlow = null;
-    this.currentIndex = 0;
-    this.eventListeners = {};
-
-    this.executedWorkflow = {
-      data: null,
-      state: null,
-    };
-
-    this.onStatesUpdated = ({ data, id }) => {
-      if (id === this.executedWorkflow.data?.id) {
-        this.executedWorkflow.state = data.state;
-        this.states.update(this.id, { state: this.state });
-      }
-    };
-    this.onStatesStopped = (id) => {
-      if (id !== this.id) return;
-
-      this.stop();
-    };
-  }
-
-  async init() {
-    try {
-      if (this.collection.flow.length === 0) return;
-
-      const { workflows } = await browser.storage.local.get('workflows');
-
-      this.workflows = workflows;
-      this.startedTimestamp = Date.now();
-
-      if (this.collection?.options.atOnce) {
-        const filteredWorkflows = this.collection.flow.reduce(
-          (acc, { itemId, type }) => {
-            if (type !== 'workflow') return acc;
-
-            const currentWorkflow = workflows.find(({ id }) => id === itemId);
-
-            if (currentWorkflow) {
-              acc.push(currentWorkflow);
-            }
-
-            return acc;
-          },
-          []
-        );
-
-        executedWorkflows(filteredWorkflows, {
-          blocksHandler,
-          states: this.states,
-          logger: this.logger,
-        });
-      } else {
-        await this.states.add(this.id, {
-          state: this.state,
-          collectionId: this.collection.id,
-        });
-
-        this.states.on('stop', this.onStatesStopped);
-        this.states.on('update', this.onStatesUpdated);
-
-        this._flowHandler(this.collection.flow[0]);
-      }
-    } catch (error) {
-      console.error(error);
-    }
-  }
-
-  on(name, listener) {
-    (this.eventListeners[name] = this.eventListeners[name] || []).push(
-      listener
-    );
-  }
-
-  dispatchEvent(name, params) {
-    const listeners = this.eventListeners[name];
-
-    if (!listeners) return;
-
-    listeners.forEach((callback) => {
-      callback(params);
-    });
-  }
-
-  async destroy(status) {
-    try {
-      if (this.isDestroyed) return;
-
-      this.isDestroyed = true;
-      this.dispatchEvent('destroyed', { id: this.id });
-
-      const { name, icon } = this.collection;
-
-      await this.logger.add({
-        name,
-        icon,
-        status,
-        id: this.id,
-        data: this.data,
-        endedAt: Date.now(),
-        history: this.history,
-        collectionId: this.collection.id,
-        startedAt: this.startedTimestamp,
-      });
-
-      await this.states.delete(this.id);
-
-      this.states.off('stop', this.onStatesStopped);
-      this.states.off('update', this.onStatesUpdated);
-
-      this.listeners = {};
-    } catch (error) {
-      console.error(error);
-    }
-  }
-
-  nextFlow() {
-    this.currentIndex += 1;
-
-    if (this.currentIndex >= this.collection.flow.length) {
-      this.destroy('success');
-
-      return;
-    }
-
-    this._flowHandler(this.collection.flow[this.currentIndex]);
-  }
-
-  get state() {
-    const data = {
-      id: this.id,
-      currentBlock: [],
-      name: this.collection.name,
-      currentIndex: this.currentIndex,
-      executedWorkflow: this.executedWorkflow,
-      startedTimestamp: this.startedTimestamp,
-    };
-
-    if (this.executedWorkflow.data) {
-      data.currentBlock.push(this.executedWorkflow.data);
-    }
-
-    if (this.executedWorkflow.state) {
-      data.currentBlock.push(this.executedWorkflow.state.currentBlock);
-    }
-
-    return data;
-  }
-
-  async stop() {
-    try {
-      if (this.executedWorkflow.data) {
-        await this.states.stop(this.executedWorkflow.data.id);
-      }
-
-      setTimeout(() => {
-        this.destroy('stopped');
-      }, 1000);
-    } catch (error) {
-      console.error(error);
-    }
-  }
-
-  async _flowHandler(flow) {
-    const currentState = await this.states.get(this.id);
-
-    if (!currentState || currentState.isDestroyed) {
-      if (this.isDestroyed) return;
-
-      await this.destroy('stopped');
-      return;
-    }
-
-    const handlerName =
-      flow.type === 'workflow' ? 'workflow' : toCamelCase(flow.itemId);
-    const handler = flowHandler[handlerName];
-    const started = Date.now();
-
-    this.currentFlow = flow;
-    await this.states.update(this.id, { state: this.state });
-
-    if (!handler) {
-      console.error(`"${flow.type}" flow doesn't have a handler`);
-      return;
-    }
-
-    try {
-      if (flow.type !== 'workflow') {
-        this.executedWorkflow = {
-          data: null,
-          state: null,
-        };
-      }
-
-      const data = await handler.call(this, flow);
-
-      this.history.push({
-        type: data.type || 'success',
-        name: data.name,
-        logId: data.id,
-        message: data.message,
-        duration: Math.round(Date.now() - started),
-      });
-      this.nextFlow();
-    } catch (error) {
-      this.history.push({
-        type: 'error',
-        name: error.name,
-        logId: error.id,
-        message: error.message,
-        duration: Math.round(Date.now() - started),
-      });
-
-      this.nextFlow();
-    }
-  }
-}
-
-export default CollectionEngine;

+ 4 - 1
src/background/index.js

@@ -7,6 +7,7 @@ import getFile from '@/utils/getFile';
 import decryptFlow, { getWorkflowPass } from '@/utils/decryptFlow';
 import convertWorkflowData from '@/utils/convertWorkflowData';
 import getBlockMessage from '@/utils/getBlockMessage';
+import * as automa from '@business';
 import {
   registerSpecificDay,
   registerContextMenu,
@@ -102,9 +103,9 @@ const workflow = {
     const convertedWorkflow = convertWorkflowData(workflowData);
     const engine = new WorkflowEngine(convertedWorkflow, {
       options,
-      blocksHandler,
       logger: this.logger,
       states: this.states,
+      blocksHandler: blocksHandler(),
     });
 
     engine.init();
@@ -644,4 +645,6 @@ message.on('workflow:register', ({ triggerBlock, workflowId }) => {
   registerWorkflowTrigger(workflowId, triggerBlock);
 });
 
+if (automa?.validate) automa.validate(message);
+
 browser.runtime.onMessage.addListener(message.listener());

+ 6 - 4
src/background/workflowEngine/blocksHandler.js

@@ -10,7 +10,9 @@ const handlers = blocksHandler.keys().reduce((acc, key) => {
   return acc;
 }, {});
 
-export default {
-  ...handlers,
-  ...customHandlers,
-};
+export default function () {
+  return {
+    ...handlers,
+    ...customHandlers(),
+  };
+}

+ 18 - 5
src/background/workflowEngine/engine.js

@@ -1,10 +1,12 @@
 import browser from 'webextension-polyfill';
 import { nanoid } from 'nanoid';
-import { tasks } from '@/utils/shared';
+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();
@@ -111,6 +113,8 @@ class WorkflowEngine {
         return;
       }
 
+      blocks = getBlocks();
+
       const checkParams = this.options?.checkParams ?? true;
       const hasParams = triggerBlock.data.parameters?.length > 0;
       if (checkParams && hasParams) {
@@ -242,7 +246,7 @@ class WorkflowEngine {
     this.workerId += 1;
 
     const workerId = `worker-${this.workerId}`;
-    const worker = new Worker(workerId, this);
+    const worker = new Worker(workerId, this, { blocksDetail: blocks });
     worker.init(detail);
 
     this.workers.set(worker.id, worker);
@@ -263,7 +267,7 @@ class WorkflowEngine {
       detail.name !== 'delay' ||
       detail.replacedValue ||
       detail.name === 'javascript-code' ||
-      (tasks[detail.name]?.refDataKeys && this.saveLog)
+      (blocks[detail.name]?.refDataKeys && this.saveLog)
     ) {
       const { activeTabUrl, variables, loopData } = JSON.parse(
         JSON.stringify(this.referenceData)
@@ -450,8 +454,17 @@ class WorkflowEngine {
       );
 
       this.isDestroyed = true;
-      this.referenceData = {};
-      this.eventListeners = {};
+      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);
     }

+ 6 - 5
src/background/workflowEngine/worker.js

@@ -6,17 +6,17 @@ import {
   parseJSON,
   isObject,
 } from '@/utils/helper';
-import { tasks } from '@/utils/shared';
 import referenceData from '@/utils/referenceData';
 import mustacheReplacer from '@/utils/referenceData/mustacheReplacer';
-import injectContentScript from './injectContentScript';
 import { convertData, waitTabLoaded } from './helper';
+import injectContentScript from './injectContentScript';
 
 class Worker {
-  constructor(id, engine) {
+  constructor(id, engine, options = {}) {
     this.id = id;
     this.engine = engine;
     this.settings = engine.workflow.settings;
+    this.blocksDetail = options.blocksDetail || {};
 
     this.loopEls = [];
     this.loopList = {};
@@ -148,7 +148,7 @@ class Worker {
 
     const blockHandler = this.engine.blocksHandler[toCamelCase(block.label)];
     const handler =
-      !blockHandler && tasks[block.label].category === 'interaction'
+      !blockHandler && this.blocksDetail[block.label].category === 'interaction'
         ? this.engine.blocksHandler.interactionBlock
         : blockHandler;
 
@@ -165,13 +165,14 @@ class Worker {
       activeTabUrl: this.activeTab.url,
     };
 
+    console.log(this.blocksDetail, this.blocksDetail[block.label]);
     const replacedBlock = referenceData({
       block,
       data: refData,
       refKeys:
         isRetry || block.data.disableBlock
           ? null
-          : tasks[block.label].refDataKeys,
+          : this.blocksDetail[block.label].refDataKeys,
     });
     const blockDelay = this.settings?.blockDelay || 0;
     const addBlockLog = (status, obj = {}) => {

+ 3 - 2
src/components/newtab/logs/LogsHistory.vue

@@ -233,13 +233,14 @@ import {
 } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { countDuration } from '@/utils/helper';
-import { tasks } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
 import dayjs from '@/lib/dayjs';
 import objectPath from 'object-path';
 
 const SharedCodemirror = defineAsyncComponent(() =>
   import('@/components/newtab/shared/SharedCodemirror.vue')
 );
+const blocks = getBlocks();
 
 const props = defineProps({
   currentLog: {
@@ -349,7 +350,7 @@ function translateLog(log) {
   } else {
     copyLog.name = getTranslatation(
       `workflow.blocks.${log.name}.name`,
-      tasks[log.name].name
+      blocks[log.name].name
     );
   }
 

+ 4 - 3
src/components/newtab/package/PackageSettingIOSelect.vue

@@ -52,7 +52,7 @@
 <script setup>
 /* eslint-disable */
 import { reactive, computed, onMounted } from 'vue';
-import { tasks } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
 
 const props = defineProps({
   nodes: {
@@ -70,6 +70,7 @@ const props = defineProps({
 });
 const emit = defineEmits(['update']);
 
+const blocks = getBlocks();
 const handleType = props.type === 'inputs' ? 'target' : 'source';
 
 const state = reactive({
@@ -93,7 +94,7 @@ const items = computed(() => {
       if (data.name) additionalKey = includeQuery(data.name);
       else if (data.description) additionalKey = includeQuery(data.description);
 
-      return includeQuery(tasks[label]?.name || '') || additionalKey;
+      return includeQuery(blocks[label]?.name || '') || additionalKey;
     });
   }
 
@@ -119,7 +120,7 @@ function selectItem(item) {
 }
 function getBlockName(item, type) {
   const { label, data } = item;
-  let name = tasks[label]?.name || '';
+  let name = blocks[label]?.name || '';
 
   if (data.name) name += ` (${data.name})`;
   else if (data.description) name += ` (${data.description})`;

+ 4 - 2
src/components/newtab/package/PackageSettings.vue

@@ -129,7 +129,7 @@
 import { reactive, watch, onMounted } from 'vue';
 import cloneDeep from 'lodash.clonedeep';
 import Draggable from 'vuedraggable';
-import { tasks } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
 import { debounce } from '@/utils/helper';
 
 const props = defineProps({
@@ -144,6 +144,8 @@ const props = defineProps({
 });
 const emit = defineEmits(['update', 'goBlock']);
 
+const blocks = getBlocks();
+
 const state = reactive({
   retrieved: false,
 });
@@ -160,7 +162,7 @@ function deleteBlockIo(type, index) {
 const cacheIOName = new Map();
 
 function getNodeName({ label, data }) {
-  let name = tasks[label]?.name || '';
+  let name = blocks[label]?.name || '';
 
   if (data.name) name += ` (${data.name})`;
   else if (data.description) name += ` (${data.description})`;

+ 4 - 3
src/components/newtab/shared/SharedWorkflowState.vue

@@ -33,9 +33,9 @@
         :key="block.id || block.name"
         class="flex items-center py-2"
       >
-        <v-remixicon :name="tasks[block.name].icon" />
+        <v-remixicon :name="blocks[block.name].icon" />
         <p class="flex-1 ml-2 mr-4 text-overflow">
-          {{ tasks[block.name].name }}
+          {{ blocks[block.name].name }}
         </p>
         <ui-spinner color="text-accent" size="20" />
       </div>
@@ -59,7 +59,7 @@
 import browser from 'webextension-polyfill';
 import { useI18n } from 'vue-i18n';
 import { sendMessage } from '@/utils/message';
-import { tasks } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
 import dayjs from '@/lib/dayjs';
 
 const props = defineProps({
@@ -69,6 +69,7 @@ const props = defineProps({
   },
 });
 
+const blocks = getBlocks();
 const { t } = useI18n();
 
 function formatDate(date, format) {

+ 3 - 2
src/components/newtab/workflow/WorkflowBlockList.vue

@@ -58,7 +58,7 @@
 </template>
 <script setup>
 import { useI18n } from 'vue-i18n';
-import { tasks } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
 
 defineProps({
   category: {
@@ -77,10 +77,11 @@ defineProps({
 defineEmits(['pin']);
 
 const { t, te } = useI18n();
+const blocksDetail = getBlocks();
 
 function getBlockTitle({ description, id, name }) {
   const blockPath = `workflow.blocks.${id}`;
-  if (!te(blockPath)) return tasks[id].name;
+  if (!te(blockPath)) return blocksDetail[id].name;
 
   const descPath = `${blockPath}.${description ? 'description' : 'name'}`;
   let blockDescription = te(descPath) ? t(descPath) : name;

+ 14 - 16
src/components/newtab/workflow/WorkflowDetailsCard.vue

@@ -83,8 +83,8 @@ import { computed, ref, onMounted, watch, toRaw } from 'vue';
 import { useI18n } from 'vue-i18n';
 import browser from 'webextension-polyfill';
 import { useShortcut } from '@/composable/shortcut';
-import { tasks, categories } from '@/utils/shared';
-import customBlocks from '@business/blocks';
+import { categories } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
 import WorkflowBlockList from './WorkflowBlockList.vue';
 
 defineProps({
@@ -126,20 +126,18 @@ const icons = [
   'riCommandLine',
 ];
 
-const copyTasks = { ...tasks };
-delete copyTasks['block-package'];
+const copyBlocks = getBlocks();
+delete copyBlocks['block-package'];
 
-const blocksArr = Object.entries({ ...copyTasks, ...customBlocks }).map(
-  ([key, block]) => {
-    const localeKey = `workflow.blocks.${key}.name`;
+const blocksArr = Object.entries(copyBlocks).map(([key, block]) => {
+  const localeKey = `workflow.blocks.${key}.name`;
 
-    return {
-      ...block,
-      id: key,
-      name: te(localeKey) ? t(localeKey) : block.name,
-    };
-  }
-);
+  return {
+    ...block,
+    id: key,
+    name: te(localeKey) ? t(localeKey) : block.name,
+  };
+});
 
 const descriptionCollapsed = ref(true);
 
@@ -163,9 +161,9 @@ const pinnedBlocksList = computed(() =>
       const namePath = `workflow.blocks.${id}.name`;
 
       return {
-        ...tasks[id],
+        ...copyBlocks[id],
         id,
-        name: te(namePath) ? t(namePath) : tasks[id].name,
+        name: te(namePath) ? t(namePath) : copyBlocks[id].name,
       };
     })
     .filter(({ name }) =>

+ 1 - 1
src/components/newtab/workflow/WorkflowEditBlock.vue

@@ -74,7 +74,7 @@ const components = editComponents.keys().reduce((acc, key) => {
   return acc;
 }, {});
 
-Object.assign(components, customEditComponents);
+Object.assign(components, customEditComponents());
 
 const props = defineProps({
   data: {

+ 3 - 3
src/components/newtab/workflow/WorkflowEditor.vue

@@ -70,8 +70,8 @@ import {
 } from '@braks/vue-flow';
 import cloneDeep from 'lodash.clonedeep';
 import { useStore } from '@/stores/main';
-import { tasks, categories } from '@/utils/shared';
-import customBlocks from '@business/blocks';
+import { categories } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
 import EditorSearchBlocks from './editor/EditorSearchBlocks.vue';
 
 const props = defineProps({
@@ -160,7 +160,7 @@ editor.onEdgeUpdate(({ edge, connection }) => {
   Object.assign(edge, connection);
 });
 
-const blocks = { ...tasks, ...customBlocks };
+const blocks = getBlocks();
 const settings = store.settings.editor;
 const isDisabled = computed(() => props.options.disabled ?? props.disabled);
 

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

@@ -43,7 +43,7 @@
 import browser from 'webextension-polyfill';
 import { useI18n } from 'vue-i18n';
 import { sendMessage } from '@/utils/message';
-import { tasks } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
 import dayjs from '@/lib/dayjs';
 
 defineProps({
@@ -54,11 +54,12 @@ defineProps({
 });
 
 const { t } = useI18n();
+const blocks = getBlocks();
 
 function getBlock(item) {
   if (!item.state.currentBlock) return {};
 
-  return tasks[item.state.currentBlock.name];
+  return blocks[item.state.currentBlock.name];
 }
 function formatDate(date, format) {
   if (format === 'relative') return dayjs(date).fromNow();

+ 14 - 6
src/components/newtab/workflow/edit/EditWorkflowParameters.vue

@@ -11,12 +11,17 @@
     </p>
     <table v-else class="w-full">
       <div class="text-sm grid grid-cols-12 space-x-2">
-        <div class="col-span-3" style="padding-left: 30px">Name</div>
+        <div class="col-span-3" style="padding-left: 28px">Name</div>
         <div class="col-span-2">Type</div>
         <div class="col-span-3">Placeholder</div>
         <div class="col-span-4">Default Value</div>
       </div>
-      <draggable v-model="state.parameters" tag="div" handle=".handle">
+      <draggable
+        v-model="state.parameters"
+        tag="div"
+        item-key="id"
+        handle=".handle"
+      >
         <template #item="{ element: param, index }">
           <div class="mb-4">
             <div class="grid grid-cols-12 space-x-2">
@@ -55,7 +60,8 @@
                   v-model="param.defaultValue"
                   :param-data="param"
                   :editor="true"
-                  max-width="250px"
+                  class="flex-1"
+                  style="max-width: 232px"
                 />
                 <ui-input
                   v-else
@@ -90,7 +96,7 @@
                     v-model="param.description"
                     placeholder="Description"
                     title="Description"
-                    class="mb-4"
+                    class="mb-2"
                     style="max-width: 400px"
                   />
                   <component
@@ -115,7 +121,7 @@
 import { reactive, watch } from 'vue';
 import { nanoid } from 'nanoid/non-secure';
 import cloneDeep from 'lodash.clonedeep';
-import * as workflowParameters from '@business/parameters';
+import workflowParameters from '@business/parameters';
 import Draggable from 'vuedraggable';
 import ParameterInputValue from './Parameter/ParameterInputValue.vue';
 import ParameterInputOptions from './Parameter/ParameterInputOptions.vue';
@@ -128,6 +134,8 @@ const props = defineProps({
 });
 const emit = defineEmits(['update']);
 
+const customParameters = workflowParameters();
+
 const paramTypes = {
   string: {
     id: 'string',
@@ -144,7 +152,7 @@ const paramTypes = {
     id: 'number',
     name: 'Input (number)',
   },
-  ...workflowParameters,
+  ...customParameters,
 };
 const paramTypesArr = Object.values(paramTypes).filter((item) => item.id);
 

+ 2 - 2
src/components/newtab/workflow/edit/Parameter/ParameterInputOptions.vue

@@ -2,7 +2,7 @@
   <div class="flex items-center">
     <label class="flex items-center">
       <ui-switch v-model="options.useMask" />
-      <span class="ml-2"> Use mask </span>
+      <span class="ml-2"> Use input masking </span>
     </label>
     <v-remixicon
       v-tooltip="{ content: maskInfo, allowHTML: true }"
@@ -15,7 +15,7 @@
       <span class="ml-2">Return unmask value</span>
     </label>
   </div>
-  <div v-if="options.useMask" class="mt-4">
+  <div v-if="options.useMask" class="mt-2">
     <p>Masks</p>
     <div class="space-y-2">
       <div

+ 4 - 2
src/components/newtab/workflow/editor/EditorUsedCredentials.vue

@@ -53,7 +53,7 @@
 import { ref, onMounted } from 'vue';
 import { useI18n } from 'vue-i18n';
 import objectPath from 'object-path';
-import { tasks } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
 
 const props = defineProps({
   editor: {
@@ -62,6 +62,8 @@ const props = defineProps({
   },
 });
 
+const blocks = getBlocks();
+
 const { t } = useI18n();
 
 const credentials = ref([]);
@@ -71,7 +73,7 @@ function checkCredentials() {
   const tempCreds = [];
 
   props.editor.getNodes.value.forEach(({ label, id, data }) => {
-    const keys = tasks[label]?.refDataKeys;
+    const keys = blocks[label]?.refDataKeys;
     if (!keys || !data) return;
 
     const usedCredentials = new Set();

+ 3 - 7
src/composable/editorBlock.js

@@ -1,13 +1,9 @@
 import { reactive, onMounted } from 'vue';
-import customBlocks from '@business/blocks';
-import { tasks, categories } from '@/utils/shared';
-
-const blocks = {
-  ...tasks,
-  ...customBlocks,
-};
+import { getBlocks } from '@/utils/getSharedData';
+import { categories } from '@/utils/shared';
 
 export function useEditorBlock(label) {
+  const blocks = getBlocks();
   const block = reactive({
     details: {},
     category: {},

+ 6 - 4
src/content/blocksHandler.js

@@ -10,7 +10,9 @@ const handlers = blocksHandler.keys().reduce((acc, key) => {
   return acc;
 }, {});
 
-export default {
-  ...customHandlers,
-  ...handlers,
-};
+export default function () {
+  return {
+    ...(customHandlers() || {}),
+    ...handlers,
+  };
+}

+ 8 - 2
src/content/index.js

@@ -2,6 +2,7 @@ import browser from 'webextension-polyfill';
 import findSelector from '@/lib/findSelector';
 import { toCamelCase } from '@/utils/helper';
 import { nanoid } from 'nanoid';
+import * as automa from '@business';
 import handleSelector from './handleSelector';
 import blocksHandler from './blocksHandler';
 import showExecutedBlock from './showExecutedBlock';
@@ -67,7 +68,8 @@ async function executeBlock(data) {
       return result;
     }
   }
-  const handler = blocksHandler[toCamelCase(data.name || data.label)];
+  const handlers = blocksHandler();
+  const handler = handlers[toCamelCase(data.name || data.label)];
   if (handler) {
     const result = await handler(data, { handleSelector });
     removeExecutedBlock();
@@ -149,6 +151,10 @@ function messageListener({ data, source }) {
     // window.addEventListener('load', elementObserver);
   }
 
+  if (automa?.validateWithinContent) {
+    automa.validateWithinContent();
+  }
+
   browser.runtime.onMessage.addListener((data) => {
     return new Promise((resolve, reject) => {
       if (data.isBlock) {
@@ -169,7 +175,7 @@ function messageListener({ data, source }) {
                 data: blockData,
               };
 
-              blocksHandler
+              blocksHandler()
                 .loopData(loopBlock)
                 .then(() => {
                   executeBlock(data).then(resolve).catch(reject);

+ 5 - 2
src/newtab/App.vue

@@ -52,8 +52,6 @@
   <app-survey />
 </template>
 <script setup>
-import iconFirefox from '@/assets/svg/logoFirefox.svg';
-import iconChrome from '@/assets/svg/logo.svg';
 import { ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
@@ -71,11 +69,14 @@ import { useHostedWorkflowStore } from '@/stores/hostedWorkflow';
 import { useSharedWorkflowStore } from '@/stores/sharedWorkflow';
 import { loadLocaleMessages, setI18nLanguage } from '@/lib/vueI18n';
 import { getUserWorkflows } from '@/utils/api';
+import * as automa from '@business';
 import dbLogs from '@/db/logs';
 import dayjs from '@/lib/dayjs';
 import AppSurvey from '@/components/newtab/app/AppSurvey.vue';
 import AppSidebar from '@/components/newtab/app/AppSidebar.vue';
 import dataMigration from '@/utils/dataMigration';
+import iconFirefox from '@/assets/svg/logoFirefox.svg';
+import iconChrome from '@/assets/svg/logo.svg';
 
 let icon;
 if (window.location.protocol === 'moz-extension:') {
@@ -229,6 +230,8 @@ browser.runtime.onMessage.addListener(({ type, data }) => {
     await dataMigration();
     await userStore.loadUser({ useCache: false, ttl: 2 });
 
+    if (automa?.validateWithinApp) await automa.validateWithinApp();
+
     retrieved.value = true;
 
     await Promise.allSettled([

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

@@ -297,13 +297,13 @@ import {
 } from '@/composable/shortcut';
 import { getWorkflowPermissions } from '@/utils/workflowData';
 import { fetchApi } from '@/utils/api';
-import { tasks, excludeGroupBlocks } from '@/utils/shared';
+import { getBlocks } from '@/utils/getSharedData';
+import { excludeGroupBlocks } from '@/utils/shared';
 import { functions } from '@/utils/referenceData/mustacheReplacer';
 import { useGroupTooltip } from '@/composable/groupTooltip';
 import { useCommandManager } from '@/composable/commandManager';
 import { debounce, parseJSON, throttle } from '@/utils/helper';
 import { registerWorkflowTrigger } from '@/utils/workflowTrigger';
-import customBlocks from '@business/blocks';
 import browser from 'webextension-polyfill';
 import dbStorage from '@/db/storage';
 import DroppedNode from '@/utils/editor/DroppedNode';
@@ -328,7 +328,7 @@ import EditorLocalSavedBlocks from '@/components/newtab/workflow/editor/EditorLo
 import PackageDetails from '@/components/newtab/package/PackageDetails.vue';
 import PackageSettings from '@/components/newtab/package/PackageSettings.vue';
 
-const blocks = { ...tasks, ...customBlocks };
+const blocks = getBlocks();
 
 let editorCommands = null;
 const executeCommandTimeout = null;
@@ -1015,7 +1015,7 @@ function initEditBlock(data) {
         const blockNameKey = `workflow.blocks.${sourceNode.label}.name`;
         let blockName = te(blockNameKey)
           ? t(blockNameKey)
-          : tasks[sourceNode.label].name;
+          : blocks[sourceNode.label].name;
 
         const { description, name: groupName } = sourceNode.data;
         if (description || groupName)

+ 22 - 9
src/params/App.vue

@@ -1,5 +1,8 @@
 <template>
-  <div class="w-full flex flex-col h-full max-w-lg mx-auto dark:text-gray-100">
+  <div
+    v-if="retrieved"
+    class="w-full flex flex-col h-full max-w-lg mx-auto dark:text-gray-100"
+  >
     <nav class="flex items-center w-full p-4 border-b mb-4">
       <span class="p-1 rounded-full bg-box-transparent dark:bg-none">
         <img src="@/assets/svg/logo.svg" class="w-10" />
@@ -41,7 +44,7 @@
           </div>
         </template>
         <div class="px-4 pb-4">
-          <ul class="space-y-2 divide-y">
+          <ul class="space-y-4 divide-y">
             <li v-for="(param, paramIdx) in workflow.params" :key="paramIdx">
               <component
                 :is="paramsList[param.type].valueComp"
@@ -88,13 +91,13 @@
 <script setup>
 import { onMounted, ref, computed } from 'vue';
 import browser from 'webextension-polyfill';
-import * as workflowParameters from '@business/parameters';
+import workflowParameters from '@business/parameters';
+import * as automa from '@business';
 import { useTheme } from '@/composable/theme';
 import dayjs from '@/lib/dayjs';
 import ParameterInputValue from '@/components/newtab/workflow/edit/Parameter/ParameterInputValue.vue';
 
 const paramsList = {
-  ...workflowParameters,
   string: {
     id: 'string',
     name: 'Input (string)',
@@ -105,6 +108,7 @@ const paramsList = {
 const theme = useTheme();
 theme.init();
 
+const retrieved = ref(false);
 const workflows = ref([]);
 
 const sortedWorkflows = computed(() =>
@@ -183,7 +187,7 @@ function runWorkflow(index, { data, params }) {
   const variables = params.reduce((acc, param) => {
     const valueFunc =
       getParamVal[param.type] ||
-      workflowParameters[param.type]?.getValue ||
+      paramsList[param.type]?.getValue ||
       getParamVal.default;
     const value = valueFunc(param.value || param.defaultValue);
     acc[param.name] = value;
@@ -215,10 +219,19 @@ browser.runtime.onMessage.addListener(({ name, data }) => {
   addWorkflow(data);
 });
 
-onMounted(() => {
-  const query = new URLSearchParams(window.location.search);
-  const workflowId = query.get('workflowId');
+onMounted(async () => {
+  try {
+    const query = new URLSearchParams(window.location.search);
+    const workflowId = query.get('workflowId');
+
+    if (workflowId) addWorkflow(workflowId);
+    if (automa?.validateWithinContent) await automa.validateWithinContent();
 
-  if (workflowId) addWorkflow(workflowId);
+    Object.assign(paramsList, workflowParameters());
+  } catch (error) {
+    // Do nothing
+  } finally {
+    retrieved.value = true;
+  }
 });
 </script>

+ 3 - 0
src/popup/pages/Home.vue

@@ -127,6 +127,7 @@ import { useWorkflowStore } from '@/stores/workflow';
 import { useGroupTooltip } from '@/composable/groupTooltip';
 import { useTeamWorkflowStore } from '@/stores/teamWorkflow';
 import { useHostedWorkflowStore } from '@/stores/hostedWorkflow';
+import * as automa from '@business';
 import HomeWorkflowCard from '@/components/popup/home/HomeWorkflowCard.vue';
 import HomeTeamWorkflows from '@/components/popup/home/HomeTeamWorkflows.vue';
 import HomeStartRecording from '@/components/popup/home/HomeStartRecording.vue';
@@ -301,6 +302,8 @@ onMounted(async () => {
 
   let activeTab = localStorage.getItem('popup-tab') || 'local';
 
+  if (automa?.validateWithinApp) await automa.validateWithinApp();
+
   if (activeTab === 'team' && !userStore.user?.teams) activeTab = 'local';
   else if (activeTab === 'host' && hostedWorkflowStore.toArray.length < 0)
     activeTab = 'local';

+ 4 - 2
src/utils/editor/DroppedNode.js

@@ -1,5 +1,6 @@
 import { customAlphabet } from 'nanoid';
-import { tasks, excludeOnError } from '../shared';
+import { excludeOnError } from '../shared';
+import { getBlocks } from '../getSharedData';
 
 const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 7);
 
@@ -25,7 +26,8 @@ class DroppedNode {
 
     let blockData = block;
     if (block.fromBlockBasic) {
-      blockData = { ...tasks[block.id], id: block.id };
+      const blocks = getBlocks();
+      blockData = { ...blocks[block.id], id: block.id };
     }
 
     const onErrorEnabled =

+ 6 - 0
src/utils/getSharedData.js

@@ -0,0 +1,6 @@
+import customBlocks from '@business/blocks';
+import { tasks } from './shared';
+
+export function getBlocks() {
+  return { ...tasks, ...customBlocks() };
+}

+ 0 - 3
src/utils/shared.js

@@ -1,5 +1,3 @@
-import customBlocks from '@business/blocks';
-
 export const tasks = {
   trigger: {
     name: 'Trigger',
@@ -1298,7 +1296,6 @@ export const tasks = {
       fontSize: 'regular',
     },
   },
-  ...customBlocks,
 };
 
 export const categories = {