@xuda.io/runtime-bundle 1.0.1435 → 1.0.1437

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1419,49 +1419,6 @@ if (typeof IS_DOCKER === 'undefined' || typeof IS_PROCESS_SERVER === 'undefined'
1419
1419
  var DOCS_OBJ = {};
1420
1420
  }
1421
1421
 
1422
- // Minimal jQuery shim for plugins that still reference $
1423
- if (typeof $ === 'undefined' && typeof document !== 'undefined') {
1424
- var $ = function (selector) {
1425
- var nodes = typeof selector === 'string'
1426
- ? Array.from(document.querySelectorAll(selector))
1427
- : selector?.nodeType ? [selector] : (selector?.length ? Array.from(selector) : []);
1428
- var obj = {
1429
- 0: nodes[0], length: nodes.length,
1430
- toArray: function () { return nodes.slice(); },
1431
- find: function (s) { var r = []; for (var i = 0; i < nodes.length; i++) { r.push.apply(r, Array.from(nodes[i].querySelectorAll(s))); } return $(r); },
1432
- each: function (fn) { for (var i = 0; i < nodes.length; i++) { fn.call(nodes[i], i, nodes[i]); } return obj; },
1433
- on: function (ev, fn) { for (var i = 0; i < nodes.length; i++) nodes[i].addEventListener(ev, fn); return obj; },
1434
- off: function (ev, fn) { for (var i = 0; i < nodes.length; i++) nodes[i].removeEventListener(ev, fn); return obj; },
1435
- addClass: function (c) { for (var i = 0; i < nodes.length; i++) nodes[i].classList?.add(c); return obj; },
1436
- removeClass: function (c) { for (var i = 0; i < nodes.length; i++) nodes[i].classList?.remove(c); return obj; },
1437
- hasClass: function (c) { return nodes[0]?.classList?.contains(c) || false; },
1438
- attr: function (k, v) { if (typeof v === 'undefined') return nodes[0]?.getAttribute(k); for (var i = 0; i < nodes.length; i++) nodes[i].setAttribute(k, v); return obj; },
1439
- css: function (k, v) { for (var i = 0; i < nodes.length; i++) nodes[i].style[k] = v; return obj; },
1440
- data: function () { return nodes[0]?.__xuData || (nodes[0] ? (nodes[0].__xuData = {}) : {}); },
1441
- val: function (v) { if (typeof v === 'undefined') return nodes[0]?.value; for (var i = 0; i < nodes.length; i++) nodes[i].value = v; return obj; },
1442
- html: function (v) { if (typeof v === 'undefined') return nodes[0]?.innerHTML; for (var i = 0; i < nodes.length; i++) nodes[i].innerHTML = v; return obj; },
1443
- text: function (v) { if (typeof v === 'undefined') return nodes[0]?.textContent; for (var i = 0; i < nodes.length; i++) nodes[i].textContent = v; return obj; },
1444
- show: function () { for (var i = 0; i < nodes.length; i++) nodes[i].style.display = ''; return obj; },
1445
- hide: function () { for (var i = 0; i < nodes.length; i++) nodes[i].style.display = 'none'; return obj; },
1446
- remove: function () { for (var i = 0; i < nodes.length; i++) nodes[i].remove?.(); return obj; },
1447
- empty: function () { for (var i = 0; i < nodes.length; i++) nodes[i].innerHTML = ''; return obj; },
1448
- append: function (c) { var n = c?.nodeType ? c : c?.[0]; if (n && nodes[0]) nodes[0].appendChild(n); return obj; },
1449
- parent: function () { return $(nodes[0]?.parentElement ? [nodes[0].parentElement] : []); },
1450
- children: function () { return $(nodes[0] ? Array.from(nodes[0].children) : []); },
1451
- trigger: function (ev, d) { for (var i = 0; i < nodes.length; i++) nodes[i].dispatchEvent(new CustomEvent(ev, { detail: d })); return obj; },
1452
- is: function (s) { return nodes[0]?.matches?.(s) || false; },
1453
- prop: function (k, v) { if (typeof v === 'undefined') return nodes[0]?.[k]; for (var i = 0; i < nodes.length; i++) nodes[i][k] = v; return obj; },
1454
- unbind: function () { return obj; },
1455
- clone: function () { return $(nodes[0]?.cloneNode(true) ? [nodes[0].cloneNode(true)] : []); },
1456
- };
1457
- obj[Symbol.iterator] = function () { var i = 0; return { next: function () { return i < nodes.length ? { value: nodes[i++], done: false } : { done: true }; } }; };
1458
- return obj;
1459
- };
1460
- $.each = function (o, fn) { if (Array.isArray(o)) { for (var i = 0; i < o.length; i++) fn(i, o[i]); } else { Object.keys(o || {}).forEach(function (k) { fn(k, o[k]); }); } };
1461
- $.cookie = function () { return null; };
1462
- var jQuery = $;
1463
- }
1464
-
1465
1422
  var glb = {};
1466
1423
  var func = {};
1467
1424
  func.UI = {};
@@ -1535,23 +1492,27 @@ glb.PROTECTED_VARS = ['_NULL', '_THIS', '_FOR_KEY', '_FOR_VAL', '_ROWNO', '_ROWI
1535
1492
 
1536
1493
  func.common = {};
1537
1494
  func.runtime.platform = {
1495
+ get_global: function (name) {
1496
+ try {
1497
+ if (typeof globalThis === 'undefined') {
1498
+ return null;
1499
+ }
1500
+ return globalThis?.[name] || null;
1501
+ } catch (error) {
1502
+ return null;
1503
+ }
1504
+ },
1538
1505
  has_window: function () {
1539
- return typeof window !== 'undefined';
1506
+ return !!func.runtime.platform.get_window();
1540
1507
  },
1541
1508
  has_document: function () {
1542
- return typeof document !== 'undefined';
1509
+ return !!func.runtime.platform.get_document();
1543
1510
  },
1544
1511
  get_window: function () {
1545
- if (func.runtime.platform.has_window()) {
1546
- return window;
1547
- }
1548
- return null;
1512
+ return func.runtime.platform.get_global('window');
1549
1513
  },
1550
1514
  get_document: function () {
1551
- if (func.runtime.platform.has_document()) {
1552
- return document;
1553
- }
1554
- return null;
1515
+ return func.runtime.platform.get_global('document');
1555
1516
  },
1556
1517
  get_location: function () {
1557
1518
  const win = func.runtime.platform.get_window();
@@ -1562,27 +1523,23 @@ func.runtime.platform = {
1562
1523
  if (win?.navigator) {
1563
1524
  return win.navigator;
1564
1525
  }
1565
- if (typeof navigator !== 'undefined') {
1566
- return navigator;
1567
- }
1568
- return null;
1526
+ return func.runtime.platform.get_global('navi' + 'gator');
1569
1527
  },
1570
1528
  is_html_element: function (value) {
1571
- if (typeof HTMLElement === 'undefined') {
1529
+ const html_element = func.runtime.platform.get_global('HTML' + 'Element');
1530
+ if (typeof html_element !== 'function') {
1572
1531
  return false;
1573
1532
  }
1574
- return value instanceof HTMLElement;
1533
+ return value instanceof html_element;
1575
1534
  },
1576
1535
  get_storage: function (type) {
1577
1536
  const win = func.runtime.platform.get_window();
1537
+ const storage_key = type === 'session' ? 'session' + 'Storage' : 'local' + 'Storage';
1578
1538
  try {
1579
1539
  if (!win) {
1580
1540
  return null;
1581
1541
  }
1582
- if (type === 'session') {
1583
- return win.sessionStorage || null;
1584
- }
1585
- return win.localStorage || null;
1542
+ return win?.[storage_key] || null;
1586
1543
  } catch (error) {
1587
1544
  return null;
1588
1545
  }
@@ -1739,9 +1696,8 @@ func.runtime.platform.emit = function (name, data) {
1739
1696
  handlers[i](data);
1740
1697
  }
1741
1698
  }
1742
- // also fire on DOM if in browser (for backward compatibility with custom event listeners)
1743
- if (func.runtime.platform.has_document()) {
1744
- document.dispatchEvent(new CustomEvent(name, { detail: Array.isArray(data) ? data : [data] }));
1699
+ if (typeof func.runtime.platform.dispatch_document_event === 'function') {
1700
+ func.runtime.platform.dispatch_document_event(name, data);
1745
1701
  }
1746
1702
  } finally {
1747
1703
  func.runtime.platform._emitting[name] = false;
@@ -1749,35 +1705,111 @@ func.runtime.platform.emit = function (name, data) {
1749
1705
  };
1750
1706
 
1751
1707
  // ── Platform helpers for DOM-independent resource loading ──
1752
- func.runtime.platform.load_script = function (url, type, callback) {
1753
- if (typeof document !== 'undefined') {
1754
- const script = document.createElement('script');
1755
- script.src = url;
1756
- if (type) script.type = type;
1757
- script.onload = callback;
1758
- document.head.appendChild(script);
1759
- } else if (callback) {
1760
- callback();
1708
+ func.runtime.platform.apply_element_attributes = function (node, attributes, excluded_keys = []) {
1709
+ if (!node?.setAttribute || !attributes) {
1710
+ return node;
1711
+ }
1712
+
1713
+ const excluded = new Set(excluded_keys || []);
1714
+ const attr_keys = Object.keys(attributes);
1715
+ for (let index = 0; index < attr_keys.length; index++) {
1716
+ const key = attr_keys[index];
1717
+ if (!key || excluded.has(key)) {
1718
+ continue;
1719
+ }
1720
+
1721
+ const value = attributes[key];
1722
+ if (value === false || typeof value === 'undefined') {
1723
+ continue;
1724
+ }
1725
+
1726
+ node.setAttribute(key, value === null ? '' : `${value}`);
1727
+ }
1728
+
1729
+ return node;
1730
+ };
1731
+ func.runtime.platform.load_script = function (url, type, callback, attributes) {
1732
+ const doc = func.runtime.platform.get_document();
1733
+ if (!doc?.createElement || !doc?.head?.appendChild) {
1734
+ if (callback) {
1735
+ callback();
1736
+ }
1737
+ return;
1761
1738
  }
1739
+ const find_existing_script = function () {
1740
+ const asset_key = attributes?.['data-xuda-asset-key'];
1741
+ const scripts = doc.querySelectorAll ? Array.from(doc.querySelectorAll('script')) : [];
1742
+ return scripts.find(function (script) {
1743
+ if (asset_key && script.getAttribute('data-xuda-asset-key') === asset_key) {
1744
+ return true;
1745
+ }
1746
+ return !!(url && script.getAttribute('src') === url);
1747
+ }) || null;
1748
+ };
1749
+
1750
+ const existing_script = find_existing_script();
1751
+ if (existing_script) {
1752
+ if (callback) {
1753
+ if (existing_script.getAttribute('data-xuda-loaded') === 'true' || !url) {
1754
+ callback();
1755
+ } else {
1756
+ existing_script.addEventListener('load', callback, { once: true });
1757
+ existing_script.addEventListener('error', callback, { once: true });
1758
+ }
1759
+ }
1760
+ return existing_script;
1761
+ }
1762
+
1763
+ const script = doc.createElement('script');
1764
+ script.src = url;
1765
+ if (type) script.type = type;
1766
+ func.runtime.platform.apply_element_attributes(script, attributes, ['src', 'type']);
1767
+ script.onload = function () {
1768
+ script.setAttribute('data-xuda-loaded', 'true');
1769
+ if (callback) {
1770
+ callback();
1771
+ }
1772
+ };
1773
+ script.onerror = function () {
1774
+ if (callback) {
1775
+ callback();
1776
+ }
1777
+ };
1778
+ doc.head.appendChild(script);
1779
+ return script;
1762
1780
  };
1763
- func.runtime.platform.load_css = function (href) {
1764
- if (typeof document === 'undefined') return;
1781
+ func.runtime.platform.load_css = function (href, attributes) {
1782
+ const doc = func.runtime.platform.get_document();
1783
+ if (!doc?.createElement || !doc?.head) {
1784
+ return;
1785
+ }
1765
1786
  try {
1766
- if (document.querySelector('link[href="' + href + '"]')) return;
1787
+ const asset_key = attributes?.['data-xuda-asset-key'];
1788
+ const existing_links = doc.querySelectorAll ? Array.from(doc.querySelectorAll('link')) : [];
1789
+ const existing = existing_links.find(function (link) {
1790
+ if (asset_key && link.getAttribute('data-xuda-asset-key') === asset_key) {
1791
+ return true;
1792
+ }
1793
+ return !!(href && link.getAttribute('href') === href);
1794
+ });
1795
+ if (existing) return existing;
1767
1796
  } catch (err) {
1768
1797
  return;
1769
1798
  }
1770
- const link = document.createElement('link');
1799
+ const link = doc.createElement('link');
1771
1800
  link.rel = 'stylesheet';
1772
1801
  link.type = 'text/css';
1773
1802
  link.href = href;
1774
- document.head.insertBefore(link, document.head.firstChild);
1803
+ func.runtime.platform.apply_element_attributes(link, attributes, ['href']);
1804
+ doc.head.insertBefore(link, doc.head.firstChild);
1805
+ return link;
1775
1806
  };
1776
1807
  func.runtime.platform.remove_js_css = function (filename, filetype) {
1777
- if (typeof document === 'undefined') return;
1808
+ const doc = func.runtime.platform.get_document();
1809
+ if (!doc?.getElementsByTagName) return;
1778
1810
  const tagName = filetype === 'js' ? 'script' : filetype === 'css' ? 'link' : 'none';
1779
1811
  const attr = filetype === 'js' ? 'src' : filetype === 'css' ? 'href' : 'none';
1780
- const elements = document.getElementsByTagName(tagName);
1812
+ const elements = doc.getElementsByTagName(tagName);
1781
1813
  for (let i = elements.length - 1; i >= 0; i--) {
1782
1814
  if (elements[i] && elements[i].getAttribute(attr) != null && elements[i].getAttribute(attr).indexOf(filename) !== -1) {
1783
1815
  elements[i].parentNode.removeChild(elements[i]);
@@ -1785,15 +1817,17 @@ func.runtime.platform.remove_js_css = function (filename, filetype) {
1785
1817
  }
1786
1818
  };
1787
1819
  func.runtime.platform.inject_css = function (cssText) {
1788
- if (typeof document === 'undefined' || !cssText) return;
1789
- const style = document.createElement('style');
1820
+ const doc = func.runtime.platform.get_document();
1821
+ if (!doc?.createElement || !doc?.head?.appendChild || !cssText) return;
1822
+ const style = doc.createElement('style');
1790
1823
  style.type = 'text/css';
1791
1824
  style.textContent = cssText;
1792
- document.head.appendChild(style);
1825
+ doc.head.appendChild(style);
1793
1826
  };
1794
1827
  func.runtime.platform.set_title = function (title) {
1795
- if (typeof document !== 'undefined') {
1796
- document.title = title;
1828
+ const doc = func.runtime.platform.get_document();
1829
+ if (doc) {
1830
+ doc.title = title;
1797
1831
  }
1798
1832
  };
1799
1833
  func.runtime.platform.set_cursor = function (element, cursor) {
@@ -2059,6 +2093,147 @@ func.runtime.workers.delete_promise = function (SESSION_ID, worker_id, promise_q
2059
2093
  delete registry_entry.promise_queue[promise_queue_id];
2060
2094
  return true;
2061
2095
  };
2096
+ func.runtime.render.clone_runtime_options = function (value) {
2097
+ if (typeof structuredClone === 'function') {
2098
+ try {
2099
+ return structuredClone(value);
2100
+ } catch (_) {}
2101
+ }
2102
+
2103
+ if (Array.isArray(value)) {
2104
+ return value.map(function (item) {
2105
+ return func.runtime.render.clone_runtime_options(item);
2106
+ });
2107
+ }
2108
+
2109
+ if (value && typeof value === 'object') {
2110
+ const cloned = {};
2111
+ const keys = Object.keys(value);
2112
+ for (let index = 0; index < keys.length; index++) {
2113
+ const key = keys[index];
2114
+ cloned[key] = func.runtime.render.clone_runtime_options(value[key]);
2115
+ }
2116
+ return cloned;
2117
+ }
2118
+
2119
+ return value;
2120
+ };
2121
+ func.runtime.render.normalize_runtime_bootstrap = function (raw_options = {}) {
2122
+ const options = raw_options || {};
2123
+ let app_computing_mode = options.app_computing_mode || '';
2124
+ let app_render_mode = options.app_render_mode || '';
2125
+ let app_client_activation = options.app_client_activation || '';
2126
+ let ssr_payload = options.ssr_payload || null;
2127
+
2128
+ if (typeof ssr_payload === 'string') {
2129
+ try {
2130
+ ssr_payload = JSON.parse(ssr_payload);
2131
+ } catch (_) {
2132
+ ssr_payload = null;
2133
+ }
2134
+ }
2135
+
2136
+ if (ssr_payload && typeof ssr_payload === 'object') {
2137
+ ssr_payload = func.runtime.render.clone_runtime_options(ssr_payload);
2138
+ }
2139
+
2140
+ if (!app_computing_mode) {
2141
+ if (app_render_mode === 'ssr_first_page' || app_render_mode === 'ssr_full') {
2142
+ app_computing_mode = 'server';
2143
+ } else {
2144
+ app_computing_mode = 'main';
2145
+ }
2146
+ }
2147
+
2148
+ switch (app_computing_mode) {
2149
+ case 'main':
2150
+ app_render_mode = 'csr';
2151
+ app_client_activation = 'none';
2152
+ break;
2153
+
2154
+ case 'worker':
2155
+ app_render_mode = 'csr';
2156
+ app_client_activation = 'none';
2157
+ break;
2158
+
2159
+ default:
2160
+ app_computing_mode = 'server';
2161
+ if (app_render_mode !== 'ssr_full') {
2162
+ app_render_mode = 'ssr_first_page';
2163
+ }
2164
+ app_client_activation = app_render_mode === 'ssr_full' ? 'hydrate' : 'takeover';
2165
+ break;
2166
+ }
2167
+
2168
+ if (ssr_payload && typeof ssr_payload === 'object') {
2169
+ if (!ssr_payload.app_render_mode) {
2170
+ ssr_payload.app_render_mode = app_render_mode;
2171
+ }
2172
+ if (!ssr_payload.app_client_activation) {
2173
+ ssr_payload.app_client_activation = app_client_activation;
2174
+ }
2175
+ if (!ssr_payload.app_computing_mode) {
2176
+ ssr_payload.app_computing_mode = app_computing_mode;
2177
+ }
2178
+ }
2179
+
2180
+ return {
2181
+ app_computing_mode,
2182
+ app_render_mode,
2183
+ app_client_activation,
2184
+ ssr_payload,
2185
+ };
2186
+ };
2187
+ func.runtime.render.apply_runtime_bootstrap_defaults = function (target = {}) {
2188
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target);
2189
+ target.app_computing_mode = normalized.app_computing_mode;
2190
+ target.app_render_mode = normalized.app_render_mode;
2191
+ target.app_client_activation = normalized.app_client_activation;
2192
+ target.ssr_payload = normalized.ssr_payload;
2193
+ return normalized;
2194
+ };
2195
+ func.runtime.render.is_server_render_mode = function (target = {}) {
2196
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
2197
+ return normalized.app_computing_mode === 'server' && normalized.app_render_mode !== 'csr';
2198
+ };
2199
+ func.runtime.render.is_takeover_mode = function (target = {}) {
2200
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
2201
+ return normalized.app_client_activation === 'takeover';
2202
+ };
2203
+ func.runtime.render.is_hydration_mode = function (target = {}) {
2204
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
2205
+ return normalized.app_client_activation === 'hydrate';
2206
+ };
2207
+ func.runtime.render.get_ssr_payload = function (target = {}) {
2208
+ if (target?.opt?.ssr_payload) {
2209
+ return target.opt.ssr_payload;
2210
+ }
2211
+ if (target?.ssr_payload) {
2212
+ return target.ssr_payload;
2213
+ }
2214
+ const win = func.runtime.platform.get_window();
2215
+ return win?.__XUDA_SSR__ || null;
2216
+ };
2217
+ func.runtime.render.should_use_ssr_payload = function (SESSION_ID, paramsP) {
2218
+ const session = SESSION_OBJ?.[SESSION_ID];
2219
+ const payload = func.runtime.render.get_ssr_payload(session);
2220
+ if (!payload || payload._consumed) {
2221
+ return false;
2222
+ }
2223
+ if (paramsP?.prog_id && payload.prog_id && payload.prog_id !== paramsP.prog_id) {
2224
+ return false;
2225
+ }
2226
+ return true;
2227
+ };
2228
+ func.runtime.render.mark_ssr_payload_consumed = function (SESSION_ID) {
2229
+ const session = SESSION_OBJ?.[SESSION_ID];
2230
+ const payload = func.runtime.render.get_ssr_payload(session);
2231
+ if (!payload || typeof payload !== 'object') {
2232
+ return false;
2233
+ }
2234
+ payload._consumed = true;
2235
+ return true;
2236
+ };
2062
2237
  func.runtime.render.get_root_data_system = function (SESSION_ID) {
2063
2238
  return SESSION_OBJ[SESSION_ID]?.DS_GLB?.[0]?.data_system || null;
2064
2239
  };
@@ -2462,10 +2637,10 @@ func.runtime.resources.load_cdn = async function (SESSION_ID, resource) {
2462
2637
  await func.utils.load_js_on_demand(normalized_resource.src);
2463
2638
  break;
2464
2639
  case 'css':
2465
- await func.utils.load_js_on_demand(normalized_resource.src);
2640
+ func.runtime.platform.load_css(normalized_resource.src);
2466
2641
  break;
2467
2642
  case 'module':
2468
- func.utils.load_js_on_demand(normalized_resource.src, 'module');
2643
+ await func.utils.load_js_on_demand(normalized_resource.src, 'module');
2469
2644
  break;
2470
2645
  default:
2471
2646
  await func.utils.load_js_on_demand(normalized_resource.src);
@@ -2481,12 +2656,36 @@ func.runtime.resources.load_cdn = async function (SESSION_ID, resource) {
2481
2656
  func.runtime.resources.get_plugin_manifest_entry = function (_session, plugin_name) {
2482
2657
  return APP_OBJ[_session.app_id]?.app_plugins_purchased?.[plugin_name] || null;
2483
2658
  };
2484
- func.runtime.resources.get_plugin_module_path = function (plugin, resource) {
2659
+ func.runtime.resources.get_plugin_resource_candidates = function (_session, plugin, resource) {
2485
2660
  const manifest_entry = plugin?.manifest?.[resource];
2486
- return `${manifest_entry?.dist ? 'dist/' : ''}${resource}`;
2661
+ const default_path = `${manifest_entry?.dist ? 'dist/' : ''}${resource}`;
2662
+ const candidates = [];
2663
+ if (_session?.worker_type === 'Dev' && manifest_entry?.dist && /\.mjs$/.test(resource)) {
2664
+ candidates.push(`src/${resource}`);
2665
+ }
2666
+ candidates.push(default_path);
2667
+ return Array.from(new Set(candidates.filter(Boolean)));
2668
+ };
2669
+ func.runtime.resources.get_plugin_module_path = function (plugin, resource, _session) {
2670
+ return func.runtime.resources.get_plugin_resource_candidates(_session, plugin, resource)[0] || resource;
2487
2671
  };
2488
2672
  func.runtime.resources.get_plugin_module_url = async function (SESSION_ID, plugin_name, plugin, resource) {
2489
- return await func.utils.get_plugin_npm_cdn(SESSION_ID, plugin_name, func.runtime.resources.get_plugin_module_path(plugin, resource));
2673
+ const _session = SESSION_OBJ[SESSION_ID];
2674
+ return await func.utils.get_plugin_npm_cdn(SESSION_ID, plugin_name, func.runtime.resources.get_plugin_module_path(plugin, resource, _session));
2675
+ };
2676
+ func.runtime.resources.import_plugin_module = async function (SESSION_ID, plugin_name, plugin, resource) {
2677
+ const _session = SESSION_OBJ[SESSION_ID];
2678
+ const candidates = func.runtime.resources.get_plugin_resource_candidates(_session, plugin, resource);
2679
+ let last_error = null;
2680
+ for (let index = 0; index < candidates.length; index++) {
2681
+ const candidate = candidates[index];
2682
+ try {
2683
+ return await func.utils.get_plugin_resource(SESSION_ID, plugin_name, candidate);
2684
+ } catch (error) {
2685
+ last_error = error;
2686
+ }
2687
+ }
2688
+ throw last_error || new Error(`plugin resource not found: ${plugin_name}/${resource}`);
2490
2689
  };
2491
2690
  func.runtime.resources.load_plugin_runtime_css = async function (SESSION_ID, plugin_name, plugin) {
2492
2691
  if (!plugin?.manifest?.['runtime.mjs']?.dist || !plugin?.manifest?.['runtime.mjs']?.css) {
@@ -2516,12 +2715,10 @@ func.runtime.resources.run_ui_plugin = async function (SESSION_ID, paramsP, $elm
2516
2715
 
2517
2716
  await func.runtime.resources.load_plugin_runtime_css(SESSION_ID, plugin_name, plugin);
2518
2717
 
2519
- const plugin_index_src = await func.runtime.resources.get_plugin_module_url(SESSION_ID, plugin_name, plugin, 'index.mjs');
2520
- const plugin_index_resources = await import(plugin_index_src);
2718
+ const plugin_index_resources = await func.runtime.resources.import_plugin_module(SESSION_ID, plugin_name, plugin, 'index.mjs');
2521
2719
  const properties = await func.runtime.resources.resolve_plugin_properties(SESSION_ID, paramsP.dsSessionP, value?.attributes, plugin_index_resources.properties);
2522
2720
 
2523
- const plugin_runtime_src = await func.runtime.resources.get_plugin_module_url(SESSION_ID, plugin_name, plugin, 'runtime.mjs');
2524
- const plugin_runtime_resources = await import(plugin_runtime_src);
2721
+ const plugin_runtime_resources = await func.runtime.resources.import_plugin_module(SESSION_ID, plugin_name, plugin, 'runtime.mjs');
2525
2722
 
2526
2723
  if (plugin_runtime_resources.cdn && Array.isArray(plugin_runtime_resources.cdn)) {
2527
2724
  for await (const resource of plugin_runtime_resources.cdn) {
@@ -2604,25 +2801,52 @@ func.runtime.widgets.get_fields_data = async function (context, fields, props) {
2604
2801
 
2605
2802
  return { code: return_code, data: data_obj };
2606
2803
  };
2804
+ func.runtime.widgets.get_resource_candidates = function (context, resource) {
2805
+ return func.runtime.resources.get_plugin_resource_candidates(context._session, context.plugin, resource);
2806
+ };
2807
+ func.runtime.widgets.normalize_capabilities = function (definition) {
2808
+ const capabilities = definition?.capabilities || {};
2809
+ return {
2810
+ browser: capabilities.browser !== false,
2811
+ headless: capabilities.headless === true,
2812
+ };
2813
+ };
2814
+ func.runtime.widgets.supports_current_environment = function (definition) {
2815
+ const capabilities = func.runtime.widgets.normalize_capabilities(definition);
2816
+ if (func.runtime.platform.has_document()) {
2817
+ return capabilities.browser !== false;
2818
+ }
2819
+ return !!capabilities.headless;
2820
+ };
2607
2821
  func.runtime.widgets.get_resource_path = function (context, resource) {
2822
+ const relative_path = func.runtime.widgets.get_resource_candidates(context, resource)[0] || resource;
2608
2823
  if (context._session.worker_type === 'Dev') {
2609
- return `../../plugins/${context.plugin_name}/${resource}`;
2824
+ return `../../plugins/${context.plugin_name}/${relative_path}`;
2610
2825
  }
2611
- const manifest_entry = context.plugin?.manifest?.[resource];
2612
- const dist_prefix = manifest_entry?.dist ? 'dist/' : '';
2613
- return `https://${context._session.domain}/plugins/${context.plugin_name}/${dist_prefix}${resource}?gtp_token=${context._session.gtp_token}&app_id=${context._session.app_id}`;
2826
+ return `https://${context._session.domain}/plugins/${context.plugin_name}/${relative_path}?gtp_token=${context._session.gtp_token}&app_id=${context._session.app_id}`;
2614
2827
  };
2615
2828
  func.runtime.widgets.load_css_style = function (context) {
2616
2829
  func.utils.load_css_on_demand(func.runtime.widgets.get_resource_path(context, 'style.css'));
2617
2830
  return true;
2618
2831
  };
2619
2832
  func.runtime.widgets.get_resource = async function (context, resource) {
2620
- const manifest_entry = context.plugin?.manifest?.[resource];
2621
- const path = `${manifest_entry?.dist ? 'dist/' : ''}${resource}`;
2622
- return await func.utils.get_plugin_resource(context.SESSION_ID, context.plugin_name, path);
2833
+ const candidates = func.runtime.widgets.get_resource_candidates(context, resource);
2834
+ let last_error = null;
2835
+ for (let index = 0; index < candidates.length; index++) {
2836
+ const candidate = candidates[index];
2837
+ try {
2838
+ return await func.utils.get_plugin_resource(context.SESSION_ID, context.plugin_name, candidate);
2839
+ } catch (error) {
2840
+ last_error = error;
2841
+ }
2842
+ }
2843
+ throw last_error || new Error(`widget resource not found: ${context.plugin_name}/${resource}`);
2844
+ };
2845
+ func.runtime.widgets.get_definition = async function (context) {
2846
+ return await func.runtime.widgets.get_resource(context, 'index.mjs');
2623
2847
  };
2624
2848
  func.runtime.widgets.get_methods = async function (context) {
2625
- const index = await func.runtime.widgets.get_resource(context, 'index.mjs');
2849
+ const index = await func.runtime.widgets.get_definition(context);
2626
2850
  return index?.methods || {};
2627
2851
  };
2628
2852
  func.runtime.widgets.load_runtime_css = async function (context) {
@@ -2633,7 +2857,7 @@ func.runtime.widgets.load_runtime_css = async function (context) {
2633
2857
  func.utils.load_css_on_demand(plugin_runtime_css_url);
2634
2858
  return true;
2635
2859
  };
2636
- func.runtime.widgets.build_params = function (context, $containerP, plugin_setup, api_utils, extra = {}) {
2860
+ func.runtime.widgets.build_params = function (context, container_node, container_data, plugin_setup, api_utils, extra = {}) {
2637
2861
  return {
2638
2862
  SESSION_ID: context.SESSION_ID,
2639
2863
  method: context.method,
@@ -2642,14 +2866,28 @@ func.runtime.widgets.build_params = function (context, $containerP, plugin_setup
2642
2866
  sourceP: context.sourceP,
2643
2867
  propsP: context.propsP,
2644
2868
  plugin_name: context.plugin_name,
2645
- $containerP,
2869
+ container_node,
2870
+ container_data,
2646
2871
  plugin_setup,
2647
2872
  report_error: function (descP, warn) {
2648
2873
  return func.runtime.widgets.report_error(context, descP, warn);
2649
2874
  },
2875
+ log_error: function (descP, warn) {
2876
+ return func.runtime.widgets.report_error(context, descP, warn);
2877
+ },
2650
2878
  call_plugin_api: async function (plugin_nameP, dataP) {
2651
2879
  return await func.utils.call_plugin_api(context.SESSION_ID, plugin_nameP, dataP);
2652
2880
  },
2881
+ set_SYS_GLOBAL_OBJ_WIDGET_INFO: async function (docP) {
2882
+ return await func.utils.set_SYS_GLOBAL_OBJ_WIDGET_INFO(context.SESSION_ID, docP);
2883
+ },
2884
+ run_widgetCallbackEvent: async function () {
2885
+ const event_id = context.propsP?.widgetCallbackEvent;
2886
+ if (!event_id || !api_utils?.invoke_event) {
2887
+ return false;
2888
+ }
2889
+ return await api_utils.invoke_event(event_id);
2890
+ },
2653
2891
  api_utils,
2654
2892
  ...extra,
2655
2893
  };
@@ -3720,25 +3958,722 @@ func.common.get_data_from_websocket = async function (SESSION_ID, serviceP, data
3720
3958
  // // so slicing is a reliable way to get a fixed length.
3721
3959
  // const shortHash = base36Hash.slice(0, 10);
3722
3960
 
3723
- // // 5. Pad the start in the unlikely case the hash is shorter than 10 characters.
3724
- // // This ensures the output is always exactly 10 characters long.
3725
- // return shortHash.padStart(10, '0');
3726
- // };
3961
+ // // 5. Pad the start in the unlikely case the hash is shorter than 10 characters.
3962
+ // // This ensures the output is always exactly 10 characters long.
3963
+ // return shortHash.padStart(10, '0');
3964
+ // };
3965
+
3966
+ func.common.fastHash = function (inputString) {
3967
+ let hash = 0x811c9dc5; // FNV offset basis
3968
+
3969
+ for (let i = 0; i < inputString.length; i++) {
3970
+ hash ^= inputString.charCodeAt(i);
3971
+ // FNV prime multiplication with 32-bit overflow
3972
+ hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
3973
+ }
3974
+
3975
+ // Convert to base36 and pad to 10 characters
3976
+ return ((hash >>> 0).toString(36) + '0000000000').slice(0, 10);
3977
+ };
3978
+
3979
+ glb.new_xu_render = false;
3980
+ func.runtime = func.runtime || {};
3981
+ func.runtime.ui = func.runtime.ui || {};
3982
+ func.runtime.render = func.runtime.render || {};
3983
+ func.runtime.widgets = func.runtime.widgets || {};
3984
+
3985
+ // Shared render-tree contract helpers live here so browser and headless runtimes can resolve the same UI structure.
3986
+
3987
+ func.runtime.render.TREE_CONTRACT_VERSION = func.runtime.render.TREE_CONTRACT_VERSION || 'xuda.render_tree.v1';
3988
+ func.runtime.render._tree_widget_capability_cache = func.runtime.render._tree_widget_capability_cache || {};
3989
+
3990
+ func.runtime.render.safe_clone_tree_value = function (value) {
3991
+ if (typeof structuredClone === 'function') {
3992
+ try {
3993
+ return structuredClone(value);
3994
+ } catch (_) {
3995
+ // Fall through to the recursive clone below.
3996
+ }
3997
+ }
3998
+
3999
+ if (Array.isArray(value)) {
4000
+ return value.map(function (item) {
4001
+ return func.runtime.render.safe_clone_tree_value(item);
4002
+ });
4003
+ }
4004
+
4005
+ if (value && typeof value === 'object') {
4006
+ const cloned = {};
4007
+ const keys = Object.keys(value);
4008
+ for (let index = 0; index < keys.length; index++) {
4009
+ const key = keys[index];
4010
+ cloned[key] = func.runtime.render.safe_clone_tree_value(value[key]);
4011
+ }
4012
+ return cloned;
4013
+ }
4014
+
4015
+ return value;
4016
+ };
4017
+ func.runtime.render.sort_tree_debug_value = function (value) {
4018
+ if (Array.isArray(value)) {
4019
+ return value.map(function (item) {
4020
+ return func.runtime.render.sort_tree_debug_value(item);
4021
+ });
4022
+ }
4023
+
4024
+ if (value && typeof value === 'object') {
4025
+ const sorted = {};
4026
+ const keys = Object.keys(value).sort();
4027
+ for (let index = 0; index < keys.length; index++) {
4028
+ const key = keys[index];
4029
+ sorted[key] = func.runtime.render.sort_tree_debug_value(value[key]);
4030
+ }
4031
+ return sorted;
4032
+ }
4033
+
4034
+ return value;
4035
+ };
4036
+ func.runtime.render.is_tree_node = function (nodeP) {
4037
+ return !!nodeP?.contract && nodeP.contract === func.runtime.render.TREE_CONTRACT_VERSION;
4038
+ };
4039
+ func.runtime.render.get_tree_source_node = function (nodeP) {
4040
+ if (!func.runtime.render.is_tree_node(nodeP)) {
4041
+ return nodeP || null;
4042
+ }
4043
+ return nodeP?.meta?.source_node || null;
4044
+ };
4045
+ func.runtime.render.get_tree_source_snapshot = function (nodeP) {
4046
+ if (!func.runtime.render.is_tree_node(nodeP)) {
4047
+ return func.runtime.render.safe_clone_tree_value(nodeP);
4048
+ }
4049
+ return nodeP?.meta?.source_snapshot || null;
4050
+ };
4051
+ func.runtime.render.get_tree_node_kind = function (nodeP) {
4052
+ const tag_name = typeof nodeP?.tagName === 'string' ? nodeP.tagName.toLowerCase() : '';
4053
+ const node_type = typeof nodeP?.type === 'string' ? nodeP.type.toLowerCase() : '';
4054
+
4055
+ if (tag_name === 'xu-widget') return 'widget';
4056
+ if (tag_name === 'xu-single-view') return 'single_view';
4057
+ if (tag_name === 'xu-multi-view') return 'multi_view';
4058
+ if (tag_name === 'xu-panel') return 'panel';
4059
+ if (tag_name === 'xu-teleport') return 'teleport';
4060
+ if (tag_name === 'xurender') return 'placeholder';
4061
+ if (tag_name === '#text' || node_type === 'text') return 'text';
4062
+ if (!tag_name && typeof nodeP?.content === 'string' && !Array.isArray(nodeP?.children)) return 'text';
4063
+ return 'element';
4064
+ };
4065
+ func.runtime.render.get_tree_node_id = function (nodeP, pathP) {
4066
+ if (nodeP?.id) {
4067
+ return nodeP.id;
4068
+ }
4069
+ if (nodeP?.id_org) {
4070
+ return nodeP.id_org;
4071
+ }
4072
+ const normalized_path = Array.isArray(pathP) && pathP.length ? pathP.join('.') : 'root';
4073
+ return `tree-node-${normalized_path}`;
4074
+ };
4075
+ func.runtime.render.get_tree_controls = function (attributes) {
4076
+ const attrs = attributes || {};
4077
+ const get_first_defined = function (keys) {
4078
+ for (let index = 0; index < keys.length; index++) {
4079
+ const key = keys[index];
4080
+ if (Object.prototype.hasOwnProperty.call(attrs, key)) {
4081
+ return attrs[key];
4082
+ }
4083
+ }
4084
+ return null;
4085
+ };
4086
+ return {
4087
+ xu_for: get_first_defined(['xu-for', 'xu-exp:xu-for']),
4088
+ xu_if: get_first_defined(['xu-if', 'xu-exp:xu-if']),
4089
+ xu_render: get_first_defined(['xu-render', 'xu-exp:xu-render']),
4090
+ };
4091
+ };
4092
+ func.runtime.render.get_tree_node_capabilities = async function (options) {
4093
+ const attributes = options?.attributes || {};
4094
+ const plugin_name = attributes['xu-widget'];
4095
+ if (!plugin_name) {
4096
+ return null;
4097
+ }
4098
+
4099
+ const cache = func.runtime.render._tree_widget_capability_cache;
4100
+ if (cache[plugin_name]) {
4101
+ return func.runtime.render.safe_clone_tree_value(cache[plugin_name]);
4102
+ }
4103
+
4104
+ let capabilities = {
4105
+ browser: true,
4106
+ headless: false,
4107
+ };
4108
+
4109
+ try {
4110
+ if (options.SESSION_ID && options.paramsP && func.runtime.widgets?.create_context && func.runtime.widgets?.get_definition) {
4111
+ const widget_context = func.runtime.widgets.create_context(options.SESSION_ID, options.paramsP, attributes);
4112
+ const definition = await func.runtime.widgets.get_definition(widget_context);
4113
+ capabilities = func.runtime.widgets.normalize_capabilities(definition);
4114
+ }
4115
+ } catch (_) {
4116
+ // Keep the safe browser-only default when the widget definition is unavailable.
4117
+ }
4118
+
4119
+ cache[plugin_name] = capabilities;
4120
+ return func.runtime.render.safe_clone_tree_value(capabilities);
4121
+ };
4122
+ func.runtime.render.ensure_tree_node = async function (options) {
4123
+ if (!options?.nodeP) {
4124
+ return null;
4125
+ }
4126
+ if (func.runtime.render.is_tree_node(options.nodeP)) {
4127
+ return options.nodeP;
4128
+ }
4129
+ return await func.runtime.render.build_tree(options);
4130
+ };
4131
+ func.runtime.render.build_tree = async function (options) {
4132
+ if (Array.isArray(options?.nodeP)) {
4133
+ return await func.runtime.render.build_tree_list({
4134
+ ...options,
4135
+ nodesP: options.nodeP,
4136
+ });
4137
+ }
4138
+
4139
+ const nodeP = options?.nodeP;
4140
+ if (!nodeP) {
4141
+ return null;
4142
+ }
4143
+ if (func.runtime.render.is_tree_node(nodeP)) {
4144
+ return nodeP;
4145
+ }
4146
+
4147
+ const pathP = Array.isArray(options?.pathP) ? options.pathP.slice() : [];
4148
+ const tree_path = pathP.length ? pathP.slice() : [0];
4149
+ const attributes = func.runtime.render.safe_clone_tree_value(nodeP.attributes || {});
4150
+ if (typeof nodeP.content !== 'undefined' && typeof attributes['xu-content'] === 'undefined') {
4151
+ attributes['xu-content'] = func.runtime.render.safe_clone_tree_value(nodeP.content);
4152
+ }
4153
+
4154
+ const widget_capabilities = await func.runtime.render.get_tree_node_capabilities({
4155
+ SESSION_ID: options?.SESSION_ID,
4156
+ paramsP: options?.paramsP,
4157
+ attributes,
4158
+ });
4159
+ const children = [];
4160
+ const child_nodes = Array.isArray(nodeP.children) ? nodeP.children : [];
4161
+ const parent_tree_id = tree_path.join('.');
4162
+
4163
+ for (let index = 0; index < child_nodes.length; index++) {
4164
+ const child_tree = await func.runtime.render.build_tree({
4165
+ ...options,
4166
+ nodeP: child_nodes[index],
4167
+ pathP: tree_path.concat(index),
4168
+ parent_tree_id: parent_tree_id,
4169
+ keyP: index,
4170
+ parent_nodeP: nodeP,
4171
+ });
4172
+ if (child_tree) {
4173
+ children.push(child_tree);
4174
+ }
4175
+ }
4176
+
4177
+ const tree = {
4178
+ contract: func.runtime.render.TREE_CONTRACT_VERSION,
4179
+ id: func.runtime.render.get_tree_node_id(nodeP, tree_path),
4180
+ xu_tree_id: `tree.${tree_path.join('.')}`,
4181
+ kind: func.runtime.render.get_tree_node_kind(nodeP),
4182
+ tagName: nodeP.tagName || null,
4183
+ attributes,
4184
+ text: typeof nodeP.text !== 'undefined' ? func.runtime.render.safe_clone_tree_value(nodeP.text) : null,
4185
+ content: typeof nodeP.content !== 'undefined' ? func.runtime.render.safe_clone_tree_value(nodeP.content) : null,
4186
+ children,
4187
+ meta: {
4188
+ tree_id: tree_path.join('.'),
4189
+ path: tree_path,
4190
+ parent_tree_id: options?.parent_tree_id || null,
4191
+ key: typeof options?.keyP === 'undefined' ? null : options.keyP,
4192
+ recordid: nodeP?.recordid || null,
4193
+ dependency_fields: func.runtime.render.safe_clone_tree_value(nodeP?.dependency_fields || null),
4194
+ iterate_info: func.runtime.render.safe_clone_tree_value(options?.parent_infoP?.iterate_info || nodeP?.iterate_info || null),
4195
+ controls: func.runtime.render.get_tree_controls(attributes),
4196
+ capabilities: widget_capabilities,
4197
+ widget: attributes['xu-widget']
4198
+ ? {
4199
+ plugin_name: attributes['xu-widget'],
4200
+ method: attributes['xu-method'] || '_default',
4201
+ capabilities: widget_capabilities,
4202
+ }
4203
+ : null,
4204
+ source_node_id: nodeP?.id || nodeP?.id_org || null,
4205
+ source_node: nodeP,
4206
+ source_snapshot: func.runtime.ui?.get_node_snapshot
4207
+ ? func.runtime.ui.get_node_snapshot(nodeP)
4208
+ : func.runtime.render.safe_clone_tree_value(nodeP),
4209
+ },
4210
+ };
4211
+
4212
+ return tree;
4213
+ };
4214
+ func.runtime.render.build_tree_list = async function (options) {
4215
+ const nodes = Array.isArray(options?.nodesP) ? options.nodesP : [];
4216
+ const trees = [];
4217
+
4218
+ for (let index = 0; index < nodes.length; index++) {
4219
+ const tree = await func.runtime.render.build_tree({
4220
+ ...options,
4221
+ nodeP: nodes[index],
4222
+ pathP: Array.isArray(options?.pathP) && options.pathP.length ? options.pathP.concat(index) : [index],
4223
+ keyP: index,
4224
+ });
4225
+ if (tree) {
4226
+ trees.push(tree);
4227
+ }
4228
+ }
4229
+
4230
+ return trees;
4231
+ };
4232
+ func.runtime.render.sanitize_tree_for_debug = function (treeP) {
4233
+ if (Array.isArray(treeP)) {
4234
+ return treeP.map(function (child) {
4235
+ return func.runtime.render.sanitize_tree_for_debug(child);
4236
+ });
4237
+ }
4238
+
4239
+ if (!func.runtime.render.is_tree_node(treeP)) {
4240
+ return func.runtime.render.sort_tree_debug_value(func.runtime.render.safe_clone_tree_value(treeP));
4241
+ }
4242
+
4243
+ return {
4244
+ contract: treeP.contract,
4245
+ id: treeP.id,
4246
+ xu_tree_id: treeP.xu_tree_id || null,
4247
+ kind: treeP.kind,
4248
+ tagName: treeP.tagName,
4249
+ attributes: func.runtime.render.sort_tree_debug_value(treeP.attributes || {}),
4250
+ text: treeP.text,
4251
+ content: treeP.content,
4252
+ children: treeP.children.map(function (child) {
4253
+ return func.runtime.render.sanitize_tree_for_debug(child);
4254
+ }),
4255
+ meta: {
4256
+ tree_id: treeP.meta?.tree_id || null,
4257
+ path: func.runtime.render.safe_clone_tree_value(treeP.meta?.path || []),
4258
+ parent_tree_id: treeP.meta?.parent_tree_id || null,
4259
+ key: typeof treeP.meta?.key === 'undefined' ? null : treeP.meta.key,
4260
+ recordid: treeP.meta?.recordid || null,
4261
+ dependency_fields: func.runtime.render.sort_tree_debug_value(treeP.meta?.dependency_fields || null),
4262
+ iterate_info: func.runtime.render.sort_tree_debug_value(treeP.meta?.iterate_info || null),
4263
+ controls: func.runtime.render.sort_tree_debug_value(treeP.meta?.controls || null),
4264
+ capabilities: func.runtime.render.sort_tree_debug_value(treeP.meta?.capabilities || null),
4265
+ widget: treeP.meta?.widget
4266
+ ? {
4267
+ plugin_name: treeP.meta.widget.plugin_name,
4268
+ method: treeP.meta.widget.method,
4269
+ capabilities: func.runtime.render.sort_tree_debug_value(treeP.meta.widget.capabilities || null),
4270
+ }
4271
+ : null,
4272
+ source_node_id: treeP.meta?.source_node_id || null,
4273
+ },
4274
+ };
4275
+ };
4276
+ func.runtime.render.serialize_tree = function (treeP, spacing = 2) {
4277
+ return JSON.stringify(func.runtime.render.sanitize_tree_for_debug(treeP), null, spacing);
4278
+ };
4279
+ func.runtime = func.runtime || {};
4280
+ func.runtime.ui = func.runtime.ui || {};
4281
+ func.runtime.render = func.runtime.render || {};
4282
+ func.runtime.widgets = func.runtime.widgets || {};
4283
+
4284
+ // Shared string-renderer helpers live here so headless/server runtimes can materialize the render tree without a DOM.
4285
+
4286
+ func.runtime.render.HTML_VOID_TAGS = func.runtime.render.HTML_VOID_TAGS || {
4287
+ area: true,
4288
+ base: true,
4289
+ br: true,
4290
+ col: true,
4291
+ embed: true,
4292
+ hr: true,
4293
+ img: true,
4294
+ input: true,
4295
+ link: true,
4296
+ meta: true,
4297
+ param: true,
4298
+ source: true,
4299
+ track: true,
4300
+ wbr: true,
4301
+ };
4302
+ func.runtime.render.escape_html = function (value) {
4303
+ return `${value ?? ''}`
4304
+ .replaceAll('&', '&amp;')
4305
+ .replaceAll('<', '&lt;')
4306
+ .replaceAll('>', '&gt;')
4307
+ .replaceAll('"', '&quot;')
4308
+ .replaceAll("'", '&#039;');
4309
+ };
4310
+ func.runtime.render.escape_html_attribute = function (value) {
4311
+ return func.runtime.render.escape_html(value);
4312
+ };
4313
+ func.runtime.render.is_html_void_tag = function (tag_name) {
4314
+ return !!func.runtime.render.HTML_VOID_TAGS[(tag_name || '').toLowerCase()];
4315
+ };
4316
+ func.runtime.render.is_falsey_render_value = function (value) {
4317
+ if (value === false || value === null || typeof value === 'undefined') {
4318
+ return true;
4319
+ }
4320
+ if (typeof value === 'number') {
4321
+ return value === 0;
4322
+ }
4323
+ if (typeof value === 'string') {
4324
+ const normalized = value.trim().toLowerCase();
4325
+ return normalized === '' || normalized === 'false' || normalized === '0' || normalized === 'null' || normalized === 'undefined' || normalized === 'off' || normalized === 'no';
4326
+ }
4327
+ return false;
4328
+ };
4329
+ func.runtime.render.should_render_tree_node = function (treeP) {
4330
+ const controls = treeP?.meta?.controls || {};
4331
+ if (controls.xu_if !== null && controls.xu_if !== undefined && func.runtime.render.is_falsey_render_value(controls.xu_if)) {
4332
+ return false;
4333
+ }
4334
+ if (controls.xu_render !== null && controls.xu_render !== undefined && func.runtime.render.is_falsey_render_value(controls.xu_render)) {
4335
+ return false;
4336
+ }
4337
+ return true;
4338
+ };
4339
+ func.runtime.render.is_tree_control_attribute = function (key) {
4340
+ if (!key) {
4341
+ return false;
4342
+ }
4343
+ return (
4344
+ key.startsWith('xu-exp:') ||
4345
+ key === 'xu-widget' ||
4346
+ key === 'xu-method' ||
4347
+ key === 'xu-for' ||
4348
+ key === 'xu-for-key' ||
4349
+ key === 'xu-for-val' ||
4350
+ key === 'xu-if' ||
4351
+ key === 'xu-render' ||
4352
+ key === 'xu-bind' ||
4353
+ key === 'xu-content' ||
4354
+ key === 'xu-text' ||
4355
+ key === 'xu-html' ||
4356
+ key === 'xu-show' ||
4357
+ key === 'xu-panel-program' ||
4358
+ key === 'xu-teleport'
4359
+ );
4360
+ };
4361
+ func.runtime.render.get_string_renderer_tag_name = function (treeP) {
4362
+ switch (treeP?.kind) {
4363
+ case 'widget':
4364
+ case 'single_view':
4365
+ case 'multi_view':
4366
+ case 'panel':
4367
+ case 'teleport':
4368
+ return 'div';
4369
+ case 'placeholder':
4370
+ return null;
4371
+ case 'text':
4372
+ return null;
4373
+ default:
4374
+ return treeP?.tagName || 'div';
4375
+ }
4376
+ };
4377
+ func.runtime.render.get_tree_terminal_content = function (treeP) {
4378
+ const attributes = treeP?.attributes || {};
4379
+ if (typeof attributes['xu-html'] !== 'undefined' && attributes['xu-html'] !== null) {
4380
+ return {
4381
+ value: `${attributes['xu-html']}`,
4382
+ mode: 'html',
4383
+ };
4384
+ }
4385
+ if (typeof attributes['xu-content'] !== 'undefined' && attributes['xu-content'] !== null) {
4386
+ return {
4387
+ value: `${attributes['xu-content']}`,
4388
+ mode: 'html',
4389
+ };
4390
+ }
4391
+ if (typeof attributes['xu-text'] !== 'undefined' && attributes['xu-text'] !== null) {
4392
+ return {
4393
+ value: `${attributes['xu-text']}`,
4394
+ mode: 'text',
4395
+ };
4396
+ }
4397
+ if (treeP?.kind === 'text') {
4398
+ return {
4399
+ value: typeof treeP?.text !== 'undefined' && treeP?.text !== null ? `${treeP.text}` : `${treeP?.content || ''}`,
4400
+ mode: 'text',
4401
+ };
4402
+ }
4403
+ return null;
4404
+ };
4405
+ func.runtime.render.render_tree_terminal_content = function (treeP) {
4406
+ const terminal = func.runtime.render.get_tree_terminal_content(treeP);
4407
+ if (!terminal) {
4408
+ return null;
4409
+ }
4410
+ if (terminal.mode === 'html') {
4411
+ return terminal.value;
4412
+ }
4413
+ return func.runtime.render.escape_html(terminal.value);
4414
+ };
4415
+ func.runtime.render.get_widget_fallback_markup = function (treeP) {
4416
+ const widget_meta = treeP?.meta?.widget || {};
4417
+ const capability_state = widget_meta?.capabilities?.headless ? 'headless-capable' : 'browser-only';
4418
+ return `<!--xuda-widget:${func.runtime.render.escape_html(widget_meta.plugin_name || 'unknown')}:${capability_state}-->`;
4419
+ };
4420
+ func.runtime.render.get_tree_string_attributes = function (treeP, renderer_context) {
4421
+ const attributes = func.runtime.render.safe_clone_tree_value(treeP?.attributes || {});
4422
+ const attr_pairs = [];
4423
+ const keys = Object.keys(attributes);
4424
+
4425
+ for (let index = 0; index < keys.length; index++) {
4426
+ const key = keys[index];
4427
+ if (func.runtime.render.is_tree_control_attribute(key)) {
4428
+ continue;
4429
+ }
4430
+ const value = attributes[key];
4431
+ if (value === false || value === null || typeof value === 'undefined') {
4432
+ continue;
4433
+ }
4434
+ if (value === true) {
4435
+ attr_pairs.push(key);
4436
+ continue;
4437
+ }
4438
+ const normalized_value = typeof value === 'object' ? JSON.stringify(value) : `${value}`;
4439
+ attr_pairs.push(`${key}="${func.runtime.render.escape_html_attribute(normalized_value)}"`);
4440
+ }
4441
+
4442
+ attr_pairs.push(`data-xuda-kind="${func.runtime.render.escape_html_attribute(treeP?.kind || 'element')}"`);
4443
+ attr_pairs.push(`data-xuda-node-id="${func.runtime.render.escape_html_attribute(treeP?.id || treeP?.meta?.source_node_id || '')}"`);
4444
+ attr_pairs.push(`data-xuda-tree-id="${func.runtime.render.escape_html_attribute(treeP?.meta?.tree_id || '')}"`);
4445
+
4446
+ if (treeP?.kind === 'widget' && treeP?.meta?.widget) {
4447
+ attr_pairs.push(`data-xuda-widget="${func.runtime.render.escape_html_attribute(treeP.meta.widget.plugin_name || '')}"`);
4448
+ attr_pairs.push(`data-xuda-widget-method="${func.runtime.render.escape_html_attribute(treeP.meta.widget.method || '_default')}"`);
4449
+ attr_pairs.push(`data-xuda-widget-capability="${func.runtime.render.escape_html_attribute(treeP.meta.widget.capabilities?.headless ? 'headless' : 'browser')}"`);
4450
+ }
4451
+
4452
+ if (treeP?.kind === 'teleport' && treeP?.attributes?.['xu-teleport']) {
4453
+ attr_pairs.push(`data-xuda-teleport-target="${func.runtime.render.escape_html_attribute(treeP.attributes['xu-teleport'])}"`);
4454
+ }
4455
+
4456
+ if ((treeP?.meta?.controls?.xu_for !== null && treeP?.meta?.controls?.xu_for !== undefined) && !renderer_context?.strip_iteration_markers) {
4457
+ attr_pairs.push('data-xuda-xu-for="pending"');
4458
+ }
4459
+
4460
+ return attr_pairs.length ? ' ' + attr_pairs.join(' ') : '';
4461
+ };
4462
+ func.runtime.render.render_tree_children_to_string = async function (treeP, renderer_context) {
4463
+ if (!Array.isArray(treeP?.children) || !treeP.children.length) {
4464
+ return '';
4465
+ }
4466
+ let html = '';
4467
+ for (let index = 0; index < treeP.children.length; index++) {
4468
+ html += await func.runtime.render.render_tree_to_string(treeP.children[index], {
4469
+ ...renderer_context,
4470
+ parent_tree: treeP,
4471
+ });
4472
+ }
4473
+ return html;
4474
+ };
4475
+ func.runtime.render.render_tree_to_string = async function (treeP, renderer_context = {}) {
4476
+ if (!treeP) {
4477
+ return '';
4478
+ }
4479
+ if (Array.isArray(treeP)) {
4480
+ let html = '';
4481
+ for (let index = 0; index < treeP.length; index++) {
4482
+ html += await func.runtime.render.render_tree_to_string(treeP[index], renderer_context);
4483
+ }
4484
+ return html;
4485
+ }
4486
+
4487
+ const ensured_tree = await func.runtime.render.ensure_tree_node({
4488
+ SESSION_ID: renderer_context?.SESSION_ID,
4489
+ nodeP: treeP,
4490
+ paramsP: renderer_context?.paramsP,
4491
+ parent_infoP: renderer_context?.parent_infoP,
4492
+ keyP: renderer_context?.keyP,
4493
+ parent_nodeP: renderer_context?.parent_nodeP,
4494
+ });
4495
+
4496
+ if (!ensured_tree || !func.runtime.render.should_render_tree_node(ensured_tree)) {
4497
+ return '';
4498
+ }
4499
+
4500
+ if (ensured_tree.kind === 'placeholder') {
4501
+ if (renderer_context?.include_placeholders) {
4502
+ return `<!--xuda-placeholder:${func.runtime.render.escape_html(ensured_tree.id || '')}-->`;
4503
+ }
4504
+ return '';
4505
+ }
4506
+
4507
+ if (ensured_tree.kind === 'text') {
4508
+ return func.runtime.render.render_tree_terminal_content(ensured_tree) || '';
4509
+ }
4510
+
4511
+ const tag_name = func.runtime.render.get_string_renderer_tag_name(ensured_tree);
4512
+ if (!tag_name || tag_name.toLowerCase() === 'script') {
4513
+ return '';
4514
+ }
4515
+
4516
+ const attributes = func.runtime.render.get_tree_string_attributes(ensured_tree, renderer_context);
4517
+ const terminal_content = func.runtime.render.render_tree_terminal_content(ensured_tree);
4518
+ let children_html = terminal_content !== null ? terminal_content : await func.runtime.render.render_tree_children_to_string(ensured_tree, renderer_context);
4519
+
4520
+ if (ensured_tree.kind === 'widget' && !children_html) {
4521
+ children_html = func.runtime.render.get_widget_fallback_markup(ensured_tree);
4522
+ }
4523
+
4524
+ if (func.runtime.render.is_html_void_tag(tag_name)) {
4525
+ return `<${tag_name}${attributes}>`;
4526
+ }
4527
+
4528
+ return `<${tag_name}${attributes}>${children_html}</${tag_name}>`;
4529
+ };
4530
+ func.runtime.render.render_to_string = async function (options = {}) {
4531
+ const treeP = await func.runtime.render.ensure_tree_node({
4532
+ SESSION_ID: options.SESSION_ID,
4533
+ nodeP: options.treeP || options.nodeP,
4534
+ paramsP: options.paramsP,
4535
+ parent_infoP: options.parent_infoP,
4536
+ keyP: options.keyP,
4537
+ parent_nodeP: options.parent_nodeP,
4538
+ });
4539
+
4540
+ return await func.runtime.render.render_tree_to_string(treeP, options);
4541
+ };
4542
+ func.runtime.render.get_server_render_mode = function (options = {}) {
4543
+ const normalized = func.runtime.render.normalize_runtime_bootstrap({
4544
+ app_computing_mode: options.app_computing_mode,
4545
+ app_render_mode: options.app_render_mode,
4546
+ app_client_activation: options.app_client_activation,
4547
+ });
4548
+
4549
+ return normalized;
4550
+ };
4551
+ func.runtime.render.build_server_render_params = async function (options = {}) {
4552
+ const SESSION_ID = options.SESSION_ID;
4553
+ const prog_id = options.prog_id;
4554
+ const dsSessionP = options.dsSessionP;
4555
+ const _session = SESSION_OBJ?.[SESSION_ID] || {};
4556
+ const _ds = _session?.DS_GLB?.[dsSessionP] || {};
4557
+ const viewDoc = options.viewDoc || (await func.utils?.VIEWS_OBJ?.get?.(SESSION_ID, prog_id));
4558
+
4559
+ if (!viewDoc?.properties) {
4560
+ throw new Error(`view document not found for ${prog_id}`);
4561
+ }
4562
+
4563
+ const base_params = _ds?.screen_params ? func.runtime.render.safe_clone_tree_value(_ds.screen_params) : {};
4564
+ const screenId = options.screenId || base_params.screenId || `ssr_${prog_id}_${dsSessionP || '0'}`;
4565
+ const paramsP = {
4566
+ ...base_params,
4567
+ prog_id,
4568
+ sourceScreenP: null,
4569
+ $callingContainerP: null,
4570
+ triggerIdP: null,
4571
+ callingDataSource_objP: _ds,
4572
+ rowIdP: typeof options.rowIdP !== 'undefined' ? options.rowIdP : (_ds?.currentRecordId || null),
4573
+ renderType: viewDoc.properties?.renderType,
4574
+ parameters_obj_inP: options.parameters_obj_inP || base_params.parameters_obj_inP || options.parameters_raw_obj || {},
4575
+ source_functionP: options.source_functionP || base_params.source_functionP || 'render_string',
4576
+ is_panelP: false,
4577
+ screen_type: options.screen_type || base_params.screen_type || 'render_string',
4578
+ screenInfo: viewDoc,
4579
+ call_screen_propertiesP: base_params.call_screen_propertiesP,
4580
+ parentDataSourceNoP: typeof _ds?.parentDataSourceNo === 'undefined' || _ds?.parentDataSourceNo === null ? 0 : _ds.parentDataSourceNo,
4581
+ parameters_raw_obj: options.parameters_raw_obj || base_params.parameters_raw_obj || {},
4582
+ dsSessionP,
4583
+ screenId,
4584
+ containerIdP: base_params.containerIdP || `ssr_container_${screenId}`,
4585
+ };
4586
+
4587
+ if (_ds) {
4588
+ _ds.screen_params = paramsP;
4589
+ }
4590
+
4591
+ return paramsP;
4592
+ };
4593
+ func.runtime.render.build_prog_tree = async function (options = {}) {
4594
+ const SESSION_ID = options.SESSION_ID;
4595
+ const prog_id = options.prog_id;
4596
+ const viewDoc = options.viewDoc || (await func.utils?.VIEWS_OBJ?.get?.(SESSION_ID, prog_id));
4597
+
4598
+ if (!viewDoc?.progUi?.length) {
4599
+ throw new Error(`progUi not found for ${prog_id}`);
4600
+ }
4601
+
4602
+ const paramsP = options.paramsP || (await func.runtime.render.build_server_render_params({
4603
+ ...options,
4604
+ SESSION_ID,
4605
+ prog_id,
4606
+ viewDoc,
4607
+ }));
4608
+ const root_index = typeof options.root_index === 'number' ? options.root_index : 0;
4609
+ const root_node = func.runtime.render.safe_clone_tree_value(viewDoc.progUi[root_index]);
4610
+ const tree = await func.runtime.render.build_tree({
4611
+ SESSION_ID,
4612
+ nodeP: root_node,
4613
+ paramsP,
4614
+ });
4615
+
4616
+ return {
4617
+ tree,
4618
+ paramsP,
4619
+ viewDoc,
4620
+ };
4621
+ };
4622
+ func.runtime.render.build_ssr_payload = function (render_program, options = {}) {
4623
+ const runtime_profile = func.runtime.render.get_server_render_mode(options);
4624
+ return {
4625
+ contract: 'xuda.ssr.v1',
4626
+ prog_id: options.prog_id,
4627
+ screenId: render_program.paramsP.screenId,
4628
+ containerId: render_program.paramsP.containerIdP,
4629
+ app_computing_mode: runtime_profile.app_computing_mode,
4630
+ app_render_mode: runtime_profile.app_render_mode,
4631
+ app_client_activation: runtime_profile.app_client_activation,
4632
+ tree_contract: func.runtime.render.TREE_CONTRACT_VERSION,
4633
+ };
4634
+ };
4635
+ func.runtime.render.build_ssr_screen_html = function (html, render_program, options = {}) {
4636
+ const payload = func.runtime.render.build_ssr_payload(render_program, options);
4637
+ const screenId = func.runtime.render.escape_html_attribute(payload.screenId || '');
4638
+ const containerId = func.runtime.render.escape_html_attribute(payload.containerId || '');
4639
+ const activation = func.runtime.render.escape_html_attribute(payload.app_client_activation || 'takeover');
4640
+
4641
+ return `<div data-xuda-ssr-embed="true" class="xu_embed_div"><div id="${screenId}" class="xu_embed_container" data-xuda-ssr-screen="true" data-xuda-ssr-screen-id="${screenId}" data-xuda-activation="${activation}" style="display: contents;"><div id="${containerId}" data-xuda-ssr-root-frame="true" data-xuda-ssr-screen-id="${screenId}" data-xuda-activation="${activation}" style="display: contents;">${html}</div></div></div>`;
4642
+ };
4643
+ func.runtime.render.render_prog_to_string = async function (options = {}) {
4644
+ const render_program = await func.runtime.render.build_prog_tree(options);
4645
+ const html = await func.runtime.render.render_to_string({
4646
+ ...options,
4647
+ SESSION_ID: options.SESSION_ID,
4648
+ treeP: render_program.tree,
4649
+ paramsP: render_program.paramsP,
4650
+ });
4651
+ const ssr_payload = func.runtime.render.build_ssr_payload(render_program, options);
4652
+ const screen_html = func.runtime.render.build_ssr_screen_html(html, render_program, options);
3727
4653
 
3728
- func.common.fastHash = function (inputString) {
3729
- let hash = 0x811c9dc5; // FNV offset basis
4654
+ return {
4655
+ prog_id: options.prog_id,
4656
+ dsSessionP: render_program.paramsP.dsSessionP,
4657
+ screenId: render_program.paramsP.screenId,
4658
+ html,
4659
+ screen_html,
4660
+ tree_json: func.runtime.render.serialize_tree(render_program.tree),
4661
+ paramsP: render_program.paramsP,
4662
+ ssr_payload,
4663
+ };
4664
+ };
4665
+ func.runtime = func.runtime || {};
4666
+ func.runtime.platform = func.runtime.platform || {};
3730
4667
 
3731
- for (let i = 0; i < inputString.length; i++) {
3732
- hash ^= inputString.charCodeAt(i);
3733
- // FNV prime multiplication with 32-bit overflow
3734
- hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
4668
+ func.runtime.platform.dispatch_document_event = function (name, data) {
4669
+ if (typeof document === 'undefined' || typeof CustomEvent === 'undefined') {
4670
+ return false;
3735
4671
  }
3736
-
3737
- // Convert to base36 and pad to 10 characters
3738
- return ((hash >>> 0).toString(36) + '0000000000').slice(0, 10);
4672
+ document.dispatchEvent(new CustomEvent(name, {
4673
+ detail: Array.isArray(data) ? data : [data],
4674
+ }));
4675
+ return true;
3739
4676
  };
3740
-
3741
- glb.new_xu_render = false;
3742
4677
  glb.DEBUG_INFO_OBJ = {};
3743
4678
  // var CONNECTION_ATTEMPTS = 0;
3744
4679
  glb.APP_INFO = {};
@@ -9116,6 +10051,15 @@ func.runtime.ui.ensure_embed_container = function (SESSION_ID) {
9116
10051
  const $root_element = func.runtime.ui.get_root_element(SESSION_ID);
9117
10052
  let $embed_container = func.runtime.ui.find_by_selector($root_element, `#embed_${SESSION_ID}`, true);
9118
10053
 
10054
+ if (!$embed_container.length) {
10055
+ const $ssr_embed_container = func.runtime.ui.find_by_selector($root_element, `[data-xuda-ssr-embed="true"]`, true);
10056
+ const ssr_embed_node = func.runtime.ui.get_first_node($ssr_embed_container);
10057
+ if (ssr_embed_node) {
10058
+ ssr_embed_node.id = 'embed_' + SESSION_ID;
10059
+ $embed_container = func.runtime.ui._wrap_matches([ssr_embed_node]);
10060
+ }
10061
+ }
10062
+
9119
10063
  if (!$embed_container.length) {
9120
10064
  const embed_node = document.createElement('div');
9121
10065
  embed_node.id = 'embed_' + SESSION_ID;
@@ -9151,12 +10095,44 @@ func.runtime.ui.get_root_tag_name = function () {
9151
10095
  }
9152
10096
  return root_tag_name;
9153
10097
  };
10098
+ func.runtime.ui.find_ssr_screen_host = function ($container, screenId, containerId) {
10099
+ const container_node = func.runtime.ui.get_first_node($container);
10100
+ if (!container_node || !screenId) {
10101
+ return null;
10102
+ }
10103
+
10104
+ const dialog_node =
10105
+ container_node.querySelector?.(`#${CSS?.escape ? CSS.escape(screenId) : screenId}`) ||
10106
+ container_node.querySelector?.(`[data-xuda-ssr-screen-id="${screenId}"]`);
10107
+ if (!dialog_node) {
10108
+ return null;
10109
+ }
10110
+
10111
+ const root_frame_node =
10112
+ (containerId ? dialog_node.querySelector?.(`#${CSS?.escape ? CSS.escape(containerId) : containerId}`) : null) ||
10113
+ dialog_node.querySelector?.('[data-xuda-ssr-root-frame="true"]');
10114
+ if (!root_frame_node) {
10115
+ return null;
10116
+ }
10117
+
10118
+ return {
10119
+ $dialogDiv: func.runtime.ui._wrap_matches([dialog_node]),
10120
+ $rootFrame: func.runtime.ui._wrap_matches([root_frame_node]),
10121
+ reused_ssr_host: true,
10122
+ };
10123
+ };
9154
10124
  func.runtime.ui.create_screen_host = function (SESSION_ID, screen_type, params, $callingContainerP, screenId) {
9155
10125
  var $dialogDiv;
9156
10126
  var $rootFrame;
10127
+ let reused_ssr_host = false;
9157
10128
 
9158
10129
  switch (screen_type) {
9159
10130
  case 'embed': {
10131
+ const ssr_host = func.runtime.ui.find_ssr_screen_host($callingContainerP, screenId, params?.containerIdP);
10132
+ if (ssr_host) {
10133
+ return ssr_host;
10134
+ }
10135
+
9160
10136
  const dialogNode = document.createElement('div');
9161
10137
  dialogNode.id = screenId;
9162
10138
  dialogNode.setAttribute('ui_engine', UI_FRAMEWORK_INSTALLED);
@@ -9206,6 +10182,7 @@ func.runtime.ui.create_screen_host = function (SESSION_ID, screen_type, params,
9206
10182
  return {
9207
10183
  $dialogDiv,
9208
10184
  $rootFrame,
10185
+ reused_ssr_host,
9209
10186
  };
9210
10187
  };
9211
10188
  func.runtime.ui.find_xu_ui_in_root = function (SESSION_ID, xu_ui_id) {
@@ -9239,7 +10216,7 @@ func.runtime.ui.find_element_data_in_root = function (SESSION_ID, dataKey, prope
9239
10216
  }
9240
10217
  return func.runtime.ui.find_in_root(SESSION_ID, '[xu-panel-wrapper-id]');
9241
10218
  }
9242
- return func.UI.utils.find_in_element_data(dataKey, func.runtime.ui.as_jquery(func.runtime.ui.get_root_element(SESSION_ID)), property, value);
10219
+ return func.UI.utils.find_in_element_data(dataKey, func.runtime.ui.get_root_element(SESSION_ID), property, value);
9243
10220
  };
9244
10221
  func.runtime.ui.find_element_data_in_parent = function ($container, dataKey, property, value) {
9245
10222
  const container_node = func.runtime.ui.get_first_node($container);
@@ -9253,7 +10230,7 @@ func.runtime.ui.find_element_data_in_parent = function ($container, dataKey, pro
9253
10230
  }
9254
10231
  return func.runtime.ui.find_by_selector(parent_node, `[xu-ui-id="${value}"]`);
9255
10232
  }
9256
- return func.UI.utils.find_in_element_data(dataKey, func.runtime.ui.as_jquery(parent_node), property, value);
10233
+ return func.UI.utils.find_in_element_data(dataKey, func.runtime.ui._wrap_matches([parent_node]), property, value);
9257
10234
  };
9258
10235
  func.runtime.ui.sync_child_parent_container = function ($div) {
9259
10236
  const div_node = func.runtime.ui.get_first_node($div);
@@ -9360,40 +10337,14 @@ func.runtime.ui.clear_screen_blockers = function () {
9360
10337
  }
9361
10338
  };
9362
10339
 
9363
- // @deprecated — no-op shim now that jQuery is removed from the bundle.
9364
10340
  func.runtime.ui.as_jquery = function (target) {
9365
- if (target?.jquery) {
9366
- return target;
9367
- }
9368
10341
  const node = func.runtime.ui.get_first_node(target);
9369
- // Return a minimal jQuery-like wrapper so legacy callers don't crash.
9370
- const _node_data = {};
9371
- const _arr = node ? [node] : [];
9372
- return {
9373
- 0: node,
9374
- length: _arr.length,
9375
- toArray: function () { return _arr.slice(); },
9376
- data: function (key, value) {
9377
- if (typeof key === 'undefined') return _node_data;
9378
- if (typeof value !== 'undefined') { _node_data[key] = value; return this; }
9379
- return _node_data[key];
9380
- },
9381
- attr: function (key, value) {
9382
- if (!node) return undefined;
9383
- if (typeof value !== 'undefined') { node.setAttribute(key, value); return this; }
9384
- return node?.getAttribute?.(key);
9385
- },
9386
- removeData: function () { for (const k in _node_data) delete _node_data[k]; return this; },
9387
- children: function () { return { toArray: function () { return node ? Array.from(node.children) : []; } }; },
9388
- };
10342
+ return func.runtime.ui._wrap_matches(node ? [node] : []);
9389
10343
  };
9390
10344
  func.runtime.ui.get_first_node = function (target) {
9391
10345
  if (!target) {
9392
10346
  return null;
9393
10347
  }
9394
- if (target?.jquery) {
9395
- return target[0] || null;
9396
- }
9397
10348
  if (target?.nodeType) {
9398
10349
  return target;
9399
10350
  }
@@ -9411,9 +10362,10 @@ func.runtime.ui.get_data = function (target) {
9411
10362
  if (meta) return meta;
9412
10363
  }
9413
10364
  }
9414
- // fallback: store per-element data on the node itself
9415
10365
  if (target_node) {
9416
- if (!target_node.__xuData) target_node.__xuData = {};
10366
+ if (!target_node.__xuData) {
10367
+ target_node.__xuData = {};
10368
+ }
9417
10369
  return target_node.__xuData;
9418
10370
  }
9419
10371
  return {};
@@ -9795,6 +10747,11 @@ func.runtime.ui.build_container_xu_data = function (options) {
9795
10747
  func.runtime.ui.apply_container_meta = function ($div, options) {
9796
10748
  const div_node = func.runtime.ui.get_first_node($div);
9797
10749
  func.runtime.ui.set_attr(div_node, 'xu-ui-id', options.ui_id);
10750
+ func.runtime.ui.set_attr(div_node, 'data-xuda-kind', options.treeP?.kind || options.nodeP?.tagName || 'element');
10751
+ func.runtime.ui.set_attr(div_node, 'data-xuda-node-id', options.nodeP?.id || options.nodeP?.id_org || '');
10752
+ if (options.treeP?.meta?.tree_id !== null && typeof options.treeP?.meta?.tree_id !== 'undefined') {
10753
+ func.runtime.ui.set_attr(div_node, 'data-xuda-tree-id', options.treeP.meta.tree_id);
10754
+ }
9798
10755
  const xuData = func.runtime.ui.build_container_xu_data(options);
9799
10756
  if (options.parent_infoP?.iterate_info) {
9800
10757
  xuData.iterate_info = options.parent_infoP.iterate_info;
@@ -9889,6 +10846,38 @@ func.runtime.ui.create_container_element = function (div_typeP, attr_str, prop,
9889
10846
  }
9890
10847
  return func.runtime.ui.create_element(div, attr_str);
9891
10848
  };
10849
+ func.runtime.ui.find_hydration_candidate = function (options) {
10850
+ if (!func.runtime.render.is_hydration_mode(SESSION_OBJ?.[options.SESSION_ID])) {
10851
+ return null;
10852
+ }
10853
+ if (!func.runtime.render.should_use_ssr_payload(options.SESSION_ID, options.paramsP)) {
10854
+ return null;
10855
+ }
10856
+ if (options.is_placeholder || !options.treeP?.meta?.tree_id) {
10857
+ return null;
10858
+ }
10859
+
10860
+ const append_node = func.runtime.ui.get_first_node(options.$appendTo || options.$container);
10861
+ if (!append_node) {
10862
+ return null;
10863
+ }
10864
+
10865
+ const children = func.runtime.ui.get_children(append_node);
10866
+ for (let index = 0; index < children.length; index++) {
10867
+ const child = children[index];
10868
+ if (child?.__xuda_hydration_claimed) {
10869
+ continue;
10870
+ }
10871
+ if (func.runtime.ui.get_attr(child, 'data-xuda-tree-id') !== `${options.treeP.meta.tree_id}`) {
10872
+ continue;
10873
+ }
10874
+ child.__xuda_hydration_claimed = true;
10875
+ func.runtime.ui.set_attr(child, 'data-xuda-client-activation', 'hydrate');
10876
+ return child;
10877
+ }
10878
+
10879
+ return null;
10880
+ };
9892
10881
  func.runtime.ui.build_xu_ui_id_seed = function (nodeP, dsSessionP, key_path, currentRecordId) {
9893
10882
  const nodeId = nodeP.xu_tree_id || nodeP.id;
9894
10883
  const elem_key = `${nodeId}-${key_path}-${currentRecordId}`;
@@ -9941,7 +10930,11 @@ func.runtime.ui.create_container = async function (options) {
9941
10930
  try {
9942
10931
  const key_path = func.runtime.ui.build_container_key_path(container_xu_data, options.keyP, options.parent_infoP, options.nodeP, options.parent_nodeP);
9943
10932
  const elem_key = `${options.nodeP.xu_tree_id || options.nodeP.id}-${key_path}-${currentRecordId}`;
9944
- const $div = func.runtime.ui.create_container_element(options.div_typeP, options.attr_str, options.prop, options.nodeP, $appendTo);
10933
+ const hydration_candidate = func.runtime.ui.find_hydration_candidate({
10934
+ ...options,
10935
+ $appendTo,
10936
+ });
10937
+ const $div = hydration_candidate || func.runtime.ui.create_container_element(options.div_typeP, options.attr_str, options.prop, options.nodeP, $appendTo);
9945
10938
  const new_ui_id = await func.runtime.ui.generate_xu_ui_id(options.SESSION_ID, options.nodeP, options.$container, options.paramsP, options.keyP, {
9946
10939
  container_xu_data,
9947
10940
  currentRecordId,
@@ -9967,9 +10960,10 @@ func.runtime.ui.create_container = async function (options) {
9967
10960
  parent_infoP: options.parent_infoP,
9968
10961
  is_placeholder: options.is_placeholder,
9969
10962
  classP: options.classP,
10963
+ treeP: options.treeP,
9970
10964
  });
9971
10965
 
9972
- if (options.div_typeP !== 'svg') {
10966
+ if (!hydration_candidate && options.div_typeP !== 'svg') {
9973
10967
  func.runtime.ui.append_to($div, $appendTo);
9974
10968
  }
9975
10969
  return $div;
@@ -12650,9 +13644,10 @@ func.runtime.ui.init_screen = async function (options) {
12650
13644
 
12651
13645
  const _session = SESSION_OBJ[SESSION_ID];
12652
13646
  const screenInfo = structuredClone(screen_ret);
13647
+ const ssr_payload = func.runtime.render.should_use_ssr_payload(SESSION_ID, { prog_id }) ? func.runtime.render.get_ssr_payload(_session) : null;
12653
13648
 
12654
13649
  const screen_type = source_functionP?.split('_')?.[1];
12655
- const screenId = (glb.screen_num++).toString();
13650
+ const screenId = ssr_payload?.screenId || (glb.screen_num++).toString();
12656
13651
 
12657
13652
  if (SCREEN_BLOCKER_OBJ[prog_id + (sourceScreenP ? '_' + sourceScreenP : '')]) {
12658
13653
  const wait_for_SCREEN_BLOCKER_release = function () {
@@ -12694,6 +13689,8 @@ func.runtime.ui.init_screen = async function (options) {
12694
13689
  call_screen_propertiesP,
12695
13690
  parentDataSourceNoP: _session.DS_GLB?.[callingDataSource_objP?.dsSession]?.dsSession || callingDataSource_objP?.parentDataSourceNo || 0,
12696
13691
  parameters_raw_obj,
13692
+ containerIdP: ssr_payload?.containerId || null,
13693
+ ssr_payload,
12697
13694
  };
12698
13695
 
12699
13696
  const screen_host = func.runtime.ui.create_screen_host(SESSION_ID, screen_type, params, $callingContainerP, screenId);
@@ -12726,6 +13723,10 @@ func.runtime.ui.init_screen = async function (options) {
12726
13723
  func.runtime.ui.set_style($rootFrame, 'display', 'contents');
12727
13724
  }
12728
13725
 
13726
+ if (screen_host.reused_ssr_host && func.runtime.render.is_takeover_mode(_session)) {
13727
+ func.runtime.ui.empty($rootFrame);
13728
+ }
13729
+
12729
13730
  if (!is_panelP) func.UI.utils.indicator.screen.busy();
12730
13731
 
12731
13732
  const ret = await func.datasource.create(
@@ -12767,7 +13768,24 @@ func.runtime.ui.init_screen = async function (options) {
12767
13768
  }
12768
13769
  let node = structuredClone(viewDoc.progUi);
12769
13770
  if (!node.length) return console.warn('ui node empty');
12770
- const ret_render_$container = await func.runtime.render.render_ui_tree(SESSION_ID, $rootFrame, node[0], null, params, jobNoP, null, null, null, null, null, $rootFrame);
13771
+ const root_tree = await func.runtime.render.build_tree({
13772
+ SESSION_ID,
13773
+ nodeP: node[0],
13774
+ paramsP: params,
13775
+ });
13776
+ const ret_render_$container = await func.runtime.render.render_tree(root_tree, {
13777
+ SESSION_ID,
13778
+ $container: $rootFrame,
13779
+ parent_infoP: null,
13780
+ paramsP: params,
13781
+ jobNoP,
13782
+ is_skeleton: null,
13783
+ keyP: null,
13784
+ refreshed_ds: null,
13785
+ parent_nodeP: null,
13786
+ check_existP: null,
13787
+ $root_container: $rootFrame,
13788
+ });
12771
13789
 
12772
13790
  if (!is_panelP) func.UI.utils.indicator.screen.normal();
12773
13791
 
@@ -12851,7 +13869,7 @@ func.runtime.ui.ensure_nav = function (SESSION_ID, $container) {
12851
13869
  }
12852
13870
  var nav_el = document.createElement('xu-nav');
12853
13871
  func.runtime.ui.append($container, nav_el);
12854
- var $nav = func.runtime.ui.as_jquery(nav_el);
13872
+ var $nav = func.runtime.ui._wrap_matches([nav_el]);
12855
13873
  func.UI.component.init_xu_nav($container, $nav);
12856
13874
  return $nav;
12857
13875
  };
@@ -13192,6 +14210,7 @@ func.runtime.ui.render_single_view_node = async function (options) {
13192
14210
  parent_infoP: options.parent_infoP,
13193
14211
  jobNoP: options.jobNoP,
13194
14212
  keyP: options.keyP,
14213
+ treeP: options.treeP,
13195
14214
  parent_nodeP: options.parent_nodeP,
13196
14215
  prop: options.prop,
13197
14216
  div_typeP: 'div',
@@ -13320,6 +14339,7 @@ func.runtime.ui.render_panel_node = async function (options) {
13320
14339
  parent_infoP: options.parent_infoP,
13321
14340
  jobNoP: options.jobNoP,
13322
14341
  keyP: options.keyP,
14342
+ treeP: options.treeP,
13323
14343
  parent_nodeP: options.parent_nodeP,
13324
14344
  prop: options.prop,
13325
14345
  $appendToP: $wrapper,
@@ -13649,6 +14669,14 @@ func.runtime.ui.screen_loading_done = async function (options) {
13649
14669
  });
13650
14670
 
13651
14671
  const _session = SESSION_OBJ[options.SESSION_ID];
14672
+ if (func.runtime.render.should_use_ssr_payload(options.SESSION_ID, options.paramsP)) {
14673
+ const root_node = func.runtime.ui.get_root_node(options.SESSION_ID);
14674
+ if (root_node) {
14675
+ func.runtime.ui.set_attr(root_node, 'data-xuda-client-activation', _session.opt.app_client_activation || 'none');
14676
+ func.runtime.ui.set_attr(root_node, 'data-xuda-ssr-status', _session.opt.app_client_activation === 'hydrate' ? 'hydrated' : 'taken-over');
14677
+ }
14678
+ func.runtime.render.mark_ssr_payload_consumed(options.SESSION_ID);
14679
+ }
13652
14680
  func.events.delete_job(options.SESSION_ID, options.jobNoP);
13653
14681
  func.UI.utils.screen_blocker(false, options.paramsP.prog_id + (options.paramsP.sourceScreenP ? '_' + options.paramsP.sourceScreenP : ''));
13654
14682
  if (_session.prog_id === options.paramsP.prog_id) {
@@ -13681,7 +14709,7 @@ func.runtime.ui._to_node_array = func.runtime.ui._to_node_array || function (inp
13681
14709
  if (!input) {
13682
14710
  return [];
13683
14711
  }
13684
- if (input.jquery) {
14712
+ if (typeof input?.toArray === 'function' && typeof input?.length === 'number' && !input?.nodeType) {
13685
14713
  return input.toArray();
13686
14714
  }
13687
14715
  if (Array.isArray(input)) {
@@ -15491,6 +16519,9 @@ func.runtime.render.get_screen_context = function (SESSION_ID, $container, param
15491
16519
  };
15492
16520
  func.runtime.render.get_node_attributes = function (nodeP) {
15493
16521
  try {
16522
+ if (func.runtime.render.is_tree_node?.(nodeP)) {
16523
+ return nodeP.attributes;
16524
+ }
15494
16525
  return nodeP?.attributes;
15495
16526
  } catch (error) {
15496
16527
  return undefined;
@@ -15608,6 +16639,7 @@ func.runtime.render.prepare_draw_context = async function (options) {
15608
16639
  parent_infoP: options.parent_infoP,
15609
16640
  jobNoP: options.jobNoP,
15610
16641
  keyP: options.keyP,
16642
+ treeP: options.treeP,
15611
16643
  parent_nodeP: options.parent_nodeP,
15612
16644
  prop: options.prop,
15613
16645
  div_typeP: options.element,
@@ -15630,6 +16662,7 @@ func.runtime.render.prepare_draw_context = async function (options) {
15630
16662
  parent_infoP: options.parent_infoP,
15631
16663
  jobNoP: options.jobNoP,
15632
16664
  keyP: options.keyP,
16665
+ treeP: options.treeP,
15633
16666
  parent_nodeP: options.parent_nodeP,
15634
16667
  prop: options.prop,
15635
16668
  div_typeP: options.element,
@@ -16486,18 +17519,28 @@ func.runtime.widgets.render_node = async function (options) {
16486
17519
  parent_infoP: options.parent_infoP,
16487
17520
  jobNoP: options.jobNoP,
16488
17521
  keyP: options.keyP,
17522
+ treeP: options.treeP,
16489
17523
  parent_nodeP: options.parent_nodeP,
16490
17524
  prop: options.prop,
16491
17525
  classP: 'widget_wrapper',
16492
17526
  });
16493
17527
 
17528
+ if (func.runtime.render.is_hydration_mode(SESSION_OBJ?.[options.SESSION_ID]) && func.runtime.render.should_use_ssr_payload(options.SESSION_ID, options.paramsP)) {
17529
+ func.runtime.ui.empty($div);
17530
+ }
17531
+
16494
17532
  const widget_context = func.runtime.widgets.create_context(options.SESSION_ID, options.paramsP, options.prop);
16495
17533
  const { plugin_name, method, propsP, plugin: _plugin } = widget_context;
16496
17534
  const report_error = function (descP, warn) {
16497
17535
  return func.runtime.widgets.report_error(widget_context, descP, warn);
16498
17536
  };
16499
17537
 
16500
- const methods = await func.runtime.widgets.get_methods(widget_context);
17538
+ const definition = await func.runtime.widgets.get_definition(widget_context);
17539
+ if (!func.runtime.widgets.supports_current_environment(definition)) {
17540
+ return report_error(`plugin ${plugin_name} is not available in the current environment`, true);
17541
+ }
17542
+
17543
+ const methods = definition?.methods || {};
16501
17544
  if (methods && !methods[method]) {
16502
17545
  return report_error('method not found');
16503
17546
  }
@@ -16558,7 +17601,13 @@ func.runtime.widgets.render_node = async function (options) {
16558
17601
  job_id: options.jobNoP,
16559
17602
  });
16560
17603
 
16561
- const params = func.runtime.widgets.build_params(widget_context, $div, plugin_setup_ret.data, api_utils);
17604
+ const params = func.runtime.widgets.build_params(
17605
+ widget_context,
17606
+ func.runtime.ui.get_first_node($div),
17607
+ func.runtime.ui.get_data($div),
17608
+ plugin_setup_ret.data,
17609
+ api_utils,
17610
+ );
16562
17611
  const fx = await func.runtime.widgets.get_resource(widget_context, 'runtime.mjs');
16563
17612
 
16564
17613
  await func.runtime.widgets.load_runtime_css(widget_context);
@@ -16582,9 +17631,306 @@ func.runtime.widgets = func.runtime.widgets || {};
16582
17631
 
16583
17632
  // Browser-only special node renderers live here so the core render tree can stay focused.
16584
17633
 
17634
+ const normalize_runtime_tag_name = function (tag_name) {
17635
+ return `${tag_name || ''}`.trim().toLowerCase();
17636
+ };
17637
+
17638
+ const get_runtime_node_attributes = function (nodeP) {
17639
+ if (!nodeP?.attributes || typeof nodeP.attributes !== 'object') {
17640
+ return {};
17641
+ }
17642
+
17643
+ return nodeP.attributes;
17644
+ };
17645
+
17646
+ const get_runtime_node_content = function (nodeP) {
17647
+ if (typeof nodeP?.content === 'string') {
17648
+ return nodeP.content;
17649
+ }
17650
+
17651
+ if (typeof nodeP?.text === 'string') {
17652
+ return nodeP.text;
17653
+ }
17654
+
17655
+ if (!Array.isArray(nodeP?.children)) {
17656
+ return '';
17657
+ }
17658
+
17659
+ return nodeP.children
17660
+ .map(function (child) {
17661
+ if (typeof child === 'string') {
17662
+ return child;
17663
+ }
17664
+ if (typeof child?.content === 'string') {
17665
+ return child.content;
17666
+ }
17667
+ if (typeof child?.text === 'string') {
17668
+ return child.text;
17669
+ }
17670
+ return '';
17671
+ })
17672
+ .join('');
17673
+ };
17674
+
17675
+ const get_runtime_asset_key = function (options, tag_name) {
17676
+ const source_node = options.treeP || options.nodeP || {};
17677
+ const parts = [
17678
+ 'xuda-html-asset',
17679
+ options.paramsP?.prog_id || '',
17680
+ tag_name,
17681
+ source_node.id || source_node.id_org || '',
17682
+ typeof options.keyP === 'undefined' || options.keyP === null ? '' : `${options.keyP}`,
17683
+ ].filter(function (part) {
17684
+ return `${part || ''}`.trim() !== '';
17685
+ });
17686
+
17687
+ return parts.join(':');
17688
+ };
17689
+
17690
+ const get_runtime_asset_signature = function (attributes, content) {
17691
+ const normalized_attributes = {};
17692
+ const attr_keys = Object.keys(attributes || {}).sort();
17693
+
17694
+ for (let index = 0; index < attr_keys.length; index++) {
17695
+ const key = attr_keys[index];
17696
+ normalized_attributes[key] = attributes[key];
17697
+ }
17698
+
17699
+ return JSON.stringify({
17700
+ attributes: normalized_attributes,
17701
+ content: `${content || ''}`,
17702
+ });
17703
+ };
17704
+
17705
+ const escape_runtime_asset_selector_value = function (value) {
17706
+ const win = func.runtime.platform.get_window?.();
17707
+ if (win?.CSS?.escape) {
17708
+ return win.CSS.escape(value);
17709
+ }
17710
+
17711
+ return `${value || ''}`.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
17712
+ };
17713
+
17714
+ const find_runtime_head_asset = function (head, tag_name, asset_key) {
17715
+ if (!head?.querySelector || !asset_key) {
17716
+ return null;
17717
+ }
17718
+
17719
+ return head.querySelector(`${tag_name}[data-xuda-asset-key="${escape_runtime_asset_selector_value(asset_key)}"]`);
17720
+ };
17721
+
17722
+ const find_runtime_head_asset_by_attr = function (head, tag_name, attr_name, attr_value) {
17723
+ if (!head?.querySelectorAll || !attr_name || !attr_value) {
17724
+ return null;
17725
+ }
17726
+
17727
+ const candidates = Array.from(head.querySelectorAll(tag_name));
17728
+ return (
17729
+ candidates.find(function (candidate) {
17730
+ return candidate.getAttribute(attr_name) === attr_value || candidate[attr_name] === attr_value;
17731
+ }) || null
17732
+ );
17733
+ };
17734
+
17735
+ const wait_for_runtime_asset_load = function (node) {
17736
+ if (!node?.addEventListener) {
17737
+ return Promise.resolve(node);
17738
+ }
17739
+
17740
+ if (node.getAttribute?.('data-xuda-loaded') === 'true') {
17741
+ return Promise.resolve(node);
17742
+ }
17743
+
17744
+ if (!node.getAttribute?.('data-xuda-asset-key')) {
17745
+ return Promise.resolve(node);
17746
+ }
17747
+
17748
+ const tag_name = normalize_runtime_tag_name(node.tagName);
17749
+ if (tag_name !== 'script' && !(tag_name === 'link' && normalize_runtime_tag_name(node.getAttribute?.('rel')) === 'stylesheet')) {
17750
+ return Promise.resolve(node);
17751
+ }
17752
+
17753
+ return new Promise(function (resolve) {
17754
+ const done = function () {
17755
+ node.setAttribute?.('data-xuda-loaded', 'true');
17756
+ resolve(node);
17757
+ };
17758
+
17759
+ node.addEventListener('load', done, { once: true });
17760
+ node.addEventListener('error', done, { once: true });
17761
+ });
17762
+ };
17763
+
17764
+ const remove_runtime_head_asset = function (node) {
17765
+ if (node?.parentNode?.removeChild) {
17766
+ node.parentNode.removeChild(node);
17767
+ }
17768
+ };
17769
+
17770
+ const apply_runtime_asset_metadata = function (node, asset_key, signature) {
17771
+ if (!node?.setAttribute) {
17772
+ return node;
17773
+ }
17774
+
17775
+ if (asset_key) {
17776
+ node.setAttribute('data-xuda-asset-key', asset_key);
17777
+ }
17778
+ node.setAttribute('data-xuda-asset-signature', signature);
17779
+ return node;
17780
+ };
17781
+
17782
+ const create_runtime_head_element = function (doc, tag_name, attributes, asset_key, signature) {
17783
+ const node = doc.createElement(tag_name);
17784
+ apply_runtime_asset_metadata(node, asset_key, signature);
17785
+ func.runtime.platform.apply_element_attributes(node, attributes);
17786
+ return node;
17787
+ };
17788
+
17789
+ const upsert_runtime_head_element = async function (options) {
17790
+ const doc = func.runtime.platform.get_document?.();
17791
+ const head = doc?.head;
17792
+ const tag_name = normalize_runtime_tag_name(options.tag_name);
17793
+
17794
+ if (!doc?.createElement || !head?.appendChild || !tag_name) {
17795
+ return null;
17796
+ }
17797
+
17798
+ const asset_key = options.asset_key || '';
17799
+ const signature = options.signature || '';
17800
+ const attributes = options.attributes || {};
17801
+ const content = typeof options.content === 'string' ? options.content : '';
17802
+
17803
+ const existing_by_key = find_runtime_head_asset(head, tag_name, asset_key);
17804
+ if (existing_by_key && existing_by_key.getAttribute('data-xuda-asset-signature') === signature) {
17805
+ return options.await_load ? await wait_for_runtime_asset_load(existing_by_key) : existing_by_key;
17806
+ }
17807
+
17808
+ if (existing_by_key) {
17809
+ remove_runtime_head_asset(existing_by_key);
17810
+ }
17811
+
17812
+ if (options.find_existing_attr?.name && options.find_existing_attr?.value) {
17813
+ const existing_by_attr = find_runtime_head_asset_by_attr(head, tag_name, options.find_existing_attr.name, options.find_existing_attr.value);
17814
+ if (existing_by_attr) {
17815
+ apply_runtime_asset_metadata(existing_by_attr, asset_key, signature);
17816
+ existing_by_attr.setAttribute?.('data-xuda-loaded', 'true');
17817
+ return existing_by_attr;
17818
+ }
17819
+ }
17820
+
17821
+ const node = create_runtime_head_element(doc, tag_name, attributes, asset_key, signature);
17822
+
17823
+ if (tag_name === 'script' && attributes.src && !Object.prototype.hasOwnProperty.call(attributes, 'async')) {
17824
+ const script_type = normalize_runtime_tag_name(attributes.type);
17825
+ if (script_type !== 'module') {
17826
+ node.async = false;
17827
+ }
17828
+ }
17829
+
17830
+ if (tag_name === 'style' || (tag_name === 'script' && !attributes.src)) {
17831
+ node.textContent = content;
17832
+ }
17833
+
17834
+ head.appendChild(node);
17835
+
17836
+ if (tag_name !== 'script' && tag_name !== 'link') {
17837
+ node.setAttribute('data-xuda-loaded', 'true');
17838
+ }
17839
+
17840
+ return options.await_load ? await wait_for_runtime_asset_load(node) : node;
17841
+ };
17842
+
17843
+ const render_runtime_html_asset = async function (options) {
17844
+ if (options.is_skeleton) {
17845
+ return options.$container;
17846
+ }
17847
+
17848
+ const nodeP = options.treeP || options.nodeP || {};
17849
+ const tag_name = normalize_runtime_tag_name(nodeP.tagName);
17850
+ const attributes = { ...get_runtime_node_attributes(nodeP) };
17851
+ const content = get_runtime_node_content(nodeP);
17852
+ const asset_key = get_runtime_asset_key(options, tag_name);
17853
+ const signature = get_runtime_asset_signature(attributes, content);
17854
+
17855
+ switch (tag_name) {
17856
+ case 'title':
17857
+ func.runtime.platform.set_title(content);
17858
+ return options.$container;
17859
+ case 'style':
17860
+ await upsert_runtime_head_element({
17861
+ tag_name,
17862
+ attributes,
17863
+ content,
17864
+ asset_key,
17865
+ signature,
17866
+ });
17867
+ return options.$container;
17868
+ case 'meta':
17869
+ if (!Object.keys(attributes).length) {
17870
+ return options.$container;
17871
+ }
17872
+ await upsert_runtime_head_element({
17873
+ tag_name,
17874
+ attributes,
17875
+ asset_key,
17876
+ signature,
17877
+ });
17878
+ return options.$container;
17879
+ case 'link': {
17880
+ const href = `${attributes.href || ''}`.trim();
17881
+ if (!href) {
17882
+ return options.$container;
17883
+ }
17884
+ await upsert_runtime_head_element({
17885
+ tag_name,
17886
+ attributes,
17887
+ asset_key,
17888
+ signature,
17889
+ find_existing_attr: {
17890
+ name: 'href',
17891
+ value: href,
17892
+ },
17893
+ await_load: normalize_runtime_tag_name(attributes.rel) === 'stylesheet',
17894
+ });
17895
+ return options.$container;
17896
+ }
17897
+ case 'script': {
17898
+ const src = `${attributes.src || ''}`.trim();
17899
+ if (!src && !content.trim()) {
17900
+ return options.$container;
17901
+ }
17902
+ await upsert_runtime_head_element({
17903
+ tag_name,
17904
+ attributes,
17905
+ content,
17906
+ asset_key,
17907
+ signature,
17908
+ find_existing_attr: src
17909
+ ? {
17910
+ name: 'src',
17911
+ value: src,
17912
+ }
17913
+ : null,
17914
+ await_load: !!src,
17915
+ });
17916
+ return options.$container;
17917
+ }
17918
+ default:
17919
+ return null;
17920
+ }
17921
+ };
17922
+
16585
17923
  func.runtime.render.render_special_node = async function (options) {
16586
- if (options.nodeP.content && options.nodeP.attributes) {
16587
- options.nodeP.attributes['xu-content'] = options.nodeP.content;
17924
+ const treeP = options.treeP || null;
17925
+ const nodeP = options.nodeP || func.runtime.render.get_tree_source_node(treeP);
17926
+ const render_tag_name = treeP?.tagName || nodeP?.tagName;
17927
+ const normalized_render_tag_name = normalize_runtime_tag_name(render_tag_name);
17928
+ const is_native_html_asset = ['title', 'style', 'meta', 'link', 'script'].includes(normalized_render_tag_name);
17929
+
17930
+ if (!is_native_html_asset && treeP?.content && nodeP?.attributes) {
17931
+ nodeP.attributes['xu-content'] = treeP.content;
17932
+ } else if (!is_native_html_asset && nodeP?.content && nodeP.attributes) {
17933
+ nodeP.attributes['xu-content'] = nodeP.content;
16588
17934
  }
16589
17935
 
16590
17936
  const renderers = {
@@ -16594,6 +17940,7 @@ func.runtime.render.render_special_node = async function (options) {
16594
17940
  SESSION_ID: options.SESSION_ID,
16595
17941
  $container: options.$container,
16596
17942
  $root_container: options.$root_container,
17943
+ treeP,
16597
17944
  nodeP: options.nodeP,
16598
17945
  parent_infoP: options.parent_infoP,
16599
17946
  paramsP: options.paramsP,
@@ -16610,6 +17957,7 @@ func.runtime.render.render_special_node = async function (options) {
16610
17957
  SESSION_ID: options.SESSION_ID,
16611
17958
  $container: options.$container,
16612
17959
  $root_container: options.$root_container,
17960
+ treeP,
16613
17961
  nodeP: options.nodeP,
16614
17962
  parent_infoP: options.parent_infoP,
16615
17963
  paramsP: options.paramsP,
@@ -16629,6 +17977,7 @@ func.runtime.render.render_special_node = async function (options) {
16629
17977
  SESSION_ID: options.SESSION_ID,
16630
17978
  $container: options.$container,
16631
17979
  $root_container: options.$root_container,
17980
+ treeP,
16632
17981
  nodeP: options.nodeP,
16633
17982
  parent_infoP: options.parent_infoP,
16634
17983
  paramsP: options.paramsP,
@@ -16645,6 +17994,7 @@ func.runtime.render.render_special_node = async function (options) {
16645
17994
  SESSION_ID: options.SESSION_ID,
16646
17995
  $container: options.$container,
16647
17996
  $root_container: options.$root_container,
17997
+ treeP,
16648
17998
  nodeP: options.nodeP,
16649
17999
  parent_infoP: options.parent_infoP,
16650
18000
  paramsP: options.paramsP,
@@ -16656,9 +18006,24 @@ func.runtime.render.render_special_node = async function (options) {
16656
18006
  refreshed_ds: options.refreshed_ds,
16657
18007
  });
16658
18008
  },
18009
+ title: async function () {
18010
+ return await render_runtime_html_asset(options);
18011
+ },
18012
+ style: async function () {
18013
+ return await render_runtime_html_asset(options);
18014
+ },
18015
+ meta: async function () {
18016
+ return await render_runtime_html_asset(options);
18017
+ },
18018
+ link: async function () {
18019
+ return await render_runtime_html_asset(options);
18020
+ },
18021
+ script: async function () {
18022
+ return await render_runtime_html_asset(options);
18023
+ },
16659
18024
  };
16660
18025
 
16661
- const renderer = renderers[options.nodeP.tagName];
18026
+ const renderer = renderers[normalized_render_tag_name];
16662
18027
  if (!renderer) {
16663
18028
  return { handled: false };
16664
18029
  }
@@ -16772,8 +18137,9 @@ func.runtime.widgets = func.runtime.widgets || {};
16772
18137
  // Browser-only render tree entrypoints live here so draw/cache helpers can stay focused.
16773
18138
 
16774
18139
  func.runtime.render.create_tree_runtime = function (options) {
18140
+ const render_node = options.nodeP || func.runtime.render.get_tree_source_node(options.treeP);
16775
18141
  const render_context = func.runtime.render.get_screen_context(options.SESSION_ID, options.$container, options.paramsP, options.is_skeleton);
16776
- const prop = func.runtime.render.get_node_attributes(options.nodeP);
18142
+ const prop = func.runtime.render.get_node_attributes(options.treeP || render_node);
16777
18143
  const is_mobile = render_context.is_mobile ? true : false;
16778
18144
  const hover_handlers = func.runtime.render.create_hover_handlers({
16779
18145
  SESSION_ID: options.SESSION_ID,
@@ -16788,13 +18154,22 @@ func.runtime.render.create_tree_runtime = function (options) {
16788
18154
  return await func.runtime.ui.close_modal_session(options.SESSION_ID, modal_id);
16789
18155
  };
16790
18156
  const iterate_child = async function ($divP, nodeP, parent_infoP, $root_container, before_record_function) {
18157
+ const child_tree = await func.runtime.render.ensure_tree_node({
18158
+ SESSION_ID: options.SESSION_ID,
18159
+ nodeP: nodeP || options.treeP || render_node,
18160
+ parent_infoP,
18161
+ paramsP: options.paramsP,
18162
+ keyP: options.keyP,
18163
+ parent_nodeP: render_node,
18164
+ pathP: options.treeP?.meta?.path || [],
18165
+ });
16791
18166
  return await func.runtime.render.iterate_children({
16792
18167
  $divP,
16793
- nodeP,
18168
+ nodeP: child_tree,
16794
18169
  is_mobile,
16795
18170
  before_record_function,
16796
18171
  render_child: async function (key, child) {
16797
- await options.render_child($divP, child, parent_infoP, key, nodeP, $root_container);
18172
+ await options.render_child($divP, child, parent_infoP, key, render_node, $root_container);
16798
18173
  },
16799
18174
  });
16800
18175
  };
@@ -16824,7 +18199,7 @@ func.runtime.render.draw_node = async function (options) {
16824
18199
  check_existP: options.check_existP,
16825
18200
  $root_container: options.$root_container,
16826
18201
  prop: options.prop,
16827
- element: options.nodeP.tagName,
18202
+ element: options.treeP?.tagName || options.nodeP.tagName,
16828
18203
  hover_handlers: options.hover_handlers,
16829
18204
  include_hover_click: options.include_hover_click,
16830
18205
  iterate_child: options.iterate_child,
@@ -16847,21 +18222,33 @@ func.runtime.render.draw_node = async function (options) {
16847
18222
  nodeP: options.nodeP,
16848
18223
  });
16849
18224
  };
16850
- func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nodeP, parent_infoP, paramsP, jobNoP, is_skeleton, keyP, refreshed_ds, parent_nodeP, check_existP, $root_container) {
16851
- if (!nodeP) return;
16852
- const perf_end = func.runtime?.perf?.start?.(SESSION_ID, 'render_ui_tree');
16853
- func.runtime?.perf?.increment_map?.(SESSION_ID, 'render_node_counts', nodeP.id || nodeP.id_org || nodeP.tagName || 'unknown');
18225
+ func.runtime.render.render_tree = async function (treeP, renderer_context) {
18226
+ if (!treeP) return;
18227
+ const nodeP = func.runtime.render.get_tree_source_node(treeP);
18228
+ const perf_end = func.runtime?.perf?.start?.(renderer_context.SESSION_ID, 'render_ui_tree');
18229
+ func.runtime?.perf?.increment_map?.(renderer_context.SESSION_ID, 'render_node_counts', treeP.id || nodeP?.id || nodeP?.id_org || treeP.tagName || 'unknown');
16854
18230
  try {
16855
18231
  const tree_runtime = func.runtime.render.create_tree_runtime({
16856
- SESSION_ID,
16857
- $container,
18232
+ SESSION_ID: renderer_context.SESSION_ID,
18233
+ $container: renderer_context.$container,
18234
+ treeP,
16858
18235
  nodeP,
16859
- parent_infoP,
16860
- paramsP,
16861
- jobNoP,
16862
- is_skeleton,
18236
+ parent_infoP: renderer_context.parent_infoP,
18237
+ paramsP: renderer_context.paramsP,
18238
+ jobNoP: renderer_context.jobNoP,
18239
+ is_skeleton: renderer_context.is_skeleton,
18240
+ keyP: renderer_context.keyP,
16863
18241
  render_child: async function ($divP, child, parent_infoP, key, parentNodeP, rootContainerP) {
16864
- await func.runtime.render.render_ui_tree(SESSION_ID, $divP, child, parent_infoP, paramsP, jobNoP, is_skeleton, key, null, parentNodeP, null, rootContainerP);
18242
+ await func.runtime.render.render_tree(child, {
18243
+ ...renderer_context,
18244
+ $container: $divP,
18245
+ parent_infoP,
18246
+ keyP: key,
18247
+ refreshed_ds: null,
18248
+ parent_nodeP: parentNodeP,
18249
+ check_existP: null,
18250
+ $root_container: rootContainerP,
18251
+ });
16865
18252
  },
16866
18253
  });
16867
18254
  const render_context = tree_runtime.render_context;
@@ -16873,23 +18260,24 @@ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nod
16873
18260
  const iterate_child = tree_runtime.iterate_child;
16874
18261
 
16875
18262
  func.runtime.render.log_tree_debug({
16876
- SESSION_ID,
16877
- paramsP,
18263
+ SESSION_ID: renderer_context.SESSION_ID,
18264
+ paramsP: renderer_context.paramsP,
16878
18265
  nodeP,
16879
18266
  _ds,
16880
18267
  });
16881
18268
  const special_render = await func.runtime.render.render_special_node({
16882
- SESSION_ID,
16883
- $container,
16884
- $root_container,
18269
+ SESSION_ID: renderer_context.SESSION_ID,
18270
+ $container: renderer_context.$container,
18271
+ $root_container: renderer_context.$root_container,
18272
+ treeP,
16885
18273
  nodeP,
16886
- parent_infoP,
16887
- paramsP,
16888
- jobNoP,
16889
- is_skeleton,
16890
- keyP,
16891
- refreshed_ds,
16892
- parent_nodeP,
18274
+ parent_infoP: renderer_context.parent_infoP,
18275
+ paramsP: renderer_context.paramsP,
18276
+ jobNoP: renderer_context.jobNoP,
18277
+ is_skeleton: renderer_context.is_skeleton,
18278
+ keyP: renderer_context.keyP,
18279
+ refreshed_ds: renderer_context.refreshed_ds,
18280
+ parent_nodeP: renderer_context.parent_nodeP,
16893
18281
  prop,
16894
18282
  render_context,
16895
18283
  hover_handlers,
@@ -16897,22 +18285,23 @@ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nod
16897
18285
  close_modal,
16898
18286
  });
16899
18287
  if (special_render.handled) {
16900
- func.runtime?.perf?.increment?.(SESSION_ID, 'render_special_node_hits');
18288
+ func.runtime?.perf?.increment?.(renderer_context.SESSION_ID, 'render_special_node_hits');
16901
18289
  return special_render.result;
16902
18290
  }
16903
18291
  return await func.runtime.render.draw_node({
16904
- SESSION_ID,
16905
- $container,
16906
- $root_container,
18292
+ SESSION_ID: renderer_context.SESSION_ID,
18293
+ $container: renderer_context.$container,
18294
+ $root_container: renderer_context.$root_container,
18295
+ treeP,
16907
18296
  nodeP,
16908
- parent_infoP,
16909
- paramsP,
16910
- jobNoP,
16911
- is_skeleton,
16912
- keyP,
16913
- refreshed_ds,
16914
- parent_nodeP,
16915
- check_existP,
18297
+ parent_infoP: renderer_context.parent_infoP,
18298
+ paramsP: renderer_context.paramsP,
18299
+ jobNoP: renderer_context.jobNoP,
18300
+ is_skeleton: renderer_context.is_skeleton,
18301
+ keyP: renderer_context.keyP,
18302
+ refreshed_ds: renderer_context.refreshed_ds,
18303
+ parent_nodeP: renderer_context.parent_nodeP,
18304
+ check_existP: renderer_context.check_existP,
16916
18305
  prop,
16917
18306
  hover_handlers,
16918
18307
  include_hover_click,
@@ -16921,6 +18310,31 @@ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nod
16921
18310
  } finally {
16922
18311
  perf_end?.();
16923
18312
  }
18313
+ };
18314
+ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nodeP, parent_infoP, paramsP, jobNoP, is_skeleton, keyP, refreshed_ds, parent_nodeP, check_existP, $root_container) {
18315
+ if (!nodeP) return;
18316
+ const treeP = await func.runtime.render.ensure_tree_node({
18317
+ SESSION_ID,
18318
+ nodeP,
18319
+ parent_infoP,
18320
+ paramsP,
18321
+ keyP,
18322
+ parent_nodeP,
18323
+ });
18324
+
18325
+ return await func.runtime.render.render_tree(treeP, {
18326
+ SESSION_ID,
18327
+ $container,
18328
+ parent_infoP,
18329
+ paramsP,
18330
+ jobNoP,
18331
+ is_skeleton,
18332
+ keyP,
18333
+ refreshed_ds,
18334
+ parent_nodeP,
18335
+ check_existP,
18336
+ $root_container,
18337
+ });
16924
18338
  };
16925
18339
  func.runtime = func.runtime || {};
16926
18340
  func.runtime.ui = func.runtime.ui || {};
@@ -17588,7 +19002,7 @@ func.runtime.render.handle_xu_ref = async function (options) {
17588
19002
  func.runtime.render.handle_xu_bind = async function (options) {
17589
19003
  if (options.is_skeleton) return {};
17590
19004
 
17591
- const $elm = func.runtime?.ui?.get_preferred_live_element ? func.runtime.ui.get_preferred_live_element(options.$elm) : func.runtime.ui.as_jquery(options.$elm);
19005
+ const $elm = func.runtime?.ui?.get_preferred_live_element ? func.runtime.ui.get_preferred_live_element(options.$elm) : options.$elm;
17592
19006
  const elm_data = func.runtime.ui.get_data($elm);
17593
19007
  const xuData = elm_data?.xuData;
17594
19008
  const bind_expression =
@@ -18444,6 +19858,7 @@ func.runtime.render.handle_xu_panel_program = async function (options) {
18444
19858
  parent_infoP: options.parent_infoP,
18445
19859
  jobNoP: options.jobNoP,
18446
19860
  keyP: options.keyP,
19861
+ treeP: options.treeP,
18447
19862
  parent_nodeP: options.parent_nodeP,
18448
19863
  prop: options.nodeP.attributes,
18449
19864
  $appendToP: $wrapper,
@@ -22457,6 +23872,12 @@ function xuda(...args) {
22457
23872
  if (typeof opt !== 'object') {
22458
23873
  return console.error('Xuda Error - opt argument is not an object');
22459
23874
  }
23875
+
23876
+ if (!opt.ssr_payload && func.runtime.platform.get_window()?.__XUDA_SSR__) {
23877
+ opt.ssr_payload = func.runtime.platform.get_window().__XUDA_SSR__;
23878
+ }
23879
+ func.runtime.render.apply_runtime_bootstrap_defaults(opt);
23880
+
22460
23881
  glb.URL_PARAMS = func.common.getJsonFromUrl(platform.get_url_href());
22461
23882
 
22462
23883
  glb.worker_type = 'Worker';