@xuda.io/runtime-bundle
Version:
The Xuda Runtime Bundle refers to a collection of scripts and libraries packaged together to provide the necessary runtime environment for executing plugins or components in the Xuda platform.
3,367 lines • 113 kB
JavaScript
'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,
// lodash type-checks + common helpers (lodash was removed from the runtime;
// programs still call these on the global `_`).
isBoolean: function (v) { return typeof v === 'boolean'; },
isString: function (v) { return typeof v === 'string'; },
isNumber: function (v) { return typeof v === 'number'; },
isFunction: function (v) { return typeof v === 'function'; },
isObject: function (v) { return v !== null && (typeof v === 'object' || typeof v === 'function'); },
isPlainObject: function (v) { return v !== null && typeof v === 'object' && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null); },
isNil: function (v) { return v == null; },
isUndefined: function (v) { return v === undefined; },
isNull: function (v) { return v === null; },
isInteger: Number.isInteger,
isNaN: function (v) { return typeof v === 'number' && v !== v; },
keys: function (o) { return o ? Object.keys(o) : []; },
values: function (o) { return o ? Object.values(o) : []; },
size: function (o) { return o == null ? 0 : (typeof o.length === 'number' ? o.length : Object.keys(o).length); },
includes: function (c, v) { return c == null ? false : (typeof c.includes === 'function' ? c.includes(v) : Object.values(c).indexOf(v) > -1); },
filter: function (c, fn) { return (c ? (Array.isArray(c) ? c : Object.values(c)) : []).filter(function (x, i) { return fn(x, i); }); },
last: function (a) { return a && a.length ? a[a.length - 1] : undefined; },
first: function (a) { return a && a.length ? a[0] : undefined; },
head: function (a) { return a && a.length ? a[0] : undefined; },
uniq: function (a) { return a ? Array.from(new Set(a)) : []; },
compact: function (a) { return a ? a.filter(Boolean) : []; },
assign: Object.assign,
merge: function (t) { for (var i = 1; i < arguments.length; i++) Object.assign(t || {}, arguments[i]); return t; },
capitalize: function (s) { s = String(s == null ? '' : s); return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase(); },
noop: function () {},
};
}
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 get_attribute_source = function (source) {
if (!source) {
return {};
}
if (typeof source === 'string') {
try {
const parsed = JSON.parse(source);
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
} catch (_) {
return {};
}
}
return typeof source === 'object' && !Array.isArray(source) ? source : {};
};
const normalize_node_attributes = function (node) {
let attributes = get_attribute_source(node?.attributes);
attributes = merge_attributes(attributes, get_attribute_source(node?.attributes_raw_obj));
attributes = merge_attributes(attributes, get_attribute_source(node?.attributes_raw));
return attributes;
};
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, normalize_node_attributes(node));
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, normalize_node_attributes(node));
normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
continue;
}
let next_node = node;
const merged_node_attributes = normalize_node_attributes(node);
if (!xu_isEqual(merged_node_attributes, node.attributes || {})) {
next_node = {
...next_node,
attributes: merged_node_attributes,
};
state.changed = true;
}
if (Array.isArray(node.children) && node.children.length) {
const next_children = normalize_nodes(node.children, state);
if (next_children !== node.children) {
next_node = {
...next_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 root_node_attributes = normalize_node_attributes(root_node);
if (!xu_isEqual(root_node_attributes, root_node.attributes || {})) {
state.changed = true;
}
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 (Object.prototype.hasOwnProperty.call(nodeP.attributes, val.data.parameter)) {
params_res[val.data.parameter] = nodeP.attributes[val.data.parameter];
} else if (Object.prototype.hasOwnProperty.call(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_bind_node = function (elm) {
if (!elm) {
return null;
}
if (elm.nodeType) {
return elm;
}
if (Array.isArray(elm) || typeof elm.length === 'number') {
return elm[0] || null;
}
return null;
};
func.runtime.bind.is_value_node = function (node) {
if (!node?.tagName) {
return false;
}
const tag_name = node.tagName.toLowerCase();
return ['input', 'select', 'textarea'].includes(tag_name) || typeof node.value !== 'undefined';
};
func.runtime.bind.get_bind_value_node = function (elm) {
const node = func.runtime.bind.get_bind_node(elm);
if (!node) {
return null;
}
if (func.runtime.bind.is_value_node(node)) {
return node;
}
return node.querySelector?.('input, select, textarea') || node;
};
func.runtime.bind.should_use_live_text_listener = function (elm) {
const node = func.runtime.bind.get_bind_value_node(elm);
if (!node?.tagName) {
return false;
}
const tag_name = node.tagName.toLowerCase();
const type = (node.type || node.getAttribute?.('type') || '').toLowerCase();
if (tag_name === 'textarea') {
return true;
}
if (tag_name !== 'input') {
return false;
}
return !['button', 'checkbox', 'file', 'hidden', 'image', 'radio', 'reset', 'submit'].includes(type);
};
func.runtime.bind.get_live_text_debounce_ms = function () {
return 200;
};
func.runtime.bind.to_finite_number = function (value) {
if (value === '' || value === null || typeof value === 'undefined') {
return null;
}
const numeric_value = Number(value);
return Number.isFinite(numeric_value) ? numeric_value : null;
};
func.runtime.bind.get_select_option_number = function (option) {
if (!option) {
return null;
}
const candidate_attributes = ['value', 'data-value', 'data-xuda-value', 'data-xu-value', 'xu-value'];
for (let index = 0; index < candidate_attributes.length; index++) {
const attr_value = option.getAttribute?.(candidate_attributes[index]);
const numeric_value = func.runtime.bind.to_finite_number(attr_value);
if (numeric_value !== null) {
return numeric_value;
}
}
return null;
};
func.runtime.bind.remember_select_numeric_context = function (elm, field_type, value) {
if (field_type !== 'number' || elm?.tagName?.toLowerCase?.() !== 'select') {
return false;
}
const numeric_value = func.runtime.bind.to_finite_number(value);
if (numeric_value === null || elm.selectedIndex < 0) {
return false;
}
elm.__xuda_select_numeric_bind_context = {
selectedIndex: elm.selectedIndex,
value: numeric_value,
};
return true;
};
func.runtime.bind.apply_select_value_once = function (elm, value, field_type) {
const options = Array.from(elm.options || []);
if (!options.length) {
return false;
}
const string_value = value === null || typeof value === 'undefined' ? '' : String(value);
const numeric_value = func.runtime.bind.to_finite_number(value);
let matched_index = -1;
if (field_type === 'number' && numeric_value !== null) {
matched_index = options.findIndex(function (option) {
return func.runtime.bind.get_select_option_number(option) === numeric_value;
});
if (matched_index < 0 && Number.isInteger(numeric_value)) {
if (options[numeric_value - 1]) {
matched_index = numeric_value - 1;
} else if (options[numeric_value]) {
matched_index = numeric_value;
}
}
} else {
matched_index = options.findIndex(function (option) {
return String(option.value) === string_value || String(option.getAttribute?.('value')) === string_value;
});
}
if (matched_index >= 0) {
elm.selectedIndex = matched_index;
func.runtime.bind.remember_select_numeric_context(elm, field_type, value);
return true;
}
return false;
};
func.runtime.bind.schedule_select_value_retry = function (elm, value, field_type, scheduled_at) {
if (elm.__xuda_select_set_retry_timer_ids) {
for (let index = 0; index < elm.__xuda_select_set_retry_timer_ids.length; index++) {
clearTimeout(elm.__xuda_select_set_retry_timer_ids[index]);
}
}
const retry_delays = [0, 50, 250, 500];
elm.__xuda_select_set_retry_timer_ids = retry_delays.map(function (delay) {
return setTimeout(function () {
if (!elm.isConnected) {
return;
}
if (elm.__xuda_select_last_user_change_ts && elm.__xuda_select_last_user_change_ts > scheduled_at) {
return;
}
func.runtime.bind.apply_select_value_once(elm, value, field_type);
}, delay);
});
};
func.runtime.bind.set_select_value = function (elm, value, field_type) {
if (elm?.tagName?.toLowerCase?.() !== 'select') {
return false;
}
const options = Array.from(elm.options || []);
const string_value = value === null || typeof value === 'undefined' ? '' : String(value);
if (elm.multiple) {
const selected_values = Array.isArray(value)
? value.map(function (item) {
return String(item);
})
: [string_value];
options.forEach(function (option) {
option.selected = selected_values.includes(String(option.value));
});
return true;
}
const scheduled_at = Date.now();
func.runtime.bind.apply_select_value_once(elm, value, field_type);
func.runtime.bind.schedule_select_value_retry(elm, value, field_type, scheduled_at);
return true;
};
func.runtime.bind.get_select_numeric_value = function (elm, raw_value) {
const raw_numeric_value = func.runtime.bind.to_finite_number(raw_value);
if (raw_numeric_value !== null) {
return raw_numeric_value;
}
if (elm?.tagName?.toLowerCase?.() !== 'select') {
return raw_value;
}
const selected_option = elm.options?.[elm.selectedIndex];
const selected_option_number = func.runtime.bind.get_select_option_number(selected_option);
if (selected_option_number !== null) {
return selected_option_number;
}
const context = elm.__xuda_select_numeric_bind_context;
if (context && Number.isFinite(context.value) && Number.isFinite(context.selectedIndex) && elm.selectedIndex >= 0) {
return context.value + (elm.selectedIndex - context.selectedIndex);
}
if (elm.selectedIndex >= 0) {
return elm.selectedIndex + 1;
}
return raw_value;
};
func.runtime.bind.normalize_raw_value = function (elm, field_prop, raw_value) {
const field_type = func.runtime.bind.get_field_type(field_prop);
if (field_type === 'number') {
return func.runtime.bind.get_select_numeric_value(elm, raw_value);
}
return raw_value;
};
func.runtime.bind.track_pending_update = function (SESSION_ID, promise) {
const session_obj = SESSION_OBJ?.[SESSION_ID];
if (!session_obj || !promise?.finally) {
return promise;
}
if (!session_obj.pending_bind_updates) {
session_obj.pending_bind_updates = new Set();
}
const tracked_promise = promise.finally(function () {
session_obj.pending_bind_updates.delete(tracked_promise);
});
session_obj.pending_bind_updates.add(tracked_promise);
return tracked_promise;
};
func.runtime.bind.wait_for_pending_updates = async function (SESSION_ID) {
const pending_bind_updates = SESSION_OBJ?.[SESSION_ID]?.pending_bind_updates;
if (!pending_bind_updates?.size) {
return;
}
await Promise.allSettled(Array.from(pending_bind_updates));
};
func.runtime.bind.attach_live_text_listener = function (adapter_name, adapter, elm, handler) {
const node = func.runtime.bind.get_bind_value_node(elm);
if (!node?.addEventListener || typeof handler !== 'function') {
return false;
}
const listener_key = `__xuda_${adapter_name}_live_text_bind_listeners`;
const listener_state_key = `${listener_key}_state`;
const previous_listeners = node[listener_key];
if (previous_listeners) {
for (let index = 0; index < previous_listeners.length; index++) {
node.removeEventListener(previous_listeners[index].event_name, previous_listeners[index].listener);
}
}
if (node[listener_state_key]?.debounce_timer) {
clearTimeout(node[listener_state_key].debounce_timer);
}
const debounce_ms = func.runtime.bind.get_live_text_debounce_ms();
const state = {
debounce_timer: null,
last_value: adapter.getter ? adapter.getter.call(adapter, node) : node.value,
pending_event: null,
pending_value: adapter.getter ? adapter.getter.call(adapter, node) : node.value,
};
node[listener_state_key] = state;
const listener = function (event) {
const current_value = adapter.getter ? adapter.getter.call(adapter, node) : node.value;
if (xu_isEqual(current_value, state.pending_value)) {
return;
}
state.pending_event = event;
state.pending_value = current_value;
clearTimeout(state.debounce_timer);
state.debounce_timer = setTimeout(function () {
const next_value = adapter.getter ? adapter.getter.call(adapter, node) : node.value;
state.debounce_timer = null;
state.pending_value = next_value;
if (xu_isEqual(next_value, state.last_value)) {
return;
}
state.last_value = next_value;
return handler(state.pending_event);
}, debounce_ms);
};
const listeners = [];
for (const event_name of ['input', 'keyup']) {
node.addEventListener(event_name, listener);
listeners.push({ event_name, listener });
}
node[listener_key] = listeners;
return true;
};
func.runtime.bind.normalize_adapter = function (adapter, adapter_name = 'adapter') {
if (!func.runtime.bind.is_valid_adapter(adapter)) {
return adapter;
}
return {
getter: function (elm) {
return adapter.getter.call(adapter, func.runtime.bind.get_bind_value_node(elm) || elm);
},
setter: function (elm, value) {
return adapter.setter.call(adapter, func.runtime.bind.get_bind_value_node(elm) || elm, value);
},
listener: function (elm, handler) {
const node = func.runtime.bind.get_bind_value_node(elm) || elm;
if (func.runtime.bind.should_use_live_text_listener(node)) {
return func.runtime.bind.attach_live_text_listener(adapter_name, adapter, node, handler);
}
return adapter.listener.call(adapter, node, handler);
},
};
};
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 func.runtime.bind.normalize_adapter(native_adapter, 'native');
}
const plugin_bind = UI_FRAMEWORK_PLUGIN?.bind;
if (!plugin_bind) {
return func.runtime.bind.normalize_adapter(native_adapter, 'native');
}
if (func.runtime.bind.is_valid_adapter(plugin_bind)) {
return func.runtime.bind.normalize_adapter(plugin_bind, 'plugin');
}
if (typeof plugin_bind === 'function') {
try {
const bind_instance = new plugin_bind();
if (func.runtime.bind.is_valid_adapter(bind_instance)) {
return func.runtime.bind.normalize_adapter(bind_instance, 'plugin');
}
} catch (error) {}
try {
const bind_factory = plugin_bind();
if (func.runtime.bind.is_valid_adapter(bind_factory)) {
return func.runtime.bind.normalize_adapter(bind_factory, 'plugin');
}
} catch (error) {}
}
return func.runtime.bind.normalize_adapter(native_adapter, 'native');
};
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`;
}
if (!is_dynamic_field) {
const _ds = SESSION_OBJ[SESSION_ID]?.DS_GLB?.[_dsP];
const table_id = _ds?._dataSourceTableId;
if (table_id) {
try {
const table_obj = await func.utils.FILES_OBJ.get(SESSION_ID, table_id);
const table_field_prop = func.common.find_item_by_key(table_obj?.tableFields || [], 'field_id', field_id);
const table_field_type = table_field_prop?.props?.fieldType;
if (table_field_type) {
field_prop = {
...field_prop,
props: {
...(field_prop.props || {}),
fieldType: table_field_type,
},
};
}
} catch (error) {}
}
}
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) {
// Plugin property schemas can carry function-valued defaults (e.g. tippy
// `placement: () => "top"`), which structuredClone cannot clone and throws on,
// breaking every element that uses the plugin. xu_cloneDeep tries
// structuredClone first and falls back to a recursive clone that preserves
// function references, so the schema clones cleanly either way.
let resolved_properties = xu_cloneDeep(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) {
const plugin_element = func.runtime.ui.get_first_node?.($elm) || $elm?.[0] || $elm;
if (!plugin_element) {
return false;
}
await plugin_runtime_resources.fn(plugin_name, plugin_element, 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;
const server_origin = typeof globalThis !== 'undefined' ? globalThis.__XU_SERVER_ORIGIN__ : '';
if (server_origin) {
return `${server_origin}/plugins/${context.plugin_name}/${relative_path}?gtp_token=${context._session.gtp_token}&app_id=${context._session.app_id}`;
}
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.info('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.info('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 upsert_rep_request_from_remote_response = async function (db, rep_id, table_req_id, json) {
if (!json?.data?.opt) return;
try {
let existing_doc;
try {
existing_doc = await db.get(rep_id);
} catch (err) {}
const rep_doc = {
_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,
};
if (existing_doc?._rev) {
rep_doc._rev = existing_doc._rev;
}
await db.put(rep_doc);
func.db.pouch.set_db_replication_from_server(SESSION_ID);
} catch (err) {}
};
const read_remote_dbs = async function (db, rep_id, table_req_id) {
const json = await func.common.perform_rpi_request(SESSION_ID, serviceP, opt, data);
await upsert_rep_request_from_remote_response(db, rep_id, table_req_id, json);
return json;
};
const should_retry_live_preview_remote_read = function (json) {
return (
_session?.engine_mode === 'live_preview' &&
serviceP === 'dbs_read' &&
dataP.table_id &&
!dataP.count &&
!dataP.reduce &&
Array.isArray(json?.data?.rows) &&
!json.data.rows.length
);
};
const read_local_dbs_with_live_preview_fallback = async function (db, rep_id, table_req_id) {
const json = {
code: 1,
data: await func.db.pouch[serviceP](SESSION_ID, data),
};
if (should_retry_live_preview_remote_read(json)) {
return await read_remote_dbs(db, rep_id, table_req_id);
}
return json;
};
const read_dbs_pouch = async function (db) {
if (_session?.DS_GLB?.[dsSession]?.refreshed && (dataP.filterModelMongo || dataP.filterModelSql)) {
return await read_local_dbs_with_live_preview_fallback(db, get_rep_id(), null);
}
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 await read_local_dbs_with_live_preview_fallback(db, rep_id, table_req_id);
}
try {
const doc = await db.get(rep_id);
if (doc.stat < 3) throw 'replication not ready';
return await read_local_dbs_with_live_preview_fallback(db, rep_id, table_req_id);
} catch (err) {
return await read_remote_dbs(db, rep_id, table_req_id);
}
};
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);
if (_session?.engine_mode === 'live_preview' && !_session?.is_draft_runtime && ['dbs_update', 'dbs_create', 'dbs_delete'].includes(serviceP)) {
const json = await func.common.perform_rpi_request(SESSION_ID, serviceP, opt, data);
return success_callback(json, true);
}
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) {
// number/boolean/bigint -> string is a lossless coercion (String(v) is always exact); don't surface
// it as a runtime warning — it routes through report_issue and shows as an "Unhandled Runtime Error".
if (typeP === 'string' && (typeof valP === 'number' || typeof valP === 'boolean' || typeof valP === 'bigint')) return;
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 is_debug_live_runtime = ['Dev', 'Debug'].includes(_session?.worker_type) && ['live_preview', 'miniapp'].includes(_session?.engine_mode);
let local_runtime_cache_tag =
typeof globalThis !== 'undefined'
? globalThis.__XU_RUNTIME_MODULE_CACHE_TAG__ || globalThis.__XU_SERVER_BOOTSTRAP__?.version
: 0;
if (is_debug_live_runtime && typeof globalThis !== 'undefined') {
globalThis.__XU_RUNTIME_MODULE_CACHE_TAG__ = globalThis.__XU_RUNTIME_MODULE_CACHE_TAG__ || Date.now();
local_runtime_cache_tag = globalThis.__XU_RUNTIME_MODULE_CACHE_TAG__;
}
const cache_tag =
local_runtime_cache_tag ||
_session?.build_info?.runtime_ts ||
_session?.build_info?.last_changed_ts ||
_session?.build_info?.server_ts ||
_session?.opt?.app_build_id ||
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, options) {
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, options);
};
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;
// XU_PERF: opt-in fast paths (shallow render-context copies, drive-ref
// pre-scan, keyed xu-for reuse). Default off = legacy behavior byte-for-byte.
glb.XU_PERF = glb.XU_PERF || false;