UNPKG

@xuda.io/runtime-bundle

Version:

The Xuda Runtime Bundle refers to a collection of scripts and libraries packaged together to provide the necessary runtime environment for executing plugins or components in the Xuda platform.

3,484 lines • 110 kB
// todos
//=====================
// check unique index id
// check unique field id

var app_obj,
  progs_obj,
  _,
  progs_str,
  hide_not_in_use_check,
  is_server,
  deployments,
  UglifyJS,
  JSON5,
  _conf,
  warn_plugin_check,
  PROTECTED_VARS = ['_NULL', '_THIS', '_FOR_KEY', '_FOR_VAL', '_ROWNO', '_ROWID', '_ROWDOC', '_KEY', '_VAL'];

const program_ref_triggers_arr = ['call_page', 'call_modal', 'call_popover', 'get_data', 'set_data', 'batch', 'call_native_javascript', 'call_evaluate_javascript', 'call_alert', 'alter_ui_element', 'create_ui_element', 'update', 'raise_event'];

export const init = function (app_obj_in, progs_obj_in = {}, _in, _hide_not_in_use_check, is_server_in, deployments_in = [], UglifyJS_in, JSON5_in, _conf_in, _warn_plugin_check) {
  app_obj = app_obj_in;
  progs_obj = progs_obj_in;
  progs_str = JSON.stringify(progs_obj);
  hide_not_in_use_check = _hide_not_in_use_check;
  _ = _in;
  is_server = is_server_in;
  deployments = deployments_in;
  UglifyJS = UglifyJS_in;
  JSON5 = JSON5_in;
  _conf = _conf_in;
  warn_plugin_check = _warn_plugin_check;
  // if (progs_str.includes("reduce_counter")) {
  //   debugger;
  // }
};

export const check = function (doc) {
  var ret = [];

  // try {

  // } catch (error) {
  //   debugger;
  // }

  var dependency_progs = [];
  try {
    if (_.isEmpty(doc)) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_110',
          data: `doc in empty`,
        },
      });
    }

    doc.studio_meta.not_in_use = false;
    ret = check_structure(doc);
    if (!ret?.length) {
      // if (!doc.properties.menuType) {
      //   throw new Error("", {
      //     cause: {
      //       code: "CHK_MSG_OBJ_GEN_010",
      //       data: `invalid properties.menuType`,
      //     },
      //   });
      // } else {
      if (doc.properties.menuType !== 'folder') {
        if (doc.properties.menuType !== 'table') {
          // if (doc._id === "1630415111085_1630391769418") debugger;

          if (is_server) {
            for (let val of deployments) {
              if (val.deploy_data?.prog_id === doc._id) {
                if (!doc.studio_meta.used_by_deployment_ids) {
                  doc.studio_meta.used_by_deployment_ids = [];
                }
                doc.studio_meta.used_by_deployment_ids.push(val._id);
              }
            }
          }
          if (!doc.studio_meta.used_by_deployment_id) {
            const check_programs_are_active = function (arr) {
              if (!arr.length) return false;
              var count = 0;
              for (let val of arr) {
                if (progs_obj?.[val.prog_id]?.studio_meta?.not_in_use) {
                  count++;
                  break;
                }
              }
              return count !== arr.length;
            };
            if (!hide_not_in_use_check) {
              const ret_prog_in_use = find_trigger_property_value_in_progs(doc, program_ref_triggers_arr, 'prog', doc._id);

              if (!ret_prog_in_use || !check_programs_are_active(ret_prog_in_use)) {
                const ret_panel_prog_in_use = find_panel_prog_in_progs(doc, doc._id);

                if (!ret_panel_prog_in_use || !check_programs_are_active(ret_panel_prog_in_use)) {
                  const ret_fieldValue_in_use = find_prog_in_fieldset_fieldValue_progs(doc, doc._id);

                  if (!ret_fieldValue_in_use || !check_programs_are_active(ret_fieldValue_in_use)) {
                    doc.studio_meta.not_in_use = true;

                    // search in implementations
                    // if (is_server) {
                    //   for (let val of deployments) {
                    //     if (val.deploy_data?.prog_id === doc._id) {
                    //       debugger;
                    //       doc.studio_meta.used_by_deployment_id = val._id;
                    //       break;
                    //     }
                    //   }
                    // }
                    // if (!doc.studio_meta.used_by_deployment_id) {
                    //   // ret.push({
                    //   //   code: "CHK_MSG_GEN_022",
                    //   //   data: `program not in use`,
                    //   //   type: "W",
                    //   //   category: `prog`,
                    //   //   ref: doc._id,
                    //   //   not_in_use: true,
                    //   // });

                    //   doc.studio_meta.not_in_use = true;
                    // }
                  }
                }
              }
            }
          }

          // if (doc.studio_meta.not_in_use)
          //   return { errors: ret, warnings: [], doc, dependency_progs };

          ret = [...ret, ...check_prog_events(doc)];
          // find program dependencies

          dependency_progs = find_trigger_property_value_in_progs(doc, program_ref_triggers_arr, 'prog', null, true) || [];

          dependency_progs = [...dependency_progs, ...(find_panel_prog_in_progs(doc, null, true) || [])];
        } else {
          if (!hide_not_in_use_check) {
            // check use for table
            if (!find_table_in_progs(doc, doc._id)) {
              doc.studio_meta.not_in_use = true;
              // ret.push({
              //   code: "CHK_MSG_GEN_024",
              //   data: `table not in use`,
              //   type: "W",
              //   category: `table`,
              //   ref: doc._id,
              //   not_in_use: true,
              // });
            }
          }
        }
      }
      // }

      // if (!doc.properties.menuName) {
      //   throw new Error("", {
      //     cause: {
      //       code: "CHK_MSG_OBJ_GEN_040",
      //       data: `properties.menuName is empty`,
      //     },
      //   });
      // }

      switch (doc.properties.menuType) {
        case 'globals':
          ret = [...ret, ...check_globals(doc)];
          break;

        case 'table':
          ret = [...ret, ...check_table(doc)];
          break;

        case 'component':
          ret = [...ret, ...check_component(doc)];
          break;

        case 'javascript':
          ret = [...ret, ...check_javascript(doc)];
          break;

        case 'get_data':
          ret = [...ret, ...check_get_data(doc)];
          break;

        case 'set_data':
          ret = [...ret, ...check_set_data(doc)];
          break;

        case 'batch':
          ret = [...ret, ...check_batch(doc)];
          break;

        case 'api':
          ret = [...ret, ...check_api(doc)];
          break;

        case 'alert':
          ret = [...ret, ...check_alert(doc)];
          break;

        case 'route':
          ret = [...ret, ...check_route(doc)];
          break;

        // case "call_project_api":
        //   ret = [...ret, ...call_project_api(doc)];
        //   break;

        case 'folder':
          break;

        default:
          ret.push({
            code: 'CHK_MSG_GEN_050',
            data: `invalid ${doc.properties.menuType} properties.menuType`,
            type: 'E',
            category: `prog`,
            ref: doc._id,
            id: `${doc._id}`,
          });
          break;
      }
    }
    var final_ret = {
      check_errors: [],
      check_warnings: [],
      doc,
      dependency_progs,
    };
    for (let [key, val] of Object.entries(ret)) {
      if (val.type === 'E') {
        final_ret.check_errors.push(val);
      }
      if (val.type === 'W') {
        final_ret.check_warnings.push(val);
      }
    }

    return final_ret;
  } catch (err) {
    if (err.cause) {
      const { code, data } = err.cause;
      ret.push({
        code,
        data,
        type: 'E',
      });
    } else {
      console.error(err);
      debugger;
    }

    return { check_errors: ret, check_warnings: [], doc, dependency_progs };
  }
};

export const check_structure = function (doc) {
  var ret = [];

  try {
    if (!app_obj || !_.isObject(app_obj) || _.isEmpty(app_obj)) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_002',
          data: `invalid app_obj`,
        },
      });
    }

    if (!doc || !_.isObject(doc) || _.isEmpty(doc)) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_004',
          data: `invalid doc`,
        },
      });
    }

    try {
      JSON5.parse(JSON.stringify(doc));
    } catch (err) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_005',
          data: `error parsing JSON doc`,
        },
      });
    }

    if (!doc._id) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_050',
          data: `missing _id value`,
        },
      });
    }
    if (!doc._rev) {
      if (progs_obj?.[doc._id]) {
        throw new Error('', {
          cause: {
            code: 'CHK_MSG_OBJ_GEN_052',
            data: `found existing doc _id`,
          },
        });
      }
    }
    ////////// properties ////////////////

    if (!doc.properties) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_100',
          data: `missing properties object`,
        },
      });
    }
    if (!_.isObject(doc.properties)) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_102',
          data: `invalid properties object`,
        },
      });
    }

    if (!doc.properties.menuType) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_104',
          data: `missing properties.menuType`,
        },
      });
    }
    if (!doc.properties.menuName) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_106',
          data: `missing properties.menuName`,
        },
      });
    }
    ////////// studio_meta ////////////////
    if (!doc.studio_meta) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_112',
          data: `missing studio_meta property`,
        },
      });
    }
    if (!_.isObject(doc.studio_meta)) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_114',
          data: `invalid studio_meta object`,
        },
      });
    }
    if (!doc.studio_meta.parentId) {
      throw new Error('', {
        cause: {
          code: 'CHK_MSG_OBJ_GEN_112',
          data: `missing studio_meta.parentId value`,
        },
      });
    }

    const check_progFields = function () {
      ///////////// progFields
      var ids = [];
      if (!_.isArray(doc.progFields)) {
        throw new Error('', {
          cause: {
            code: 'CHK_MSG_OBJ_GEN_402',
            data: `invalid progFields Array property`,
          },
        });
      }

      for (let val of doc.progFields) {
        if (!_.isObject(val)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_404',
              data: `invalid progFields object property`,
            },
          });
        }
        if (!val.id) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_406',
              data: `missing progFields.id property`,
            },
          });
        }
        if (ids.includes(val.id)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_408',
              data: `${val.id} id must be unique in progFields property`,
            },
          });
        }
        ids.push(val.id);
        if (!_.isObject(val.data)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_410',
              data: `invalid progFields.data object property`,
            },
          });
        }

        if (!val.data.field_id) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_412',
              data: `invalid progFields.data.field_id property`,
            },
          });
        }
        if (!val.data.type) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_414',
              data: `invalid progFields.data.type property`,
            },
          });
        }
        if (!_.isObject(val.props)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_416',
              data: `invalid progFields.props Object property`,
            },
          });
        }
        if (!val.props.fieldType) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_418',
              data: `invalid progFields.props.fieldType property`,
            },
          });
        }
        if (!_.isObject(val.props.propExpressions)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_4120',
              data: `invalid al.props.propExpressions Object property`,
            },
          });
        }

        check_triggers(val.workflow);
      }
    };
    const check_progEvents = function () {
      ///////////// progEvents
      var ids = [];
      if (!_.isArray(doc.progEvents)) {
        throw new Error('', {
          cause: {
            code: 'CHK_MSG_OBJ_GEN_502',
            data: `invalid progEvents Array property`,
          },
        });
      }

      for (let val of doc.progEvents) {
        if (!_.isObject(val)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_504',
              data: `invalid progEvents object property`,
            },
          });
        }
        if (!val.id) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_506',
              data: `missing progEvents.id property`,
            },
          });
        }
        if (ids.includes(val.id)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_508',
              data: `${val.id} id must be unique in progEvents property`,
            },
          });
        }
        ids.push(val.id);
        if (!_.isObject(val.data)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_510',
              data: `invalid progEvents.data object property`,
            },
          });
        }

        if (typeof val.data.condition === 'undefined') {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_512',
              data: `undefined progEvents.data.condition string property`,
            },
          });
        }
        if (typeof val.data.event_name === 'undefined') {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_514',
              data: `undefined progEvents.data.event_name string property`,
            },
          });
        }
        if (typeof val.data.properties === 'undefined') {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_516',
              data: `undefined progEvents.data.properties string property`,
            },
          });
        }
        if (typeof val.data.type === 'undefined') {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_518',
              data: `undefined progEvents.data.type string property`,
            },
          });
        }

        if (!_.isArray(val.data.parameters)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_522',
              data: `invalid progEvents.data.parameters Array property`,
            },
          });
        }

        if (!_.isObject(val.props)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_522',
              data: `invalid progEvents.props Object property`,
            },
          });
        }

        check_triggers(val.workflow);
      }
    };
    const check_triggers = function (workflow) {
      if (!workflow) return;
      var ids = [];
      if (!_.isArray(workflow)) {
        throw new Error('', {
          cause: {
            code: 'CHK_MSG_OBJ_GEN_850',
            data: `invalid workflow Array property`,
          },
        });
      }

      for (let val of workflow) {
        if (!_.isObject(val)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_852',
              data: `invalid workflow item object property`,
            },
          });
        }

        if (!val.id) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_854',
              data: `missing workflow.id property`,
            },
          });
        }
        if (ids.includes(val.id)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_856',
              data: `${val.id} id must be unique in workflow property`,
            },
          });
        }
        ids.push(val.id);

        if (!_.isObject(val.data)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_858',
              data: `invalid workflow.data Object property`,
            },
          });
        }

        if (!_.isObject(val.props)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_860',
              data: `invalid workflow.props item object property`,
            },
          });
        }
      }
    };
    const check_progDataSource = function () {
      ///////////// progDataSource
      if (!_.isObject(doc.progDataSource)) {
        throw new Error('', {
          cause: {
            code: 'CHK_MSG_OBJ_GEN_602',
            data: `invalid progDataSource Object property`,
          },
        });
      }

      if (typeof doc.progDataSource.dataSourceType === 'undefined') {
        throw new Error('', {
          cause: {
            code: 'CHK_MSG_OBJ_GEN_604',
            data: `undefined progDataSource.dataSourceType string property`,
          },
        });
      }
    };
    const check_progUi = function () {
      ///////////// progUi
      if (!_.isArray(doc.progUi)) {
        throw new Error('', {
          cause: {
            code: 'CHK_MSG_OBJ_GEN_702',
            data: `invalid progUi Array property`,
          },
        });
      }

      var ids = [];
      const run_tree = function (node) {
        for (let item of node) {
          if (!_.isObject(item)) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_704',
                data: `invalid progUi item Object property`,
              },
            });
          }

          if (!_.isObject(item.attributes)) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_706',
                data: `invalid progUi attributes item Object property`,
              },
            });
          }

          if (typeof item.type === 'undefined') {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_708',
                data: `undefined progUi type item string property`,
              },
            });
          }
          if (typeof item.tagName === 'undefined') {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_710',
                data: `undefined progUi tagName item string property`,
              },
            });
          }

          if (!item.id) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_712',
                data: `missing progUi id item property`,
              },
            });
          }
          if (ids.includes(item.id)) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_714',
                data: `${item.id} id must be unique in progUi property`,
              },
            });
          }
          ids.push(item.id);

          check_triggers(item.workflow);

          if (item.children) {
            run_tree(item.children);
          }
        }
      };
      run_tree(doc.progUi);
    };

    switch (doc.properties.menuType) {
      case 'table':
        if (!doc.tableFields) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_200',
              data: `missing tableFields property`,
            },
          });
        }
        ///////////// tableFields
        if (!_.isArray(doc.tableFields)) {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_202',
              data: `invalid tableFields Array property`,
            },
          });
        }
        var ids = [];

        for (let val of doc.tableFields) {
          if (!_.isObject(val)) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_204',
                data: `invalid tableFields object property`,
              },
            });
          }
          if (!val.id) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_206',
                data: `missing tableFields.id property`,
              },
            });
          }
          if (ids.includes(val.id)) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_208',
                data: `${val.id} id must be unique in tableFields property`,
              },
            });
          }
          ids.push(val.id);
          if (!_.isObject(val.data)) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_210',
                data: `invalid tableFields.data object property`,
              },
            });
          }
          if (!val.data.field_id) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_212',
                data: `invalid tableFields.data.field_id property`,
              },
            });
          }
          if (!_.isObject(val.props)) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_214',
                data: `invalid tableFields.props object property`,
              },
            });
          }
          if (!val.props.fieldType) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_216',
                data: `invalid tableFields.props.fieldType property`,
              },
            });
          }
          ///////////// tableIndexes
          if (!_.isArray(doc.tableIndexes)) {
            throw new Error('', {
              cause: {
                code: 'CHK_MSG_OBJ_GEN_302',
                data: `invalid tableIndexes Array property`,
              },
            });
          }
          var index_ids = [];
          for (let val of doc.tableIndexes) {
            if (!_.isObject(val)) {
              throw new Error('', {
                cause: {
                  code: 'CHK_MSG_OBJ_GEN_304',
                  data: `invalid tableIndexes object property`,
                },
              });
            }
            if (!val.id) {
              throw new Error('', {
                cause: {
                  code: 'CHK_MSG_OBJ_GEN_306',
                  data: `missing tableIndexes.id property`,
                },
              });
            }
            if (index_ids.includes(val.id)) {
              throw new Error('', {
                cause: {
                  code: 'CHK_MSG_OBJ_GEN_308',
                  data: `${val.id} id must be unique in tableIndexes property`,
                },
              });
            }
            index_ids.push(val.id);
            if (!_.isObject(val.data)) {
              throw new Error('', {
                cause: {
                  code: 'CHK_MSG_OBJ_GEN_310',
                  data: `invalid tableIndexes.data object property`,
                },
              });
            }
            if (!val.data.name) {
              throw new Error('', {
                cause: {
                  code: 'CHK_MSG_OBJ_GEN_312',
                  data: `invalid tableIndexes.data.name property`,
                },
              });
            }
            if (!_.isArray(val.data.keys)) {
              throw new Error('', {
                cause: {
                  code: 'CHK_MSG_OBJ_GEN_312',
                  data: `invalid tableIndexes.data.keys Array property`,
                },
              });
            }
          }
        }

        break;

      case 'globals':
        check_progFields();
        check_progEvents();
        break;

      case 'get_data':
      case 'set_data':
      case 'batch':
      case 'api':
        check_progFields();
        check_progEvents();
        check_progDataSource();
        break;

      case 'component':
        check_progFields();
        check_progEvents();
        check_progDataSource();
        check_progUi();

        if (typeof doc.properties.uiFramework === 'undefined') {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_120',
              data: `undefined properties.uiFramework string property`,
            },
          });
        }
        if (typeof doc.properties.renderType === 'undefined') {
          throw new Error('', {
            cause: {
              code: 'CHK_MSG_OBJ_GEN_122',
              data: `undefined properties.renderType string property`,
            },
          });
        }
        break;

      case 'alert':
        break;

      case 'javascript':
        break;

      case 'folder':
        break;

      default:
        break;
    }
  } catch (err) {
    if (err.cause) {
      const { code, data } = err.cause;
      ret.push({
        code,
        data,
        type: 'E',
        category: 'document',
        structure_error: true,
      });
    } else {
      console.error(err);
      debugger;
    }
  }
  return ret;
};

const check_table = function (doc) {
  var ret = [];

  /////////////  db socket ////////////////////
  if (!doc.properties.databaseSocket) {
    ret.push({
      code: 'CHK_MSG_TBL_GEN_010',
      data: 'database socket not defined',
      type: doc.studio_meta.not_in_use ? 'W' : 'E',
      category: `table`,
      ref: doc._id,
      id: doc._id,
    });
  } else {
    if (!app_obj?.app_plugins_purchased?.[doc.properties.databaseSocket]) {
      ret.push({
        code: 'CHK_MSG_TBL_GEN_0020',
        data: `invalid ${doc.properties.databaseSocket} database socket`,
        type: doc.studio_meta.not_in_use || warn_plugin_check ? 'W' : 'E',
        category: `table`,
        ref: doc._id,
        id: doc._id,
      });
    } else {
      if (!app_obj.app_plugins_purchased[doc.properties.databaseSocket].installed) {
        ret.push({
          code: 'CHK_MSG_TBL_GEN_030',
          data: `${doc.properties.databaseSocket} database socket not installed`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category: `table`,
          ref: doc._id,
          id: doc._id,
        });
      }
    }
  }

  /////////////  fields ////////////////////
  if (doc.tableIndexes?.length && !doc?.tableFields?.length) {
    ret.push({
      code: 'CHK_MSG_TBL_FLD_090',
      data: `table has no fields`,
      type: doc.studio_meta.not_in_use ? 'W' : 'E',
      category: `table`,
      ref: doc._id,
      id: doc._id,
    });
  }
  var name_counts_obj = {};

  for (let field of doc.tableFields) {
    if (!field?.data?.field_id) {
      ret.push({
        code: 'CHK_MSG_TBL_FLD_100',
        data: `missing field_id`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        category: `table`,
        ref: field.id,
        id: `${doc._id}-${field.id}`,
      });
    } else {
      if (!/^[A-Za-z]+[\w\-\:\.]*$/.test(field.data.field_id)) {
        ret.push({
          code: 'CHK_MSG_TBL_FLD_020',
          data: `invalid field_id`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category: `table`,
          ref: field.id,
          id: `${doc._id}-${field.id}`,
        });
      }

      if (!name_counts_obj[field.data.field_id]) {
        name_counts_obj[field.data.field_id] = 1;
      } else {
        name_counts_obj[field.data.field_id]++;
      }
      if (!hide_not_in_use_check) {
        if (!find_field_in_progs(doc, field.data.field_id)) {
          ret.push({
            code: 'CHK_MSG_TBL_FLD_022',
            data: `table field "${field.data.field_id}" not in use`,
            type: 'W',
            category: `table`,
            ref: doc._id,
            not_in_use: true,
            id: `${doc._id}-${field.id}`,
          });
        }
      }
    }
  }

  for (let [key, val] of Object.entries(name_counts_obj)) {
    if (val > 1) {
      ret.push({
        code: 'CHK_MSG_TBL_FLD_200',
        data: `duplicate field id for ${key}`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        id: `${doc._id}-${key}`,
      });
    }
  }
  /////////////  indexes ////////////////////

  if (!doc.tableIndexes?.length) {
    if (doc.properties.databaseSocket !== '@xuda.io/xuda-dbs-plugin-xuda') {
      ret.push({
        code: 'CHK_MSG_TBL_IDX_040',
        data: `at least one index has to be defined`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        id: `${doc._id}`,
      });
    }
  } else {
    let found_unique;
    for (let index of doc.tableIndexes) {
      if (index?.data?.unique) {
        found_unique = true;
      }
      if (!index?.data?.name) {
        ret.push({
          code: 'CHK_MSG_TBL_IDX_060',
          data: `tableIndexes.data.name cannot be empty in "${index.id}" index`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          id: `${doc._id}`,
        });
      }
      if (!index?.data?.keys?.length) {
        ret.push({
          code: 'CHK_MSG_TBL_IDX_070',
          data: `tableIndexes.data.keys cannot be empty in "${index.id}" index`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          id: `${doc._id}-${index.id}`,
        });
      } else {
        for (let key of index.data.keys) {
          if (!doc.tableFields || !_.isArray(doc.tableFields) || !find_item_by_key(doc.tableFields, 'field_id', key)) {
            ret.push({
              code: 'CHK_MSG_TBL_IDX_080',
              data: `invalid key ${key} in tableIndexes.data.keys for "${index.id}" index`,
              type: doc.studio_meta.not_in_use ? 'W' : 'E',
              id: `${doc._id}-${index.id}-${key}`,
            });
          }
        }
      }
    }

    // if (!found_unique) {
    //   ret.push({
    //     code: "CHK_MSG_TBL_IDX_050",
    //     data: `at least one index has to be defined`,
    //     type: doc.studio_meta.not_in_use ? "W" : "E",
    //     id: `${doc._id}`,
    //   });
    // }
  }

  // WARNINGS //

  /////////////  fields ////////////////////
  if (!doc?.tableFields?.length) {
    ret.push({
      code: 'CHK_MSG_TBL_FLD_012',
      data: `table has no fields`,
      type: 'W',
      id: `${doc._id}`,
    });
  }

  for (let field of doc.tableFields) {
    if (!['string', 'number', 'boolean', 'object', 'array', 'date'].includes(field?.props?.fieldType)) {
      ret.push({
        code: 'CHK_MSG_TBL_FLD_022',
        data: `invalid fieldType for field ${field?.data?.field_id}`,
        type: 'E',
        id: `${doc._id}-${field.id}`,
      });
    }
  }

  return ret;
};
const check_globals = function (doc) {
  var ret = [];

  ret = [...ret, ...check_prog_fieldset(doc)];

  return ret;
};
const check_component = function (doc) {
  var ret = [];

  /////////////  uiFramework ////////////////////
  if (!doc.properties.uiFramework) {
    ret.push({
      code: 'CHK_MSG_PRG_COM_010',
      data: 'component Ui Framework not defined',
      type: doc.studio_meta.not_in_use ? 'W' : 'E',
      category: `component`,
      ref: doc._id,
      id: `${doc._id}`,
    });
  } else {
    if (!app_obj?.app_plugins_purchased?.[doc.properties.uiFramework]) {
      ret.push({
        code: 'CHK_MSG_PRG_COM_020',
        data: `invalid ${doc.properties.uiFramework} Ui Framework`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        category: `component`,
        ref: doc._id,
        id: `${doc._id}`,
      });
    } else {
      if (!app_obj.app_plugins_purchased[doc.properties.uiFramework].installed) {
        ret.push({
          code: 'CHK_MSG_PRG_COM_030',
          data: `${doc.properties.uiFramework} Ui Framework not installed`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category: `component`,
          ref: doc._id,
          id: `${doc._id}`,
        });
      }
    }
  }

  ret = [...ret, ...check_prog_fieldset(doc)];
  ret = [...ret, ...check_prog_parameters(doc)];
  ret = [...ret, ...check_prog_datasource(doc)];

  /////////////  ui  ////////////////////

  if (doc.progUi) {
    var xu_tree_id_counts_obj = {};
    const run_tree = function (node) {
      for (let item of node) {
        if (item.xu_tree_id) {
          if (!xu_tree_id_counts_obj[item.xu_tree_id]) {
            xu_tree_id_counts_obj[item.xu_tree_id] = 1;
          } else {
            xu_tree_id_counts_obj[item.xu_tree_id]++;
          }
        }

        if (item.attributes) {
          var name_counts_obj = {};
          for (let [key, val] of Object.entries(item.attributes)) {
            if (!key) {
              ret.push({
                code: 'CHK_MSG_PRG_GUI_010',
                data: `missing attribute in tree_id ${item.id}`,
                type: doc.studio_meta.not_in_use ? 'W' : 'E',
                category: `component_ui`,
                ref: item.id,
                id: `${doc._id}-${item.id}-${key}`,
              });
            } else {
              // console.log(key);
              if (key.substr(0, 6) === 'xu-exp') {
                // if (item.id === "node-c1adfbb7-17b4-4631-b17d-000db66cc88d")

                ret = [...ret, ...check_prog_expression(doc, val, item.id, 'ui_attribute')];
              } else {
                if (!/^[A-Za-z]+[\w\-\:\.]*$/.test(key)) {
                  ret.push({
                    code: 'CHK_MSG_PRG_GUI_020',
                    data: `invalid attribute "${key}" in tag "${item.tagName}" "${item.id}"`,
                    type: doc.studio_meta.not_in_use ? 'W' : 'E',
                    category: `component_ui`,
                    ref: item.id,
                    id: `${doc._id}-${item.id}-${key}`,
                  });
                }
              }

              if (key.substr(0, 6) === 'xu-on:') {
                if (!_.isEmpty(val)) {
                  for (let trigger_item of val) {
                    // try {
                    ret = [...ret, ...check_prog_triggers(doc, trigger_item.workflow, 'ui_trigger', item.id, item.attributes.tree_id)];
                    // } catch (err) {
                    //   console.error("***********", err);
                    //   debugger;
                    // }
                  }
                }
              }

              if (!name_counts_obj[key]) {
                name_counts_obj[key] = 1;
              } else {
                name_counts_obj[key]++;
              }
            }
          }

          for (let [key, val] of Object.entries(name_counts_obj)) {
            if (val > 1) {
              ret.push({
                code: 'CHK_MSG_PRG_GUI_200',
                data: `duplicate attribute id for ${key} in tree_id ${item.attribute.tree_id}`,
                type: doc.studio_meta.not_in_use ? 'W' : 'E',
                category: `component_ui`,
                ref: doc._id,
                id: `${doc._id}-${item.id}-${key}`,
              });
            }
          }
        }

        // if (!_.isEmpty(item.workflow)) {
        //   debugger;
        //   ret = [
        //     ...ret,
        //     ...check_prog_triggers(doc, item.workflow, "ui", item.data.tree_id),
        //   ];
        // }

        if (item.children) {
          run_tree(item.children);
        }
      }
    };
    run_tree(doc.progUi);

    for (let [key, val] of Object.entries(xu_tree_id_counts_obj)) {
      if (val > 1) {
        ret.push({
          code: 'CHK_MSG_PRG_GUI_250',
          data: `duplicate tree_id "${key}"`,
          type: 'W',
          category: `component_ui`,
          ref: doc._id,
          id: `${doc._id}-${key}`,
        });
      }
    }
  }

  return ret;
};

const check_get_data = function (doc) {
  var ret = [];

  ret = [...ret, ...check_prog_fieldset(doc)];
  ret = [...ret, ...check_prog_parameters(doc)];
  ret = [...ret, ...check_prog_datasource(doc)];

  return ret;
};
const check_batch = function (doc) {
  var ret = [];

  ret = [...ret, ...check_prog_fieldset(doc)];
  ret = [...ret, ...check_prog_parameters(doc)];
  ret = [...ret, ...check_prog_datasource(doc)];

  return ret;
};
const check_api = function (doc) {
  var ret = [];

  ret = [...ret, ...check_prog_fieldset(doc)];
  ret = [...ret, ...check_prog_parameters(doc)];
  ret = [...ret, ...check_prog_datasource(doc)];

  return ret;
};
const check_set_data = function (doc) {
  var ret = [];

  ret = [...ret, ...check_prog_fieldset(doc)];
  ret = [...ret, ...check_prog_parameters(doc)];
  ret = [...ret, ...check_prog_datasource(doc)];

  return ret;
};

const check_alert = function (doc) {
  var ret = [];

  if (_.isEmpty(doc.alertData)) {
    ret.push({
      code: 'CHK_MSG_PRG_ART_010',
      data: `no alert properties defined`,
      type: doc.studio_meta.not_in_use ? 'W' : 'E',
      category: `alert`,
      ref: doc._id,
      id: `${doc._id}`,
    });
  } else {
    if (!doc.alertData.alertType) {
      ret.push({
        code: 'CHK_MSG_PRG_ART_020',
        data: `missing alert type property`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        category: `alert`,
        ref: doc._id,
        id: `${doc._id}`,
      });
    }
    if (!doc.alertData.alertDisplay) {
      ret.push({
        code: 'CHK_MSG_PRG_ART_030',
        data: `missing alert display property`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        category: `alert`,
        ref: doc._id,
        id: `${doc._id}`,
      });
    }
    if (!doc.alertData.alertTitle && !doc.alertData.alertTitleFx && !doc.alertData.alertBody && !doc.alertData.alertBodyFx) {
      ret.push({
        code: 'CHK_MSG_PRG_ART_032',
        data: `missing alert title and body value`,
        type: 'W',
        category: `alert`,
        ref: doc._id,
        id: `${doc._id}`,
      });
    }
    // if (!doc.alertData.alertBody && !doc.alertData.alertBodyFx) {
    //   ret.push({
    //     code: "CHK_MSG_PRG_ART_042",
    //     data: `missing alert body property`,
    //     type: "W",
    //     category: `alert`,
    //     ref: doc._id,
    //   });
    // }
  }

  return ret;
};
const check_route = function (doc) {
  var ret = [];

  if (_.isEmpty(doc?.routeMenu?.menu)) {
    ret.push({
      code: 'CHK_MSG_RTE_MNU_010',
      data: `no route properties defined`,
      type: doc.studio_meta.not_in_use ? 'W' : 'E',
      category: `alert`,
      ref: doc._id,
      id: `${doc._id}`,
    });
  } else {
    function checkMenuItems(menu) {
      let flatMenu = {};

      function recurse(items) {
        for (let item of items) {
          if (!item.prog_id) {
            ret.push({
              code: 'CHK_MSG_RTE_MNU_020',
              data: `no program to call has been defined`,
              type: doc.studio_meta.not_in_use ? 'W' : 'E',
              category: `alert`,
              ref: item.id,
              id: `${doc._id}`,
            });
            continue;
          }
          if (!progs_obj?.[item.prog_id]) {
            ret.push({
              code: 'CHK_MSG_RTE_MNU_030',
              data: `program ${item.prog_id} not exist`,
              type: doc.studio_meta.not_in_use ? 'W' : 'E',
              category: `alert`,
              ref: item.id,
              id: `${doc._id}`,
            });
            continue;
          }
          if (item.children && item.children.length > 0) {
            recurse(item.children);
          }
        }
      }

      recurse(menu);
      return flatMenu;
    }

    checkMenuItems(doc.routeMenu.menu);
  }

  return ret;
};
// const call_project_api = function (doc) {
//   var ret = [];

//   if (_.isEmpty(doc.alertData)) {
//     ret.push({
//       code: "CHK_MSG_PRG_ART_010",
//       data: `no alert properties defined`,
//       type: doc.studio_meta.not_in_use ? "W" : "E",
//       category: `alert`,
//       ref: doc._id,
//       id: `${doc._id}`,
//     });
//   }

//   return ret;
// };

const check_prog_fieldset = function (doc) {
  var ret = [];

  if (!doc.progFields || _.isEmpty(doc.progFields)) {
    if (doc.progDataSource.dataSourceTableId) {
      ret.push({
        code: 'CHK_MSG_PRG_FLD_012',
        data: `datasource table defined but fieldset is empty`,
        type: 'W',
        category: 'fieldset',
        id: `${doc._id}`,
      });
    }
  } else {
    var name_counts_obj = {};

    for (let field of doc.progFields) {
      if (!field?.data?.field_id) {
        ret.push({
          code: 'CHK_MSG_PRG_FLD_010',
          data: `missing field_id`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category: 'fieldset',
          ref: field.id,
          id: `${doc._id}-${field.id}`,
        });
      } else {
        if (!/^[A-Za-z]+[\w\-\:\.]*$/.test(field.data.field_id) && !PROTECTED_VARS.includes(field.data.field_id)) {
          ret.push({
            code: 'CHK_MSG_PRG_FLD_020',
            data: `invalid ${field.data.field_id} field_id`,
            type: doc.studio_meta.not_in_use ? 'W' : 'E',
            category: 'fieldset',
            ref: field.id,
            id: `${doc._id}-${field.id}`,
          });
        }

        if (!name_counts_obj[field.data.field_id]) {
          name_counts_obj[field.data.field_id] = 1;
        } else {
          name_counts_obj[field.data.field_id]++;
        }
        if (!hide_not_in_use_check) {
          if (!find_field_in_progs(doc, field.data.field_id)) {
            // find in Out parameters
            var found_parameter_out_match;
            if (!_.isEmpty(doc.properties.progParams)) {
              for (let parameter_item of doc.properties.progParams) {
                if (parameter_item.data.dir === 'out' && parameter_item.data.parameter === field.data.field_id) {
                  found_parameter_out_match = true;
                  break;
                }
              }
            }
            if (!found_parameter_out_match) {
              ret.push({
                code: 'CHK_MSG_PRG_FLD_022',
                data: `field "${field.data.field_id}" not in use`,
                type: 'W',
                category: 'fieldset',
                ref: field.id,
                not_in_use: true,
                id: `${doc._id}-${field.id}`,
              });
            }
          }
        }
      }

      if (!field?.data?.type) {
        ret.push({
          code: 'CHK_MSG_PRG_FLD_030',
          data: `missing field type`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category: 'fieldset',
          ref: field.id,
          id: `${doc._id}-${field.id}`,
        });
        return ret;
      } else {
        switch (field.data.type) {
          case 'virtual':
            break;

          case 'exp':
            if (!field?.props?.fieldValue || !field?.props?.propExpressions?.fieldValue) {
              ret.push({
                code: 'CHK_MSG_PRG_FLD_070',
                data: `invalid value or value expression for ${field.data.field_id} field type Exp`,
                type: doc.studio_meta.not_in_use ? 'W' : 'E',
                category: 'fieldset',
                ref: field.id,
                id: `${doc._id}-${field.id}`,
              });
            }
            break;

          case 'table':
            if (doc.properties.menuType === 'globals') {
              ret.push({
                code: 'CHK_MSG_PRG_FLD_040',
                data: `invalid field type for ${field.data.field_id} in globals`,
                type: doc.studio_meta.not_in_use ? 'W' : 'E',
                category: 'fieldset',
                ref: field.id,
                id: `${doc._id}-${field.id}`,
              });
            } else {
              // check against table

              if (!doc.progDataSource || _.isEmpty(doc.progDataSource) || !doc.progDataSource.dataSourceType || doc.progDataSource.dataSourceType === 'none') {
                ret.push({
                  code: 'CHK_MSG_PRG_FLD_300',
                  data: `undefined datasource for ${field.data.field_id} field`,
                  type: doc.studio_meta.not_in_use ? 'W' : 'E',
                  category: 'fieldset',
                  ref: field.id,
                  id: `${doc._id}-${field.id}`,
                });
                return ret;
              } else {
                var ret_dsc = check_prog_datasource(doc);
                if (ret_dsc.length) return ret;

                var _datasource_table_arr = progs_obj?.[doc.progDataSource.dataSourceTableId]?.tableFields;
                if (!_datasource_table_arr) {
                  ret.push({
                    code: 'CHK_MSG_PRG_FLD_310',
                    data: `no fields found in datasource table ${doc.progDataSource.dataSourceTableId} `,
                    type: doc.studio_meta.not_in_use ? 'W' : 'E',
                    category: 'fieldset',
                    ref: field.id,
                    id: `${doc._id}-${field.id}`,
                  });
                  return ret;
                }

                if (!find_item_by_key(_datasource_table_arr, 'field_id', field.data.field_id)) {
                  if (!doc.progDataSource.dataSourceReduce || field.data.field_id !== 'REDUCE_VALUE') {
                    ret.push({
                      code: 'CHK_MSG_PRG_FLD_320',
                      data: `invalid field ${field.data.field_id} in datasource table`,
                      type: doc.studio_meta.not_in_use ? 'W' : 'E',
                      category: 'fieldset',
                      ref: field.id,
                      id: `${doc._id}-${field.id}`,
                    });
                    return ret;
                  }
                }
              }
            }
            break;

          default:
            ret.push({
              code: 'CHK_MSG_PRG_FLD_060',
              data: `invalid field type for ${field.data.field_id}`,
              type: doc.studio_meta.not_in_use ? 'W' : 'E',
              category: 'fieldset',
              ref: field.id,
              id: `${doc._id}-${field.id}`,
            });
            return ret;
            break;
        }
      }

      if (!field?.props?.fieldType && !field?.props?.propExpressions?.fieldType) {
        ret.push({
          code: 'CHK_MSG_PRG_FLD_130',
          data: `missing field fieldType`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category: 'fieldset',
          ref: field.id,
          id: `${doc._id}-${field.id}`,
        });
        return ret;
      } else if (field.props.fieldType) {
        switch (field.props.fieldType) {
          case 'string':
            if (field?.props?.fieldValue && typeof field.props.fieldValue !== 'string') {
              ret.push({
                code: 'CHK_MSG_PRG_FLD_200',
                data: `invalid value type ${field.props.fieldType} for ${field.data.field_id} field`,
                type: doc.studio_meta.not_in_use ? 'W' : 'E',
                category: 'fieldset',
                ref: field.id,
                id: `${doc._id}-${field.id}`,
              });
            }

            break;

          case 'number':
          case 'boolean':
            if (field?.props?.fieldValue) {
              if (_.isNaN(_.toNumber(field?.props?.fieldValue))) {
                ret.push({
                  code: 'CHK_MSG_PRG_FLD_210',
                  data: `invalid value type ${field.props.fieldType} for ${field.data.field_id} field`,
                  type: 'E',
                  category: 'fieldset',
                  ref: field.id,
                  id: `${doc._id}-${field.id}`,
                });
              }
            }

            break;

          case 'object':
          case 'array':
            if (field?.props?.fieldValue) {
              try {
                JSON5.parse(field.props.fieldValue);
              } catch (err) {
                ret.push({
                  code: 'CHK_MSG_PRG_FLD_220',
                  data: `invalid value type "${field.props.fieldType}" for "${field.data.field_id}" field`,
                  type: doc.studio_meta.not_in_use ? 'W' : 'E',
                  category: 'fieldset',
                  ref: field.id,
                  id: `${doc._id}-${field.id}`,
                });
              }
            }
            break;

          default:
            ret.push({
              code: 'CHK_MSG_PRG_FLD_160',
              data: `invalid field fieldType for ${field.data.field_id}`,
              type: doc.studio_meta.not_in_use ? 'W' : 'E',
              category: 'fieldset',
              ref: field.id,
              id: `${doc._id}-${field.id}`,
            });
            return ret;
            break;
        }
      }

      // if (!_.isEmpty(field.workflow)) {}
      // propExpressions

      if (!_.isEmpty(field.workflow)) {
        ret = [...ret, ...check_prog_triggers(doc, field.workflow, 'field', field.id, field.data.field_id)];
      }
    }

    for (let [key, val] of Object.entries(name_counts_obj)) {
      if (val > 1) {
        ret.push({
          code: 'CHK_MSG_PRG_FLD_200',
          data: `duplicate field id for ${key}`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category: 'fieldset',
          id: `${doc._id}-${key}`, // was `${doc._id}-${field.id}-${key}`,
        });
      }
    }
  }

  return ret;
};
const check_prog_events = function (doc) {
  var ret = [];

  if (!doc.progEvents || _.isEmpty(doc.progEvents)) return ret;
  var name_counts_obj = {};
  for (let event_item of doc.progEvents) {
    switch (event_item?.data?.type) {
      case 'user_defined':
        if (!event_item.data.event_name) {
          ret.push({
            code: 'CHK_MSG_PRG_EVT_010',
            data: `missing event name for ${event_item.id}`,
            type: 'W',
            category: 'event',
            ref: event_item.id,
            id: `${doc._id}-${event_item.id}`,
          });
          continue;
        } else {
          if (!/^[A-Za-z]+[\w\-\:\.]*$/.test(event_item.data.event_name)) {
            ret.push({
              code: 'CHK_MSG_PRG_EVT_020',
              data: `invalid event name for event "${event_item.data.event_name || event_item.data.type}"`,
              type: doc.studio_meta.not_in_use ? 'W' : 'E',
              category: 'event',
              ref: event_item.id,
              id: `${doc._id}-${event_item.id}`,
            });
          }

          // search if in use
          if (!hide_not_in_use_check) {
            const ret_event_id_use = find_trigger_property_value_in_progs(doc, ['raise_event'], 'event', event_item.data.event_name);

            if (!ret_event_id_use?.length) {
              ret.push({
                code: 'CHK_MSG_PRG_EVT_112',
                data: `event "${event_item.data.event_name}" not in use`,
                type: 'W',
                category: 'event',
                ref: event_item.id,
                not_in_use: true,
                id: `${doc._id}-${event_item.id}`,
              });
            }
          }
        }

        if (event_item.data.condition) {
          ret = [...ret, ...check_prog_expression(doc, event_item.data.condition, event_item.id, 'condition')];
        } else {
          if (!name_counts_obj[event_item.id]) {
            name_counts_obj[event_item.id] = 1;
          } else {
            name_counts_obj[event_item.id]++;
          }
        }

        // validate parameters TBD
        break;

      case 'screen_ready':
      case 'client_interval':
        if (doc.properties.menuType !== 'component') {
          ret.push({
            code: 'CHK_MSG_PRG_EVT_030',
            data: `event type "${event_item.data.type}" only allowed for "${doc.properties.menuType}" in event "${event_item.data.event_name || event_item.data.type}"`,
            type: doc.studio_meta.not_in_use ? 'W' : 'E',
            category: 'event',
            ref: event_item.id,
            id: `${doc._id}-${event_item.id}`,
          });
        }

        if (event_item.data.type === 'client_interval' && !event_item.data.properties) {
          ret.push({
            code: 'CHK_MSG_PRG_EVT_035',
            data: `empty interval property in event "${event_item.data.event_name || event_item.data.type}"`,
            type: doc.studio_meta.not_in_use ? 'W' : 'E',
            category: 'event',
            ref: event_item.id,
            id: `${doc._id}-${event_item.id}`,
          });
        }

        break;

      // case "after_init":
      // case "before_close":
      case 'on_load':
      case 'on_exit':
      case 'before_record':
      case 'after_record':
      case 'record_not_found':
      case 'locate_not_found':
        break;

      default:
        ret.push({
          code: 'CHK_MSG_PRG_EVT_018',
          data: `invalid event type for ${event_item.id}`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category: 'event',
          ref: event_item.id,
          id: `${doc._id}-${event_item.id}`,
        });
        break;
    }

    // check workflow
    if (!_.isEmpty(event_item.workflow)) {
      ret = [...ret, ...check_prog_triggers(doc, event_item.workflow, 'event', event_item.id, event_item.data.event_name || event_item.data.type)];
    } else {
      ret.push({
        code: 'CHK_MSG_PRG_EVT_022',
        data: `event "${event_item.data.event_name || event_item.data.type}" is empty`,
        type: 'W',
        category: 'event',
        ref: event_item.id,
        id: `${doc._id}-${event_item.id}`,
      });
    }
  }

  for (let [key, val] of Object.entries(name_counts_obj)) {
    if (val > 1) {
      ret.push({
        code: 'CHK_MSG_PRG_EVT_102',
        data: `duplicate "${key}" event `,
        type: 'W',
        category: 'event',
        id: `${doc._id}-${event_item.id}-${key}`,
      });
    }
  }

  return ret;
};
const check_prog_triggers = function (doc, workflow, source_type, source_id, source_name) {
  var ret = [];

  if (!workflow || _.isEmpty(workflow)) return ret;

  const check_target_program_parameters = function (trigger_item) {
    if (!trigger_item.data.name.prog) return;

    const target_program_parameters_arr = progs_obj?.[trigger_item.data.name.prog]?.properties?.progParams || [];

    if (trigger_item?.data?.name?.parameters && !_.isEmpty(trigger_item.data.name.parameters)) {
      for (let [key, val] of Object.entries(trigger_item.data.name.parameters)) {
        const param_ret = find_item_by_key(target_program_parameters_arr, 'parameter', key);
        if (_.isEmpty(param_ret)) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_370',
            data: `parameter "${key}" not found in the target program "${progs_obj?.[trigger_item.data.name.prog].properties.menuName}" parameter list in ${source_type} "${source_name}" trigger`,
            type: 'W',
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}-${key}`,
          });
          return;
        }
        if (param_ret.data.dir !== 'in' && val.value) {
          const ret_local_field = find_item_by_key(doc.progFields, 'field_id', val.value);

          if (!ret_local_field) {
            let ret_parent_field;
            const check_parent_program = function (prog_id) {
              let parent_prog_id = progs_obj?.[prog_id]?.studio_meta?.parentId;
              if (parent_prog_id && progs_obj?.[parent_prog_id]) {
                ret_parent_field = find_item_by_key(progs_obj?.[parent_prog_id]?.progFields || [], 'field_id', val.value);
                if (!ret_parent_field) {
                  return check_parent_program(parent_prog_id);
                }
              }
            };
            check_parent_program(doc._id);
            if (!ret_parent_field) {
              const ret_global_field = find_item_by_key(progs_obj?.[`global_${app_obj._id}`]?.progFields || [], 'field_id', val.value);
              if (!ret_global_field) {
                // not 100% warning field might found in a caller dataset, need to run all programs TBD
                ret.push({
                  code: 'CHK_MSG_PRG_TRG_372',
                  data: `"${val.value}" parameter "Out" not found in the program Dataset in ${source_type} "${source_name}" trigger`,
                  type: 'W',
                  category: `${source_type}_trigger`,
                  ref: trigger_item.id,
                  source_id,
                  id: `${doc._id}-${source_id}-${trigger_item.id}-${key}`,
                });
              }
            }
          }
          // ret.push({
          //   code: "CHK_MSG_PRG_TRG_372",
          //   data: `parameter "${key}" not set to "In" in the target program "${
          //     progs_obj?.[trigger_item.data.name.prog].properties.menuName
          //   }" parameter list in ${source_type} "${source_name}" trigger`,
          //   type: "W",
          //   category: `${source_type}_trigger`,
          //   ref: trigger_item.id,
          //   source_id,
          // });
        }
      }
    } else {
      // found parameters in the target program but empty in the calling program
      if (target_program_parameters_arr.length) {
        ret.push({
          code: 'CHK_MSG_PRG_TRG_374',
          data: `found parameters in "${progs_obj?.[trigger_item.data.name.prog].properties.menuName}" but empty parameter to exchange in ${source_type} "${source_name}" trigger`,
          type: 'W',
          category: `${source_type}_trigger`,
          ref: trigger_item.id,
          source_id,
          id: `${doc._id}-${source_id}-${trigger_item.id}`,
        });
      }
    }
  };

  for (let trigger_item of workflow) {
    const get_prog_id = function () {
      let _prop = trigger_item.data?.name?.properties;
      let _prog_id = trigger_item.data.name.prog_id || _prop?.['xu-exp:prog'];

      return _prog_id;
    };

    const err_type = doc.studio_meta.not_in_use ? 'W' : trigger_item.data.enabled ? 'E' : 'W';
    switch (trigger_item?.data?.action) {
      case 'comment':
        break;
      case 'update':
        if (!trigger_item?.data?.name?.value) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_020',
            data: `no value to update in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        } else {
          // ret = [
          //   ...ret,
          //   ...check_prog_expression(
          //     doc,
          //     trigger_item.data.name.value,
          //     trigger_item.id
          //   ),
          // ];

          const validate_update_function = function (exp) {
            var viewFields = [];
            try {
              // parse json
              var json = JSON5.parse(exp.replace(/\n/gi, ''));
              for (const [key, val] of Object.entries(json)) {
                let id = key.substr(1, key.length - 1);
                viewFields.push({
                  val: val,
                  id: id,
                });
              }
              return viewFields;
            } catch (e) {
              if (!exp) {
                return;
              }
              if (exp.substr(exp.length - 1, 1) === ';') exp = exp.substr(0, exp.length - 1); // remove closing if exist;
              var exp_arr = exp.split(/;\s*(?=(?:[^"]|"[^"]*")*$)/g); // split ; outside quotes
              if (exp_arr?.length) {
                for (let val of exp_arr) {
                  // run view fields
                  var pos = val.indexOf(':');
                  var seg = [val.substring(0, pos), val.substring(pos + 1)];
                  if (seg && seg.length === 2) {
                    let id = seg[0].substr(1, seg[0].length);
                    viewFields.push({
                      id: id,
                      val: seg[1],
                      //  , props: {},
                    });
                  } else {
                    ret.push({
                      code: 'CHK_MSG_PRG_TRG_040',
                      data: `invalid json; too many segments in${source_type} "${source_name}" trigger`,
                      type: err_type,
                      category: `${source_type}_trigger`,
                      ref: trigger_item.id,
                      source_id,
                      id: `${doc._id}-${source_id}-${trigger_item.id}`,
                    });
                  }
                }
              }

              return viewFields;
            }
          };

          // check syntax
          const ret_update_fields_arr = validate_update_function(trigger_item.data.name.value);
          if (!ret_update_fields_arr.length) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_050',
              data: `no fields to update in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          } else {
            for (let val of ret_update_fields_arr) {
              const ret_fields = find_field_in_progs(doc, val.id);
              if (!ret_fields) {
                ret.push({
                  code: 'CHK_MSG_PRG_TRG_060',
                  data: `field "${val.id}" not found in the project programs for update in ${source_type} "${source_name}" trigger`,
                  type: err_type,
                  category: `${source_type}_trigger`,
                  ref: trigger_item.id,
                  source_id,
                  id: `${doc._id}-${source_id}-${trigger_item.id}`,
                });
              }
            }
          }
        }

        break;

      case 'raise_event':
        //  !trigger_item?.data?.name?.properties['xu-exp:event']
        if (!trigger_item?.data?.name?.event && !trigger_item?.data?.name?.properties['xu-exp:event']) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_100',
            data: `invalid event name for "${trigger_item.data.action}" in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        } else if (trigger_item?.data?.name?.event) {
          if (!/^[A-Za-z]+[\w\-\:\.]*$/.test(trigger_item.data.name.event)) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_110',
              data: `invalid event name ${trigger_item.data.name.event} in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          } else {
            // check if event exist
            const ret_event_name = find_event_id_in_progs(doc, trigger_item.data.name.event);

            if (!ret_event_name.length) {
              ret.push({
                code: 'CHK_MSG_PRG_TRG_120',
                data: `call performed to non exist event name called from "${trigger_item.data.name.event}" in ${source_type} "${source_name}" trigger`,
                type: err_type,
                category: `${source_type}_trigger`,
                ref: trigger_item.id,
                source_id,
                id: `${doc._id}-${source_id}-${trigger_item.id}`,
              });
            } else {
              // if exist check parameters
              if (ret_event_name.length === 1 && !_.isEmpty(trigger_item.data.name.parameters)) {
                for (let [key, val] of Object.entries(trigger_item.data.name.parameters)) {
                  if (_.isEmpty(progs_obj?.[ret_event_name[0].prog_id].progFields)) {
                    if (!find_item_by_key(progs_obj?.[ret_event_name[0].prog_id].progFields, 'field_id', key)) {
                      ret.push({
                        code: 'CHK_MSG_PRG_TRG_130',
                        data: `parameter "${key}" not exist in the dataset of the target program called from event name "${trigger_item.data.name.event}" in ${source_type} "${source_name}" trigger`,
                        type: 'W',
                        category: `${source_type}_trigger`,
                        ref: trigger_item.id,
                        source_id,
                        id: `${doc._id}-${source_id}-${trigger_item.id}-${key}`,
                      });
                    }
                  }
                }
              }
            }
          }
        }
        break;

      case 'call_page':
      case 'call_modal':
      case 'call_popover':
        if (!trigger_item.data.name.prog) {
          if (!trigger_item.data?.name?.properties?.['xu-exp:prog']) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_310',
              data: `empty program to call in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          }
        } else {
          if (!progs_obj?.[trigger_item.data.name.prog]) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_320',
              data: `invalid program to call in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          } else {
            // validate call to component
            if (progs_obj?.[trigger_item.data.name.prog].properties.menuType !== 'component') {
              ret.push({
                code: 'CHK_MSG_PRG_TRG_334',
                data: `invalid program type "${trigger_item.data.action}" to call in ${source_type} "${source_name}" trigger`,
                type: err_type,
                category: `${source_type}_trigger`,
                ref: trigger_item.id,
                source_id,
                id: `${doc._id}-${source_id}-${trigger_item.id}`,
              });
            } else {
              // if exist check parameters
              check_target_program_parameters(trigger_item);
            }
          }
        }

        break;

      case 'call_project_api':
        if (!trigger_item.data.name.prog) {
          if (!trigger_item.data?.name?.properties?.['xu-exp:prog']) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_310',
              data: `empty program to call in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          }
        } else {
          if (!progs_obj?.[trigger_item.data.name.prog]) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_320',
              data: `invalid program to call in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          } else {
            // validate call to component
            if (progs_obj?.[trigger_item.data.name.prog].properties.menuType !== 'api') {
              ret.push({
                code: 'CHK_MSG_PRG_TRG_334',
                data: `invalid program type "${trigger_item.data.action}" to call in ${source_type} "${source_name}" trigger`,
                type: err_type,
                category: `${source_type}_trigger`,
                ref: trigger_item.id,
                source_id,
                id: `${doc._id}-${source_id}-${trigger_item.id}`,
              });
            } else {
              // if exist check parameters
              check_target_program_parameters(trigger_item);
            }
          }
        }

        // if (!trigger_item.data.name?.properties?.outputField) {
        //   if (!trigger_item.data?.name?.properties?.["xu-exp:prog"]) {
        //     ret.push({
        //       code: "CHK_MSG_PRG_TRG_310",
        //       data: `empty program to call in ${source_type} "${source_name}" trigger`,
        //       type: err_type,
        //       category: `${source_type}_trigger`,
        //       ref: trigger_item.id,
        //       source_id,
        //       id: `${doc._id}-${source_id}-${trigger_item.id}`,
        //     });
        //   }
        // }

        break;

      case 'call_system_api': {
        const _api_method = trigger_item.data.name.api_method;
        if (!_api_method) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_380',
            data: `api_method not defined in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        } else {
          if (typeof _conf !== 'undefined') {
            if (!_conf.cpi_methods[_api_method]) {
              ret.push({
                code: 'CHK_MSG_PRG_TRG_382',
                data: `api method ${_api_method} unrecognized in ${source_type} "${source_name}" trigger`,
                type: err_type,
                category: `${source_type}_trigger`,
                ref: trigger_item.id,
                source_id,
                id: `${doc._id}-${source_id}-${trigger_item.id}`,
              });
            } else {
              if (_conf.cpi_methods[_api_method].private) {
                return ret.push({
                  code: 'CHK_MSG_PRG_TRG_384',
                  data: `api method ${_api_method} is private in ${source_type} "${source_name}" trigger`,
                  type: err_type,
                  category: `${source_type}_trigger`,
                  ref: trigger_item.id,
                  source_id,
                  id: `${doc._id}-${source_id}-${trigger_item.id}`,
                });
              }
              if (_conf.cpi_methods[_api_method].fields) {
                for (let [key, val] of Object.entries(_conf.cpi_methods[_api_method].fields)) {
                  if (val.mandatory && !trigger_item.data.name?.payload?.[key]) {
                    ret.push({
                      code: 'CHK_MSG_PRG_TRG_386',
                      data: `field ${key} for api method ${_api_method} is mandatory field in ${source_type} "${source_name}" trigger`,
                      type: err_type,
                      category: `${source_type}_trigger`,
                      ref: trigger_item.id,
                      source_id,
                      id: `${doc._id}-${source_id}-${trigger_item.id}`,
                    });
                  }
                }
              }
            }
          }
        }
        break;
      }

      case 'call_external_api': {
        if (!trigger_item.data.name.method) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_388',
            data: `method not defined in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        }

        if (!trigger_item.data.name.url) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_389',
            data: `url not defined in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        }
        break;
      }

      case 'get_data':
      case 'set_data':
      case 'batch':
      case 'call_native_javascript':
      case 'call_evaluate_javascript':
      case 'call_alert':
        // check program exist
        if (!trigger_item.data.name.prog) {
          if (!trigger_item.data?.name?.properties?.['xu-exp:prog']) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_310',
              data: `empty program to call in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          }
        } else {
          if (!progs_obj?.[trigger_item.data.name.prog]) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_320',
              data: `invalid program to call in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          } else {
            // validate call to the same menuType
            if (progs_obj?.[trigger_item.data.name.prog].properties.menuType !== trigger_item.data.action) {
              if (['call_native_javascript', 'call_evaluate_javascript'].includes(trigger_item.data.action) && progs_obj?.[trigger_item.data.name.prog].properties.menuType === 'javascript') {
                continue;
              }

              if (['call_alert'].includes(trigger_item.data.action) && progs_obj?.[trigger_item.data.name.prog].properties.menuType === 'alert') {
                continue;
              }

              ret.push({
                code: 'CHK_MSG_PRG_TRG_330',
                data: `call set to "${trigger_item.data.action}" but found "${progs_obj?.[trigger_item.data.name.prog].properties.menuType}" instead in ${source_type} "${source_name}" trigger`,
                type: err_type,
                category: `${source_type}_trigger`,
                ref: trigger_item.id,
                source_id,
                id: `${doc._id}-${source_id}-${trigger_item.id}`,
              });
            } else {
              // if exist check parameters
              check_target_program_parameters(trigger_item);
            }
          }
        }

        break;

      case 'execute_native_javascript':
      case 'execute_evaluate_javascript':
        // validate script

        check_prog_expression(doc, trigger_item.data.name.value, trigger_item.id, 'script');

        break;

      case 'emit_event':
        if (!trigger_item?.data?.name?.value) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_800',
            data: `no event defined for "emit_event" in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `alert`,
            ref: doc._id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        } else {
          if (!/^[A-Za-z]+[\w\-\:\.]*$/.test(trigger_item.data.name.value)) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_810',
              data: `invalid event name ${trigger_item.data.name.value} in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          }
          //   else {
          //     // check if event exist
          //     const ret_event_name = find_event_id_in_progs(
          //       doc,
          //       trigger_item.data.name.value
          //     );

          //     if (!ret_event_name.length) {
          //       ret.push({
          //         code: "CHK_MSG_PRG_TRG_820",
          //         data: `call performed to non exist event name called from "${trigger_item.data.name.value}" in ${source_type} "${source_name}" trigger`,
          //         type: err_type,
          //         category: `${source_type}_trigger`,
          //         ref: trigger_item.id,
          //         source_id,
          //       });
          //     }
          //     //  else {
          //     //   // if exist check parameters
          //     //   if (
          //     //     ret_event_name.length === 1 &&
          //     //     !_.isEmpty(trigger_item.data.name.parameters)
          //     //   ) {
          //     //     for (let [key, val] of Object.entries(
          //     //       trigger_item.data.name.parameters
          //     //     )) {
          //     //       if (
          //     //         _.isEmpty(progs_obj[ret_event_name[0].prog_id].progFields)
          //     //       ) {
          //     //         if (
          //     //           !find_item_by_key(
          //     //             progs_obj[ret_event_name[0].prog_id].progFields,
          //     //             "field_id",
          //     //             key
          //     //           )
          //     //         ) {
          //     //           ret.push({
          //     //             code: "CHK_MSG_PRG_TRG_130",
          //     //             data: `parameter "${key}" not exist in the dataset of the target program called from event name "${trigger_item.data.name.event}" in ${source_type} "${source_name}" trigger`,
          //     //             type: "W",
          //     //             category: `${source_type}_trigger`,
          //     //             ref: trigger_item.id,
          //     //           });
          //     //         }
          //     //       }
          //     //     }
          //     //   }
          //     // }
          //   }
        }

        break;

      case 'alert':
        if (_.isEmpty(trigger_item.data.name)) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_610',
            data: `no alert properties defined`,
            type: err_type,
            category: `alert`,
            ref: doc._id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        } else {
          if (!trigger_item.data.name.alertType) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_612',
              data: `missing alert type property`,
              type: err_type,
              category: `alert`,
              ref: doc._id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          }
          if (!trigger_item.data.name.alertDisplay) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_614',
              data: `missing alert display property`,
              type: err_type,
              category: `alert`,
              ref: doc._id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          }
          if (!trigger_item.data.name.alertTitle) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_616',
              data: `missing alert title property`,
              type: 'W',
              category: `alert`,
              ref: doc._id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          }
          if (!trigger_item.data.name.alertBody) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_618',
              data: `missing alert body property`,
              type: 'W',
              category: `alert`,
              ref: doc._id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          }
        }

        break;

      case 'loader_on':
      case 'loader_off':
        if (doc.properties.menuType !== 'component') {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_400',
            data: `"${trigger_item.data.action}" can only be executed by component in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        }

        break;

      case 'invoke_action':
        if (doc.properties.menuType !== 'component') {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_400',
            data: `"${trigger_item.data.action}" can only be executed by component in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        }
        // validate against list

        if (
          ![
            'act_next',
            'act_prev',
            'act_first',
            'act_last',
            'act_reload',
            'act_refresh',
            // "act_close",
            // "act_back",
            'act_print',
            'act_save',
            'act_delete',
          ].includes(trigger_item.data.name.value)
        ) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_400',
            data: `invalid "${trigger_item.data.name.value}" for "${trigger_item.data.action}" in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        }

        break;

      case 'delay':
        // validate time
        if (!trigger_item?.data?.name?.value) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_520',
            data: `empty delay value in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        }
        break;

      case 'call_library':
        if (!trigger_item?.data?.name?.plugin_name) {
          ret.push({
            code: 'CHK_MSG_PRG_TRG_510',
            data: `empty library value in ${source_type} "${source_name}" trigger`,
            type: err_type,
            category: `${source_type}_trigger`,
            ref: trigger_item.id,
            source_id,
            id: `${doc._id}-${source_id}-${trigger_item.id}`,
          });
        } else {
          if (!trigger_item?.data?.name?.library_method) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_516',
              data: `empty library ${trigger_item.data.name.plugin_name} method in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          }

          if (!app_obj?.app_plugins_purchased?.[trigger_item.data.name.plugin_name]) {
            ret.push({
              code: 'CHK_MSG_PRG_TRG_512',
              data: `invalid library call ${trigger_item.data.name.plugin_name} in ${source_type} "${source_name}" trigger`,
              type: err_type,
              category: `${source_type}_trigger`,
              ref: trigger_item.id,
              source_id,
              id: `${doc._id}-${source_id}-${trigger_item.id}`,
            });
          } else {
            if (!app_obj.app_plugins_purchased[trigger_item.data.name.plugin_name].installed) {
              ret.push({
                code: 'CHK_MSG_PRG_TRG_514',
                data: ` library "${trigger_item.data.name.plugin_name}" not installed in ${source_type} "${source_name}" trigger`,
                type: err_type,
                category: `${source_type}_trigger`,
                ref: trigger_item.id,
                source_id,
                id: `${doc._id}-${source_id}-${trigger_item.id}`,
              });
            }
          }
        }

        break;

      case 'alter_ui_element':
      case 'create_ui_element':
      case 'info_ui_element':
      case 'remove_ui_element':
      case 'empty_ui_element':
      case 'cache_ui_element':
      case 'load_ui_element':
        ret.push({
          code: 'CHK_MSG_PRG_TRG_900',
          data: `deprecated "${trigger_item?.data?.action}" action in ${source_type} "${source_name}" trigger`,
          type: err_type,
          category: `${source_type}_trigger`,
          ref: trigger_item.id,
          source_id,
          id: `${doc._id}-${source_id}-${trigger_item.id}`,
        });
        break;

      default:
        ret.push({
          code: 'CHK_MSG_PRG_TRG_008',
          data: `invalid trigger type "${trigger_item.data.action}" for ${source_type} "${source_name}" trigger`,
          type: err_type,
          category: `${source_type}_trigger`,
          ref: trigger_item.id,
          source_id,
          id: `${doc._id}-${source_id}-${trigger_item.id}`,
        });
        break;
    }

    if (trigger_item.props.condition) {
      ret = [...ret, ...check_prog_expression(doc, trigger_item.props.condition, trigger_item.id, 'condition')];
    }
  }

  return ret;
};
const check_prog_datasource = function (doc) {
  var ret = [];

  if (!doc.progDataSource || _.isEmpty(doc.progDataSource) || !doc.progDataSource.dataSourceType || doc.progDataSource.dataSourceType === 'none') {
    return ret;
  }

  switch (doc.progDataSource.dataSourceType) {
    case 'table':
      if (!doc.progDataSource.dataSourceTableId && !doc?.progDataSource?.dataSourceTableIdExp) {
        ret.push({
          code: 'CHK_MSG_PRG_DSC_010',
          data: `missing table id for datasource`,
          type: 'E',
          id: `${doc._id}`,
        });
        return ret;
      } else {
        if (doc.progDataSource.dataSourceTableId && !progs_obj?.[doc.progDataSource.dataSourceTableId]) {
          ret.push({
            code: 'CHK_MSG_PRG_DSC_030',
            data: `invalid table id  ${doc.progDataSource.dataSourceTableId} for datasource`,
            type: 'E',
            id: `${doc._id}`,
          });
          return ret;
        }

        // check consist indexes against table
        let found_error;

        function arraysEqual(arr1, arr2) {
          if (arr1.length !== arr2.length) return false; // If lengths are not equal, return false
          let sortedArr1 = arr1.slice().sort(); // Sort the first array
          let sortedArr2 = arr2.slice().sort(); // Sort the second array

          // Compare each element of both sorted arrays
          for (let i = 0; i < sortedArr1.length; i++) {
            if (sortedArr1[i] !== sortedArr2[i]) return false;
          }
          return true; // Return true if arrays are equal
        }

        if (doc.progDataSource.dataSourceIndexesObj) {
          const table_indexes_arr = progs_obj?.[doc.progDataSource.dataSourceTableId]?.tableIndexes;
          for (let [index_id, index_data] of Object.entries(doc.progDataSource.dataSourceIndexesObj)) {
            if (table_indexes_arr || !find_item_by_id(table_indexes_arr, index_id)) {
              ret.push({
                code: 'CHK_MSG_PRG_DSC_032',
                data: `find invalid index in table ${progs_obj?.[doc.progDataSource.dataSourceTableId]?.properties?.menuName || doc.progDataSource.dataSourceTableId} for index ${index_id} in dataSourceIndexesObj`,
                type: 'E',
                id: `${doc._id}`,
              });
              found_error = true;
              break;
            }
            let prog_keys = [];
            for (let [field_id, val] of Object.entries(index_data)) {
              prog_keys.push(field_id);
            }

            if (!arraysEqual(prog_keys, table_indexes_arr.keys)) {
              ret.push({
                code: 'CHK_MSG_PRG_DSC_034',
                data: `find in-consist index keys in table ${progs_obj?.[doc.progDataSource.dataSourceTableId]?.properties?.menuName || doc.progDataSource.dataSourceTableId} for index ${index_id} in dataSourceIndexesObj`,
                type: 'E',
                id: `${doc._id}`,
              });
              found_error = true;
              break;
            }
          }
          if (found_error) return ret;
        }
      }
      if (doc.properties.menuType !== 'set_data') {
        // check index
        if (!doc.progDataSource.dataSourceIndexId && !doc.progDataSource.dataSourceIndexIdExp) {
          ret.push({
            code: 'CHK_MSG_PRG_DSC_042',
            data: `index not defined for the datasource table`,
            type: 'W',
            id: `${doc._id}`,
          });
        } else if (doc.progDataSource.dataSourceIndexId) {
          if (!progs_obj?.[doc.progDataSource.dataSourceTableId]?.tableIndexes) {
            ret.push({
              code: 'CHK_MSG_PRG_DSC_030',
              data: `invalid table id ${doc.progDataSource.dataSourceTableId} for datasource`,
              type: 'E',
              id: `${doc._id}`,
            });
            return ret;
          } else {
            const table_indexes_arr = progs_obj?.[doc.progDataSource.dataSourceTableId].tableIndexes;

            if (!find_item_by_id(table_indexes_arr, doc.progDataSource.dataSourceIndexId)) {
              ret.push({
                code: 'CHK_MSG_PRG_DSC_040',
                data: `invalid index in table ${progs_obj?.[doc.progDataSource.dataSourceTableId]?.properties?.menuName || doc.progDataSource.dataSourceTableId} for index ${doc.progDataSource.dataSourceIndexId}`,
                type: 'E',
                id: `${doc._id}`,
              });
              return ret;
            }
          }
        }

        // check sort
        if (!doc.progDataSource.dataSourceSort && !doc.progDataSource.dataSourceSortExp) {
          ret.push({
            code: 'CHK_MSG_PRG_DSC_200',
            data: `missing sort value for datasource`,
            type: 'E',
            id: `${doc._id}`,
          });
        } else if (doc.progDataSource.dataSourceSort) {
          if (!['asc', 'des'].includes(doc.progDataSource.dataSourceSort)) {
            ret.push({
              code: 'CHK_MSG_PRG_DSC_210',
              data: `invalid sort value for datasource`,
              type: 'E',
              id: `${doc._id}`,
            });
          }
        }

        // check skip
        if (doc.progDataSource.dataSourceSkip && typeof doc.progDataSource.dataSourceSkip !== 'number') {
          ret.push({
            code: 'CHK_MSG_PRG_DSC_220',
            data: `invalid skip value for datasource`,
            type: 'E',
            id: `${doc._id}`,
          });
        }

        // check limit
        if (doc.progDataSource.dataSourceLimit && typeof doc.progDataSource.dataSourceLimit !== 'number') {
          ret.push({
            code: 'CHK_MSG_PRG_DSC_230',
            data: `invalid limit value for datasource`,
            type: 'E',
            id: `${doc._id}`,
          });
        }
      }

      break;

    default:
      ret.push({
        code: 'CHK_MSG_PRG_DSC_040',
        data: `invalid datasource type ${doc.progDataSource.dataSourceType}`,
        type: 'E',
        id: `${doc._id}`,
      });
      break;
  }

  return ret;
};
const check_prog_parameters = function (doc) {
  var ret = [];

  if (!doc?.properties?.progParams) return ret;
  var name_counts_obj = {};

  for (let parameter of doc.properties.progParams) {
    if (!parameter?.data?.parameter) {
      ret.push({
        code: 'CHK_MSG_PRG_PRM_010',
        data: `missing parameter name`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        category: 'parameter',
        ref: parameter.id,
        id: `${doc._id}-${parameter.id}`,
      });
    } else {
      if (!/^[A-Za-z]+[\w\-\:\.]*$/.test(parameter.data.parameter)) {
        ret.push({
          code: 'CHK_MSG_PRG_PRM_020',
          data: `invalid parameter name`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category: 'parameter',
          ref: parameter.id,
          id: `${doc._id}-${parameter.id}`,
        });
      }

      if (!name_counts_obj[parameter.data.parameter]) {
        name_counts_obj[parameter.data.parameter] = 1;
      } else {
        name_counts_obj[parameter.data.parameter]++;
      }
      if (!hide_not_in_use_check) {
        if (parameter.data.dir === 'in') {
          if (!find_field_in_progs(doc, parameter.data.parameter)) {
            ret.push({
              code: 'CHK_MSG_PRG_PRM_022',
              data: `parameter "${parameter.data.parameter}" not in use`,
              type: 'W',
              category: 'parameter',
              ref: parameter.id,
              not_in_use: true,
              id: `${doc._id}-${parameter.id}`,
            });
          }
        }
      }
    }

    if (!parameter?.data?.type) {
      ret.push({
        code: 'CHK_MSG_PRG_PRM_030',
        data: `missing parameter type for ${parameter.data.parameter}`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        category: 'parameter',
        ref: parameter.id,
        id: `${doc._id}-${parameter.id}`,
      });
      return ret;
    } else {
      switch (parameter.data.type) {
        case 'string':
          break;

        case 'number':
        case 'boolean':
          break;

        case 'object':
          break;

        case 'array':
          break;

        default:
          ret.push({
            code: 'CHK_MSG_PRG_PRM_040',
            data: `invalid parameter type "${parameter.data.type}" for ${parameter.data.parameter}`,
            type: doc.studio_meta.not_in_use ? 'W' : 'E',
            category: 'parameter',
            ref: parameter.id,
            id: `${doc._id}-${parameter.id}`,
          });
          return ret;
          break;
      }
    }

    if (!parameter?.data?.dir) {
      ret.push({
        code: 'CHK_MSG_PRG_PRM_080',
        data: `missing parameter dir for ${parameter.data.parameter}`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        category: 'parameter',
        ref: parameter.id,
        id: `${doc._id}-${parameter.id}`,
      });
      return ret;
    } else {
      switch (parameter.data.dir) {
        case 'in':
          break;

        case 'out':
          if (!find_item_by_key(doc.progFields, 'field_id', parameter.data.parameter)) {
            ret.push({
              code: 'CHK_MSG_PRG_PRM_100',
              data: `missing parameter return in the fieldset for ${parameter.data.parameter}`,
              type: doc.studio_meta.not_in_use ? 'W' : 'E',
              category: 'parameter',
              ref: parameter.id,
              id: `${doc._id}-${parameter.id}`,
            });
          }
          break;

        default:
          ret.push({
            code: 'CHK_MSG_PRG_PRM_090',
            data: `invalid parameter dir for ${parameter.data.parameter}`,
            type: doc.studio_meta.not_in_use ? 'W' : 'E',
            category: 'parameter',
            ref: parameter.id,
            id: `${doc._id}-${parameter.id}`,
          });
          return ret;
          break;
      }
    }
  }

  for (let [key, val] of Object.entries(name_counts_obj)) {
    if (val > 1) {
      ret.push({
        code: 'CHK_MSG_PRG_PRM_050',
        data: `duplicate parameter name for ${key}`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        category: 'parameter',
        ref: parameter.id,
        id: `${doc._id}-${parameter.id}-${key}`,
      });
    }
  }

  return ret;
};
const check_javascript = function (doc) {
  var ret = [];

  if (!doc?.scriptData?.value) {
    ret.push({
      code: 'CHK_MSG_PRG_JVS_012',
      data: 'no script to run',
      type: 'W',
      id: `${doc._id}`,
    });
  } else {
    // validate script syntax
    // ret = [
    //   ...ret,
    //   ...check_prog_expression(doc, doc.scriptData.value, doc._id, "script"),
    // ];
  }

  return ret;
};

const find_item_by_key = function (arr, key, val) {
  return arr.find(function (e) {
    return e.data[key] === val;
  });
};
const find_item_by_id = function (arr, val) {
  return arr.find(function (e) {
    return e.id === val;
  });
};
const find_field_in_progs = function (doc, field_id, prog_id) {
  const regex = new RegExp(`@${field_id}\\b`);
  if (!regex.test(progs_str)) {
    return;
  }

  var ret = [];

  for (let [key, prog] of Object.entries(progs_obj)) {
    var fields_arr = [];
    if (prog.tableFields) {
      fields_arr = prog.tableFields;
    }
    if (prog.progFields) {
      fields_arr = prog.progFields;
    }

    if (_.isEmpty(fields_arr)) {
      continue;
    }

    for (let field_item of fields_arr) {
      if (field_item.data.field_id === field_id) {
        ret.push({
          prog_id: prog._id,
          field_item,
          properties: prog.properties,
        });
      }
    }
  }
  // if (!ret.length) return false;
  return ret;
};
const check_prog_expression = function (doc, exp, path, category) {
  // if (!path) debugger;
  var ret = [];
  if (!exp) return ret;

  // if (is_server && doc.properties.runAt !== "server") return ret;
  if (!UglifyJS) return ret;

  const regex = /@\w+/g;

  const matches = exp.match(regex);

  var field_matches = {};
  try {
    if (matches) {
      for (let field of matches) {
        const ret_field = find_field_in_progs(doc, field.substr(1));
        if (!ret_field) {
          ret.push({
            code: 'CHK_MSG_PRG_EXP_012',
            data: `field "${field}" not found in the project datasets using expression "${exp.substr(0, 20)}.."  in ${path}`,
            type: 'W',
            category,
            source_id: path,
            id: `${doc._id}-${path}-${field}`,
          });
        } else {
          field_matches[field] = ret_field;
        }
      }
    }
  } catch (error) {
    debugger;
  }

  // error ocurred
  if (!_.isEmpty(ret)) {
    return ret;
  }

  if (_.isEmpty(field_matches)) {
    // try {
    //   eval(exp);
    // } catch (err) {
    //   ret.push({
    //     code: "CHK_MSG_PRG_EXP_022",
    //     data: `plain expression error in ${exp.substr(0, 20)}.. in ${path}`,
    //     type: "W",
    //   });
    // }

    var options = { warnings: true };
    var result = UglifyJS.minify(exp, options);
    if (result.error) {
      ret.push({
        code: 'CHK_MSG_PRG_EXP_022',
        data: `expression error ${result.error.message} in ${exp.substr(0, 20)}.. in ${path}`,
        type: doc.studio_meta.not_in_use ? 'W' : 'E',
        category,
        details: result.error,
        source_id: path,
        id: `${doc._id}-${path}`,
      });
    }
    if (result.warnings) {
      ret.push({
        code: 'CHK_MSG_PRG_EXP_024',
        data: `expression warning ${result.warnings[0]} in ${exp.substr(0, 20)}.. in ${path}`,
        type: 'W',
        category,
        details: result.warnings,
        source_id: path,
        id: `${doc._id}-${path}`,
      });
    }
  } else {
    var clean_exp = exp;
    var field_type = 'string';
    var skip_eval;
    for (let [key, val] of Object.entries(field_matches)) {
      if (val.length > 1) {
        var field_ind_found_in_current_program;
        for (let [ret_key, ret_val] of Object.entries(val)) {
          if (ret_val.prog_id === doc._id) {
            field_ind_found_in_current_program = ret_key;
            break;
          }
        }

        if (typeof field_ind_found_in_current_program === 'undefined') {
          skip_eval = true;
          break; // skip check if field type cannot be determine
        } else {
          field_type = val[field_ind_found_in_current_program]?.field_item?.props?.fieldType;
        }
      } else {
        field_type = val[0]?.field_item?.props?.fieldType;
      }

      // clean_exp = exp.replace(/@\w+/g, "''");
      switch (field_type) {
        case 'string':
          clean_exp = clean_exp.replaceAll(key, "''");
          break;
        case 'number':
        case 'boolean':
        case 'bool':
          clean_exp = clean_exp.replaceAll(key, '0');
          break;
        case 'object':
          clean_exp = clean_exp.replaceAll(key, '{}');
          break;
        case 'array':
          clean_exp = clean_exp.replaceAll(key, '[]');
          break;

        default:
          // clean_exp = clean_exp.replaceAll(key, "''");
          break;
      }
    }
    if (!skip_eval) {
      // try {
      //   eval(clean_exp);
      // } catch (err) {
      //   debugger;
      //   ret.push({
      //     code: "CHK_MSG_PRG_EXP_022",
      //     data: `fields expression error in ${exp.substr(0, 20)}.. in ${path}`,
      //     type: "W",
      //   });
      // }
      var options = { warnings: true };
      var result = UglifyJS.minify(clean_exp, options);

      if (result.error) {
        ret.push({
          code: 'CHK_MSG_PRG_EXP_026',
          data: `expression error ${result.error.message} in ${exp.substr(0, 20)}.. in ${path}`,
          type: doc.studio_meta.not_in_use ? 'W' : 'E',
          category,
          details: result.error,
          source_id: path,
          id: `${doc._id}-${path}`,
        });
      }
      if (result.warnings) {
        ret.push({
          code: 'CHK_MSG_PRG_EXP_028',
          data: `expression warning ${result.warnings[0]} in ${exp.substr(0, 20)}.. in ${path}`,
          type: 'W',
          category,
          details: result.warnings,
          source_id: path,
          id: `${doc._id}-${path}`,
        });
      }
    }
  }

  // @field exist
  // validate js syntax
  // validate spam

  return ret;
};

const find_event_id_in_progs = function (doc, event_id) {
  var ret = [];

  for (let [key, prog] of Object.entries(progs_obj)) {
    if (!prog.progEvents || _.isEmpty(prog.progEvents)) {
      continue;
    }
    //&& prog._id !== doc._id
    for (let event_item of prog.progEvents) {
      if (event_item.data.event_name === event_id) {
        ret.push({
          prog_id: prog._id,
          event_item,
          properties: prog.properties,
        });
      }
    }
  }
  if (!ret.length) return false;
  return ret;
};
const find_xu_tree_id_in_progs = function (doc, xu_tree_id) {
  var ret = [];

  for (let [key, prog] of Object.entries(progs_obj)) {
    if (!prog.progUi || _.isEmpty(prog.progUi)) {
      continue;
    }

    const run_tree = function (node) {
      for (let item of node) {
        if (item.xu_tree_id === xu_tree_id) {
          ret.push({
            prog_id: prog._id,
            item,
            properties: prog.properties,
          });
        }

        if (item.children) {
          run_tree(item.children);
        }
      }
    };
    run_tree(prog.progUi);
  }
  if (!ret.length) return false;
  return ret;
};

const find_trigger_property_value_in_progs = function (doc, triggers_arr, property, value, search_in_doc_prog_only) {
  var ret = [];

  const find_in_triggers = function (prog, workflow) {
    if (!workflow) return;
    try {
      for (let val of workflow) {
        if (triggers_arr.includes(val?.data?.action)) {
          const str = JSON.stringify(val);
          // const regex = new RegExp(`${value}\\b`);
          // const regex = new RegExp(`@${value}\\b`);
          const regex = new RegExp(`\\b${value}\\b`);
          // if (val.data.action === "alter_ui_element") {
          //   debugger;
          // }
          if (value === null || val?.data?.name?.[property] === value || regex.test(str)) {
            if (val?.data?.name?.[property]) {
              ret.push({
                prog_id: prog._id,
                val,
                properties: prog.properties,
                prop_value: val?.data?.name?.[property],
              });
            } else {
              ret.push({
                prog_id: prog._id,
                val,
                properties: prog.properties,
              });
            }
          }
        }

        // check also in call router
      }
    } catch (error) {
      console.error(doc._id, workflow);
    }
  };

  const run_program_search = function (prog) {
    // find in events
    if (prog.progEvents && !_.isEmpty(prog.progEvents)) {
      for (let event_item of prog.progEvents) {
        if (event_item.workflow) {
          find_in_triggers(prog, event_item.workflow);
        }
      }
    }

    // find in fields
    if (prog.progFields && !_.isEmpty(prog.progFields)) {
      for (let field_item of prog.progFields) {
        if (field_item.workflow) {
          find_in_triggers(prog, field_item.workflow);
        }
      }
    }

    // find in ui workflow
    if (prog.progUi && !_.isEmpty(prog.progUi)) {
      const run_tree = function (node) {
        try {
          for (let item of node) {
            if (item.attributes) {
              for (let [key, val] of Object.entries(item.attributes)) {
                if (key.substr(0, 6) === 'xu-on:') {
                  if (!_.isEmpty(val)) {
                    for (let trigger_item of val) {
                      find_in_triggers(prog, trigger_item.workflow);
                    }
                  }
                }
              }
            }

            if (item.children) {
              run_tree(item.children);
            }
          }
        } catch (error) {
          console.error('find in ui workflow', error);
        }
      };
      run_tree(prog.progUi);
    }
  };
  if (!search_in_doc_prog_only) {
    for (let [key, prog] of Object.entries(progs_obj)) {
      run_program_search(prog);
    }
  } else {
    run_program_search(progs_obj?.[doc._id]);
  }

  if (!ret.length) return false;
  return ret;
};

const find_panel_prog_in_progs = function (doc, prog_id, search_in_doc_prog_only) {
  var ret = [];

  const find_in_prog = function (prog) {
    if (_.isEmpty(prog.progUi)) return;

    const run_tree = function (node) {
      for (let item of node) {
        if (item.tagName === 'xu-panel') {
          const str = JSON.stringify(item);
          const regex = new RegExp(`\\b${prog_id}\\b`);
          // const regex = new RegExp(`@${prog_id}\\b`);

          // const exactLength = prog_id.length; // Change this to your desired exact length
          // const regex = new RegExp(`^.{${exactLength}}$`);

          if (prog_id === null || item?.attributes?.program?.prog === prog_id || regex.test(str)) {
            if (item?.attributes?.program?.prog) {
              ret.push({
                prog_id: prog._id,
                item,
                prop_value: item.attributes.program.prog,
              });
            } else {
              ret.push({
                prog_id: prog._id,
                item,
              });
            }
          }
        }

        if (item.children) {
          run_tree(item.children);
        }
      }
    };
    run_tree(prog.progUi);
  };

  if (!search_in_doc_prog_only) {
    for (let [key, prog] of Object.entries(progs_obj)) {
      find_in_prog(prog);
    }
  } else {
    find_in_prog(progs_obj?.[doc._id]);
  }
  if (!ret.length) return false;
  return ret;
};
const find_table_in_progs = function (doc, table_id, search_in_doc_prog_only) {
  var ret = [];

  // for (let [key, prog] of Object.entries(progs_obj)) {
  //   if (
  //     !prog.progDataSource ||
  //     _.isEmpty(prog.progDataSource) ||
  //     prog.progDataSource.dataSourceType !== "table"
  //   ) {
  //     continue;
  //   }

  //   if (
  //     prog.progDataSource.dataSourceTableId === table_id ||
  //     prog.progDataSource?.dataSourceTableIdExp?.includes(table_id)
  //   ) {
  //     ret.push({
  //       prog_id: prog._id,
  //     });
  //   }
  // }

  const find_in_prog = function (prog) {
    if (!prog.progDataSource || _.isEmpty(prog.progDataSource) || prog.progDataSource.dataSourceType !== 'table') {
      return;
    }

    if (prog.progDataSource.dataSourceTableId === table_id || prog.progDataSource?.dataSourceTableIdExp?.includes(table_id)) {
      ret.push({
        prog_id: prog._id,
      });
    }
  };

  if (!search_in_doc_prog_only) {
    for (let [key, prog] of Object.entries(progs_obj)) {
      find_in_prog(prog);
    }
  } else {
    find_in_prog(progs_obj?.[doc._id]);
  }

  if (!ret.length) return false;
  return ret;
};

const find_prog_in_fieldset_fieldValue_progs = function (doc, prog_id, search_in_doc_prog_only) {
  var ret = [];

  const find_in_prog = function (prog) {
    if (!prog.progFields) return;
    for (let field_item of prog.progFields) {
      const str = JSON.stringify(field_item);

      const regex = new RegExp(`\\b${prog_id}\\b`);

      if (prog_id === null || field_item?.props?.fieldValue === prog_id || regex.test(str)) {
        if (field_item?.props?.fieldValue) {
          ret.push({
            prog_id: prog._id,
            field_item,
            properties: prog.properties,
            prop_value: field_item.props.fieldValue,
          });
        } else {
          ret.push({
            prog_id: prog._id,
            field_item,
            properties: prog.properties,
          });
        }
      }
    }
  };

  if (!search_in_doc_prog_only) {
    for (let [key, prog] of Object.entries(progs_obj)) {
      find_in_prog(prog);
    }
  } else {
    find_in_prog(progs_obj?.[doc._id]);
  }
  if (!ret.length) return false;
  return ret;
};