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.

1,562 lines (1,369 loc) • 148 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, z; const PROTECTED_VARS = ['_NULL', '_THIS', '_FOR_KEY', '_FOR_VAL', '_ROWNO', '_ROWID', '_ROWDOC', '_KEY', '_VAL']; const ALL_MENU_TYPE = ['globals', 'component', 'batch', 'get_data', 'set_data', 'alert', 'javascript', 'api', 'table', 'folder']; 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, _z) { 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; z = _z; // if (progs_str.includes("reduce_counter")) { // debugger; // } }; export const check = function (doc) { var ret = []; var dependency_progs = []; // Helper function to add validation errors and continue const addValidationError = (code, data, type = 'E', category = 'document', ref = null, id = null, not_in_use = false) => { const error = { code, data, type, category, }; if (ref) error.ref = ref; if (id) error.id = id; if (not_in_use) error.not_in_use = not_in_use; ret.push(error); }; // Early validation checks if (_.isEmpty(doc)) { addValidationError('CHK_MSG_OBJ_GEN_110', 'doc in empty'); return { check_errors: ret, check_warnings: [], doc, dependency_progs: [], }; } let critical_error = false; // Run structure validation first ret = check_structure(doc); // Only continue if structure validation passed // if (!ret?.length) { if (!critical_error) { doc.studio_meta.not_in_use = false; // Process non-folder items if (doc.properties.menuType !== 'folder') { if (doc.properties.menuType !== 'table') { // Handle deployment dependencies for server environment 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); } } } // Check if program is in use 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; } } } } } // Add program event validation results 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 { // Handle table usage checking if (!hide_not_in_use_check) { if (!find_table_in_progs(doc, doc._id)) { doc.studio_meta.not_in_use = true; } } } } // Run menu type specific validation 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 'folder': // No additional validation for folders break; default: addValidationError('CHK_MSG_GEN_050', `invalid ${doc.properties.menuType} properties.menuType`, 'E', 'prog', doc._id, doc._id); break; } } // Separate errors and warnings 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; }; export const check_structure = function (doc) { var ret = []; // Helper function to add validation errors and continue const addValidationError = (code, data) => { ret.push({ code, data, type: 'E', category: 'document', structure_error: true, }); }; // Early exit conditions that prevent further validation if (!app_obj || !_.isObject(app_obj) || _.isEmpty(app_obj)) { addValidationError('CHK_MSG_OBJ_GEN_002', 'invalid app_obj'); critical_error = true; return ret; } if (!doc || !_.isObject(doc) || _.isEmpty(doc)) { addValidationError('CHK_MSG_OBJ_GEN_004', 'doc is not a object'); critical_error = true; return ret; } try { JSON5.parse(JSON.stringify(doc)); } catch (err) { addValidationError('CHK_MSG_OBJ_GEN_005', 'error parsing JSON doc'); critical_error = true; return ret; } if (z) { const schema = get_zod_schema(doc.properties.menuType); const result = schema.safeParse(doc); if (!result.success) { result.error.errors.forEach((error) => { const msg = `${error.message}: ${error.path}(${error.expected})`; addValidationError('CHK_MSG_OBJ_GEN_000', msg); }); critical_error = true; return ret; } } // Continue validation even with errors if (!doc._id) { addValidationError('CHK_MSG_OBJ_GEN_050', 'missing _id value'); } if (doc?.studio_meta?.source === 'studio ai' && doc?.properties?.menuType && doc._id && doc._id.substr(0, 7) !== new_node_id(doc.properties.menuType, doc.app_id).substr(0, 7)) { addValidationError('CHK_MSG_OBJ_GEN_051', 'invalid _id value (e.g: 5b1_tbl_a953848a71c5)'); } if (!doc._rev) { if (progs_obj?.[doc._id]) { addValidationError('CHK_MSG_OBJ_GEN_052', 'duplicate doc _id'); } } ////////// properties //////////////// if (!doc.properties) { addValidationError('CHK_MSG_OBJ_GEN_100', 'missing properties object'); } else { if (!_.isObject(doc.properties)) { addValidationError('CHK_MSG_OBJ_GEN_102', 'invalid properties object'); } else { if (!doc.properties.menuType) { addValidationError('CHK_MSG_OBJ_GEN_104', 'missing properties.menuType'); } else if (!ALL_MENU_TYPE.includes(doc.properties.menuType)) { addValidationError('CHK_MSG_OBJ_GEN_105', `invalid properties.menuType (valid values: ${ALL_MENU_TYPE.join(',')})`); } if (!doc.properties.menuName) { addValidationError('CHK_MSG_OBJ_GEN_106', 'missing properties.menuName'); } } } ////////// studio_meta //////////////// if (!doc.studio_meta) { addValidationError('CHK_MSG_OBJ_GEN_112', 'missing studio_meta property'); } else { if (!_.isObject(doc.studio_meta)) { addValidationError('CHK_MSG_OBJ_GEN_112', 'invalid studio_meta object'); } else { if (!doc.studio_meta.parentId) { addValidationError('CHK_MSG_OBJ_GEN_114', 'missing studio_meta.parentId value'); } else { let menuType = doc?.properties?.menuType === 'globals' ? 'globals' : doc?.properties?.menuType === 'table' ? 'database' : doc?.properties?.menuType === 'route' ? 'routes' : 'programs'; if (doc.studio_meta.parentId !== menuType && !progs_obj[doc.studio_meta.parentId]) { addValidationError('CHK_MSG_OBJ_GEN_116', 'invalid studio_meta.parentId value'); } } } } const check_progFields = function () { if (!doc.progFields) return; var ids = []; if (!_.isArray(doc.progFields)) { addValidationError('CHK_MSG_OBJ_GEN_402', 'invalid progFields Array property'); return; } const validTypes = ['virtual', 'table', 'expression', 'datasource']; const validFieldTypes = ['string', 'number', 'boolean', 'array', 'object']; for (let val of doc.progFields) { if (!_.isObject(val)) { addValidationError('CHK_MSG_OBJ_GEN_404', `Invalid progFields object `); continue; } if (!val.id) { addValidationError('CHK_MSG_OBJ_GEN_406', 'Missing progFields ID property'); } const fieldId = val.id ? `(${val.id})` : ''; if (val.id) { if (ids.includes(val.id)) { addValidationError('CHK_MSG_OBJ_GEN_408', `Duplicate progFields ID (${val.id})`); continue; } ids.push(val.id); } if (!_.isObject(val.data)) { addValidationError('CHK_MSG_OBJ_GEN_410', `Invalid progFields data object ${fieldId}`); } else { if (!val.data.field_id) { addValidationError('CHK_MSG_OBJ_GEN_412', `Missing progFields field_id property ${fieldId}`); } if (!val.data.type) { addValidationError('CHK_MSG_OBJ_GEN_414', `Missing progFields type property ${fieldId}`); } else if (!validTypes.includes(val.data.type)) { addValidationError('CHK_MSG_OBJ_GEN_415', `Invalid progFields type "${val.data.type}" ${fieldId}. Must be one of: ${validTypes.join(', ')}`); } } if (!_.isObject(val.props)) { addValidationError('CHK_MSG_OBJ_GEN_416', `Invalid progFields props object ${fieldId}`); } else { if (!val.props.fieldType) { addValidationError('CHK_MSG_OBJ_GEN_418', `Missing progFields fieldType property ${fieldId}`); } else if (!validFieldTypes.includes(val.props.fieldType)) { addValidationError('CHK_MSG_OBJ_GEN_419', `Invalid progFields fieldType "${val.props.fieldType}" ${fieldId}. Must be one of: ${validFieldTypes.join(', ')}`); } if (!_.isObject(val.props.propExpressions)) { addValidationError('CHK_MSG_OBJ_GEN_4120', `Invalid progFields propExpressions object ${fieldId}`); } else { // Validate propExpressions only contains allowed properties const allowedProps = ['fieldValue', 'fieldType']; const actualProps = Object.keys(val.props.propExpressions); const invalidProps = actualProps.filter((prop) => !allowedProps.includes(prop)); if (invalidProps.length > 0) { addValidationError('CHK_MSG_OBJ_GEN_4121', `Invalid propExpressions properties: ${invalidProps.join(', ')} ${fieldId}. Only allowed: ${allowedProps.join(', ')}`); } } } check_workflow(val.workflow); } }; const check_progEvents = function () { if (!doc.progEvents) return; var ids = []; if (!_.isArray(doc.progEvents)) { addValidationError('CHK_MSG_OBJ_GEN_502', 'invalid progEvents Array property'); return; } for (let val of doc.progEvents) { if (!_.isObject(val)) { addValidationError('CHK_MSG_OBJ_GEN_504', 'invalid progEvents object property'); continue; } if (!val.id) { addValidationError('CHK_MSG_OBJ_GEN_506', 'missing progEvents.id property'); continue; } if (ids.includes(val.id)) { addValidationError('CHK_MSG_OBJ_GEN_508', `${val.id} id must be unique in progEvents property`); continue; } ids.push(val.id); if (!_.isObject(val.data)) { addValidationError('CHK_MSG_OBJ_GEN_510', 'invalid progEvents.data object property'); continue; } if (typeof val.data.condition === 'undefined') { addValidationError('CHK_MSG_OBJ_GEN_512', 'undefined progEvents.data.condition string property'); } if (typeof val.data.event_name === 'undefined') { addValidationError('CHK_MSG_OBJ_GEN_514', 'undefined progEvents.data.event_name string property'); } if (typeof val.data.properties === 'undefined') { addValidationError('CHK_MSG_OBJ_GEN_516', 'undefined progEvents.data.properties string property'); } if (typeof val.data.type === 'undefined') { addValidationError('CHK_MSG_OBJ_GEN_518', 'undefined progEvents.data.type string property'); } if (!_.isArray(val.data.parameters)) { addValidationError('CHK_MSG_OBJ_GEN_522', 'invalid progEvents.data.parameters Array property'); } if (!_.isObject(val.props)) { addValidationError('CHK_MSG_OBJ_GEN_522', 'invalid progEvents.props Object property'); } check_workflow(val.workflow); } }; const check_workflow = function (workflow) { if (!workflow) return; var ids = []; if (!_.isArray(workflow)) { addValidationError('CHK_MSG_OBJ_GEN_850', 'invalid workflow Array property'); return; } for (let val of workflow) { if (!_.isObject(val)) { addValidationError('CHK_MSG_OBJ_GEN_852', 'invalid workflow item object property'); continue; } if (!val.id) { addValidationError('CHK_MSG_OBJ_GEN_854', 'missing workflow.id property'); continue; } if (ids.includes(val.id)) { addValidationError('CHK_MSG_OBJ_GEN_856', `${val.id} id must be unique in workflow property`); continue; } ids.push(val.id); if (!_.isObject(val.data)) { addValidationError('CHK_MSG_OBJ_GEN_858', 'invalid workflow.data Object property'); } if (!_.isObject(val.props)) { addValidationError('CHK_MSG_OBJ_GEN_860', 'invalid workflow.props item object property'); } } }; const check_progDataSource = function () { if (!doc.progDataSource) return; if (!_.isObject(doc.progDataSource)) { addValidationError('CHK_MSG_OBJ_GEN_602', 'invalid progDataSource Object property'); return; } if (typeof doc.progDataSource.dataSourceType === 'undefined') { addValidationError('CHK_MSG_OBJ_GEN_604', 'undefined progDataSource.dataSourceType string property'); } }; const check_progUi = function () { if (!doc.progUi) return; if (!_.isArray(doc.progUi)) { addValidationError('CHK_MSG_OBJ_GEN_702', 'invalid progUi Array property'); return; } var ids = []; const run_tree = function (node) { for (let item of node) { if (!_.isObject(item)) { addValidationError('CHK_MSG_OBJ_GEN_704', 'invalid progUi item Object property'); continue; } if (!item.id) { addValidationError('CHK_MSG_OBJ_GEN_712', 'missing progUi id item property'); } if (item?.id) { if (item?.type === 'comment') { addValidationError('CHK_MSG_OBJ_GEN_705', `Comment type is not supported in progUi (${item.id})`); continue; } if (item?.type === 'text') { addValidationError('CHK_MSG_OBJ_GEN_706', `Text type is not supported in progUi (${item.id}). Convert to "element" type and add a "text" property instead`); continue; } if (item?.attributes && !_.isObject(item.attributes)) { addValidationError('CHK_MSG_OBJ_GEN_707', `Invalid progUi attributes object property (${item.id})`); } if (typeof item.type === 'undefined') { addValidationError('CHK_MSG_OBJ_GEN_708', `Missing progUi type property (${item.id})`); } if (typeof item.tagName === 'undefined') { addValidationError('CHK_MSG_OBJ_GEN_710', `Missing progUi tagName property (${item.id})`); } if (item.id !== 'root' && doc?.studio_meta?.source === 'studio ai' && !detectCryptoString(item.id.replace('node-', '').replace('ui-', '')).uuid) { addValidationError('CHK_MSG_OBJ_GEN_713', `ProgUi item ID must be in UUID format (${item.id})`); } if (ids.includes(item.id)) { addValidationError('CHK_MSG_OBJ_GEN_714', `Duplicate progUi item ID found (${item.id})`); continue; } ids.push(item.id); } check_workflow(item.workflow); if (item.children) { run_tree(item.children); } } }; run_tree(doc.progUi); }; // Only proceed with menu type validation if we have the required properties if (doc.properties && doc.properties.menuType) { switch (doc.properties.menuType) { case 'table': if (!doc.tableFields) { addValidationError('CHK_MSG_OBJ_GEN_200', 'missing tableFields property'); } else { if (!_.isArray(doc.tableFields)) { addValidationError('CHK_MSG_OBJ_GEN_202', 'invalid tableFields Array property'); } else { var ids = []; for (let val of doc.tableFields) { if (!_.isObject(val)) { addValidationError('CHK_MSG_OBJ_GEN_204', 'invalid tableFields object property'); continue; } if (!val.id) { addValidationError('CHK_MSG_OBJ_GEN_206', 'missing tableFields.id property'); continue; } if (ids.includes(val.id)) { addValidationError('CHK_MSG_OBJ_GEN_208', `${val.id} id must be unique in tableFields property`); continue; } ids.push(val.id); if (!_.isObject(val.data)) { addValidationError('CHK_MSG_OBJ_GEN_210', 'invalid tableFields.data object property'); continue; } if (!val.data.field_id) { addValidationError('CHK_MSG_OBJ_GEN_212', 'invalid tableFields.data.field_id property'); } if (!_.isObject(val.props)) { addValidationError('CHK_MSG_OBJ_GEN_214', 'invalid tableFields.props object property'); continue; } if (!val.props.fieldType) { addValidationError('CHK_MSG_OBJ_GEN_216', 'invalid tableFields.props.fieldType property'); } } } } // tableIndexes validation if (!doc.tableIndexes) { addValidationError('CHK_MSG_OBJ_GEN_302', 'missing tableIndexes property'); } else if (!_.isArray(doc.tableIndexes)) { addValidationError('CHK_MSG_OBJ_GEN_302', 'invalid tableIndexes Array property'); } else { var index_ids = []; for (let val of doc.tableIndexes) { if (!_.isObject(val)) { addValidationError('CHK_MSG_OBJ_GEN_304', 'invalid tableIndexes object property'); continue; } if (!val.id) { addValidationError('CHK_MSG_OBJ_GEN_306', 'missing tableIndexes.id property'); continue; } if (index_ids.includes(val.id)) { addValidationError('CHK_MSG_OBJ_GEN_308', `${val.id} id must be unique in tableIndexes property`); continue; } index_ids.push(val.id); if (!_.isObject(val.data)) { addValidationError('CHK_MSG_OBJ_GEN_310', 'invalid tableIndexes.data object property'); continue; } if (!val.data.name) { addValidationError('CHK_MSG_OBJ_GEN_312', 'invalid tableIndexes.data.name property'); } if (!_.isArray(val.data.keys)) { addValidationError('CHK_MSG_OBJ_GEN_312', '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 (doc.properties && typeof doc.properties.uiFramework === 'undefined') { addValidationError('CHK_MSG_OBJ_GEN_120', 'undefined properties.uiFramework string property'); } if (doc.properties && typeof doc.properties.renderType === 'undefined') { addValidationError('CHK_MSG_OBJ_GEN_122', 'undefined properties.renderType string property'); } break; case 'alert': break; case 'javascript': break; case 'folder': break; default: break; } } 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: e