 'use strict';

if (typeof IS_DOCKER === 'undefined' || typeof IS_PROCESS_SERVER === 'undefined') {
  var SESSION_OBJ = {};
  var DOCS_OBJ = {};
}

var glb = {};
var func = {};
func.UI = {};
func.GLB = {};
func.mobile = {};
func.runtime = {};
func.runtime.bind = {};
func.runtime.program = {};
func.runtime.resources = {};
func.runtime.render = {};
func.runtime.session = {};
func.runtime.workers = {};
func.runtime.ui = {};
func.runtime.widgets = {};
glb.IS_STUDIO = null;

// Lodash replacement utilities
var xu_isEmpty = function (val) {
  if (val == null) return true;
  if (typeof val === 'boolean' || typeof val === 'number') return !val;
  if (typeof val === 'string' || Array.isArray(val)) return val.length === 0;
  if (val instanceof Map || val instanceof Set) return val.size === 0;
  return Object.keys(val).length === 0;
};

var xu_isEqual = function (a, b) {
  if (a === b) return true;
  if (a == null || b == null) return a === b;
  if (typeof a !== typeof b) return false;
  if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
  if (typeof a !== 'object') return false;
  var keysA = Object.keys(a);
  var keysB = Object.keys(b);
  if (keysA.length !== keysB.length) return false;
  for (var i = 0; i < keysA.length; i++) {
    if (!Object.prototype.hasOwnProperty.call(b, keysA[i]) || !xu_isEqual(a[keysA[i]], b[keysA[i]])) return false;
  }
  return true;
};

var xu_get = function (obj, path, defaultVal) {
  var keys = typeof path === 'string' ? path.split('.') : path;
  var result = obj;
  for (var i = 0; i < keys.length; i++) {
    if (result == null) return defaultVal;
    result = result[keys[i]];
  }
  return result === undefined ? defaultVal : result;
};

var xu_set = function (obj, path, value) {
  var keys = typeof path === 'string' ? path.split('.') : path;
  var current = obj;
  for (var i = 0; i < keys.length - 1; i++) {
    if (current[keys[i]] == null) current[keys[i]] = {};
    current = current[keys[i]];
  }
  current[keys[keys.length - 1]] = value;
  return obj;
};

var xu_clone = function (value) {
  if (Array.isArray(value)) return value.slice();
  if (value && typeof value === 'object') return { ...value };
  return value;
};

var xu_cloneDeep = function (value) {
  if (typeof structuredClone === 'function') {
    try {
      return structuredClone(value);
    } catch (error) {}
  }

  if (Array.isArray(value)) {
    return value.map(function (item) {
      return xu_cloneDeep(item);
    });
  }

  if (value && typeof value === 'object') {
    var ret = {};
    Object.keys(value).forEach(function (key) {
      ret[key] = xu_cloneDeep(value[key]);
    });
    return ret;
  }

  return value;
};

var xu_map = function (collection, iteratee) {
  if (!collection) return [];

  if (Array.isArray(collection)) {
    return collection.map(function (value, index) {
      return iteratee ? iteratee(value, index) : value;
    });
  }

  return Object.keys(collection).map(function (key) {
    return iteratee ? iteratee(collection[key], key) : collection[key];
  });
};

var xu_forEach = function (collection, iteratee) {
  if (!collection || typeof iteratee !== 'function') return collection;

  if (Array.isArray(collection)) {
    collection.forEach(function (value, index) {
      iteratee(value, index);
    });
    return collection;
  }

  Object.keys(collection).forEach(function (key) {
    iteratee(collection[key], key);
  });
  return collection;
};

var xu_find = function (collection, predicate) {
  if (!collection || typeof predicate !== 'function') return undefined;

  var values = Array.isArray(collection)
    ? collection
    : Object.keys(collection).map(function (key) {
        return collection[key];
      });

  for (var i = 0; i < values.length; i++) {
    if (predicate(values[i], i)) return values[i];
  }
};

var xu_findIndex = function (collection, predicate) {
  if (!Array.isArray(collection) || typeof predicate !== 'function') return -1;

  for (var i = 0; i < collection.length; i++) {
    if (predicate(collection[i], i)) return i;
  }
  return -1;
};

var xu_reduce = function (collection, iteratee, accumulator) {
  if (!collection || typeof iteratee !== 'function') return accumulator;

  var keys = Array.isArray(collection) ? collection.map(function (_value, index) { return index; }) : Object.keys(collection);
  var has_accumulator = arguments.length > 2;
  var result = accumulator;

  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var value = Array.isArray(collection) ? collection[key] : collection[key];

    if (!has_accumulator) {
      result = value;
      has_accumulator = true;
      continue;
    }

    result = iteratee(result, value, key);
  }

  return result;
};

var xu_debounce = function (callback, wait) {
  var timeout_id = null;
  return function () {
    var args = arguments;
    var context = this;
    clearTimeout(timeout_id);
    timeout_id = setTimeout(function () {
      callback.apply(context, args);
    }, wait || 0);
  };
};

var xu_toStringSafe = function (value) {
  if (typeof value === 'string') return value;
  if (value == null) return '';
  return String(value);
};

var xu_some = function (collection, predicate) {
  if (!collection || typeof predicate !== 'function') return false;

  var values = Array.isArray(collection)
    ? collection
    : Object.keys(collection).map(function (key) {
        return collection[key];
      });

  for (var i = 0; i < values.length; i++) {
    if (predicate(values[i], i)) return true;
  }
  return false;
};

var xu_has = function (obj, key) {
  return !!obj && Object.prototype.hasOwnProperty.call(obj, key);
};

var xu_runtime_global = typeof globalThis !== 'undefined' ? globalThis : {};

if (typeof xu_runtime_global._ === 'undefined') {
  xu_runtime_global._ = {
    clone: xu_clone,
    cloneDeep: xu_cloneDeep,
    debounce: xu_debounce,
    each: xu_forEach,
    find: xu_find,
    findIndex: xu_findIndex,
    forEach: xu_forEach,
    get: xu_get,
    has: xu_has,
    isArray: Array.isArray,
    isEmpty: xu_isEmpty,
    map: xu_map,
    reduce: xu_reduce,
    some: xu_some,
    toString: xu_toStringSafe,
  };
}

if (typeof _ === 'undefined') {
  var _ = xu_runtime_global._;
}

var PROJECT_OBJ = {};

var APP_OBJ = {};
var SESSION_ID = null;
var EXP_BUSY = false;

glb.PROTECTED_VARS = ['_NULL', '_THIS', '_FOR_KEY', '_FOR_VAL', '_ROWNO', '_ROWID', '_ROWDOC', '_KEY', '_VAL'];

// glb.newRecord = 999999;

func.common = {};
func.runtime.platform = {
  get_global: function (name) {
    try {
      if (typeof globalThis === 'undefined') {
        return null;
      }
      return globalThis?.[name] || null;
    } catch (error) {
      return null;
    }
  },
  has_window: function () {
    return !!func.runtime.platform.get_window();
  },
  has_document: function () {
    return !!func.runtime.platform.get_document();
  },
  get_window: function () {
    return func.runtime.platform.get_global('window');
  },
  get_document: function () {
    return func.runtime.platform.get_global('document');
  },
  get_location: function () {
    const win = func.runtime.platform.get_window();
    return win?.location || null;
  },
  get_navigator: function () {
    const win = func.runtime.platform.get_window();
    if (win?.navigator) {
      return win.navigator;
    }
    return func.runtime.platform.get_global('navi' + 'gator');
  },
  is_html_element: function (value) {
    const html_element = func.runtime.platform.get_global('HTML' + 'Element');
    if (typeof html_element !== 'function') {
      return false;
    }
    return value instanceof html_element;
  },
  get_storage: function (type) {
    const win = func.runtime.platform.get_window();
    const storage_key = type === 'session' ? 'session' + 'Storage' : 'local' + 'Storage';
    try {
      if (!win) {
        return null;
      }
      return win?.[storage_key] || null;
    } catch (error) {
      return null;
    }
  },
  get_storage_item: function (key, type) {
    const storage = func.runtime.platform.get_storage(type);
    if (!storage) {
      return null;
    }
    try {
      return storage.getItem(key);
    } catch (error) {
      return null;
    }
  },
  set_storage_item: function (key, value, type) {
    const storage = func.runtime.platform.get_storage(type);
    if (!storage) {
      return false;
    }
    try {
      storage.setItem(key, value);
      return true;
    } catch (error) {
      return false;
    }
  },
  get_cookie_item: function (key) {
    if (!key) {
      return null;
    }
    const doc = func.runtime.platform.get_document();
    const cookie_string = doc?.cookie;
    if (!cookie_string) {
      return null;
    }
    const cookie_entry = cookie_string.split('; ').find(function (cookie) {
      return cookie.startsWith(key + '=');
    });
    if (!cookie_entry) {
      return null;
    }
    return cookie_entry.split('=').slice(1).join('=') || null;
  },
  get_url_href: function () {
    return func.runtime.platform.get_location()?.href || '';
  },
  get_url_search: function () {
    return func.runtime.platform.get_location()?.search || '';
  },
  get_url_hash: function () {
    return func.runtime.platform.get_location()?.hash || '';
  },
  get_host: function () {
    return func.runtime.platform.get_location()?.host || '';
  },
  get_hostname: function () {
    return func.runtime.platform.get_location()?.hostname || '';
  },
  get_device_uuid: function () {
    const win = func.runtime.platform.get_window();
    return win?.device?.uuid || null;
  },
  get_device_name: function () {
    const win = func.runtime.platform.get_window();
    return win?.device?.name || null;
  },
  get_inner_size: function () {
    const win = func.runtime.platform.get_window();
    return {
      width: win?.innerWidth || 0,
      height: win?.innerHeight || 0,
    };
  },
  add_window_listener: function (name, handler) {
    const win = func.runtime.platform.get_window();
    if (!win?.addEventListener) {
      return false;
    }
    win.addEventListener(name, handler);
    return true;
  },
  dispatch_body_event: function (event) {
    const doc = func.runtime.platform.get_document();
    if (!doc?.body?.dispatchEvent) {
      return false;
    }
    doc.body.dispatchEvent(event);
    return true;
  },
  reload_top_window: function () {
    const win = func.runtime.platform.get_window();
    if (!win?.top?.location?.reload) {
      return false;
    }
    win.top.location.reload();
    return true;
  },
  get_service_worker: function () {
    const nav = func.runtime.platform.get_navigator();
    return nav?.serviceWorker || null;
  },
  has_service_worker: function () {
    return !!func.runtime.platform.get_service_worker();
  },
  register_service_worker: function (script_url) {
    const service_worker = func.runtime.platform.get_service_worker();
    if (!service_worker?.register) {
      return Promise.reject(new Error('serviceWorker is not available'));
    }
    return service_worker.register(script_url);
  },
  add_service_worker_listener: function (name, handler) {
    const service_worker = func.runtime.platform.get_service_worker();
    if (!service_worker?.addEventListener) {
      return false;
    }
    service_worker.addEventListener(name, handler);
    return true;
  },
};

// ── Platform-agnostic event bus ──
// Works in browser, worker, and Node environments.
// In browser, bridge DOM events into this bus so core code never touches $(document) directly.
func.runtime.platform._event_bus = {};
func.runtime.platform.on = function (name, handler) {
  if (!func.runtime.platform._event_bus[name]) {
    func.runtime.platform._event_bus[name] = [];
  }
  func.runtime.platform._event_bus[name].push(handler);
};
func.runtime.platform.off = function (name, handler) {
  const handlers = func.runtime.platform._event_bus[name];
  if (!handlers) return;
  if (!handler) {
    delete func.runtime.platform._event_bus[name];
    return;
  }
  const index = handlers.indexOf(handler);
  if (index !== -1) {
    handlers.splice(index, 1);
  }
};
func.runtime.platform._emitting = {};
func.runtime.platform.emit = function (name, data) {
  // re-entrancy guard: prevent infinite loops when DOM bridge triggers the same event
  if (func.runtime.platform._emitting[name]) return;
  func.runtime.platform._emitting[name] = true;
  try {
    const handlers = func.runtime.platform._event_bus[name];
    if (handlers) {
      for (let i = 0; i < handlers.length; i++) {
        handlers[i](data);
      }
    }
    if (typeof func.runtime.platform.dispatch_document_event === 'function') {
      func.runtime.platform.dispatch_document_event(name, data);
    }
  } finally {
    func.runtime.platform._emitting[name] = false;
  }
};

// ── Platform helpers for DOM-independent resource loading ──
func.runtime.platform.apply_element_attributes = function (node, attributes, excluded_keys = []) {
  if (!node?.setAttribute || !attributes) {
    return node;
  }

  const excluded = new Set(excluded_keys || []);
  const attr_keys = Object.keys(attributes);
  for (let index = 0; index < attr_keys.length; index++) {
    const key = attr_keys[index];
    if (!key || excluded.has(key)) {
      continue;
    }

    const value = attributes[key];
    if (value === false || typeof value === 'undefined') {
      continue;
    }

    node.setAttribute(key, value === null ? '' : `${value}`);
  }

  return node;
};
func.runtime.platform.load_script = function (url, type, callback, attributes) {
  const normalized_url = typeof url === 'string' ? url.trim() : '';
  if (!normalized_url || normalized_url === 'undefined' || normalized_url === 'null') {
    if (callback) {
      callback();
    }
    return null;
  }
  const doc = func.runtime.platform.get_document();
  if (!doc?.createElement || !doc?.head?.appendChild) {
    if (callback) {
      callback();
    }
    return;
  }
  const find_existing_script = function () {
    const asset_key = attributes?.['data-xuda-asset-key'];
    const scripts = doc.querySelectorAll ? Array.from(doc.querySelectorAll('script')) : [];
    return scripts.find(function (script) {
      if (asset_key && script.getAttribute('data-xuda-asset-key') === asset_key) {
        return true;
      }
      return script.getAttribute('src') === normalized_url;
    }) || null;
  };

  const existing_script = find_existing_script();
  if (existing_script) {
    if (callback) {
      if (existing_script.getAttribute('data-xuda-loaded') === 'true' || !url) {
        callback();
      } else {
        existing_script.addEventListener('load', callback, { once: true });
        existing_script.addEventListener('error', callback, { once: true });
      }
    }
    return existing_script;
  }

  const script = doc.createElement('script');
  script.src = normalized_url;
  if (type) script.type = type;
  func.runtime.platform.apply_element_attributes(script, attributes, ['src', 'type']);
  script.onload = function () {
    script.setAttribute('data-xuda-loaded', 'true');
    if (callback) {
      callback();
    }
  };
  script.onerror = function () {
    if (callback) {
      callback();
    }
  };
  doc.head.appendChild(script);
  return script;
};
func.runtime.platform.load_css = function (href, attributes) {
  const normalized_href = typeof href === 'string' ? href.trim() : '';
  if (!normalized_href || normalized_href === 'undefined' || normalized_href === 'null') {
    return null;
  }
  const doc = func.runtime.platform.get_document();
  if (!doc?.createElement || !doc?.head) {
    return;
  }
  try {
    const asset_key = attributes?.['data-xuda-asset-key'];
    const existing_links = doc.querySelectorAll ? Array.from(doc.querySelectorAll('link')) : [];
    const existing = existing_links.find(function (link) {
      if (asset_key && link.getAttribute('data-xuda-asset-key') === asset_key) {
        return true;
      }
      return link.getAttribute('href') === normalized_href;
    });
    if (existing) return existing;
  } catch (err) {
    return;
  }
  const link = doc.createElement('link');
  link.rel = 'stylesheet';
  link.type = 'text/css';
  link.href = normalized_href;
  func.runtime.platform.apply_element_attributes(link, attributes, ['href']);
  doc.head.insertBefore(link, doc.head.firstChild);
  return link;
};
func.runtime.platform.remove_js_css = function (filename, filetype) {
  const doc = func.runtime.platform.get_document();
  if (!doc?.getElementsByTagName) return;
  const tagName = filetype === 'js' ? 'script' : filetype === 'css' ? 'link' : 'none';
  const attr = filetype === 'js' ? 'src' : filetype === 'css' ? 'href' : 'none';
  const elements = doc.getElementsByTagName(tagName);
  for (let i = elements.length - 1; i >= 0; i--) {
    if (elements[i] && elements[i].getAttribute(attr) != null && elements[i].getAttribute(attr).indexOf(filename) !== -1) {
      elements[i].parentNode.removeChild(elements[i]);
    }
  }
};
func.runtime.platform.inject_css = function (cssText) {
  const doc = func.runtime.platform.get_document();
  if (!doc?.createElement || !doc?.head?.appendChild || !cssText) return;
  const style = doc.createElement('style');
  style.type = 'text/css';
  style.textContent = cssText;
  doc.head.appendChild(style);
};
func.runtime.platform.set_title = function (title) {
  const doc = func.runtime.platform.get_document();
  if (doc) {
    doc.title = title;
  }
};
func.runtime.platform.set_cursor = function (element, cursor) {
  const node = func.runtime.ui?.get_first_node ? func.runtime.ui.get_first_node(element) : element;
  if (node?.style) {
    node.style.cursor = cursor;
  }
};
func.runtime.program.normalize_doc_for_runtime = function (doc) {
  if (!doc || doc.__xudaRuntimeNormalized || !Array.isArray(doc.progUi) || !doc.progUi.length) {
    return doc;
  }

  const normalize_tag_name = function (tag_name) {
    return `${tag_name || ''}`.trim().toLowerCase();
  };
  const merge_attributes = function (target, source) {
    const merged = { ...(target || {}) };
    const source_attributes = source || {};
    const keys = Object.keys(source_attributes);

    for (let index = 0; index < keys.length; index++) {
      const key = keys[index];
      const value = source_attributes[key];
      if (typeof value === 'undefined') {
        continue;
      }

      if (key === 'class' && merged.class && value) {
        const next_value = `${merged.class} ${value}`.trim();
        merged.class = Array.from(new Set(next_value.split(/\s+/).filter(Boolean))).join(' ');
        continue;
      }

      if (key === 'style' && merged.style && value) {
        merged.style = `${merged.style}; ${value}`.trim();
        continue;
      }

      if (typeof merged[key] === 'undefined') {
        merged[key] = value;
      }
    }

    return merged;
  };
  const normalize_nodes = function (nodes, state) {
    const normalized_nodes = [];

    for (let index = 0; index < (nodes || []).length; index++) {
      const node = nodes[index];
      if (!node || typeof node !== 'object') {
        continue;
      }

      const tag_name = normalize_tag_name(node.tagName);

      if (tag_name === '!doctype') {
        state.changed = true;
        continue;
      }

      if (tag_name === 'html') {
        state.changed = true;
        state.root_attributes = merge_attributes(state.root_attributes, node.attributes);
        normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
        continue;
      }

      if (tag_name === 'head') {
        state.changed = true;
        normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
        continue;
      }

      if (tag_name === 'body') {
        state.changed = true;
        state.root_attributes = merge_attributes(state.root_attributes, node.attributes);
        normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
        continue;
      }

      let next_node = node;
      if (Array.isArray(node.children) && node.children.length) {
        const next_children = normalize_nodes(node.children, state);
        if (next_children !== node.children) {
          next_node = {
            ...node,
            children: next_children,
          };
          state.changed = true;
        }
      }

      normalized_nodes.push(next_node);
    }

    return normalized_nodes;
  };

  const [root_node, ...extra_nodes] = doc.progUi;
  if (!root_node || typeof root_node !== 'object') {
    return doc;
  }

  const state = {
    changed: false,
    root_attributes: {},
  };

  const normalized_children = normalize_nodes([...(root_node.children || []), ...extra_nodes], state);
  const merged_attributes = merge_attributes(root_node.attributes, state.root_attributes);

  if (!state.changed && !Object.keys(state.root_attributes).length) {
    doc.__xudaRuntimeNormalized = true;
    return doc;
  }

  return {
    ...doc,
    __xudaRuntimeNormalized: true,
    progUi: [
      {
        ...root_node,
        attributes: merged_attributes,
        children: normalized_children,
      },
    ],
  };
};

func.runtime.env = {
  get_url_params: function () {
    const search = func.runtime.platform.get_url_search();
    return new URLSearchParams(search);
  },
  get_url_parameters_object: function () {
    const search_params = func.runtime.env.get_url_params();
    const parameters = {};
    for (const [key, value] of search_params.entries()) {
      parameters[key] = value;
    }
    return parameters;
  },
  get_default_session_value: function (key) {
    switch (key) {
      case 'domain':
        return func.runtime.platform.get_host();
      case 'engine_mode':
        return 'miniapp';
      case 'app_id':
        return 'unknown';
      default:
        return null;
    }
  },
};
func.runtime.session.create_tab_id = function () {
  const session_storage = func.runtime.platform.get_storage('session');
  const local_storage = func.runtime.platform.get_storage('local');
  var page_tab_id = session_storage?.getItem('tabID');
  if (page_tab_id == null) {
    var local_tab_id = local_storage?.getItem('tabID');
    page_tab_id = local_tab_id == null ? 1 : Number(local_tab_id) + 1;
    func.runtime.platform.set_storage_item('tabID', page_tab_id, 'local');
    func.runtime.platform.set_storage_item('tabID', page_tab_id, 'session');
  }
  return page_tab_id;
};
func.runtime.session.get_fingerprint = function (components, instance_id) {
  const device_uuid = func.runtime.platform.get_device_uuid();
  if (func.utils.get_device() && device_uuid) {
    if (instance_id) {
      return instance_id + device_uuid;
    }
    return device_uuid;
  }
  const fingerprint_id = Fingerprint2.x64hash128(
    components
      .map(function (pair) {
        return pair.value;
      })
      .join(),
    31,
  );

  if (instance_id) {
    return instance_id + fingerprint_id + func.runtime.session.create_tab_id();
  }
  return fingerprint_id;
};
func.runtime.session.create_state = function (SESSION_ID, options) {
  const runtime_host = func.runtime.platform.get_host();
  SESSION_OBJ[SESSION_ID] = {
    JOB_NO: 1000,
    opt: options.opt,
    root_element: options.root_element,
    worker_type: options.worker_type,
    api_callback: options.api_callback,
    CODE_BUNDLE: options.code_bundle,
    SLIM_BUNDLE: options.slim_bundle,
    WORKER_OBJ: {
      jobs: [],
      num: 1000,
      stat: null,
    },
    DS_GLB: {},
    SYS_GLOBAL_OBJ_FIREBASE_AUTH_INFO: {
      token: '',
      first_name: '',
      last_name: '',
      email: '',
      user_id: '',
      picture: '',
      verified_email: '',
      locale: '',
      error_code: '',
      error_msg: '',
    },
    SYS_GLOBAL_OBJ_CLIENT_INFO: {
      fingerprint: '',
      device: '',
      user_agent: '',
      browser_version: '',
      browser_name: '',
      engine_version: '',
      client_ip: '',
      engine_name: '',
      os_name: '',
      os_version: '',
      device_model: '',
      device_vendor: '',
      device_type: '',
      screen_current_resolution_x: '',
      screen_current_resolution_y: '',
      screen_available_resolution_x: '',
      screen_available_resolution_y: '',
      language: '',
      time_zone: '',
      cpu_architecture: '',
      uuid: '',
    },
    PUSH_NOTIFICATION_GRANTED: null,
    FIREBASE_TOKEN_ID: null,
    USR_OBJ: {},
    debug_js: null,
    DS_UI_EVENTS_GLB: {},
    host: runtime_host,
    req_id: 0,
    build_info: {},
    CACHE_REQ: {},
    url_params: {
      ...func.common.getParametersFromUrl(),
      ...options.url_params,
    },
  };
  func.runtime.workers.ensure_registry(SESSION_ID);
  return SESSION_OBJ[SESSION_ID];
};
func.runtime.session.is_slim = function (SESSION_ID) {
  const session = typeof SESSION_ID === 'undefined' || SESSION_ID === null ? null : SESSION_OBJ?.[SESSION_ID];
  if (session && typeof session.SLIM_BUNDLE !== 'undefined') {
    return !!session.SLIM_BUNDLE;
  }
  return !!glb.SLIM_BUNDLE;
};
func.runtime.session.set_default_value = function (_session, key, value) {
  _session[key] = value || func.runtime.env.get_default_session_value(key);
  return _session[key];
};
func.runtime.session.populate_client_info = function (_session, components) {
  const _client_info = _session.SYS_GLOBAL_OBJ_CLIENT_INFO;
  const platform = func.runtime.platform;
  const { engine_mode } = _session;

  _client_info.fingerprint = func.runtime.session.get_fingerprint(components);

  if (engine_mode === 'live_preview') {
    const inner_size = platform.get_inner_size();
    _client_info.screen_current_resolution_x = inner_size.width;
    _client_info.screen_current_resolution_y = inner_size.height;
    _client_info.screen_available_resolution_x = inner_size.width;
    _client_info.screen_available_resolution_y = inner_size.height;
  } else {
    _client_info.screen_current_resolution_x = components[6].value[0];
    _client_info.screen_current_resolution_y = components[6].value[1];
    _client_info.screen_available_resolution_x = components[7].value[0];
    _client_info.screen_available_resolution_y = components[7].value[1];
  }

  const client = new ClientJS();
  _client_info.device = func.utils.get_device();

  const browser_data = client.getBrowserData();
  _client_info.user_agent = browser_data.ua;
  _client_info.browser_version = browser_data.browser.name;
  _client_info.browser_name = browser_data.browser.version;
  _client_info.engine_version = browser_data.engine.name;
  _client_info.engine_name = browser_data.engine.version;
  _client_info.os_name = browser_data.os.name;
  _client_info.os_version = browser_data.os.version;
  _client_info.device_model = browser_data.device.name;
  _client_info.device_vendor = browser_data.device.name;
  _client_info.device_type = browser_data.device.name;
  _client_info.language = client.getLanguage();
  _client_info.time_zone = client.getTimeZone();
  _client_info.cpu_architecture = browser_data.cpu.architecture;

  if (['android', 'ios', 'windows', 'macos', 'linux', 'live_preview'].includes(engine_mode) && func.utils.get_device()) {
    _client_info.uuid = platform.get_device_uuid();
    const device_name = platform.get_device_name();
    if (device_name) {
      _client_info.device_name = device_name;
    }
  }
  return _client_info;
};
func.runtime.workers.ensure_registry = function (SESSION_ID) {
  if (!WEB_WORKER[SESSION_ID]) {
    WEB_WORKER[SESSION_ID] = {};
  }
  return WEB_WORKER[SESSION_ID];
};
func.runtime.workers.get_registry_entry = function (SESSION_ID, worker_id) {
  return func.runtime.workers.ensure_registry(SESSION_ID)?.[worker_id] || null;
};
func.runtime.workers.set_registry_entry = function (SESSION_ID, worker_id, entry) {
  const worker_registry = func.runtime.workers.ensure_registry(SESSION_ID);
  worker_registry[worker_id] = entry;
  return worker_registry[worker_id];
};
func.runtime.workers.build_worker_name = function (glb_worker_type, session, prog_obj, worker_id, build_id) {
  return (
    `${typeof session.SLIM_BUNDLE === 'undefined' || !session.SLIM_BUNDLE ? '' : 'Slim '}${prog_obj.menuName} worker` +
    ' ' +
    glb_worker_type +
    ': #' +
    worker_id.toString() +
    ' ' +
    (build_id || '') +
    ' ' +
    session.domain
  );
};
func.runtime.workers.is_server_transport = function (session) {
  return !!(RUNTIME_SERVER_WEBSOCKET && RUNTIME_SERVER_WEBSOCKET_CONNECTED && (!session.opt.app_computing_mode || session.opt.app_computing_mode === 'server'));
};
func.runtime.workers.send_message = function (SESSION_ID, worker_id, session, msg, process_pid) {
  const registry_entry = func.runtime.workers.get_registry_entry(SESSION_ID, worker_id);
  if (!registry_entry?.worker) {
    return false;
  }

  if (func.runtime.workers.is_server_transport(session)) {
    if (process_pid) {
      msg.process_pid = process_pid;
    }
    registry_entry.worker.emit('message', msg);
    return true;
  }

  registry_entry.worker.postMessage(msg);
  return true;
};
func.runtime.workers.set_promise = function (SESSION_ID, worker_id, promise_queue_id, value) {
  const registry_entry = func.runtime.workers.get_registry_entry(SESSION_ID, worker_id);
  if (!registry_entry) {
    return null;
  }
  registry_entry.promise_queue[promise_queue_id] = value;
  return registry_entry.promise_queue[promise_queue_id];
};
func.runtime.workers.get_promise = function (SESSION_ID, worker_id, promise_queue_id) {
  const registry_entry = func.runtime.workers.get_registry_entry(SESSION_ID, worker_id);
  if (!registry_entry) {
    return null;
  }
  return registry_entry.promise_queue[promise_queue_id];
};
func.runtime.workers.delete_promise = function (SESSION_ID, worker_id, promise_queue_id) {
  const registry_entry = func.runtime.workers.get_registry_entry(SESSION_ID, worker_id);
  if (!registry_entry?.promise_queue) {
    return false;
  }
  delete registry_entry.promise_queue[promise_queue_id];
  return true;
};
func.runtime.render.clone_runtime_options = function (value) {
  if (typeof structuredClone === 'function') {
    try {
      return structuredClone(value);
    } catch (_) {}
  }

  if (Array.isArray(value)) {
    return value.map(function (item) {
      return func.runtime.render.clone_runtime_options(item);
    });
  }

  if (value && typeof value === 'object') {
    const cloned = {};
    const keys = Object.keys(value);
    for (let index = 0; index < keys.length; index++) {
      const key = keys[index];
      cloned[key] = func.runtime.render.clone_runtime_options(value[key]);
    }
    return cloned;
  }

  return value;
};
func.runtime.render.normalize_runtime_bootstrap = function (raw_options = {}) {
  const options = raw_options || {};
  let app_computing_mode = options.app_computing_mode || '';
  let app_render_mode = options.app_render_mode || '';
  let app_client_activation = options.app_client_activation || '';
  let ssr_payload = options.ssr_payload || null;

  if (typeof ssr_payload === 'string') {
    try {
      ssr_payload = JSON.parse(ssr_payload);
    } catch (_) {
      ssr_payload = null;
    }
  }

  if (ssr_payload && typeof ssr_payload === 'object') {
    ssr_payload = func.runtime.render.clone_runtime_options(ssr_payload);
  }

  if (!app_computing_mode) {
    if (app_render_mode === 'ssr_first_page' || app_render_mode === 'ssr_full') {
      app_computing_mode = 'server';
    } else {
      app_computing_mode = 'main';
    }
  }

  switch (app_computing_mode) {
    case 'main':
      app_render_mode = 'csr';
      app_client_activation = 'none';
      break;

    case 'worker':
      app_render_mode = 'csr';
      app_client_activation = 'none';
      break;

    default:
      app_computing_mode = 'server';
      if (app_render_mode !== 'ssr_full') {
        app_render_mode = 'ssr_first_page';
      }
      app_client_activation = app_render_mode === 'ssr_full' ? 'hydrate' : 'takeover';
      break;
  }

  if (ssr_payload && typeof ssr_payload === 'object') {
    if (!ssr_payload.app_render_mode) {
      ssr_payload.app_render_mode = app_render_mode;
    }
    if (!ssr_payload.app_client_activation) {
      ssr_payload.app_client_activation = app_client_activation;
    }
    if (!ssr_payload.app_computing_mode) {
      ssr_payload.app_computing_mode = app_computing_mode;
    }
  }

  return {
    app_computing_mode,
    app_render_mode,
    app_client_activation,
    ssr_payload,
  };
};
func.runtime.render.apply_runtime_bootstrap_defaults = function (target = {}) {
  const normalized = func.runtime.render.normalize_runtime_bootstrap(target);
  target.app_computing_mode = normalized.app_computing_mode;
  target.app_render_mode = normalized.app_render_mode;
  target.app_client_activation = normalized.app_client_activation;
  target.ssr_payload = normalized.ssr_payload;
  return normalized;
};
func.runtime.render.is_server_render_mode = function (target = {}) {
  const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
  return normalized.app_computing_mode === 'server' && normalized.app_render_mode !== 'csr';
};
func.runtime.render.is_takeover_mode = function (target = {}) {
  const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
  return normalized.app_client_activation === 'takeover';
};
func.runtime.render.is_hydration_mode = function (target = {}) {
  const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
  return normalized.app_client_activation === 'hydrate';
};
func.runtime.render.get_ssr_payload = function (target = {}) {
  if (target?.opt?.ssr_payload) {
    return target.opt.ssr_payload;
  }
  if (target?.ssr_payload) {
    return target.ssr_payload;
  }
  const win = func.runtime.platform.get_window();
  return win?.__XUDA_SSR__ || null;
};
func.runtime.render.should_use_ssr_payload = function (SESSION_ID, paramsP) {
  const session = SESSION_OBJ?.[SESSION_ID];
  const payload = func.runtime.render.get_ssr_payload(session);
  if (!payload || payload._consumed) {
    return false;
  }
  if (paramsP?.prog_id && payload.prog_id && payload.prog_id !== paramsP.prog_id) {
    return false;
  }
  return true;
};
func.runtime.render.mark_ssr_payload_consumed = function (SESSION_ID) {
  const session = SESSION_OBJ?.[SESSION_ID];
  const payload = func.runtime.render.get_ssr_payload(session);
  if (!payload || typeof payload !== 'object') {
    return false;
  }
  payload._consumed = true;
  return true;
};
func.runtime.render.get_root_data_system = function (SESSION_ID) {
  return SESSION_OBJ[SESSION_ID]?.DS_GLB?.[0]?.data_system || null;
};
func.runtime.render.resolve_xu_for_source = async function (SESSION_ID, dsSessionP, value) {
  let arr = value;
  let reference_source_obj;
  const normalized_reference = typeof value === 'string' && value.startsWith('@') ? value.substring(1) : value;
  const _progFields = await func.datasource.get_progFields(SESSION_ID, dsSessionP);
  let view_field_obj = func.common.find_item_by_key(_progFields, 'field_id', normalized_reference);

  if (view_field_obj || normalized_reference !== value) {
    reference_source_obj = await func.datasource.get_value(SESSION_ID, normalized_reference, dsSessionP);
    arr = reference_source_obj?.ret?.value;
  } else {
    if (typeof value === 'string') {
      arr = eval(value.replaceAll('\\', ''));
    }
    if (typeof arr === 'number') {
      arr = Array.from(Array(arr).keys());
    }
  }

  return {
    arr,
    reference_source_obj,
  };
};
func.runtime.render.apply_iterate_value_to_ds = function (SESSION_ID, dsSessionP, currentRecordId, progFields, field_id, value, is_dynamic_field) {
  if (is_dynamic_field) {
    func.datasource.add_dynamic_field_to_ds(SESSION_ID, dsSessionP, field_id, value);
    return true;
  }

  let view_field_obj = func.common.find_item_by_key(progFields || [], 'field_id', field_id);
  if (!view_field_obj) {
    console.error('field not exist in dataset for xu-for method');
    return false;
  }

  let _ds = SESSION_OBJ[SESSION_ID].DS_GLB[dsSessionP];
  try {
    const row_idx = func.common.find_ROWID_idx(_ds, currentRecordId);
    _ds.data_feed.rows[row_idx][field_id] = value;
    return true;
  } catch (err) {
    console.error(err);
    return false;
  }
};
func.runtime.render.build_iterate_info = function (options) {
  return {
    _val: options._val,
    _key: options._key,
    iterator_key: options.iterator_key,
    iterator_val: options.iterator_val,
    is_key_dynamic_field: options.is_key_dynamic_field,
    is_val_dynamic_field: options.is_val_dynamic_field,
    reference_source_obj: options.reference_source_obj,
  };
};
func.runtime.render.apply_iterate_info_to_current_record = function (SESSION_ID, dsSessionP, currentRecordId, progFields, iterate_info) {
  if (!iterate_info) {
    return false;
  }
  func.runtime.render.apply_iterate_value_to_ds(SESSION_ID, dsSessionP, currentRecordId, progFields, iterate_info.iterator_key, iterate_info._key, iterate_info.is_key_dynamic_field);
  func.runtime.render.apply_iterate_value_to_ds(SESSION_ID, dsSessionP, currentRecordId, progFields, iterate_info.iterator_val, iterate_info._val, iterate_info.is_val_dynamic_field);
  return true;
};
func.runtime.render.sync_iterate_info_to_dataset = function (_ds, iterate_info) {
  if (!iterate_info) {
    return false;
  }

  const sync_field = function (field_id, value, is_dynamic_field) {
    if (is_dynamic_field) {
      _ds.dynamic_fields[field_id].value = value;
      return true;
    }
    try {
      const row_idx = func.common.find_ROWID_idx(_ds, _ds.currentRecordId);
      _ds.data_feed.rows[row_idx][field_id] = value;
      return true;
    } catch (err) {
      console.error(err);
      return false;
    }
  };

  sync_field(iterate_info.iterator_key, iterate_info._key, iterate_info.is_key_dynamic_field);
  sync_field(iterate_info.iterator_val, iterate_info._val, iterate_info.is_val_dynamic_field);
  return true;
};
func.runtime.program.get_params_obj = async function (SESSION_ID, prog_id, nodeP, dsSession) {
  const _prog = await func.utils.VIEWS_OBJ.get(SESSION_ID, prog_id);
  if (!_prog) return;

  let params_res = {},
    params_raw = {};
  if (_prog?.properties?.progParams) {
    for await (const [key, val] of Object.entries(_prog.properties.progParams)) {
      if (!['in', 'out'].includes(val.data.dir)) continue;

      if (nodeP.attributes) {
        if (nodeP.attributes[val.data.parameter]) {
          params_res[val.data.parameter] = nodeP.attributes[val.data.parameter];
        } else if (nodeP.attributes[`xu-exp:${val.data.parameter}`]) {
          if (val.data.dir == 'out') {
            params_res[val.data.parameter] = nodeP.attributes[`xu-exp:${val.data.parameter}`].replaceAll('@', '');
          } else {
            let ret = await func.expression.get(SESSION_ID, nodeP.attributes[`xu-exp:${val.data.parameter}`], dsSession, 'parameters');
            params_res[val.data.parameter] = ret.result;
            params_raw[val.data.parameter] = nodeP.attributes[`xu-exp:${val.data.parameter}`];
          }
        }
        continue;
      }
      console.warn(`Warning: Program ${_prog.properties.menuName} expected In parameter: ${val.data.parameter} but received null instead`);
    }
  }
  return { params_res, params_raw };
};
func.runtime.bind.build_datasource_changes = function (dsSessionP, currentRecordId, field_id, value) {
  return {
    [dsSessionP]: {
      [currentRecordId]: {
        [field_id]: value,
      },
    },
  };
};
func.runtime.bind.get_native_adapter = function () {
  const has_explicit_value = function (elm) {
    return !!elm?.hasAttribute?.('value');
  };
  const get_listener_event = function (elm) {
    const tag_name = elm?.tagName?.toLowerCase?.();
    const type = (elm?.type || '').toLowerCase();

    if (tag_name === 'select' || ['checkbox', 'radio'].includes(type)) {
      return 'change';
    }
    return 'input';
  };

  return {
    getter: function (elm) {
      if (!elm) {
        return undefined;
      }

      const tag_name = elm?.tagName?.toLowerCase?.();
      const type = (elm?.type || '').toLowerCase();

      if (tag_name === 'select' && elm.multiple) {
        return Array.from(elm.options || [])
          .filter(function (option) {
            return option.selected;
          })
          .map(function (option) {
            return option.value;
          });
      }

      if (type === 'checkbox') {
        return has_explicit_value(elm) ? elm.value : !!elm.checked;
      }

      if (type === 'radio') {
        return elm.value;
      }

      return typeof elm.value !== 'undefined' ? elm.value : undefined;
    },
    setter: function (elm, value) {
      if (!elm) {
        return false;
      }

      const tag_name = elm?.tagName?.toLowerCase?.();
      const type = (elm?.type || '').toLowerCase();

      if (tag_name === 'select' && elm.multiple) {
        const selected_values = Array.isArray(value)
          ? value.map(function (item) {
            return String(item);
          })
          : [String(value)];
        Array.from(elm.options || []).forEach(function (option) {
          option.selected = selected_values.includes(String(option.value));
        });
        return true;
      }

      if (type === 'checkbox' || type === 'radio') {
        return true;
      }

      if (typeof elm.value !== 'undefined') {
        elm.value = value === null || typeof value === 'undefined' ? '' : String(value);
      }
      return true;
    },
    listener: function (elm, handler) {
      if (!elm?.addEventListener || typeof handler !== 'function') {
        return false;
      }

      const event_name = get_listener_event(elm);
      const listener_key = '__xuda_native_bind_listener_' + event_name;
      if (elm[listener_key]) {
        elm.removeEventListener(event_name, elm[listener_key]);
      }
      elm.addEventListener(event_name, handler);
      elm[listener_key] = handler;
      return true;
    },
  };
};
func.runtime.bind.is_valid_adapter = function (adapter) {
  return !!(
    adapter &&
    typeof adapter.getter === 'function' &&
    typeof adapter.setter === 'function' &&
    typeof adapter.listener === 'function'
  );
};
func.runtime.bind.get_adapter = function (SESSION_ID) {
  const native_adapter = func.runtime.bind.get_native_adapter();
  if (func.runtime.session.is_slim(SESSION_ID)) {
    return native_adapter;
  }

  const plugin_bind = UI_FRAMEWORK_PLUGIN?.bind;
  if (!plugin_bind) {
    return native_adapter;
  }

  if (func.runtime.bind.is_valid_adapter(plugin_bind)) {
    return plugin_bind;
  }

  if (typeof plugin_bind === 'function') {
    try {
      const bind_instance = new plugin_bind();
      if (func.runtime.bind.is_valid_adapter(bind_instance)) {
        return bind_instance;
      }
    } catch (error) {}

    try {
      const bind_factory = plugin_bind();
      if (func.runtime.bind.is_valid_adapter(bind_factory)) {
        return bind_factory;
      }
    } catch (error) {}
  }

  return native_adapter;
};
func.runtime.bind.resolve_field = async function (SESSION_ID, prog_id, dsSessionP, field_id, iterate_info) {
  let _prog_id = prog_id;
  let _dsP = dsSessionP;
  let is_dynamic_field = false;
  let field_prop;

  const find_in_view = async function (field_id, prog_id) {
    const view_ret = await func.utils.VIEWS_OBJ.get(SESSION_ID, prog_id);
    if (!view_ret) {
      return null;
    }
    return func.common.find_item_by_key(view_ret.progFields, 'field_id', field_id);
  };

  if (['_FOR_VAL', '_FOR_KEY'].includes(field_id)) {
    is_dynamic_field = true;
    if (iterate_info && (iterate_info.iterator_val === field_id || iterate_info.iterator_key === field_id)) {
      const iter_value = iterate_info.iterator_val === field_id ? iterate_info._val : iterate_info._key;
      const toType = function (obj) {
        return {}.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
      };
      field_prop = {
        id: field_id,
        data: { type: 'virtual', field_id },
        props: { fieldType: typeof iter_value !== 'undefined' ? toType(iter_value) : 'string' },
        value: iter_value,
      };
    } else {
      field_prop = SESSION_OBJ[SESSION_ID]?.DS_GLB?.[_dsP]?.dynamic_fields?.[field_id];
    }
  } else {
    field_prop = await find_in_view(field_id, _prog_id);
    if (!field_prop) {
      const ret_get_value = await func.datasource.get_value(SESSION_ID, field_id, _dsP);
      if (ret_get_value.found) {
        _dsP = ret_get_value.dsSessionP;
        let _ds = SESSION_OBJ[SESSION_ID].DS_GLB[_dsP];
        _prog_id = _ds?.prog_id;
        field_prop = await find_in_view(field_id, _prog_id);
        if (!field_prop) {
          field_prop = _ds?.dynamic_fields?.[field_id];
          if (field_prop) {
            is_dynamic_field = true;
          }
        }
      }
    }
  }

  if (!field_prop) {
    throw `field ${field_id} not found in the program scope`;
  }

  return {
    bind_field_id: field_id,
    field_prop,
    is_dynamic_field,
    dsSessionP: _dsP,
    prog_id: _prog_id,
  };
};
func.runtime.bind.get_field_type = function (field_prop) {
  return field_prop?.props?.fieldType;
};
func.runtime.bind.toggle_array_value = function (arr_value_before_cast, value_from_getter) {
  if (arr_value_before_cast.includes(value_from_getter)) {
    return arr_value_before_cast.filter((item) => !xu_isEqual(item, value_from_getter));
  }
  arr_value_before_cast.push(value_from_getter);
  return arr_value_before_cast;
};
func.runtime.bind.get_cast_value = async function (SESSION_ID, field_prop, input_field_type, raw_value) {
  const field_type = func.runtime.bind.get_field_type(field_prop);
  if (field_type === 'object') {
    return await func.common.get_cast_val(SESSION_ID, 'xu-bind', 'value', input_field_type, raw_value);
  }
  return await func.common.get_cast_val(SESSION_ID, 'xu-bind', 'value', field_type, raw_value);
};
func.runtime.bind.get_source_value = function (_ds, bind_field_id, is_dynamic_field) {
  if (is_dynamic_field) {
    return _ds.dynamic_fields[bind_field_id].value;
  }
  const row_idx = func.common.find_ROWID_idx(_ds, _ds.currentRecordId);
  return _ds.data_feed.rows?.[row_idx]?.[bind_field_id];
};
func.runtime.bind.format_display_value = function ($elm, field_prop, bind_field_id, expression_value, value, input_field_type) {
  const field_type = func.runtime.bind.get_field_type(field_prop);
  const elm_value = func.runtime.ui.get_attr($elm, 'value');

  if (field_type === 'array' && input_field_type === 'checkbox' && elm_value) {
    return value.includes(elm_value);
  }
  if (field_type === 'array' && input_field_type === 'radio' && elm_value) {
    if (value.includes(elm_value)) {
      return elm_value;
    }
    return false;
  }
  if (field_type === 'object' && expression_value.split('.').length > 1) {
    let str = expression_value.replace(bind_field_id, '(' + JSON.stringify(value) + ')');
    return eval(str);
  }
  return value;
};
func.runtime.bind.update_reference_source_array = async function (options) {
  const field_type = func.runtime.bind.get_field_type(options.field_prop);
  const reference_source_obj = options.iterate_info?.reference_source_obj;
  if (!reference_source_obj || reference_source_obj.ret.type !== 'array' || options.iterate_info?.iterator_val !== options.bind_field_id) {
    return false;
  }

  const arr_idx = Number(options.iterate_info._key);
  const dataset_arr = await func.datasource.get_value(options.SESSION_ID, reference_source_obj.fieldIdP, options.dsSessionP, reference_source_obj.currentRecordId);
  let new_arr = structuredClone(dataset_arr.ret.value);

  if (field_type === 'object' && options.val_is_reference_field) {
    let obj_item = new_arr[arr_idx];
    let e_exp = options.expression_value.replace(options.bind_field_id, 'obj_item');
    eval(e_exp + `=${JSON.stringify(options.value)}`);
    new_arr[arr_idx] = obj_item;
  } else {
    new_arr[arr_idx] = options.value;
  }

  let datasource_changes = func.runtime.bind.build_datasource_changes(options.dsSessionP, options.currentRecordId, reference_source_obj.fieldIdP, new_arr);
  await func.datasource.update(options.SESSION_ID, datasource_changes);
  return true;
};
func.runtime.resources.load_cdn = async function (SESSION_ID, resource) {
  let normalized_resource = resource;
  if (!(typeof normalized_resource === 'object' && normalized_resource !== null) && typeof normalized_resource === 'string') {
    normalized_resource = { src: normalized_resource, type: 'js' };
  }
  if (!(typeof normalized_resource === 'object' && normalized_resource !== null)) {
    throw new Error('cdn resource in wrong format');
  }

  return new Promise(async (resolve) => {
    try {
      switch (normalized_resource.type) {
        case 'js':
          await func.utils.load_js_on_demand(normalized_resource.src);
          break;
        case 'css':
          func.runtime.platform.load_css(normalized_resource.src);
          break;
        case 'module':
          await func.utils.load_js_on_demand(normalized_resource.src, 'module');
          break;
        default:
          await func.utils.load_js_on_demand(normalized_resource.src);
          break;
      }
      resolve();
    } catch (error) {
      func.utils.debug_report(SESSION_ID, 'xu-cdn', 'Fail to load: ' + normalized_resource, 'W');
      resolve();
    }
  });
};
func.runtime.resources.get_plugin_manifest_entry = function (_session, plugin_name) {
  return APP_OBJ[_session.app_id]?.app_plugins_purchased?.[plugin_name] || null;
};
func.runtime.resources.get_plugin_resource_candidates = function (_session, plugin, resource) {
  const manifest_entry = plugin?.manifest?.[resource];
  const default_path = `${manifest_entry?.dist ? 'dist/' : ''}${resource}`;
  const candidates = [];
  if (_session?.worker_type === 'Dev' && manifest_entry?.dist && /\.mjs$/.test(resource)) {
    candidates.push(`src/${resource}`);
  }
  candidates.push(default_path);
  return Array.from(new Set(candidates.filter(Boolean)));
};
func.runtime.resources.get_plugin_module_path = function (plugin, resource, _session) {
  return func.runtime.resources.get_plugin_resource_candidates(_session, plugin, resource)[0] || resource;
};
func.runtime.resources.get_plugin_module_url = async function (SESSION_ID, plugin_name, plugin, resource) {
  const _session = SESSION_OBJ[SESSION_ID];
  return await func.utils.get_plugin_npm_cdn(SESSION_ID, plugin_name, func.runtime.resources.get_plugin_module_path(plugin, resource, _session));
};
func.runtime.resources.import_plugin_module = async function (SESSION_ID, plugin_name, plugin, resource) {
  const _session = SESSION_OBJ[SESSION_ID];
  const candidates = func.runtime.resources.get_plugin_resource_candidates(_session, plugin, resource);
  let last_error = null;
  for (let index = 0; index < candidates.length; index++) {
    const candidate = candidates[index];
    try {
      return await func.utils.get_plugin_resource(SESSION_ID, plugin_name, candidate);
    } catch (error) {
      last_error = error;
    }
  }
  throw last_error || new Error(`plugin resource not found: ${plugin_name}/${resource}`);
};
func.runtime.resources.load_plugin_runtime_css = async function (SESSION_ID, plugin_name, plugin) {
  if (!plugin?.manifest?.['runtime.mjs']?.dist || !plugin?.manifest?.['runtime.mjs']?.css) {
    return false;
  }
  const plugin_runtime_css_url = await func.utils.get_plugin_npm_cdn(SESSION_ID, plugin_name, 'dist/runtime.css');
  func.utils.load_css_on_demand(plugin_runtime_css_url);
  return true;
};
func.runtime.resources.resolve_plugin_properties = async function (SESSION_ID, dsSessionP, attributes, properties) {
  let resolved_properties = structuredClone(properties);
  for await (let [prop_name, prop_val] of Object.entries(resolved_properties || {})) {
    prop_val.value = attributes?.[prop_name];
    if (attributes?.[`xu-exp:${prop_name}`]) {
      const res = await func.expression.get(SESSION_ID, attributes[`xu-exp:${prop_name}`], dsSessionP, 'UI Attr EXP');
      prop_val.value = res.result;
    }
  }
  return resolved_properties;
};
func.runtime.resources.run_ui_plugin = async function (SESSION_ID, paramsP, $elm, plugin_name, value) {
  var _session = SESSION_OBJ[SESSION_ID];
  const plugin = func.runtime.resources.get_plugin_manifest_entry(_session, plugin_name);
  if (!plugin?.installed || !plugin?.manifest?.['runtime.mjs']?.exist || !plugin?.manifest?.['index.mjs']?.exist || !value?.enabled) {
    return false;
  }

  await func.runtime.resources.load_plugin_runtime_css(SESSION_ID, plugin_name, plugin);

  const plugin_index_resources = await func.runtime.resources.import_plugin_module(SESSION_ID, plugin_name, plugin, 'index.mjs');
  const properties = await func.runtime.resources.resolve_plugin_properties(SESSION_ID, paramsP.dsSessionP, value?.attributes, plugin_index_resources.properties);

  const plugin_runtime_resources = await func.runtime.resources.import_plugin_module(SESSION_ID, plugin_name, plugin, 'runtime.mjs');

  if (plugin_runtime_resources.cdn && Array.isArray(plugin_runtime_resources.cdn)) {
    for await (const resource of plugin_runtime_resources.cdn) {
      await func.runtime.resources.load_cdn(SESSION_ID, resource);
    }
  }

  if (plugin_runtime_resources.fn) {
    await plugin_runtime_resources.fn(plugin_name, $elm?.[0], properties);
  }
  return true;
};
func.runtime.widgets.create_context = function (SESSION_ID, paramsP, prop) {
  const _session = SESSION_OBJ[SESSION_ID];
  const plugin_name = prop['xu-widget'];
  return {
    SESSION_ID,
    _session,
    plugin_name,
    method: prop['xu-method'] || '_default',
    dsP: paramsP.dsSessionP,
    propsP: prop,
    sourceP: 'widgets',
    plugin: APP_OBJ[_session.app_id]?.app_plugins_purchased?.[plugin_name] || null,
  };
};
func.runtime.widgets.report_error = function (context, descP, warn) {
  const program = context?._session?.DS_GLB?.[context.dsP];
  if (!program) {
    return null;
  }
  func.utils.debug.log(context.SESSION_ID, program.prog_id + '_' + program.callingMenuId, {
    module: 'widgets',
    action: 'Init',
    source: context.sourceP,
    prop: descP,
    details: descP,
    result: null,
    error: warn ? false : true,
    fields: null,
    type: 'widgets',
    prog_id: program.prog_id,
  });
  return null;
};
func.runtime.widgets.get_property_value = async function (context, fieldIdP, val, props) {
  if (!val) return;
  var value = fieldIdP in props ? props[fieldIdP] : typeof val.defaultValue === 'function' ? val?.defaultValue?.() : val?.defaultValue;
  if (val.render === 'eventId') {
    value = props?.[fieldIdP]?.event;
  }

  if (props[`xu-exp:${fieldIdP}`]) {
    value = (await func.expression.get(context.SESSION_ID, props[`xu-exp:${fieldIdP}`], context.dsP, 'widget property')).result;
  }

  return func.common.get_cast_val(
    context.SESSION_ID,
    'widgets',
    fieldIdP,
    val.type,
    value,
    null,
  );
};
func.runtime.widgets.get_fields_data = async function (context, fields, props) {
  var data_obj = {};
  var return_code = 1;
  for await (const [key, val] of Object.entries(fields || {})) {
    data_obj[key] = await func.runtime.widgets.get_property_value(context, key, val, props);
    if (!data_obj[key] && val.mandatory) {
      return_code = -1;
      func.runtime.widgets.report_error(context, `${key} is a mandatory field.`);
      break;
    }
  }
  for await (const key of ['xu-bind']) {
    data_obj[key] = await func.runtime.widgets.get_property_value(context, key, props?.[key], props);
  }

  return { code: return_code, data: data_obj };
};
func.runtime.widgets.get_resource_candidates = function (context, resource) {
  return func.runtime.resources.get_plugin_resource_candidates(context._session, context.plugin, resource);
};
func.runtime.widgets.normalize_capabilities = function (definition) {
  const capabilities = definition?.capabilities || {};
  return {
    browser: capabilities.browser !== false,
    headless: capabilities.headless === true,
  };
};
func.runtime.widgets.supports_current_environment = function (definition) {
  const capabilities = func.runtime.widgets.normalize_capabilities(definition);
  if (func.runtime.platform.has_document()) {
    return capabilities.browser !== false;
  }
  return !!capabilities.headless;
};
func.runtime.widgets.get_resource_path = function (context, resource) {
  const relative_path = func.runtime.widgets.get_resource_candidates(context, resource)[0] || resource;
  if (context._session.worker_type === 'Dev') {
    return `../../plugins/${context.plugin_name}/${relative_path}`;
  }
  return `https://${context._session.domain}/plugins/${context.plugin_name}/${relative_path}?gtp_token=${context._session.gtp_token}&app_id=${context._session.app_id}`;
};
func.runtime.widgets.load_css_style = function (context) {
  func.utils.load_css_on_demand(func.runtime.widgets.get_resource_path(context, 'style.css'));
  return true;
};
func.runtime.widgets.get_resource = async function (context, resource) {
  const candidates = func.runtime.widgets.get_resource_candidates(context, resource);
  let last_error = null;
  for (let index = 0; index < candidates.length; index++) {
    const candidate = candidates[index];
    try {
      return await func.utils.get_plugin_resource(context.SESSION_ID, context.plugin_name, candidate);
    } catch (error) {
      last_error = error;
    }
  }
  throw last_error || new Error(`widget resource not found: ${context.plugin_name}/${resource}`);
};
func.runtime.widgets.get_definition = async function (context) {
  return await func.runtime.widgets.get_resource(context, 'index.mjs');
};
func.runtime.widgets.get_methods = async function (context) {
  const index = await func.runtime.widgets.get_definition(context);
  return index?.methods || {};
};
func.runtime.widgets.load_runtime_css = async function (context) {
  if (!context.plugin?.manifest?.['runtime.mjs']?.dist || !context.plugin?.manifest?.['runtime.mjs']?.css) {
    return false;
  }
  const plugin_runtime_css_url = await func.utils.get_plugin_npm_cdn(context.SESSION_ID, context.plugin_name, 'dist/runtime.css');
  func.utils.load_css_on_demand(plugin_runtime_css_url);
  return true;
};
func.runtime.widgets.build_params = function (context, container_node, container_data, plugin_setup, api_utils, extra = {}) {
  return {
    SESSION_ID: context.SESSION_ID,
    method: context.method,
    _session: context._session,
    dsP: context.dsP,
    sourceP: context.sourceP,
    propsP: context.propsP,
    plugin_name: context.plugin_name,
    container_node,
    container_data,
    plugin_setup,
    report_error: function (descP, warn) {
      return func.runtime.widgets.report_error(context, descP, warn);
    },
    log_error: function (descP, warn) {
      return func.runtime.widgets.report_error(context, descP, warn);
    },
    call_plugin_api: async function (plugin_nameP, dataP) {
      return await func.utils.call_plugin_api(context.SESSION_ID, plugin_nameP, dataP);
    },
    set_SYS_GLOBAL_OBJ_WIDGET_INFO: async function (docP) {
      return await func.utils.set_SYS_GLOBAL_OBJ_WIDGET_INFO(context.SESSION_ID, docP);
    },
    run_widgetCallbackEvent: async function () {
      const event_id = context.propsP?.widgetCallbackEvent;
      if (!event_id || !api_utils?.invoke_event) {
        return false;
      }
      return await api_utils.invoke_event(event_id);
    },
    api_utils,
    ...extra,
  };
};
func.common.find_item_by_key = function (arr, key, val) {
  return arr.find(function (e) {
    return e.data[key] === val;
  });
};
func.common.find_item_by_key_root = function (arr, key, val) {
  return arr.find(function (e) {
    return e[key] === val;
  });
};
func.common.find_ROWID_idx = function (_ds, rowId) {
  if (!_ds?.data_feed?.rows) {
    throw new Error('data_feed not found');
  }

  // Find the index of the object with the given _ROWID
  const index = _ds.data_feed.rows.findIndex((item) => item._ROWID === rowId);
  // }
  // If the index is -1, the ROWID was not found, so throw an error
  if (index === -1) {
    throw new Error(`ROWID "${rowId}" not found`);
  }

  // Return the found index
  return index;
};
func.common.input_mask = async function (actionP, valP, typeP, maskP, elemP, grid_objP, grid_row_idP, grid_col_idP, dsSessionP) {
  const module = await func.common.get_module(SESSION_ID, 'xuda-input-musk-utils-module.mjs');

  module.input_mask(actionP, valP, typeP, maskP, elemP, grid_objP, grid_row_idP, grid_col_idP, dsSessionP);
};

glb.FUNCTION_NODES_ARR = ['batch', 'get_data', 'set_data', 'alert', 'javascript', 'api'];
glb.ALL_MENU_TYPE = ['globals', 'ai_agent', 'component', ...glb.FUNCTION_NODES_ARR];
glb.emailRegex = /^[\w\.-]+@[a-zA-Z\d\.-]+\.[a-zA-Z]{2,}$/;

const FIREBASE_AUTH_PROPERTIES_ARR = ['provider', 'token', 'first_name', 'last_name', 'email', 'user_id', 'picture', 'verified_email', 'locale', 'error_code', 'error_msg'];

const CLIENT_INFO_PROPERTIES_ARR = [
  'fingerprint',
  'device',
  'user_agent',
  'browser_version',
  'browser_name',
  'engine_version',
  'engine_name',
  'client_ip',
  'os_name',
  'os_version',
  'device_model',
  'device_vendor',
  'device_type',
  'screen_current_resolution_x',
  'screen_current_resolution_y',
  'screen_available_resolution_x',
  'screen_available_resolution_y',
  'language',
  'time_zone',
  'cpu_architecture',
  'uuid',
  'cursor_pos_x',
  'cursor_pos_y',
];

const APP_PROPERTIES_ARR = ['build', 'author', 'date', 'name'];

const DATASOURCE_PROPERTIES_ARR = ['rows', 'type', 'first_row_id', 'last_row_id', 'query_from_segments_json', 'query_to_segments_json', 'locate_query_from_segments_json', 'locate_query_to_segments_json', 'first_row_segments_json', 'last_row_segments_json', 'rowid_snapshot', 'rowid'];

glb.MOBILE_ARR = ['component', 'web_app', 'ios_app', 'android_app', 'electron_app', 'osx_app', 'windows_app'];

glb.SYS_DATE_ARR = ['SYS_DATE', 'SYS_DATE_TIME', 'SYS_DATE_VALUE', 'SYS_DATE_WEEK_YEAR', 'SYS_DATE_MONTH_YEAR', 'SYS_TIME_SHORT', 'SYS_TIME'];

glb.API_OUTPUT_ARR = ['json', 'html', 'xml', 'text', 'css', 'javascript'];

const PROTECTED_NAMES_ARR = ['THIS', 'ROWID']; //tbd

func.common.db = async function (SESSION_ID, serviceP, dataP, opt = {}, dsSession) {
  return new Promise(async function (resolve, reject) {
    var _session = SESSION_OBJ[SESSION_ID];
    const app_id = _session.app_id;

    if (glb.DEBUG_MODE) {
      console.log('request', dataP);
    }

    var data = {
      app_id: app_id,
      fingerprint: _session?.SYS_GLOBAL_OBJ_CLIENT_INFO?.fingerprint,
      debug: glb.DEBUG_MODE,
      session_id: SESSION_ID,
      gtp_token: _session.gtp_token,
      app_token: _session.app_token,
      res_token: _session.res_token,
      engine_mode: _session.engine_mode,
      req_id: 'rt_req_' + crypto.randomUUID(),
      app_replicate: APP_OBJ[app_id].app_replicate,
    };

    try {
      if (typeof firebase !== 'undefined' && firebase?.auth()?.currentUser?.displayName) {
        data.device_name = firebase.auth().currentUser.displayName;
      }
    } catch (error) {}

    for (const [key, val] of Object.entries(dataP)) {
      data[key] = val;
    }

    const success_callback = function (ret) {
      if (dataP.table_id && DOCS_OBJ[app_id][dataP.table_id]) {
        func.utils.debug.watch(SESSION_ID, dataP.table_id, 'table', DOCS_OBJ[app_id][dataP.table_id].properties.menuName, {
          req: data,
          res: ret,
        });
      }

      if (glb.DEBUG_MODE) {
        console.log('response', ret);
      }
      resolve(ret, true);
    };

    const error_callback = function (err) {
      reject(err);
    };
    function cleanString(json) {
      let str = JSON.stringify(json);
      // Replace all non-alphanumeric characters with an empty string
      return str.replace(/[^a-zA-Z0-9]/g, '');
    }
    const get_rep_id = function () {
      let _data = {};
      const fields_to_skip = ['fields', 'viewSourceDesc', 'skip', 'limit', 'count', 'reduce', 'prog_id', 'sortModel', 'filterModelMongo', 'filterModelSql', 'filterModelUserMongo', 'filterModelUserSql'];

      for (let [key, val] of Object.entries(dataP)) {
        if (typeof val !== 'undefined' && val !== null && !fields_to_skip.includes(key)) {
          _data[key] = val;
        }
      }

      return cleanString(_data);
    };
    const validate_existence_of_whole_table_request = async function (db) {
      let table_req_id;
      try {
        table_req_id = cleanString({
          key: data.table_id,
          table_id: data.table_id,
        });
        const doc = await db.get(table_req_id);

        let ret = await db.find({
          selector: {
            docType: 'rep_request',
            table_id: data.table_id,
          },
        });

        if (doc.stat < 3) {
          throw 'not ready';
        }

        /// delete table refunded requests
        for (let doc of ret.docs) {
          if (doc.entire_table) continue;
          func.db.pouch.remove_db_replication_from_server(SESSION_ID, doc._id);
        }
        return { code: 1, data: table_req_id };
      } catch (err) {
        return { code: -1, data: table_req_id };
      }
    };

    const read_dbs_pouch = async function (db) {
      if (_session?.DS_GLB?.[dsSession]?.refreshed && (dataP.filterModelMongo || dataP.filterModelSql)) {
        return {
          code: 1,
          data: await func.db.pouch[serviceP](SESSION_ID, data),
        };
      }

      const rep_id = get_rep_id();

      const { code: table_req_code, data: table_req_id } = await validate_existence_of_whole_table_request(db);
      if (table_req_code > 0) {
        return {
          code: 1,
          data: await func.db.pouch[serviceP](SESSION_ID, data),
        };
      }

      try {
        const doc = await db.get(rep_id);
        if (doc.stat < 3) throw 'replication not ready';
        const json = {
          code: 1,
          data: await func.db.pouch[serviceP](SESSION_ID, data),
        };

        return json;
      } catch (err) {
        const json = await func.common.perform_rpi_request(SESSION_ID, serviceP, opt, data);
        if (json.data.opt) {
          try {
            try {
              await db.get(rep_id);
            } catch (err) {
              await db.put({
                _id: rep_id,
                selector: json.data.opt.selector,
                stat: 1,
                ts: Date.now(),
                docType: 'rep_request',
                table_id: dataP.table_id,
                prog_id: dataP.prog_id,
                entire_table: table_req_id === rep_id,
                source: 'runtime',
                e: data,
              });
            }
            func.db.pouch.set_db_replication_from_server(SESSION_ID);
          } catch (err) {}
        }
        return json;
      }
    };
    const update_dbs_pouch = async function (db) {
      try {
        // if (_session.DS_GLB[0].data_system.SYS_GLOBAL_BOL_ONLINE) {
        //   throw "online";
        // }

        const { code: table_req_code, data: table_req_id } = await validate_existence_of_whole_table_request(db);

        if (table_req_code > 0) {
          data.full_table_downloaded = true;
        }

        await db.get(dataP.row_id);
        return await func.db.pouch[serviceP](SESSION_ID, data);
      } catch (err) {
        return await func.common.perform_rpi_request(SESSION_ID, serviceP, opt, data);
      }
    };
    const create_dbs_pouch = async function (db) {
      try {
        const { code: table_req_code, data: table_req_id } = await validate_existence_of_whole_table_request(db);

        if (table_req_code > 0) {
          data.full_table_downloaded = true;
        }
        return await func.db.pouch[serviceP](SESSION_ID, data);
      } catch (err) {
        return await func.common.perform_rpi_request(SESSION_ID, serviceP, opt, data);
      }
    };
    const delete_dbs_pouch = async function (db) {
      for await (let row_id of dataP.ids || []) {
        try {
          const { code: table_req_code, data: table_req_id } = await validate_existence_of_whole_table_request(db);

          if (table_req_code > 0) {
            data.full_table_downloaded = true;
          }

          await db.get(row_id);
          let _data = structuredClone(dataP);
          _data.ids = [row_id];
          return await func.db.pouch['dbs_delete'](SESSION_ID, _data);
        } catch (err) {
          return await func.common.perform_rpi_request(SESSION_ID, serviceP, opt, data);
        }
      }
    };
    if (typeof IS_DOCKER === 'undefined' && typeof IS_PROCESS_SERVER === 'undefined') {
      try {
        if (!SESSION_OBJ?.[SESSION_ID]?.rpi_http_methods?.includes(serviceP)) {
          throw '';
        }
        const is_local_draft_pouch_runtime =
          ['miniapp', 'live_preview'].includes(_session?.engine_mode) &&
          _session?.is_draft_runtime &&
          Array.isArray(_session?.rpi_http_methods) &&
          _session.rpi_http_methods.includes('dbs_read');
        if (is_local_draft_pouch_runtime && ['dbs_read', 'dbs_update', 'dbs_create', 'dbs_delete'].includes(serviceP)) {
          return success_callback({
            code: 1,
            data: await func.db.pouch[serviceP](SESSION_ID, data),
          });
        }
        if (!(await func?.db?.pouch?.get_replication_stat(SESSION_ID))) throw '';
        const db = await func.utils.connect_pouchdb(SESSION_ID);
        switch (serviceP) {
          case 'dbs_read': {
            try {
              return success_callback(await read_dbs_pouch(db));
            } catch (err) {
              if (err === 'creating index in progress') {
                throw '';
              }
              return error_callback(err);
            }
            break;
          }
          case 'dbs_update': {
            try {
              const ret = {
                code: 1,
                data: await update_dbs_pouch(db),
              };
              return success_callback(ret);
            } catch (err) {
              return error_callback(err);
            }
            break;
          }
          case 'dbs_create': {
            try {
              const ret = {
                code: 1,
                data: await create_dbs_pouch(db),
              };
              return success_callback(ret);
            } catch (err) {
              return error_callback(err);
            }
            break;
          }
          case 'dbs_delete': {
            try {
              const ret = {
                code: 1,
                data: await delete_dbs_pouch(db),
              };
              return success_callback(ret);
            } catch (err) {
              return error_callback(err);
            }
            break;
          }

          default:
            throw '';
            break;
        }
      } catch (err) {
        try {
          const json = await func.common.perform_rpi_request(SESSION_ID, serviceP, opt, data);
          return success_callback(json, true);
        } catch (err) {
          return error_callback(err);
        }
      }
    }

    // DOCKER // PROCESS_SERVER

    const response = function (res, ret) {
      if (ret.code < 0) {
        return error_callback(ret);
      }
      success_callback(ret);
    };

    const get_white_spaced_data = function (data) {
      var e = {};
      for (const [key, val] of Object.entries(data)) {
        if (!val) {
          if (typeof val === 'boolean') {
            e[key] = 'false';
          } else {
            e[key] = '';
          }
        } else {
          if (typeof val === 'boolean') {
            e[key] = 'true';
          } else {
            e[key] = val;
          }
        }
      }

      if (data.fields && !data.fields.length) {
        e.fields = '';
      }

      return e;
    };

    if (dataP.table_id) {
      await func.utils.FILES_OBJ.get(SESSION_ID, dataP.table_id);
      await func.utils.TREE_OBJ.get(SESSION_ID, dataP.table_id);
    }
    data.db_driver = 'xuda';
    __.rpi.http_calls(serviceP, { body: get_white_spaced_data(data) }, null, response);
  });
};

func.common.getJsonFromUrl = function () {
  return func.runtime.env.get_url_params();
};
func.common.getParametersFromUrl = function () {
  return func.runtime.env.get_url_parameters_object();
};

func.common.getObjectFromUrl = function (url, element_attributes_obj, embed_params_obj) {
  var result = {};
  if (element_attributes_obj) {
    for (let [key, val] of Object.entries(element_attributes_obj)) {
      result[key] = val;
    }
  }
  if (embed_params_obj) {
    for (let [key, val] of Object.entries(embed_params_obj)) {
      result[key] = val;
    }
  }

  if (!url && typeof IS_DOCKER === 'undefined' && typeof IS_PROCESS_SERVER === 'undefined') {
    url = location.href;
  }
  var question = url.indexOf('?');
  var hash = url.indexOf('#');
  if (hash == -1 && question == -1) return result;
  if (hash == -1) hash = url.length;
  var query = question == -1 || hash == question + 1 ? url.substring(hash) : url.substring(question + 1, hash);
  // var result = {};
  query.split('&').forEach(function (part) {
    if (!part) return;
    part = part.split('+').join(' '); // replace every + with space, regexp-free version
    var eq = part.indexOf('=');
    var key = eq > -1 ? part.substr(0, eq) : part;
    var val = eq > -1 ? decodeURIComponent(part.substr(eq + 1)) : '';
    var from = key.indexOf('[');
    if (from == -1) {
      result[decodeURIComponent(key)] = val;
    } else {
      var to = key.indexOf(']', from);
      var index = decodeURIComponent(key.substring(from + 1, to));
      key = decodeURIComponent(key.substring(0, from));
      if (!result[key]) result[key] = [];
      if (!index) result[key].push(val);
      else result[key][index] = val;
    }
  });

  return result;
};

func.common.getContrast_color = function (hexcolor) {
  function colourNameToHex(colour) {
    var colours = {
      aliceblue: '#f0f8ff',
      antiquewhite: '#faebd7',
      aqua: '#00ffff',
      aquamarine: '#7fffd4',
      azure: '#f0ffff',
      beige: '#f5f5dc',
      bisque: '#ffe4c4',
      black: '#000000',
      blanchedalmond: '#ffebcd',
      blue: '#0000ff',
      blueviolet: '#8a2be2',
      brown: '#a52a2a',
      burlywood: '#deb887',
      cadetblue: '#5f9ea0',
      chartreuse: '#7fff00',
      chocolate: '#d2691e',
      coral: '#ff7f50',
      cornflowerblue: '#6495ed',
      cornsilk: '#fff8dc',
      crimson: '#dc143c',
      cyan: '#00ffff',
      darkblue: '#00008b',
      darkcyan: '#008b8b',
      darkgoldenrod: '#b8860b',
      darkgray: '#a9a9a9',
      darkgreen: '#006400',
      darkkhaki: '#bdb76b',
      darkmagenta: '#8b008b',
      darkolivegreen: '#556b2f',
      darkorange: '#ff8c00',
      darkorchid: '#9932cc',
      darkred: '#8b0000',
      darksalmon: '#e9967a',
      darkseagreen: '#8fbc8f',
      darkslateblue: '#483d8b',
      darkslategray: '#2f4f4f',
      darkturquoise: '#00ced1',
      darkviolet: '#9400d3',
      deeppink: '#ff1493',
      deepskyblue: '#00bfff',
      dimgray: '#696969',
      dodgerblue: '#1e90ff',
      firebrick: '#b22222',
      floralwhite: '#fffaf0',
      forestgreen: '#228b22',
      fuchsia: '#ff00ff',
      gainsboro: '#dcdcdc',
      ghostwhite: '#f8f8ff',
      gold: '#ffd700',
      goldenrod: '#daa520',
      gray: '#808080',
      green: '#008000',
      greenyellow: '#adff2f',
      honeydew: '#f0fff0',
      hotpink: '#ff69b4',
      'indianred ': '#cd5c5c',
      indigo: '#4b0082',
      ivory: '#fffff0',
      khaki: '#f0e68c',
      lavender: '#e6e6fa',
      lavenderblush: '#fff0f5',
      lawngreen: '#7cfc00',
      lemonchiffon: '#fffacd',
      lightblue: '#add8e6',
      lightcoral: '#f08080',
      lightcyan: '#e0ffff',
      lightgoldenrodyellow: '#fafad2',
      lightgrey: '#d3d3d3',
      lightgreen: '#90ee90',
      lightpink: '#ffb6c1',
      lightsalmon: '#ffa07a',
      lightseagreen: '#20b2aa',
      lightskyblue: '#87cefa',
      lightslategray: '#778899',
      lightsteelblue: '#b0c4de',
      lightyellow: '#ffffe0',
      lime: '#00ff00',
      limegreen: '#32cd32',
      linen: '#faf0e6',
      magenta: '#ff00ff',
      maroon: '#800000',
      mediumaquamarine: '#66cdaa',
      mediumblue: '#0000cd',
      mediumorchid: '#ba55d3',
      mediumpurple: '#9370d8',
      mediumseagreen: '#3cb371',
      mediumslateblue: '#7b68ee',
      mediumspringgreen: '#00fa9a',
      mediumturquoise: '#48d1cc',
      mediumvioletred: '#c71585',
      midnightblue: '#191970',
      mintcream: '#f5fffa',
      mistyrose: '#ffe4e1',
      moccasin: '#ffe4b5',
      navajowhite: '#ffdead',
      navy: '#000080',
      oldlace: '#fdf5e6',
      olive: '#808000',
      olivedrab: '#6b8e23',
      orange: '#ffa500',
      orangered: '#ff4500',
      orchid: '#da70d6',
      palegoldenrod: '#eee8aa',
      palegreen: '#98fb98',
      paleturquoise: '#afeeee',
      palevioletred: '#d87093',
      papayawhip: '#ffefd5',
      peachpuff: '#ffdab9',
      peru: '#cd853f',
      pink: '#ffc0cb',
      plum: '#dda0dd',
      powderblue: '#b0e0e6',
      purple: '#800080',
      rebeccapurple: '#663399',
      red: '#ff0000',
      rosybrown: '#bc8f8f',
      royalblue: '#4169e1',
      saddlebrown: '#8b4513',
      salmon: '#fa8072',
      sandybrown: '#f4a460',
      seagreen: '#2e8b57',
      seashell: '#fff5ee',
      sienna: '#a0522d',
      silver: '#c0c0c0',
      skyblue: '#87ceeb',
      slateblue: '#6a5acd',
      slategray: '#708090',
      snow: '#fffafa',
      springgreen: '#00ff7f',
      steelblue: '#4682b4',
      tan: '#d2b48c',
      teal: '#008080',
      thistle: '#d8bfd8',
      tomato: '#ff6347',
      turquoise: '#40e0d0',
      violet: '#ee82ee',
      wheat: '#f5deb3',
      white: '#ffffff',
      whitesmoke: '#f5f5f5',
      yellow: '#ffff00',
      yellowgreen: '#9acd32',
    };

    if (typeof colours[colour.toLowerCase()] != 'undefined') return colours[colour.toLowerCase()];

    return false;
  }
  if (!hexcolor.includes('#')) {
    hexcolor = colourNameToHex(hexcolor);
  }
  // If a leading # is provided, remove it
  if (hexcolor.slice(0, 1) === '#') {
    hexcolor = hexcolor.slice(1);
  }

  // Convert to RGB value
  var r = Number(hexcolor.substr(0, 2), 16);
  var g = Number(hexcolor.substr(2, 2), 16);
  var b = Number(hexcolor.substr(4, 2), 16);

  // Get YIQ ratio
  var yiq = (r * 299 + g * 587 + b * 114) / 1000;

  // Check contrast
  return yiq >= 128 ? 'black' : 'white';
};

func.common.get_url = function (SESSION_ID, method, path) {
  const _session = SESSION_OBJ[SESSION_ID] || {};
  const origin =
    ((typeof globalThis !== 'undefined' && globalThis.__XU_SERVER_ORIGIN__)) ||
    (_session.domain ? `https://${_session.domain}` : '');

  if (!origin) {
    return `/${method}${path ? '/' + path : '/'}`;
  }

  return `${origin}/${method}${path ? '/' + path : '/'}`;
};

var UI_FRAMEWORK_INSTALLED = null;
var UI_FRAMEWORK_PLUGIN = {};

func.common.get_cast_val = async function (SESSION_ID, source, attributeP, typeP, valP, errorP) {
  const report_conversion_error = function (res) {
    if (errorP) {
      return func.utils.debug_report(SESSION_ID, source.charAt(0).toUpperCase() + source.slice(1).toLowerCase(), errorP, 'W');
    }
    var msg = `error converting  ${attributeP} from ${valP} to ${typeP}`;
    func.utils.debug_report(SESSION_ID, source.charAt(0).toUpperCase() + source.slice(1).toLowerCase(), msg, 'E');
  };
  const report_conversion_warn = function (msg) {
    var msg = `type mismatch auto conversion made to ${attributeP} from value ${valP} to ${typeP}`;
    func.utils.debug_report(SESSION_ID, source.charAt(0).toUpperCase() + source.slice(1).toLowerCase(), msg, 'W');
  };
  const module = await func.common.get_module(SESSION_ID, `xuda-get-cast-util-module.mjs`);
  return module.cast(typeP, valP, report_conversion_error, report_conversion_warn);
};
var WEB_WORKER = {};
var WEB_WORKER_CALLBACK_QUEUE = {};

glb.DEBUG_MODE = null;
var DS_UI_EVENTS_GLB = {};

var RUNTIME_SERVER_WEBSOCKET = null;
var RUNTIME_SERVER_WEBSOCKET_CONNECTED = null;
var WEBSOCKET_PROCESS_PID = null;

glb.worker_queue_num = 0;
glb.websocket_queue_num = 0;

func.common._import_cache = func.common._import_cache || {};

func.common.get_module = async function (SESSION_ID, module, paramsP = {}) {
  let ret;

  const get_ret = async function (src) {
    // Cache the import() result to avoid repeated dynamic imports
    if (!func.common._import_cache[src]) {
      func.common._import_cache[src] = await import(src);
    }
    const module_ret = func.common._import_cache[src];
    var params = get_params();

    const ret = module_ret.XudaModule ? new module_ret.XudaModule(params) : await invoke_init_module(module_ret, params);

    return ret;
  };

  const get_params = function () {
    let params = {
      glb,
      func,
      APP_OBJ,
      SESSION_ID,
      PROJECT_OBJ,
      DOCS_OBJ,
      SESSION_OBJ,
      ...paramsP,
    };
    if (typeof IS_PROCESS_SERVER !== 'undefined') params.IS_PROCESS_SERVER = IS_PROCESS_SERVER;

    if (typeof IS_API_SERVER !== 'undefined') params.IS_API_SERVER = IS_API_SERVER;

    if (typeof IS_DOCKER !== 'undefined') params.IS_DOCKER = IS_DOCKER;

    return params;
  };
  const invoke_init_module = async function (module_ret, params) {
    if (!module_ret.init_module) return module_ret;

    await module_ret.init_module(params);
    return module_ret;
  };
  const _session = SESSION_OBJ[SESSION_ID];
  const append_ts = function (resource_path) {
    const cache_tag =
      _session?.build_info?.runtime_ts ||
      _session?.build_info?.last_changed_ts ||
      _session?.build_info?.server_ts ||
      _session?.opt?.app_build_id ||
      (typeof globalThis !== 'undefined' ? globalThis.__XU_SERVER_BOOTSTRAP__?.version : 0) ||
      0;

    if (!cache_tag) {
      return resource_path;
    }

    return `${resource_path}${resource_path.includes('?') ? '&' : '?'}ts=${cache_tag}`;
  };

  if (_session.worker_type === 'Dev') {
    ret = await get_ret(append_ts('./modules/' + module));

    return ret;
  }
  if (_session.worker_type === 'Debug') {
    if (typeof IS_DOCKER !== 'undefined' || typeof IS_PROCESS_SERVER !== 'undefined') {
      ret = await get_ret(func.utils.get_resource_filename(['live_preview', 'miniapp'].includes(_session.engine_mode) ? '' : _session?.opt?.app_build_id, `${_conf.xuda_home}root/dist/runtime/js/modules/` + module));
    } else {
      ret = await get_ret(
        append_ts(
          func.common.get_url(
            SESSION_ID,
            'dist',
            func.utils.get_resource_filename(['live_preview', 'miniapp'].includes(_session.engine_mode) ? '' : _session?.opt?.app_build_id, 'runtime/js/modules/' + module),
          ),
        ),
      );
    }

    return ret;
  }

  const rep = function () {
    return module.endsWith('.js') ? module.replace('.js', '.min.js') : module.replace('.mjs', '.min.mjs');
  };

  if (typeof IS_DOCKER !== 'undefined' || typeof IS_PROCESS_SERVER !== 'undefined') {
    ret = await get_ret(func.utils.get_resource_filename(['live_preview', 'miniapp'].includes(_session.engine_mode) ? '' : _session?.opt?.app_build_id, `${_conf.xuda_home}root/dist/runtime/js/modules/` + rep()));
  } else {
    ret = await get_ret(
      append_ts(
        func.common.get_url(
          SESSION_ID,
          'dist',
          func.utils.get_resource_filename(['live_preview', 'miniapp'].includes(_session.engine_mode) ? '' : _session?.opt?.app_build_id, 'runtime/js/modules/' + rep()),
        ),
      ),
    );
  }

  return ret;
};

func.api = {};
func.api.set_field_value = async function (field_id, value, avoid_refresh) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.set_field_value(field_id, value, avoid_refresh);
};
func.api.get_field_value = async function (field_id) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.get_field_value(field_id);
};
func.api.invoke_event = async function (event_id) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.invoke_event(event_id);
};
func.api.call_project_api = async function (prog_id, params) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.call_project_api(prog_id, params, null);
};
func.api.call_system_api = async function (api_method, payload) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.call_system_api(api_method, payload, null);
};

func.api.dbs_create = async function (table_id, data, cb) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.dbs_create(table_id, row_id, data, cb);
};
func.api.dbs_read = async function (table_id, selector, fields, sort, limit, skip, cb) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.dbs_read(table_id, selector, fields, sort, limit, skip, cb);
};
func.api.dbs_update = async function (table_id, row_id, data, cb) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.dbs_update(table_id, row_id, data, cb);
};
func.api.dbs_delete = async function (table_id, row_id, cb) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.dbs_delete(table_id, row_id, cb);
};

func.api.call_javascript = async function (prog_id, params, evaluate) {
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
    func,
    glb,
    SESSION_OBJ,
    SESSION_ID,
    APP_OBJ,
    dsSession: func.utils.get_last_datasource_no(SESSION_ID),
  });

  return await api_utils.call_javascript(prog_id, params, evaluate);
};

func.api.watch = function (path, cb, opt = {}) {
  if (!path) return 'path is mandatory';
  if (!cb) return 'cb (callback function) is mandatory';
  const SESSION_ID = Object.keys(SESSION_OBJ)[0];
  let _session = SESSION_OBJ[SESSION_ID];
  if (!_session.watchers) {
    _session.watchers = {};
  }

  _session.watchers[path] = { ...opt, handler: cb };

  if (opt.immediate) {
    const value = xu_get(SESSION_OBJ[SESSION_ID].DS_GLB[0], path);
    cb({ path, newValue: value, oldValue: value, timestamp: Date.now(), opt });

    if (opt.once) {
      delete _session.watchers[path];
    }
  }
  return 'ok';
};

// func.api.call_javascript = async function (prog_id, params, evaluate) {
//   const SESSION_ID = Object.keys(SESSION_OBJ)[0];
//   const api_utils = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
//     func,
//     glb,
//     SESSION_OBJ,
//     SESSION_ID,
//     APP_OBJ,
//     dsSession: func.utils.get_last_datasource_no(SESSION_ID),
//   });

//   return await api_utils.call_javascript(prog_id, params, evaluate);
// };

glb.rpi_request_queue_num = 0;
func.common.perform_rpi_request = async function (SESSION_ID, serviceP, opt = {}, data) {
  var _session = SESSION_OBJ[SESSION_ID];
  var _data_system = _session?.DS_GLB?.[0]?.data_system;

  const set_ajax = async function (stat) {
    var datasource_changes = {
      [0]: {
        ['data_system']: { SYS_GLOBAL_BOL_AJAX_BUSY: stat },
      },
    };
    await func.datasource.update(SESSION_ID, datasource_changes);
  };
  if (_data_system) {
    // _data_system.SYS_GLOBAL_BOL_AJAX_BUSY = 1;
    await set_ajax(1);
    if (!_data_system.SYS_GLOBAL_BOL_CONNECTED) {
      func.utils.alerts.toast(SESSION_ID, 'Server connection error', 'You are not connected to the server, so your request cannot be processed.', 'error');
      return { code: 88, data: {} };
    }
  }

  const http = async function () {
    const fetchWithTimeout = (url, options = {}, timeout = 600000) => {
      // 100 seconds
      const controller = new AbortController();
      const { signal } = controller;

      const timeoutPromise = new Promise((_, reject) =>
        setTimeout(() => {
          controller.abort();
          reject(new Error('Request timed out'));
        }, timeout),
      );

      const fetchPromise = fetch(url, { ...options, signal });

      return Promise.race([fetchPromise, timeoutPromise]);
    };

    var url = func.common.get_url(SESSION_ID, 'rpi', '');
    var _session = SESSION_OBJ[SESSION_ID];
    const app_id = _session.app_id;

    if (APP_OBJ[app_id].is_deployment && _session.rpi_http_methods?.includes(serviceP)) {
      const origin =
        ((typeof globalThis !== 'undefined' && globalThis.__XU_SERVER_ORIGIN__)) ||
        (_session.host ? 'https://' + _session.host : '');
      url = origin ? origin + '/rpi/' : url;
    }

    url += serviceP;

    try {
      const response = await fetchWithTimeout(url, {
        method: opt.type ? opt.type : 'POST',
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
          'xu-gtp-token': _session.gtp_token,
          'xu-app-token': _session.app_token,
        },
        body: JSON.stringify(data),
      });

      if (!response.ok) {
        throw response.status;
      }
      const json = await response.json();
      return json;
    } catch (err) {
      console.error(err);
      if (err === 503) {
        _this.func.UI.utils.progressScreen.show(SESSION_ID, `Error code ${err}, reloading in 5 sec`);
        setTimeout(async () => {
          await func.index.delete_pouch(SESSION_ID);
          location.reload();
        }, 5000);
      }
      return {};
    }
  };

  try {
    if (_session.engine_mode === 'live_preview') {
      throw new Error('live_preview');
    }
    if (_session.engine_mode === 'miniapp') {
      throw new Error('miniapp');
    }

    if (SESSION_OBJ?.[SESSION_ID]?.rpi_http_methods?.includes(serviceP)) {
      const ret = await func.common.get_data_from_websocket(SESSION_ID, serviceP, data);
      if (_data_system) {
        // _data_system.SYS_GLOBAL_BOL_AJAX_BUSY = 0;
        await set_ajax(0);
      }
      return ret;
    } else {
      throw new Error('method not found in rpi_http_methods');
    }
  } catch (err) {
    const ret = await http();
    if (_data_system) {
      // _data_system.SYS_GLOBAL_BOL_AJAX_BUSY = 0;
      await set_ajax(0);
    }
    return ret;
  }
};

func.common.get_data_from_websocket = async function (SESSION_ID, serviceP, data) {
  var _session = SESSION_OBJ[SESSION_ID];
  return new Promise(function (resolve, reject) {
    const dbs_calls = function () {
      glb.websocket_queue_num++;
      const obj = {
        service: serviceP,
        data,
        websocket_queue_num: glb.websocket_queue_num,
      };
      if (glb.IS_WORKER) {
        func.utils.post_back_to_client(SESSION_ID, 'get_dbs_data_from_websocket', _session.worker_id, obj);

        self.addEventListener('get_ws_data_worker_' + glb.websocket_queue_num, (event) => {
          resolve(event.detail.data);
        });
        // throw new Error("not ready yet");
      } else {
        if (RUNTIME_SERVER_WEBSOCKET && RUNTIME_SERVER_WEBSOCKET_CONNECTED) {
          RUNTIME_SERVER_WEBSOCKET.emit('message', obj);
          const _ws_event = 'get_ws_data_response_' + glb.websocket_queue_num;
          const _ws_handler = function (data) {
            resolve(data.data);
            func.runtime.platform.off('get_ws_data_response_' + data.e.websocket_queue_num, _ws_handler);
          };
          func.runtime.platform.on(_ws_event, _ws_handler);
        } else {
          throw new Error('fail to fetch from ws websocket inactive');
        }
      }
    };
    const heartbeat = function () {
      const obj = {
        service: 'heartbeat',
        data,
      };

      if (RUNTIME_SERVER_WEBSOCKET && RUNTIME_SERVER_WEBSOCKET_CONNECTED) {
        RUNTIME_SERVER_WEBSOCKET.emit('message', obj);
        const _hb_handler = function (data) {
          resolve(data.data);
          func.runtime.platform.off('heartbeat_response', _hb_handler);
        };
        func.runtime.platform.on('heartbeat_response', _hb_handler);
      } else {
        throw new Error('fail to fetch from ws websocket inactive');
      }
    };
    if (serviceP === 'heartbeat') {
      return heartbeat();
    }
    dbs_calls();
  });
};

// func.common.sha256 = async function (inputString) {
//   // 1. Create a hash buffer from the input string using SHA-256.
//   // This part remains the same as it provides a strong, unique cryptographic starting point.
//   const buffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(inputString));

//   // 2. Interpret the first 8 bytes (64 bits) of the hash as one big number.
//   const view = new DataView(buffer);
//   const bigInt = view.getBigUint64(0, false); // `false` for big-endian

//   // 3. Convert the BigInt to a Base36 string.
//   // The .toString(36) method handles the conversion to an alphanumeric representation (0-9, a-z).
//   const base36Hash = bigInt.toString(36);

//   // 4. Take the first 10 characters. If it's shorter, it will just return the whole string.
//   // For a 64-bit integer, the Base36 representation will be about 13 characters long,
//   // so slicing is a reliable way to get a fixed length.
//   const shortHash = base36Hash.slice(0, 10);

//   // 5. Pad the start in the unlikely case the hash is shorter than 10 characters.
//   // This ensures the output is always exactly 10 characters long.
//   return shortHash.padStart(10, '0');
// };

func.common.fastHash = function (inputString) {
  let hash = 0x811c9dc5; // FNV offset basis

  for (let i = 0; i < inputString.length; i++) {
    hash ^= inputString.charCodeAt(i);
    // FNV prime multiplication with 32-bit overflow
    hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
  }

  // Convert to base36 and pad to 10 characters
  return ((hash >>> 0).toString(36) + '0000000000').slice(0, 10);
};

glb.new_xu_render = false;
