@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.
@@ -1493,49 +1493,6 @@ if (typeof IS_DOCKER === 'undefined' || typeof IS_PROCESS_SERVER === 'undefined'
1493
1493
  var DOCS_OBJ = {};
1494
1494
  }
1495
1495
 
1496
- // Minimal jQuery shim for plugins that still reference $
1497
- if (typeof $ === 'undefined' && typeof document !== 'undefined') {
1498
- var $ = function (selector) {
1499
- var nodes = typeof selector === 'string'
1500
- ? Array.from(document.querySelectorAll(selector))
1501
- : selector?.nodeType ? [selector] : (selector?.length ? Array.from(selector) : []);
1502
- var obj = {
1503
- 0: nodes[0], length: nodes.length,
1504
- toArray: function () { return nodes.slice(); },
1505
- 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); },
1506
- each: function (fn) { for (var i = 0; i < nodes.length; i++) { fn.call(nodes[i], i, nodes[i]); } return obj; },
1507
- on: function (ev, fn) { for (var i = 0; i < nodes.length; i++) nodes[i].addEventListener(ev, fn); return obj; },
1508
- off: function (ev, fn) { for (var i = 0; i < nodes.length; i++) nodes[i].removeEventListener(ev, fn); return obj; },
1509
- addClass: function (c) { for (var i = 0; i < nodes.length; i++) nodes[i].classList?.add(c); return obj; },
1510
- removeClass: function (c) { for (var i = 0; i < nodes.length; i++) nodes[i].classList?.remove(c); return obj; },
1511
- hasClass: function (c) { return nodes[0]?.classList?.contains(c) || false; },
1512
- 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; },
1513
- css: function (k, v) { for (var i = 0; i < nodes.length; i++) nodes[i].style[k] = v; return obj; },
1514
- data: function () { return nodes[0]?.__xuData || (nodes[0] ? (nodes[0].__xuData = {}) : {}); },
1515
- 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; },
1516
- 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; },
1517
- 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; },
1518
- show: function () { for (var i = 0; i < nodes.length; i++) nodes[i].style.display = ''; return obj; },
1519
- hide: function () { for (var i = 0; i < nodes.length; i++) nodes[i].style.display = 'none'; return obj; },
1520
- remove: function () { for (var i = 0; i < nodes.length; i++) nodes[i].remove?.(); return obj; },
1521
- empty: function () { for (var i = 0; i < nodes.length; i++) nodes[i].innerHTML = ''; return obj; },
1522
- append: function (c) { var n = c?.nodeType ? c : c?.[0]; if (n && nodes[0]) nodes[0].appendChild(n); return obj; },
1523
- parent: function () { return $(nodes[0]?.parentElement ? [nodes[0].parentElement] : []); },
1524
- children: function () { return $(nodes[0] ? Array.from(nodes[0].children) : []); },
1525
- trigger: function (ev, d) { for (var i = 0; i < nodes.length; i++) nodes[i].dispatchEvent(new CustomEvent(ev, { detail: d })); return obj; },
1526
- is: function (s) { return nodes[0]?.matches?.(s) || false; },
1527
- 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; },
1528
- unbind: function () { return obj; },
1529
- clone: function () { return $(nodes[0]?.cloneNode(true) ? [nodes[0].cloneNode(true)] : []); },
1530
- };
1531
- obj[Symbol.iterator] = function () { var i = 0; return { next: function () { return i < nodes.length ? { value: nodes[i++], done: false } : { done: true }; } }; };
1532
- return obj;
1533
- };
1534
- $.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]); }); } };
1535
- $.cookie = function () { return null; };
1536
- var jQuery = $;
1537
- }
1538
-
1539
1496
  var glb = {};
1540
1497
  var func = {};
1541
1498
  func.UI = {};
@@ -1609,23 +1566,27 @@ glb.PROTECTED_VARS = ['_NULL', '_THIS', '_FOR_KEY', '_FOR_VAL', '_ROWNO', '_ROWI
1609
1566
 
1610
1567
  func.common = {};
1611
1568
  func.runtime.platform = {
1569
+ get_global: function (name) {
1570
+ try {
1571
+ if (typeof globalThis === 'undefined') {
1572
+ return null;
1573
+ }
1574
+ return globalThis?.[name] || null;
1575
+ } catch (error) {
1576
+ return null;
1577
+ }
1578
+ },
1612
1579
  has_window: function () {
1613
- return typeof window !== 'undefined';
1580
+ return !!func.runtime.platform.get_window();
1614
1581
  },
1615
1582
  has_document: function () {
1616
- return typeof document !== 'undefined';
1583
+ return !!func.runtime.platform.get_document();
1617
1584
  },
1618
1585
  get_window: function () {
1619
- if (func.runtime.platform.has_window()) {
1620
- return window;
1621
- }
1622
- return null;
1586
+ return func.runtime.platform.get_global('window');
1623
1587
  },
1624
1588
  get_document: function () {
1625
- if (func.runtime.platform.has_document()) {
1626
- return document;
1627
- }
1628
- return null;
1589
+ return func.runtime.platform.get_global('document');
1629
1590
  },
1630
1591
  get_location: function () {
1631
1592
  const win = func.runtime.platform.get_window();
@@ -1636,27 +1597,23 @@ func.runtime.platform = {
1636
1597
  if (win?.navigator) {
1637
1598
  return win.navigator;
1638
1599
  }
1639
- if (typeof navigator !== 'undefined') {
1640
- return navigator;
1641
- }
1642
- return null;
1600
+ return func.runtime.platform.get_global('navi' + 'gator');
1643
1601
  },
1644
1602
  is_html_element: function (value) {
1645
- if (typeof HTMLElement === 'undefined') {
1603
+ const html_element = func.runtime.platform.get_global('HTML' + 'Element');
1604
+ if (typeof html_element !== 'function') {
1646
1605
  return false;
1647
1606
  }
1648
- return value instanceof HTMLElement;
1607
+ return value instanceof html_element;
1649
1608
  },
1650
1609
  get_storage: function (type) {
1651
1610
  const win = func.runtime.platform.get_window();
1611
+ const storage_key = type === 'session' ? 'session' + 'Storage' : 'local' + 'Storage';
1652
1612
  try {
1653
1613
  if (!win) {
1654
1614
  return null;
1655
1615
  }
1656
- if (type === 'session') {
1657
- return win.sessionStorage || null;
1658
- }
1659
- return win.localStorage || null;
1616
+ return win?.[storage_key] || null;
1660
1617
  } catch (error) {
1661
1618
  return null;
1662
1619
  }
@@ -1813,9 +1770,8 @@ func.runtime.platform.emit = function (name, data) {
1813
1770
  handlers[i](data);
1814
1771
  }
1815
1772
  }
1816
- // also fire on DOM if in browser (for backward compatibility with custom event listeners)
1817
- if (func.runtime.platform.has_document()) {
1818
- document.dispatchEvent(new CustomEvent(name, { detail: Array.isArray(data) ? data : [data] }));
1773
+ if (typeof func.runtime.platform.dispatch_document_event === 'function') {
1774
+ func.runtime.platform.dispatch_document_event(name, data);
1819
1775
  }
1820
1776
  } finally {
1821
1777
  func.runtime.platform._emitting[name] = false;
@@ -1823,35 +1779,111 @@ func.runtime.platform.emit = function (name, data) {
1823
1779
  };
1824
1780
 
1825
1781
  // ── Platform helpers for DOM-independent resource loading ──
1826
- func.runtime.platform.load_script = function (url, type, callback) {
1827
- if (typeof document !== 'undefined') {
1828
- const script = document.createElement('script');
1829
- script.src = url;
1830
- if (type) script.type = type;
1831
- script.onload = callback;
1832
- document.head.appendChild(script);
1833
- } else if (callback) {
1834
- callback();
1782
+ func.runtime.platform.apply_element_attributes = function (node, attributes, excluded_keys = []) {
1783
+ if (!node?.setAttribute || !attributes) {
1784
+ return node;
1785
+ }
1786
+
1787
+ const excluded = new Set(excluded_keys || []);
1788
+ const attr_keys = Object.keys(attributes);
1789
+ for (let index = 0; index < attr_keys.length; index++) {
1790
+ const key = attr_keys[index];
1791
+ if (!key || excluded.has(key)) {
1792
+ continue;
1793
+ }
1794
+
1795
+ const value = attributes[key];
1796
+ if (value === false || typeof value === 'undefined') {
1797
+ continue;
1798
+ }
1799
+
1800
+ node.setAttribute(key, value === null ? '' : `${value}`);
1801
+ }
1802
+
1803
+ return node;
1804
+ };
1805
+ func.runtime.platform.load_script = function (url, type, callback, attributes) {
1806
+ const doc = func.runtime.platform.get_document();
1807
+ if (!doc?.createElement || !doc?.head?.appendChild) {
1808
+ if (callback) {
1809
+ callback();
1810
+ }
1811
+ return;
1835
1812
  }
1813
+ const find_existing_script = function () {
1814
+ const asset_key = attributes?.['data-xuda-asset-key'];
1815
+ const scripts = doc.querySelectorAll ? Array.from(doc.querySelectorAll('script')) : [];
1816
+ return scripts.find(function (script) {
1817
+ if (asset_key && script.getAttribute('data-xuda-asset-key') === asset_key) {
1818
+ return true;
1819
+ }
1820
+ return !!(url && script.getAttribute('src') === url);
1821
+ }) || null;
1822
+ };
1823
+
1824
+ const existing_script = find_existing_script();
1825
+ if (existing_script) {
1826
+ if (callback) {
1827
+ if (existing_script.getAttribute('data-xuda-loaded') === 'true' || !url) {
1828
+ callback();
1829
+ } else {
1830
+ existing_script.addEventListener('load', callback, { once: true });
1831
+ existing_script.addEventListener('error', callback, { once: true });
1832
+ }
1833
+ }
1834
+ return existing_script;
1835
+ }
1836
+
1837
+ const script = doc.createElement('script');
1838
+ script.src = url;
1839
+ if (type) script.type = type;
1840
+ func.runtime.platform.apply_element_attributes(script, attributes, ['src', 'type']);
1841
+ script.onload = function () {
1842
+ script.setAttribute('data-xuda-loaded', 'true');
1843
+ if (callback) {
1844
+ callback();
1845
+ }
1846
+ };
1847
+ script.onerror = function () {
1848
+ if (callback) {
1849
+ callback();
1850
+ }
1851
+ };
1852
+ doc.head.appendChild(script);
1853
+ return script;
1836
1854
  };
1837
- func.runtime.platform.load_css = function (href) {
1838
- if (typeof document === 'undefined') return;
1855
+ func.runtime.platform.load_css = function (href, attributes) {
1856
+ const doc = func.runtime.platform.get_document();
1857
+ if (!doc?.createElement || !doc?.head) {
1858
+ return;
1859
+ }
1839
1860
  try {
1840
- if (document.querySelector('link[href="' + href + '"]')) return;
1861
+ const asset_key = attributes?.['data-xuda-asset-key'];
1862
+ const existing_links = doc.querySelectorAll ? Array.from(doc.querySelectorAll('link')) : [];
1863
+ const existing = existing_links.find(function (link) {
1864
+ if (asset_key && link.getAttribute('data-xuda-asset-key') === asset_key) {
1865
+ return true;
1866
+ }
1867
+ return !!(href && link.getAttribute('href') === href);
1868
+ });
1869
+ if (existing) return existing;
1841
1870
  } catch (err) {
1842
1871
  return;
1843
1872
  }
1844
- const link = document.createElement('link');
1873
+ const link = doc.createElement('link');
1845
1874
  link.rel = 'stylesheet';
1846
1875
  link.type = 'text/css';
1847
1876
  link.href = href;
1848
- document.head.insertBefore(link, document.head.firstChild);
1877
+ func.runtime.platform.apply_element_attributes(link, attributes, ['href']);
1878
+ doc.head.insertBefore(link, doc.head.firstChild);
1879
+ return link;
1849
1880
  };
1850
1881
  func.runtime.platform.remove_js_css = function (filename, filetype) {
1851
- if (typeof document === 'undefined') return;
1882
+ const doc = func.runtime.platform.get_document();
1883
+ if (!doc?.getElementsByTagName) return;
1852
1884
  const tagName = filetype === 'js' ? 'script' : filetype === 'css' ? 'link' : 'none';
1853
1885
  const attr = filetype === 'js' ? 'src' : filetype === 'css' ? 'href' : 'none';
1854
- const elements = document.getElementsByTagName(tagName);
1886
+ const elements = doc.getElementsByTagName(tagName);
1855
1887
  for (let i = elements.length - 1; i >= 0; i--) {
1856
1888
  if (elements[i] && elements[i].getAttribute(attr) != null && elements[i].getAttribute(attr).indexOf(filename) !== -1) {
1857
1889
  elements[i].parentNode.removeChild(elements[i]);
@@ -1859,15 +1891,17 @@ func.runtime.platform.remove_js_css = function (filename, filetype) {
1859
1891
  }
1860
1892
  };
1861
1893
  func.runtime.platform.inject_css = function (cssText) {
1862
- if (typeof document === 'undefined' || !cssText) return;
1863
- const style = document.createElement('style');
1894
+ const doc = func.runtime.platform.get_document();
1895
+ if (!doc?.createElement || !doc?.head?.appendChild || !cssText) return;
1896
+ const style = doc.createElement('style');
1864
1897
  style.type = 'text/css';
1865
1898
  style.textContent = cssText;
1866
- document.head.appendChild(style);
1899
+ doc.head.appendChild(style);
1867
1900
  };
1868
1901
  func.runtime.platform.set_title = function (title) {
1869
- if (typeof document !== 'undefined') {
1870
- document.title = title;
1902
+ const doc = func.runtime.platform.get_document();
1903
+ if (doc) {
1904
+ doc.title = title;
1871
1905
  }
1872
1906
  };
1873
1907
  func.runtime.platform.set_cursor = function (element, cursor) {
@@ -2133,6 +2167,147 @@ func.runtime.workers.delete_promise = function (SESSION_ID, worker_id, promise_q
2133
2167
  delete registry_entry.promise_queue[promise_queue_id];
2134
2168
  return true;
2135
2169
  };
2170
+ func.runtime.render.clone_runtime_options = function (value) {
2171
+ if (typeof structuredClone === 'function') {
2172
+ try {
2173
+ return structuredClone(value);
2174
+ } catch (_) {}
2175
+ }
2176
+
2177
+ if (Array.isArray(value)) {
2178
+ return value.map(function (item) {
2179
+ return func.runtime.render.clone_runtime_options(item);
2180
+ });
2181
+ }
2182
+
2183
+ if (value && typeof value === 'object') {
2184
+ const cloned = {};
2185
+ const keys = Object.keys(value);
2186
+ for (let index = 0; index < keys.length; index++) {
2187
+ const key = keys[index];
2188
+ cloned[key] = func.runtime.render.clone_runtime_options(value[key]);
2189
+ }
2190
+ return cloned;
2191
+ }
2192
+
2193
+ return value;
2194
+ };
2195
+ func.runtime.render.normalize_runtime_bootstrap = function (raw_options = {}) {
2196
+ const options = raw_options || {};
2197
+ let app_computing_mode = options.app_computing_mode || '';
2198
+ let app_render_mode = options.app_render_mode || '';
2199
+ let app_client_activation = options.app_client_activation || '';
2200
+ let ssr_payload = options.ssr_payload || null;
2201
+
2202
+ if (typeof ssr_payload === 'string') {
2203
+ try {
2204
+ ssr_payload = JSON.parse(ssr_payload);
2205
+ } catch (_) {
2206
+ ssr_payload = null;
2207
+ }
2208
+ }
2209
+
2210
+ if (ssr_payload && typeof ssr_payload === 'object') {
2211
+ ssr_payload = func.runtime.render.clone_runtime_options(ssr_payload);
2212
+ }
2213
+
2214
+ if (!app_computing_mode) {
2215
+ if (app_render_mode === 'ssr_first_page' || app_render_mode === 'ssr_full') {
2216
+ app_computing_mode = 'server';
2217
+ } else {
2218
+ app_computing_mode = 'main';
2219
+ }
2220
+ }
2221
+
2222
+ switch (app_computing_mode) {
2223
+ case 'main':
2224
+ app_render_mode = 'csr';
2225
+ app_client_activation = 'none';
2226
+ break;
2227
+
2228
+ case 'worker':
2229
+ app_render_mode = 'csr';
2230
+ app_client_activation = 'none';
2231
+ break;
2232
+
2233
+ default:
2234
+ app_computing_mode = 'server';
2235
+ if (app_render_mode !== 'ssr_full') {
2236
+ app_render_mode = 'ssr_first_page';
2237
+ }
2238
+ app_client_activation = app_render_mode === 'ssr_full' ? 'hydrate' : 'takeover';
2239
+ break;
2240
+ }
2241
+
2242
+ if (ssr_payload && typeof ssr_payload === 'object') {
2243
+ if (!ssr_payload.app_render_mode) {
2244
+ ssr_payload.app_render_mode = app_render_mode;
2245
+ }
2246
+ if (!ssr_payload.app_client_activation) {
2247
+ ssr_payload.app_client_activation = app_client_activation;
2248
+ }
2249
+ if (!ssr_payload.app_computing_mode) {
2250
+ ssr_payload.app_computing_mode = app_computing_mode;
2251
+ }
2252
+ }
2253
+
2254
+ return {
2255
+ app_computing_mode,
2256
+ app_render_mode,
2257
+ app_client_activation,
2258
+ ssr_payload,
2259
+ };
2260
+ };
2261
+ func.runtime.render.apply_runtime_bootstrap_defaults = function (target = {}) {
2262
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target);
2263
+ target.app_computing_mode = normalized.app_computing_mode;
2264
+ target.app_render_mode = normalized.app_render_mode;
2265
+ target.app_client_activation = normalized.app_client_activation;
2266
+ target.ssr_payload = normalized.ssr_payload;
2267
+ return normalized;
2268
+ };
2269
+ func.runtime.render.is_server_render_mode = function (target = {}) {
2270
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
2271
+ return normalized.app_computing_mode === 'server' && normalized.app_render_mode !== 'csr';
2272
+ };
2273
+ func.runtime.render.is_takeover_mode = function (target = {}) {
2274
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
2275
+ return normalized.app_client_activation === 'takeover';
2276
+ };
2277
+ func.runtime.render.is_hydration_mode = function (target = {}) {
2278
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
2279
+ return normalized.app_client_activation === 'hydrate';
2280
+ };
2281
+ func.runtime.render.get_ssr_payload = function (target = {}) {
2282
+ if (target?.opt?.ssr_payload) {
2283
+ return target.opt.ssr_payload;
2284
+ }
2285
+ if (target?.ssr_payload) {
2286
+ return target.ssr_payload;
2287
+ }
2288
+ const win = func.runtime.platform.get_window();
2289
+ return win?.__XUDA_SSR__ || null;
2290
+ };
2291
+ func.runtime.render.should_use_ssr_payload = function (SESSION_ID, paramsP) {
2292
+ const session = SESSION_OBJ?.[SESSION_ID];
2293
+ const payload = func.runtime.render.get_ssr_payload(session);
2294
+ if (!payload || payload._consumed) {
2295
+ return false;
2296
+ }
2297
+ if (paramsP?.prog_id && payload.prog_id && payload.prog_id !== paramsP.prog_id) {
2298
+ return false;
2299
+ }
2300
+ return true;
2301
+ };
2302
+ func.runtime.render.mark_ssr_payload_consumed = function (SESSION_ID) {
2303
+ const session = SESSION_OBJ?.[SESSION_ID];
2304
+ const payload = func.runtime.render.get_ssr_payload(session);
2305
+ if (!payload || typeof payload !== 'object') {
2306
+ return false;
2307
+ }
2308
+ payload._consumed = true;
2309
+ return true;
2310
+ };
2136
2311
  func.runtime.render.get_root_data_system = function (SESSION_ID) {
2137
2312
  return SESSION_OBJ[SESSION_ID]?.DS_GLB?.[0]?.data_system || null;
2138
2313
  };
@@ -2536,10 +2711,10 @@ func.runtime.resources.load_cdn = async function (SESSION_ID, resource) {
2536
2711
  await func.utils.load_js_on_demand(normalized_resource.src);
2537
2712
  break;
2538
2713
  case 'css':
2539
- await func.utils.load_js_on_demand(normalized_resource.src);
2714
+ func.runtime.platform.load_css(normalized_resource.src);
2540
2715
  break;
2541
2716
  case 'module':
2542
- func.utils.load_js_on_demand(normalized_resource.src, 'module');
2717
+ await func.utils.load_js_on_demand(normalized_resource.src, 'module');
2543
2718
  break;
2544
2719
  default:
2545
2720
  await func.utils.load_js_on_demand(normalized_resource.src);
@@ -2555,12 +2730,36 @@ func.runtime.resources.load_cdn = async function (SESSION_ID, resource) {
2555
2730
  func.runtime.resources.get_plugin_manifest_entry = function (_session, plugin_name) {
2556
2731
  return APP_OBJ[_session.app_id]?.app_plugins_purchased?.[plugin_name] || null;
2557
2732
  };
2558
- func.runtime.resources.get_plugin_module_path = function (plugin, resource) {
2733
+ func.runtime.resources.get_plugin_resource_candidates = function (_session, plugin, resource) {
2559
2734
  const manifest_entry = plugin?.manifest?.[resource];
2560
- return `${manifest_entry?.dist ? 'dist/' : ''}${resource}`;
2735
+ const default_path = `${manifest_entry?.dist ? 'dist/' : ''}${resource}`;
2736
+ const candidates = [];
2737
+ if (_session?.worker_type === 'Dev' && manifest_entry?.dist && /\.mjs$/.test(resource)) {
2738
+ candidates.push(`src/${resource}`);
2739
+ }
2740
+ candidates.push(default_path);
2741
+ return Array.from(new Set(candidates.filter(Boolean)));
2742
+ };
2743
+ func.runtime.resources.get_plugin_module_path = function (plugin, resource, _session) {
2744
+ return func.runtime.resources.get_plugin_resource_candidates(_session, plugin, resource)[0] || resource;
2561
2745
  };
2562
2746
  func.runtime.resources.get_plugin_module_url = async function (SESSION_ID, plugin_name, plugin, resource) {
2563
- return await func.utils.get_plugin_npm_cdn(SESSION_ID, plugin_name, func.runtime.resources.get_plugin_module_path(plugin, resource));
2747
+ const _session = SESSION_OBJ[SESSION_ID];
2748
+ return await func.utils.get_plugin_npm_cdn(SESSION_ID, plugin_name, func.runtime.resources.get_plugin_module_path(plugin, resource, _session));
2749
+ };
2750
+ func.runtime.resources.import_plugin_module = async function (SESSION_ID, plugin_name, plugin, resource) {
2751
+ const _session = SESSION_OBJ[SESSION_ID];
2752
+ const candidates = func.runtime.resources.get_plugin_resource_candidates(_session, plugin, resource);
2753
+ let last_error = null;
2754
+ for (let index = 0; index < candidates.length; index++) {
2755
+ const candidate = candidates[index];
2756
+ try {
2757
+ return await func.utils.get_plugin_resource(SESSION_ID, plugin_name, candidate);
2758
+ } catch (error) {
2759
+ last_error = error;
2760
+ }
2761
+ }
2762
+ throw last_error || new Error(`plugin resource not found: ${plugin_name}/${resource}`);
2564
2763
  };
2565
2764
  func.runtime.resources.load_plugin_runtime_css = async function (SESSION_ID, plugin_name, plugin) {
2566
2765
  if (!plugin?.manifest?.['runtime.mjs']?.dist || !plugin?.manifest?.['runtime.mjs']?.css) {
@@ -2590,12 +2789,10 @@ func.runtime.resources.run_ui_plugin = async function (SESSION_ID, paramsP, $elm
2590
2789
 
2591
2790
  await func.runtime.resources.load_plugin_runtime_css(SESSION_ID, plugin_name, plugin);
2592
2791
 
2593
- const plugin_index_src = await func.runtime.resources.get_plugin_module_url(SESSION_ID, plugin_name, plugin, 'index.mjs');
2594
- const plugin_index_resources = await import(plugin_index_src);
2792
+ const plugin_index_resources = await func.runtime.resources.import_plugin_module(SESSION_ID, plugin_name, plugin, 'index.mjs');
2595
2793
  const properties = await func.runtime.resources.resolve_plugin_properties(SESSION_ID, paramsP.dsSessionP, value?.attributes, plugin_index_resources.properties);
2596
2794
 
2597
- const plugin_runtime_src = await func.runtime.resources.get_plugin_module_url(SESSION_ID, plugin_name, plugin, 'runtime.mjs');
2598
- const plugin_runtime_resources = await import(plugin_runtime_src);
2795
+ const plugin_runtime_resources = await func.runtime.resources.import_plugin_module(SESSION_ID, plugin_name, plugin, 'runtime.mjs');
2599
2796
 
2600
2797
  if (plugin_runtime_resources.cdn && Array.isArray(plugin_runtime_resources.cdn)) {
2601
2798
  for await (const resource of plugin_runtime_resources.cdn) {
@@ -2678,25 +2875,52 @@ func.runtime.widgets.get_fields_data = async function (context, fields, props) {
2678
2875
 
2679
2876
  return { code: return_code, data: data_obj };
2680
2877
  };
2878
+ func.runtime.widgets.get_resource_candidates = function (context, resource) {
2879
+ return func.runtime.resources.get_plugin_resource_candidates(context._session, context.plugin, resource);
2880
+ };
2881
+ func.runtime.widgets.normalize_capabilities = function (definition) {
2882
+ const capabilities = definition?.capabilities || {};
2883
+ return {
2884
+ browser: capabilities.browser !== false,
2885
+ headless: capabilities.headless === true,
2886
+ };
2887
+ };
2888
+ func.runtime.widgets.supports_current_environment = function (definition) {
2889
+ const capabilities = func.runtime.widgets.normalize_capabilities(definition);
2890
+ if (func.runtime.platform.has_document()) {
2891
+ return capabilities.browser !== false;
2892
+ }
2893
+ return !!capabilities.headless;
2894
+ };
2681
2895
  func.runtime.widgets.get_resource_path = function (context, resource) {
2896
+ const relative_path = func.runtime.widgets.get_resource_candidates(context, resource)[0] || resource;
2682
2897
  if (context._session.worker_type === 'Dev') {
2683
- return `../../plugins/${context.plugin_name}/${resource}`;
2898
+ return `../../plugins/${context.plugin_name}/${relative_path}`;
2684
2899
  }
2685
- const manifest_entry = context.plugin?.manifest?.[resource];
2686
- const dist_prefix = manifest_entry?.dist ? 'dist/' : '';
2687
- return `https://${context._session.domain}/plugins/${context.plugin_name}/${dist_prefix}${resource}?gtp_token=${context._session.gtp_token}&app_id=${context._session.app_id}`;
2900
+ return `https://${context._session.domain}/plugins/${context.plugin_name}/${relative_path}?gtp_token=${context._session.gtp_token}&app_id=${context._session.app_id}`;
2688
2901
  };
2689
2902
  func.runtime.widgets.load_css_style = function (context) {
2690
2903
  func.utils.load_css_on_demand(func.runtime.widgets.get_resource_path(context, 'style.css'));
2691
2904
  return true;
2692
2905
  };
2693
2906
  func.runtime.widgets.get_resource = async function (context, resource) {
2694
- const manifest_entry = context.plugin?.manifest?.[resource];
2695
- const path = `${manifest_entry?.dist ? 'dist/' : ''}${resource}`;
2696
- return await func.utils.get_plugin_resource(context.SESSION_ID, context.plugin_name, path);
2907
+ const candidates = func.runtime.widgets.get_resource_candidates(context, resource);
2908
+ let last_error = null;
2909
+ for (let index = 0; index < candidates.length; index++) {
2910
+ const candidate = candidates[index];
2911
+ try {
2912
+ return await func.utils.get_plugin_resource(context.SESSION_ID, context.plugin_name, candidate);
2913
+ } catch (error) {
2914
+ last_error = error;
2915
+ }
2916
+ }
2917
+ throw last_error || new Error(`widget resource not found: ${context.plugin_name}/${resource}`);
2918
+ };
2919
+ func.runtime.widgets.get_definition = async function (context) {
2920
+ return await func.runtime.widgets.get_resource(context, 'index.mjs');
2697
2921
  };
2698
2922
  func.runtime.widgets.get_methods = async function (context) {
2699
- const index = await func.runtime.widgets.get_resource(context, 'index.mjs');
2923
+ const index = await func.runtime.widgets.get_definition(context);
2700
2924
  return index?.methods || {};
2701
2925
  };
2702
2926
  func.runtime.widgets.load_runtime_css = async function (context) {
@@ -2707,7 +2931,7 @@ func.runtime.widgets.load_runtime_css = async function (context) {
2707
2931
  func.utils.load_css_on_demand(plugin_runtime_css_url);
2708
2932
  return true;
2709
2933
  };
2710
- func.runtime.widgets.build_params = function (context, $containerP, plugin_setup, api_utils, extra = {}) {
2934
+ func.runtime.widgets.build_params = function (context, container_node, container_data, plugin_setup, api_utils, extra = {}) {
2711
2935
  return {
2712
2936
  SESSION_ID: context.SESSION_ID,
2713
2937
  method: context.method,
@@ -2716,14 +2940,28 @@ func.runtime.widgets.build_params = function (context, $containerP, plugin_setup
2716
2940
  sourceP: context.sourceP,
2717
2941
  propsP: context.propsP,
2718
2942
  plugin_name: context.plugin_name,
2719
- $containerP,
2943
+ container_node,
2944
+ container_data,
2720
2945
  plugin_setup,
2721
2946
  report_error: function (descP, warn) {
2722
2947
  return func.runtime.widgets.report_error(context, descP, warn);
2723
2948
  },
2949
+ log_error: function (descP, warn) {
2950
+ return func.runtime.widgets.report_error(context, descP, warn);
2951
+ },
2724
2952
  call_plugin_api: async function (plugin_nameP, dataP) {
2725
2953
  return await func.utils.call_plugin_api(context.SESSION_ID, plugin_nameP, dataP);
2726
2954
  },
2955
+ set_SYS_GLOBAL_OBJ_WIDGET_INFO: async function (docP) {
2956
+ return await func.utils.set_SYS_GLOBAL_OBJ_WIDGET_INFO(context.SESSION_ID, docP);
2957
+ },
2958
+ run_widgetCallbackEvent: async function () {
2959
+ const event_id = context.propsP?.widgetCallbackEvent;
2960
+ if (!event_id || !api_utils?.invoke_event) {
2961
+ return false;
2962
+ }
2963
+ return await api_utils.invoke_event(event_id);
2964
+ },
2727
2965
  api_utils,
2728
2966
  ...extra,
2729
2967
  };
@@ -3794,25 +4032,722 @@ func.common.get_data_from_websocket = async function (SESSION_ID, serviceP, data
3794
4032
  // // so slicing is a reliable way to get a fixed length.
3795
4033
  // const shortHash = base36Hash.slice(0, 10);
3796
4034
 
3797
- // // 5. Pad the start in the unlikely case the hash is shorter than 10 characters.
3798
- // // This ensures the output is always exactly 10 characters long.
3799
- // return shortHash.padStart(10, '0');
3800
- // };
4035
+ // // 5. Pad the start in the unlikely case the hash is shorter than 10 characters.
4036
+ // // This ensures the output is always exactly 10 characters long.
4037
+ // return shortHash.padStart(10, '0');
4038
+ // };
4039
+
4040
+ func.common.fastHash = function (inputString) {
4041
+ let hash = 0x811c9dc5; // FNV offset basis
4042
+
4043
+ for (let i = 0; i < inputString.length; i++) {
4044
+ hash ^= inputString.charCodeAt(i);
4045
+ // FNV prime multiplication with 32-bit overflow
4046
+ hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
4047
+ }
4048
+
4049
+ // Convert to base36 and pad to 10 characters
4050
+ return ((hash >>> 0).toString(36) + '0000000000').slice(0, 10);
4051
+ };
4052
+
4053
+ glb.new_xu_render = false;
4054
+ func.runtime = func.runtime || {};
4055
+ func.runtime.ui = func.runtime.ui || {};
4056
+ func.runtime.render = func.runtime.render || {};
4057
+ func.runtime.widgets = func.runtime.widgets || {};
4058
+
4059
+ // Shared render-tree contract helpers live here so browser and headless runtimes can resolve the same UI structure.
4060
+
4061
+ func.runtime.render.TREE_CONTRACT_VERSION = func.runtime.render.TREE_CONTRACT_VERSION || 'xuda.render_tree.v1';
4062
+ func.runtime.render._tree_widget_capability_cache = func.runtime.render._tree_widget_capability_cache || {};
4063
+
4064
+ func.runtime.render.safe_clone_tree_value = function (value) {
4065
+ if (typeof structuredClone === 'function') {
4066
+ try {
4067
+ return structuredClone(value);
4068
+ } catch (_) {
4069
+ // Fall through to the recursive clone below.
4070
+ }
4071
+ }
4072
+
4073
+ if (Array.isArray(value)) {
4074
+ return value.map(function (item) {
4075
+ return func.runtime.render.safe_clone_tree_value(item);
4076
+ });
4077
+ }
4078
+
4079
+ if (value && typeof value === 'object') {
4080
+ const cloned = {};
4081
+ const keys = Object.keys(value);
4082
+ for (let index = 0; index < keys.length; index++) {
4083
+ const key = keys[index];
4084
+ cloned[key] = func.runtime.render.safe_clone_tree_value(value[key]);
4085
+ }
4086
+ return cloned;
4087
+ }
4088
+
4089
+ return value;
4090
+ };
4091
+ func.runtime.render.sort_tree_debug_value = function (value) {
4092
+ if (Array.isArray(value)) {
4093
+ return value.map(function (item) {
4094
+ return func.runtime.render.sort_tree_debug_value(item);
4095
+ });
4096
+ }
4097
+
4098
+ if (value && typeof value === 'object') {
4099
+ const sorted = {};
4100
+ const keys = Object.keys(value).sort();
4101
+ for (let index = 0; index < keys.length; index++) {
4102
+ const key = keys[index];
4103
+ sorted[key] = func.runtime.render.sort_tree_debug_value(value[key]);
4104
+ }
4105
+ return sorted;
4106
+ }
4107
+
4108
+ return value;
4109
+ };
4110
+ func.runtime.render.is_tree_node = function (nodeP) {
4111
+ return !!nodeP?.contract && nodeP.contract === func.runtime.render.TREE_CONTRACT_VERSION;
4112
+ };
4113
+ func.runtime.render.get_tree_source_node = function (nodeP) {
4114
+ if (!func.runtime.render.is_tree_node(nodeP)) {
4115
+ return nodeP || null;
4116
+ }
4117
+ return nodeP?.meta?.source_node || null;
4118
+ };
4119
+ func.runtime.render.get_tree_source_snapshot = function (nodeP) {
4120
+ if (!func.runtime.render.is_tree_node(nodeP)) {
4121
+ return func.runtime.render.safe_clone_tree_value(nodeP);
4122
+ }
4123
+ return nodeP?.meta?.source_snapshot || null;
4124
+ };
4125
+ func.runtime.render.get_tree_node_kind = function (nodeP) {
4126
+ const tag_name = typeof nodeP?.tagName === 'string' ? nodeP.tagName.toLowerCase() : '';
4127
+ const node_type = typeof nodeP?.type === 'string' ? nodeP.type.toLowerCase() : '';
4128
+
4129
+ if (tag_name === 'xu-widget') return 'widget';
4130
+ if (tag_name === 'xu-single-view') return 'single_view';
4131
+ if (tag_name === 'xu-multi-view') return 'multi_view';
4132
+ if (tag_name === 'xu-panel') return 'panel';
4133
+ if (tag_name === 'xu-teleport') return 'teleport';
4134
+ if (tag_name === 'xurender') return 'placeholder';
4135
+ if (tag_name === '#text' || node_type === 'text') return 'text';
4136
+ if (!tag_name && typeof nodeP?.content === 'string' && !Array.isArray(nodeP?.children)) return 'text';
4137
+ return 'element';
4138
+ };
4139
+ func.runtime.render.get_tree_node_id = function (nodeP, pathP) {
4140
+ if (nodeP?.id) {
4141
+ return nodeP.id;
4142
+ }
4143
+ if (nodeP?.id_org) {
4144
+ return nodeP.id_org;
4145
+ }
4146
+ const normalized_path = Array.isArray(pathP) && pathP.length ? pathP.join('.') : 'root';
4147
+ return `tree-node-${normalized_path}`;
4148
+ };
4149
+ func.runtime.render.get_tree_controls = function (attributes) {
4150
+ const attrs = attributes || {};
4151
+ const get_first_defined = function (keys) {
4152
+ for (let index = 0; index < keys.length; index++) {
4153
+ const key = keys[index];
4154
+ if (Object.prototype.hasOwnProperty.call(attrs, key)) {
4155
+ return attrs[key];
4156
+ }
4157
+ }
4158
+ return null;
4159
+ };
4160
+ return {
4161
+ xu_for: get_first_defined(['xu-for', 'xu-exp:xu-for']),
4162
+ xu_if: get_first_defined(['xu-if', 'xu-exp:xu-if']),
4163
+ xu_render: get_first_defined(['xu-render', 'xu-exp:xu-render']),
4164
+ };
4165
+ };
4166
+ func.runtime.render.get_tree_node_capabilities = async function (options) {
4167
+ const attributes = options?.attributes || {};
4168
+ const plugin_name = attributes['xu-widget'];
4169
+ if (!plugin_name) {
4170
+ return null;
4171
+ }
4172
+
4173
+ const cache = func.runtime.render._tree_widget_capability_cache;
4174
+ if (cache[plugin_name]) {
4175
+ return func.runtime.render.safe_clone_tree_value(cache[plugin_name]);
4176
+ }
4177
+
4178
+ let capabilities = {
4179
+ browser: true,
4180
+ headless: false,
4181
+ };
4182
+
4183
+ try {
4184
+ if (options.SESSION_ID && options.paramsP && func.runtime.widgets?.create_context && func.runtime.widgets?.get_definition) {
4185
+ const widget_context = func.runtime.widgets.create_context(options.SESSION_ID, options.paramsP, attributes);
4186
+ const definition = await func.runtime.widgets.get_definition(widget_context);
4187
+ capabilities = func.runtime.widgets.normalize_capabilities(definition);
4188
+ }
4189
+ } catch (_) {
4190
+ // Keep the safe browser-only default when the widget definition is unavailable.
4191
+ }
4192
+
4193
+ cache[plugin_name] = capabilities;
4194
+ return func.runtime.render.safe_clone_tree_value(capabilities);
4195
+ };
4196
+ func.runtime.render.ensure_tree_node = async function (options) {
4197
+ if (!options?.nodeP) {
4198
+ return null;
4199
+ }
4200
+ if (func.runtime.render.is_tree_node(options.nodeP)) {
4201
+ return options.nodeP;
4202
+ }
4203
+ return await func.runtime.render.build_tree(options);
4204
+ };
4205
+ func.runtime.render.build_tree = async function (options) {
4206
+ if (Array.isArray(options?.nodeP)) {
4207
+ return await func.runtime.render.build_tree_list({
4208
+ ...options,
4209
+ nodesP: options.nodeP,
4210
+ });
4211
+ }
4212
+
4213
+ const nodeP = options?.nodeP;
4214
+ if (!nodeP) {
4215
+ return null;
4216
+ }
4217
+ if (func.runtime.render.is_tree_node(nodeP)) {
4218
+ return nodeP;
4219
+ }
4220
+
4221
+ const pathP = Array.isArray(options?.pathP) ? options.pathP.slice() : [];
4222
+ const tree_path = pathP.length ? pathP.slice() : [0];
4223
+ const attributes = func.runtime.render.safe_clone_tree_value(nodeP.attributes || {});
4224
+ if (typeof nodeP.content !== 'undefined' && typeof attributes['xu-content'] === 'undefined') {
4225
+ attributes['xu-content'] = func.runtime.render.safe_clone_tree_value(nodeP.content);
4226
+ }
4227
+
4228
+ const widget_capabilities = await func.runtime.render.get_tree_node_capabilities({
4229
+ SESSION_ID: options?.SESSION_ID,
4230
+ paramsP: options?.paramsP,
4231
+ attributes,
4232
+ });
4233
+ const children = [];
4234
+ const child_nodes = Array.isArray(nodeP.children) ? nodeP.children : [];
4235
+ const parent_tree_id = tree_path.join('.');
4236
+
4237
+ for (let index = 0; index < child_nodes.length; index++) {
4238
+ const child_tree = await func.runtime.render.build_tree({
4239
+ ...options,
4240
+ nodeP: child_nodes[index],
4241
+ pathP: tree_path.concat(index),
4242
+ parent_tree_id: parent_tree_id,
4243
+ keyP: index,
4244
+ parent_nodeP: nodeP,
4245
+ });
4246
+ if (child_tree) {
4247
+ children.push(child_tree);
4248
+ }
4249
+ }
4250
+
4251
+ const tree = {
4252
+ contract: func.runtime.render.TREE_CONTRACT_VERSION,
4253
+ id: func.runtime.render.get_tree_node_id(nodeP, tree_path),
4254
+ xu_tree_id: `tree.${tree_path.join('.')}`,
4255
+ kind: func.runtime.render.get_tree_node_kind(nodeP),
4256
+ tagName: nodeP.tagName || null,
4257
+ attributes,
4258
+ text: typeof nodeP.text !== 'undefined' ? func.runtime.render.safe_clone_tree_value(nodeP.text) : null,
4259
+ content: typeof nodeP.content !== 'undefined' ? func.runtime.render.safe_clone_tree_value(nodeP.content) : null,
4260
+ children,
4261
+ meta: {
4262
+ tree_id: tree_path.join('.'),
4263
+ path: tree_path,
4264
+ parent_tree_id: options?.parent_tree_id || null,
4265
+ key: typeof options?.keyP === 'undefined' ? null : options.keyP,
4266
+ recordid: nodeP?.recordid || null,
4267
+ dependency_fields: func.runtime.render.safe_clone_tree_value(nodeP?.dependency_fields || null),
4268
+ iterate_info: func.runtime.render.safe_clone_tree_value(options?.parent_infoP?.iterate_info || nodeP?.iterate_info || null),
4269
+ controls: func.runtime.render.get_tree_controls(attributes),
4270
+ capabilities: widget_capabilities,
4271
+ widget: attributes['xu-widget']
4272
+ ? {
4273
+ plugin_name: attributes['xu-widget'],
4274
+ method: attributes['xu-method'] || '_default',
4275
+ capabilities: widget_capabilities,
4276
+ }
4277
+ : null,
4278
+ source_node_id: nodeP?.id || nodeP?.id_org || null,
4279
+ source_node: nodeP,
4280
+ source_snapshot: func.runtime.ui?.get_node_snapshot
4281
+ ? func.runtime.ui.get_node_snapshot(nodeP)
4282
+ : func.runtime.render.safe_clone_tree_value(nodeP),
4283
+ },
4284
+ };
4285
+
4286
+ return tree;
4287
+ };
4288
+ func.runtime.render.build_tree_list = async function (options) {
4289
+ const nodes = Array.isArray(options?.nodesP) ? options.nodesP : [];
4290
+ const trees = [];
4291
+
4292
+ for (let index = 0; index < nodes.length; index++) {
4293
+ const tree = await func.runtime.render.build_tree({
4294
+ ...options,
4295
+ nodeP: nodes[index],
4296
+ pathP: Array.isArray(options?.pathP) && options.pathP.length ? options.pathP.concat(index) : [index],
4297
+ keyP: index,
4298
+ });
4299
+ if (tree) {
4300
+ trees.push(tree);
4301
+ }
4302
+ }
4303
+
4304
+ return trees;
4305
+ };
4306
+ func.runtime.render.sanitize_tree_for_debug = function (treeP) {
4307
+ if (Array.isArray(treeP)) {
4308
+ return treeP.map(function (child) {
4309
+ return func.runtime.render.sanitize_tree_for_debug(child);
4310
+ });
4311
+ }
4312
+
4313
+ if (!func.runtime.render.is_tree_node(treeP)) {
4314
+ return func.runtime.render.sort_tree_debug_value(func.runtime.render.safe_clone_tree_value(treeP));
4315
+ }
4316
+
4317
+ return {
4318
+ contract: treeP.contract,
4319
+ id: treeP.id,
4320
+ xu_tree_id: treeP.xu_tree_id || null,
4321
+ kind: treeP.kind,
4322
+ tagName: treeP.tagName,
4323
+ attributes: func.runtime.render.sort_tree_debug_value(treeP.attributes || {}),
4324
+ text: treeP.text,
4325
+ content: treeP.content,
4326
+ children: treeP.children.map(function (child) {
4327
+ return func.runtime.render.sanitize_tree_for_debug(child);
4328
+ }),
4329
+ meta: {
4330
+ tree_id: treeP.meta?.tree_id || null,
4331
+ path: func.runtime.render.safe_clone_tree_value(treeP.meta?.path || []),
4332
+ parent_tree_id: treeP.meta?.parent_tree_id || null,
4333
+ key: typeof treeP.meta?.key === 'undefined' ? null : treeP.meta.key,
4334
+ recordid: treeP.meta?.recordid || null,
4335
+ dependency_fields: func.runtime.render.sort_tree_debug_value(treeP.meta?.dependency_fields || null),
4336
+ iterate_info: func.runtime.render.sort_tree_debug_value(treeP.meta?.iterate_info || null),
4337
+ controls: func.runtime.render.sort_tree_debug_value(treeP.meta?.controls || null),
4338
+ capabilities: func.runtime.render.sort_tree_debug_value(treeP.meta?.capabilities || null),
4339
+ widget: treeP.meta?.widget
4340
+ ? {
4341
+ plugin_name: treeP.meta.widget.plugin_name,
4342
+ method: treeP.meta.widget.method,
4343
+ capabilities: func.runtime.render.sort_tree_debug_value(treeP.meta.widget.capabilities || null),
4344
+ }
4345
+ : null,
4346
+ source_node_id: treeP.meta?.source_node_id || null,
4347
+ },
4348
+ };
4349
+ };
4350
+ func.runtime.render.serialize_tree = function (treeP, spacing = 2) {
4351
+ return JSON.stringify(func.runtime.render.sanitize_tree_for_debug(treeP), null, spacing);
4352
+ };
4353
+ func.runtime = func.runtime || {};
4354
+ func.runtime.ui = func.runtime.ui || {};
4355
+ func.runtime.render = func.runtime.render || {};
4356
+ func.runtime.widgets = func.runtime.widgets || {};
4357
+
4358
+ // Shared string-renderer helpers live here so headless/server runtimes can materialize the render tree without a DOM.
4359
+
4360
+ func.runtime.render.HTML_VOID_TAGS = func.runtime.render.HTML_VOID_TAGS || {
4361
+ area: true,
4362
+ base: true,
4363
+ br: true,
4364
+ col: true,
4365
+ embed: true,
4366
+ hr: true,
4367
+ img: true,
4368
+ input: true,
4369
+ link: true,
4370
+ meta: true,
4371
+ param: true,
4372
+ source: true,
4373
+ track: true,
4374
+ wbr: true,
4375
+ };
4376
+ func.runtime.render.escape_html = function (value) {
4377
+ return `${value ?? ''}`
4378
+ .replaceAll('&', '&amp;')
4379
+ .replaceAll('<', '&lt;')
4380
+ .replaceAll('>', '&gt;')
4381
+ .replaceAll('"', '&quot;')
4382
+ .replaceAll("'", '&#039;');
4383
+ };
4384
+ func.runtime.render.escape_html_attribute = function (value) {
4385
+ return func.runtime.render.escape_html(value);
4386
+ };
4387
+ func.runtime.render.is_html_void_tag = function (tag_name) {
4388
+ return !!func.runtime.render.HTML_VOID_TAGS[(tag_name || '').toLowerCase()];
4389
+ };
4390
+ func.runtime.render.is_falsey_render_value = function (value) {
4391
+ if (value === false || value === null || typeof value === 'undefined') {
4392
+ return true;
4393
+ }
4394
+ if (typeof value === 'number') {
4395
+ return value === 0;
4396
+ }
4397
+ if (typeof value === 'string') {
4398
+ const normalized = value.trim().toLowerCase();
4399
+ return normalized === '' || normalized === 'false' || normalized === '0' || normalized === 'null' || normalized === 'undefined' || normalized === 'off' || normalized === 'no';
4400
+ }
4401
+ return false;
4402
+ };
4403
+ func.runtime.render.should_render_tree_node = function (treeP) {
4404
+ const controls = treeP?.meta?.controls || {};
4405
+ if (controls.xu_if !== null && controls.xu_if !== undefined && func.runtime.render.is_falsey_render_value(controls.xu_if)) {
4406
+ return false;
4407
+ }
4408
+ if (controls.xu_render !== null && controls.xu_render !== undefined && func.runtime.render.is_falsey_render_value(controls.xu_render)) {
4409
+ return false;
4410
+ }
4411
+ return true;
4412
+ };
4413
+ func.runtime.render.is_tree_control_attribute = function (key) {
4414
+ if (!key) {
4415
+ return false;
4416
+ }
4417
+ return (
4418
+ key.startsWith('xu-exp:') ||
4419
+ key === 'xu-widget' ||
4420
+ key === 'xu-method' ||
4421
+ key === 'xu-for' ||
4422
+ key === 'xu-for-key' ||
4423
+ key === 'xu-for-val' ||
4424
+ key === 'xu-if' ||
4425
+ key === 'xu-render' ||
4426
+ key === 'xu-bind' ||
4427
+ key === 'xu-content' ||
4428
+ key === 'xu-text' ||
4429
+ key === 'xu-html' ||
4430
+ key === 'xu-show' ||
4431
+ key === 'xu-panel-program' ||
4432
+ key === 'xu-teleport'
4433
+ );
4434
+ };
4435
+ func.runtime.render.get_string_renderer_tag_name = function (treeP) {
4436
+ switch (treeP?.kind) {
4437
+ case 'widget':
4438
+ case 'single_view':
4439
+ case 'multi_view':
4440
+ case 'panel':
4441
+ case 'teleport':
4442
+ return 'div';
4443
+ case 'placeholder':
4444
+ return null;
4445
+ case 'text':
4446
+ return null;
4447
+ default:
4448
+ return treeP?.tagName || 'div';
4449
+ }
4450
+ };
4451
+ func.runtime.render.get_tree_terminal_content = function (treeP) {
4452
+ const attributes = treeP?.attributes || {};
4453
+ if (typeof attributes['xu-html'] !== 'undefined' && attributes['xu-html'] !== null) {
4454
+ return {
4455
+ value: `${attributes['xu-html']}`,
4456
+ mode: 'html',
4457
+ };
4458
+ }
4459
+ if (typeof attributes['xu-content'] !== 'undefined' && attributes['xu-content'] !== null) {
4460
+ return {
4461
+ value: `${attributes['xu-content']}`,
4462
+ mode: 'html',
4463
+ };
4464
+ }
4465
+ if (typeof attributes['xu-text'] !== 'undefined' && attributes['xu-text'] !== null) {
4466
+ return {
4467
+ value: `${attributes['xu-text']}`,
4468
+ mode: 'text',
4469
+ };
4470
+ }
4471
+ if (treeP?.kind === 'text') {
4472
+ return {
4473
+ value: typeof treeP?.text !== 'undefined' && treeP?.text !== null ? `${treeP.text}` : `${treeP?.content || ''}`,
4474
+ mode: 'text',
4475
+ };
4476
+ }
4477
+ return null;
4478
+ };
4479
+ func.runtime.render.render_tree_terminal_content = function (treeP) {
4480
+ const terminal = func.runtime.render.get_tree_terminal_content(treeP);
4481
+ if (!terminal) {
4482
+ return null;
4483
+ }
4484
+ if (terminal.mode === 'html') {
4485
+ return terminal.value;
4486
+ }
4487
+ return func.runtime.render.escape_html(terminal.value);
4488
+ };
4489
+ func.runtime.render.get_widget_fallback_markup = function (treeP) {
4490
+ const widget_meta = treeP?.meta?.widget || {};
4491
+ const capability_state = widget_meta?.capabilities?.headless ? 'headless-capable' : 'browser-only';
4492
+ return `<!--xuda-widget:${func.runtime.render.escape_html(widget_meta.plugin_name || 'unknown')}:${capability_state}-->`;
4493
+ };
4494
+ func.runtime.render.get_tree_string_attributes = function (treeP, renderer_context) {
4495
+ const attributes = func.runtime.render.safe_clone_tree_value(treeP?.attributes || {});
4496
+ const attr_pairs = [];
4497
+ const keys = Object.keys(attributes);
4498
+
4499
+ for (let index = 0; index < keys.length; index++) {
4500
+ const key = keys[index];
4501
+ if (func.runtime.render.is_tree_control_attribute(key)) {
4502
+ continue;
4503
+ }
4504
+ const value = attributes[key];
4505
+ if (value === false || value === null || typeof value === 'undefined') {
4506
+ continue;
4507
+ }
4508
+ if (value === true) {
4509
+ attr_pairs.push(key);
4510
+ continue;
4511
+ }
4512
+ const normalized_value = typeof value === 'object' ? JSON.stringify(value) : `${value}`;
4513
+ attr_pairs.push(`${key}="${func.runtime.render.escape_html_attribute(normalized_value)}"`);
4514
+ }
4515
+
4516
+ attr_pairs.push(`data-xuda-kind="${func.runtime.render.escape_html_attribute(treeP?.kind || 'element')}"`);
4517
+ attr_pairs.push(`data-xuda-node-id="${func.runtime.render.escape_html_attribute(treeP?.id || treeP?.meta?.source_node_id || '')}"`);
4518
+ attr_pairs.push(`data-xuda-tree-id="${func.runtime.render.escape_html_attribute(treeP?.meta?.tree_id || '')}"`);
4519
+
4520
+ if (treeP?.kind === 'widget' && treeP?.meta?.widget) {
4521
+ attr_pairs.push(`data-xuda-widget="${func.runtime.render.escape_html_attribute(treeP.meta.widget.plugin_name || '')}"`);
4522
+ attr_pairs.push(`data-xuda-widget-method="${func.runtime.render.escape_html_attribute(treeP.meta.widget.method || '_default')}"`);
4523
+ attr_pairs.push(`data-xuda-widget-capability="${func.runtime.render.escape_html_attribute(treeP.meta.widget.capabilities?.headless ? 'headless' : 'browser')}"`);
4524
+ }
4525
+
4526
+ if (treeP?.kind === 'teleport' && treeP?.attributes?.['xu-teleport']) {
4527
+ attr_pairs.push(`data-xuda-teleport-target="${func.runtime.render.escape_html_attribute(treeP.attributes['xu-teleport'])}"`);
4528
+ }
4529
+
4530
+ if ((treeP?.meta?.controls?.xu_for !== null && treeP?.meta?.controls?.xu_for !== undefined) && !renderer_context?.strip_iteration_markers) {
4531
+ attr_pairs.push('data-xuda-xu-for="pending"');
4532
+ }
4533
+
4534
+ return attr_pairs.length ? ' ' + attr_pairs.join(' ') : '';
4535
+ };
4536
+ func.runtime.render.render_tree_children_to_string = async function (treeP, renderer_context) {
4537
+ if (!Array.isArray(treeP?.children) || !treeP.children.length) {
4538
+ return '';
4539
+ }
4540
+ let html = '';
4541
+ for (let index = 0; index < treeP.children.length; index++) {
4542
+ html += await func.runtime.render.render_tree_to_string(treeP.children[index], {
4543
+ ...renderer_context,
4544
+ parent_tree: treeP,
4545
+ });
4546
+ }
4547
+ return html;
4548
+ };
4549
+ func.runtime.render.render_tree_to_string = async function (treeP, renderer_context = {}) {
4550
+ if (!treeP) {
4551
+ return '';
4552
+ }
4553
+ if (Array.isArray(treeP)) {
4554
+ let html = '';
4555
+ for (let index = 0; index < treeP.length; index++) {
4556
+ html += await func.runtime.render.render_tree_to_string(treeP[index], renderer_context);
4557
+ }
4558
+ return html;
4559
+ }
4560
+
4561
+ const ensured_tree = await func.runtime.render.ensure_tree_node({
4562
+ SESSION_ID: renderer_context?.SESSION_ID,
4563
+ nodeP: treeP,
4564
+ paramsP: renderer_context?.paramsP,
4565
+ parent_infoP: renderer_context?.parent_infoP,
4566
+ keyP: renderer_context?.keyP,
4567
+ parent_nodeP: renderer_context?.parent_nodeP,
4568
+ });
4569
+
4570
+ if (!ensured_tree || !func.runtime.render.should_render_tree_node(ensured_tree)) {
4571
+ return '';
4572
+ }
4573
+
4574
+ if (ensured_tree.kind === 'placeholder') {
4575
+ if (renderer_context?.include_placeholders) {
4576
+ return `<!--xuda-placeholder:${func.runtime.render.escape_html(ensured_tree.id || '')}-->`;
4577
+ }
4578
+ return '';
4579
+ }
4580
+
4581
+ if (ensured_tree.kind === 'text') {
4582
+ return func.runtime.render.render_tree_terminal_content(ensured_tree) || '';
4583
+ }
4584
+
4585
+ const tag_name = func.runtime.render.get_string_renderer_tag_name(ensured_tree);
4586
+ if (!tag_name || tag_name.toLowerCase() === 'script') {
4587
+ return '';
4588
+ }
4589
+
4590
+ const attributes = func.runtime.render.get_tree_string_attributes(ensured_tree, renderer_context);
4591
+ const terminal_content = func.runtime.render.render_tree_terminal_content(ensured_tree);
4592
+ let children_html = terminal_content !== null ? terminal_content : await func.runtime.render.render_tree_children_to_string(ensured_tree, renderer_context);
4593
+
4594
+ if (ensured_tree.kind === 'widget' && !children_html) {
4595
+ children_html = func.runtime.render.get_widget_fallback_markup(ensured_tree);
4596
+ }
4597
+
4598
+ if (func.runtime.render.is_html_void_tag(tag_name)) {
4599
+ return `<${tag_name}${attributes}>`;
4600
+ }
4601
+
4602
+ return `<${tag_name}${attributes}>${children_html}</${tag_name}>`;
4603
+ };
4604
+ func.runtime.render.render_to_string = async function (options = {}) {
4605
+ const treeP = await func.runtime.render.ensure_tree_node({
4606
+ SESSION_ID: options.SESSION_ID,
4607
+ nodeP: options.treeP || options.nodeP,
4608
+ paramsP: options.paramsP,
4609
+ parent_infoP: options.parent_infoP,
4610
+ keyP: options.keyP,
4611
+ parent_nodeP: options.parent_nodeP,
4612
+ });
4613
+
4614
+ return await func.runtime.render.render_tree_to_string(treeP, options);
4615
+ };
4616
+ func.runtime.render.get_server_render_mode = function (options = {}) {
4617
+ const normalized = func.runtime.render.normalize_runtime_bootstrap({
4618
+ app_computing_mode: options.app_computing_mode,
4619
+ app_render_mode: options.app_render_mode,
4620
+ app_client_activation: options.app_client_activation,
4621
+ });
4622
+
4623
+ return normalized;
4624
+ };
4625
+ func.runtime.render.build_server_render_params = async function (options = {}) {
4626
+ const SESSION_ID = options.SESSION_ID;
4627
+ const prog_id = options.prog_id;
4628
+ const dsSessionP = options.dsSessionP;
4629
+ const _session = SESSION_OBJ?.[SESSION_ID] || {};
4630
+ const _ds = _session?.DS_GLB?.[dsSessionP] || {};
4631
+ const viewDoc = options.viewDoc || (await func.utils?.VIEWS_OBJ?.get?.(SESSION_ID, prog_id));
4632
+
4633
+ if (!viewDoc?.properties) {
4634
+ throw new Error(`view document not found for ${prog_id}`);
4635
+ }
4636
+
4637
+ const base_params = _ds?.screen_params ? func.runtime.render.safe_clone_tree_value(_ds.screen_params) : {};
4638
+ const screenId = options.screenId || base_params.screenId || `ssr_${prog_id}_${dsSessionP || '0'}`;
4639
+ const paramsP = {
4640
+ ...base_params,
4641
+ prog_id,
4642
+ sourceScreenP: null,
4643
+ $callingContainerP: null,
4644
+ triggerIdP: null,
4645
+ callingDataSource_objP: _ds,
4646
+ rowIdP: typeof options.rowIdP !== 'undefined' ? options.rowIdP : (_ds?.currentRecordId || null),
4647
+ renderType: viewDoc.properties?.renderType,
4648
+ parameters_obj_inP: options.parameters_obj_inP || base_params.parameters_obj_inP || options.parameters_raw_obj || {},
4649
+ source_functionP: options.source_functionP || base_params.source_functionP || 'render_string',
4650
+ is_panelP: false,
4651
+ screen_type: options.screen_type || base_params.screen_type || 'render_string',
4652
+ screenInfo: viewDoc,
4653
+ call_screen_propertiesP: base_params.call_screen_propertiesP,
4654
+ parentDataSourceNoP: typeof _ds?.parentDataSourceNo === 'undefined' || _ds?.parentDataSourceNo === null ? 0 : _ds.parentDataSourceNo,
4655
+ parameters_raw_obj: options.parameters_raw_obj || base_params.parameters_raw_obj || {},
4656
+ dsSessionP,
4657
+ screenId,
4658
+ containerIdP: base_params.containerIdP || `ssr_container_${screenId}`,
4659
+ };
4660
+
4661
+ if (_ds) {
4662
+ _ds.screen_params = paramsP;
4663
+ }
4664
+
4665
+ return paramsP;
4666
+ };
4667
+ func.runtime.render.build_prog_tree = async function (options = {}) {
4668
+ const SESSION_ID = options.SESSION_ID;
4669
+ const prog_id = options.prog_id;
4670
+ const viewDoc = options.viewDoc || (await func.utils?.VIEWS_OBJ?.get?.(SESSION_ID, prog_id));
4671
+
4672
+ if (!viewDoc?.progUi?.length) {
4673
+ throw new Error(`progUi not found for ${prog_id}`);
4674
+ }
4675
+
4676
+ const paramsP = options.paramsP || (await func.runtime.render.build_server_render_params({
4677
+ ...options,
4678
+ SESSION_ID,
4679
+ prog_id,
4680
+ viewDoc,
4681
+ }));
4682
+ const root_index = typeof options.root_index === 'number' ? options.root_index : 0;
4683
+ const root_node = func.runtime.render.safe_clone_tree_value(viewDoc.progUi[root_index]);
4684
+ const tree = await func.runtime.render.build_tree({
4685
+ SESSION_ID,
4686
+ nodeP: root_node,
4687
+ paramsP,
4688
+ });
4689
+
4690
+ return {
4691
+ tree,
4692
+ paramsP,
4693
+ viewDoc,
4694
+ };
4695
+ };
4696
+ func.runtime.render.build_ssr_payload = function (render_program, options = {}) {
4697
+ const runtime_profile = func.runtime.render.get_server_render_mode(options);
4698
+ return {
4699
+ contract: 'xuda.ssr.v1',
4700
+ prog_id: options.prog_id,
4701
+ screenId: render_program.paramsP.screenId,
4702
+ containerId: render_program.paramsP.containerIdP,
4703
+ app_computing_mode: runtime_profile.app_computing_mode,
4704
+ app_render_mode: runtime_profile.app_render_mode,
4705
+ app_client_activation: runtime_profile.app_client_activation,
4706
+ tree_contract: func.runtime.render.TREE_CONTRACT_VERSION,
4707
+ };
4708
+ };
4709
+ func.runtime.render.build_ssr_screen_html = function (html, render_program, options = {}) {
4710
+ const payload = func.runtime.render.build_ssr_payload(render_program, options);
4711
+ const screenId = func.runtime.render.escape_html_attribute(payload.screenId || '');
4712
+ const containerId = func.runtime.render.escape_html_attribute(payload.containerId || '');
4713
+ const activation = func.runtime.render.escape_html_attribute(payload.app_client_activation || 'takeover');
4714
+
4715
+ 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>`;
4716
+ };
4717
+ func.runtime.render.render_prog_to_string = async function (options = {}) {
4718
+ const render_program = await func.runtime.render.build_prog_tree(options);
4719
+ const html = await func.runtime.render.render_to_string({
4720
+ ...options,
4721
+ SESSION_ID: options.SESSION_ID,
4722
+ treeP: render_program.tree,
4723
+ paramsP: render_program.paramsP,
4724
+ });
4725
+ const ssr_payload = func.runtime.render.build_ssr_payload(render_program, options);
4726
+ const screen_html = func.runtime.render.build_ssr_screen_html(html, render_program, options);
3801
4727
 
3802
- func.common.fastHash = function (inputString) {
3803
- let hash = 0x811c9dc5; // FNV offset basis
4728
+ return {
4729
+ prog_id: options.prog_id,
4730
+ dsSessionP: render_program.paramsP.dsSessionP,
4731
+ screenId: render_program.paramsP.screenId,
4732
+ html,
4733
+ screen_html,
4734
+ tree_json: func.runtime.render.serialize_tree(render_program.tree),
4735
+ paramsP: render_program.paramsP,
4736
+ ssr_payload,
4737
+ };
4738
+ };
4739
+ func.runtime = func.runtime || {};
4740
+ func.runtime.platform = func.runtime.platform || {};
3804
4741
 
3805
- for (let i = 0; i < inputString.length; i++) {
3806
- hash ^= inputString.charCodeAt(i);
3807
- // FNV prime multiplication with 32-bit overflow
3808
- hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
4742
+ func.runtime.platform.dispatch_document_event = function (name, data) {
4743
+ if (typeof document === 'undefined' || typeof CustomEvent === 'undefined') {
4744
+ return false;
3809
4745
  }
3810
-
3811
- // Convert to base36 and pad to 10 characters
3812
- return ((hash >>> 0).toString(36) + '0000000000').slice(0, 10);
4746
+ document.dispatchEvent(new CustomEvent(name, {
4747
+ detail: Array.isArray(data) ? data : [data],
4748
+ }));
4749
+ return true;
3813
4750
  };
3814
-
3815
- glb.new_xu_render = false;
3816
4751
  glb.DEBUG_INFO_OBJ = {};
3817
4752
  // var CONNECTION_ATTEMPTS = 0;
3818
4753
  glb.APP_INFO = {};
@@ -9190,6 +10125,15 @@ func.runtime.ui.ensure_embed_container = function (SESSION_ID) {
9190
10125
  const $root_element = func.runtime.ui.get_root_element(SESSION_ID);
9191
10126
  let $embed_container = func.runtime.ui.find_by_selector($root_element, `#embed_${SESSION_ID}`, true);
9192
10127
 
10128
+ if (!$embed_container.length) {
10129
+ const $ssr_embed_container = func.runtime.ui.find_by_selector($root_element, `[data-xuda-ssr-embed="true"]`, true);
10130
+ const ssr_embed_node = func.runtime.ui.get_first_node($ssr_embed_container);
10131
+ if (ssr_embed_node) {
10132
+ ssr_embed_node.id = 'embed_' + SESSION_ID;
10133
+ $embed_container = func.runtime.ui._wrap_matches([ssr_embed_node]);
10134
+ }
10135
+ }
10136
+
9193
10137
  if (!$embed_container.length) {
9194
10138
  const embed_node = document.createElement('div');
9195
10139
  embed_node.id = 'embed_' + SESSION_ID;
@@ -9225,12 +10169,44 @@ func.runtime.ui.get_root_tag_name = function () {
9225
10169
  }
9226
10170
  return root_tag_name;
9227
10171
  };
10172
+ func.runtime.ui.find_ssr_screen_host = function ($container, screenId, containerId) {
10173
+ const container_node = func.runtime.ui.get_first_node($container);
10174
+ if (!container_node || !screenId) {
10175
+ return null;
10176
+ }
10177
+
10178
+ const dialog_node =
10179
+ container_node.querySelector?.(`#${CSS?.escape ? CSS.escape(screenId) : screenId}`) ||
10180
+ container_node.querySelector?.(`[data-xuda-ssr-screen-id="${screenId}"]`);
10181
+ if (!dialog_node) {
10182
+ return null;
10183
+ }
10184
+
10185
+ const root_frame_node =
10186
+ (containerId ? dialog_node.querySelector?.(`#${CSS?.escape ? CSS.escape(containerId) : containerId}`) : null) ||
10187
+ dialog_node.querySelector?.('[data-xuda-ssr-root-frame="true"]');
10188
+ if (!root_frame_node) {
10189
+ return null;
10190
+ }
10191
+
10192
+ return {
10193
+ $dialogDiv: func.runtime.ui._wrap_matches([dialog_node]),
10194
+ $rootFrame: func.runtime.ui._wrap_matches([root_frame_node]),
10195
+ reused_ssr_host: true,
10196
+ };
10197
+ };
9228
10198
  func.runtime.ui.create_screen_host = function (SESSION_ID, screen_type, params, $callingContainerP, screenId) {
9229
10199
  var $dialogDiv;
9230
10200
  var $rootFrame;
10201
+ let reused_ssr_host = false;
9231
10202
 
9232
10203
  switch (screen_type) {
9233
10204
  case 'embed': {
10205
+ const ssr_host = func.runtime.ui.find_ssr_screen_host($callingContainerP, screenId, params?.containerIdP);
10206
+ if (ssr_host) {
10207
+ return ssr_host;
10208
+ }
10209
+
9234
10210
  const dialogNode = document.createElement('div');
9235
10211
  dialogNode.id = screenId;
9236
10212
  dialogNode.setAttribute('ui_engine', UI_FRAMEWORK_INSTALLED);
@@ -9280,6 +10256,7 @@ func.runtime.ui.create_screen_host = function (SESSION_ID, screen_type, params,
9280
10256
  return {
9281
10257
  $dialogDiv,
9282
10258
  $rootFrame,
10259
+ reused_ssr_host,
9283
10260
  };
9284
10261
  };
9285
10262
  func.runtime.ui.find_xu_ui_in_root = function (SESSION_ID, xu_ui_id) {
@@ -9313,7 +10290,7 @@ func.runtime.ui.find_element_data_in_root = function (SESSION_ID, dataKey, prope
9313
10290
  }
9314
10291
  return func.runtime.ui.find_in_root(SESSION_ID, '[xu-panel-wrapper-id]');
9315
10292
  }
9316
- return func.UI.utils.find_in_element_data(dataKey, func.runtime.ui.as_jquery(func.runtime.ui.get_root_element(SESSION_ID)), property, value);
10293
+ return func.UI.utils.find_in_element_data(dataKey, func.runtime.ui.get_root_element(SESSION_ID), property, value);
9317
10294
  };
9318
10295
  func.runtime.ui.find_element_data_in_parent = function ($container, dataKey, property, value) {
9319
10296
  const container_node = func.runtime.ui.get_first_node($container);
@@ -9327,7 +10304,7 @@ func.runtime.ui.find_element_data_in_parent = function ($container, dataKey, pro
9327
10304
  }
9328
10305
  return func.runtime.ui.find_by_selector(parent_node, `[xu-ui-id="${value}"]`);
9329
10306
  }
9330
- return func.UI.utils.find_in_element_data(dataKey, func.runtime.ui.as_jquery(parent_node), property, value);
10307
+ return func.UI.utils.find_in_element_data(dataKey, func.runtime.ui._wrap_matches([parent_node]), property, value);
9331
10308
  };
9332
10309
  func.runtime.ui.sync_child_parent_container = function ($div) {
9333
10310
  const div_node = func.runtime.ui.get_first_node($div);
@@ -9434,40 +10411,14 @@ func.runtime.ui.clear_screen_blockers = function () {
9434
10411
  }
9435
10412
  };
9436
10413
 
9437
- // @deprecated — no-op shim now that jQuery is removed from the bundle.
9438
10414
  func.runtime.ui.as_jquery = function (target) {
9439
- if (target?.jquery) {
9440
- return target;
9441
- }
9442
10415
  const node = func.runtime.ui.get_first_node(target);
9443
- // Return a minimal jQuery-like wrapper so legacy callers don't crash.
9444
- const _node_data = {};
9445
- const _arr = node ? [node] : [];
9446
- return {
9447
- 0: node,
9448
- length: _arr.length,
9449
- toArray: function () { return _arr.slice(); },
9450
- data: function (key, value) {
9451
- if (typeof key === 'undefined') return _node_data;
9452
- if (typeof value !== 'undefined') { _node_data[key] = value; return this; }
9453
- return _node_data[key];
9454
- },
9455
- attr: function (key, value) {
9456
- if (!node) return undefined;
9457
- if (typeof value !== 'undefined') { node.setAttribute(key, value); return this; }
9458
- return node?.getAttribute?.(key);
9459
- },
9460
- removeData: function () { for (const k in _node_data) delete _node_data[k]; return this; },
9461
- children: function () { return { toArray: function () { return node ? Array.from(node.children) : []; } }; },
9462
- };
10416
+ return func.runtime.ui._wrap_matches(node ? [node] : []);
9463
10417
  };
9464
10418
  func.runtime.ui.get_first_node = function (target) {
9465
10419
  if (!target) {
9466
10420
  return null;
9467
10421
  }
9468
- if (target?.jquery) {
9469
- return target[0] || null;
9470
- }
9471
10422
  if (target?.nodeType) {
9472
10423
  return target;
9473
10424
  }
@@ -9485,9 +10436,10 @@ func.runtime.ui.get_data = function (target) {
9485
10436
  if (meta) return meta;
9486
10437
  }
9487
10438
  }
9488
- // fallback: store per-element data on the node itself
9489
10439
  if (target_node) {
9490
- if (!target_node.__xuData) target_node.__xuData = {};
10440
+ if (!target_node.__xuData) {
10441
+ target_node.__xuData = {};
10442
+ }
9491
10443
  return target_node.__xuData;
9492
10444
  }
9493
10445
  return {};
@@ -9869,6 +10821,11 @@ func.runtime.ui.build_container_xu_data = function (options) {
9869
10821
  func.runtime.ui.apply_container_meta = function ($div, options) {
9870
10822
  const div_node = func.runtime.ui.get_first_node($div);
9871
10823
  func.runtime.ui.set_attr(div_node, 'xu-ui-id', options.ui_id);
10824
+ func.runtime.ui.set_attr(div_node, 'data-xuda-kind', options.treeP?.kind || options.nodeP?.tagName || 'element');
10825
+ func.runtime.ui.set_attr(div_node, 'data-xuda-node-id', options.nodeP?.id || options.nodeP?.id_org || '');
10826
+ if (options.treeP?.meta?.tree_id !== null && typeof options.treeP?.meta?.tree_id !== 'undefined') {
10827
+ func.runtime.ui.set_attr(div_node, 'data-xuda-tree-id', options.treeP.meta.tree_id);
10828
+ }
9872
10829
  const xuData = func.runtime.ui.build_container_xu_data(options);
9873
10830
  if (options.parent_infoP?.iterate_info) {
9874
10831
  xuData.iterate_info = options.parent_infoP.iterate_info;
@@ -9963,6 +10920,38 @@ func.runtime.ui.create_container_element = function (div_typeP, attr_str, prop,
9963
10920
  }
9964
10921
  return func.runtime.ui.create_element(div, attr_str);
9965
10922
  };
10923
+ func.runtime.ui.find_hydration_candidate = function (options) {
10924
+ if (!func.runtime.render.is_hydration_mode(SESSION_OBJ?.[options.SESSION_ID])) {
10925
+ return null;
10926
+ }
10927
+ if (!func.runtime.render.should_use_ssr_payload(options.SESSION_ID, options.paramsP)) {
10928
+ return null;
10929
+ }
10930
+ if (options.is_placeholder || !options.treeP?.meta?.tree_id) {
10931
+ return null;
10932
+ }
10933
+
10934
+ const append_node = func.runtime.ui.get_first_node(options.$appendTo || options.$container);
10935
+ if (!append_node) {
10936
+ return null;
10937
+ }
10938
+
10939
+ const children = func.runtime.ui.get_children(append_node);
10940
+ for (let index = 0; index < children.length; index++) {
10941
+ const child = children[index];
10942
+ if (child?.__xuda_hydration_claimed) {
10943
+ continue;
10944
+ }
10945
+ if (func.runtime.ui.get_attr(child, 'data-xuda-tree-id') !== `${options.treeP.meta.tree_id}`) {
10946
+ continue;
10947
+ }
10948
+ child.__xuda_hydration_claimed = true;
10949
+ func.runtime.ui.set_attr(child, 'data-xuda-client-activation', 'hydrate');
10950
+ return child;
10951
+ }
10952
+
10953
+ return null;
10954
+ };
9966
10955
  func.runtime.ui.build_xu_ui_id_seed = function (nodeP, dsSessionP, key_path, currentRecordId) {
9967
10956
  const nodeId = nodeP.xu_tree_id || nodeP.id;
9968
10957
  const elem_key = `${nodeId}-${key_path}-${currentRecordId}`;
@@ -10015,7 +11004,11 @@ func.runtime.ui.create_container = async function (options) {
10015
11004
  try {
10016
11005
  const key_path = func.runtime.ui.build_container_key_path(container_xu_data, options.keyP, options.parent_infoP, options.nodeP, options.parent_nodeP);
10017
11006
  const elem_key = `${options.nodeP.xu_tree_id || options.nodeP.id}-${key_path}-${currentRecordId}`;
10018
- const $div = func.runtime.ui.create_container_element(options.div_typeP, options.attr_str, options.prop, options.nodeP, $appendTo);
11007
+ const hydration_candidate = func.runtime.ui.find_hydration_candidate({
11008
+ ...options,
11009
+ $appendTo,
11010
+ });
11011
+ const $div = hydration_candidate || func.runtime.ui.create_container_element(options.div_typeP, options.attr_str, options.prop, options.nodeP, $appendTo);
10019
11012
  const new_ui_id = await func.runtime.ui.generate_xu_ui_id(options.SESSION_ID, options.nodeP, options.$container, options.paramsP, options.keyP, {
10020
11013
  container_xu_data,
10021
11014
  currentRecordId,
@@ -10041,9 +11034,10 @@ func.runtime.ui.create_container = async function (options) {
10041
11034
  parent_infoP: options.parent_infoP,
10042
11035
  is_placeholder: options.is_placeholder,
10043
11036
  classP: options.classP,
11037
+ treeP: options.treeP,
10044
11038
  });
10045
11039
 
10046
- if (options.div_typeP !== 'svg') {
11040
+ if (!hydration_candidate && options.div_typeP !== 'svg') {
10047
11041
  func.runtime.ui.append_to($div, $appendTo);
10048
11042
  }
10049
11043
  return $div;
@@ -12724,9 +13718,10 @@ func.runtime.ui.init_screen = async function (options) {
12724
13718
 
12725
13719
  const _session = SESSION_OBJ[SESSION_ID];
12726
13720
  const screenInfo = structuredClone(screen_ret);
13721
+ const ssr_payload = func.runtime.render.should_use_ssr_payload(SESSION_ID, { prog_id }) ? func.runtime.render.get_ssr_payload(_session) : null;
12727
13722
 
12728
13723
  const screen_type = source_functionP?.split('_')?.[1];
12729
- const screenId = (glb.screen_num++).toString();
13724
+ const screenId = ssr_payload?.screenId || (glb.screen_num++).toString();
12730
13725
 
12731
13726
  if (SCREEN_BLOCKER_OBJ[prog_id + (sourceScreenP ? '_' + sourceScreenP : '')]) {
12732
13727
  const wait_for_SCREEN_BLOCKER_release = function () {
@@ -12768,6 +13763,8 @@ func.runtime.ui.init_screen = async function (options) {
12768
13763
  call_screen_propertiesP,
12769
13764
  parentDataSourceNoP: _session.DS_GLB?.[callingDataSource_objP?.dsSession]?.dsSession || callingDataSource_objP?.parentDataSourceNo || 0,
12770
13765
  parameters_raw_obj,
13766
+ containerIdP: ssr_payload?.containerId || null,
13767
+ ssr_payload,
12771
13768
  };
12772
13769
 
12773
13770
  const screen_host = func.runtime.ui.create_screen_host(SESSION_ID, screen_type, params, $callingContainerP, screenId);
@@ -12800,6 +13797,10 @@ func.runtime.ui.init_screen = async function (options) {
12800
13797
  func.runtime.ui.set_style($rootFrame, 'display', 'contents');
12801
13798
  }
12802
13799
 
13800
+ if (screen_host.reused_ssr_host && func.runtime.render.is_takeover_mode(_session)) {
13801
+ func.runtime.ui.empty($rootFrame);
13802
+ }
13803
+
12803
13804
  if (!is_panelP) func.UI.utils.indicator.screen.busy();
12804
13805
 
12805
13806
  const ret = await func.datasource.create(
@@ -12841,7 +13842,24 @@ func.runtime.ui.init_screen = async function (options) {
12841
13842
  }
12842
13843
  let node = structuredClone(viewDoc.progUi);
12843
13844
  if (!node.length) return console.warn('ui node empty');
12844
- 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);
13845
+ const root_tree = await func.runtime.render.build_tree({
13846
+ SESSION_ID,
13847
+ nodeP: node[0],
13848
+ paramsP: params,
13849
+ });
13850
+ const ret_render_$container = await func.runtime.render.render_tree(root_tree, {
13851
+ SESSION_ID,
13852
+ $container: $rootFrame,
13853
+ parent_infoP: null,
13854
+ paramsP: params,
13855
+ jobNoP,
13856
+ is_skeleton: null,
13857
+ keyP: null,
13858
+ refreshed_ds: null,
13859
+ parent_nodeP: null,
13860
+ check_existP: null,
13861
+ $root_container: $rootFrame,
13862
+ });
12845
13863
 
12846
13864
  if (!is_panelP) func.UI.utils.indicator.screen.normal();
12847
13865
 
@@ -12925,7 +13943,7 @@ func.runtime.ui.ensure_nav = function (SESSION_ID, $container) {
12925
13943
  }
12926
13944
  var nav_el = document.createElement('xu-nav');
12927
13945
  func.runtime.ui.append($container, nav_el);
12928
- var $nav = func.runtime.ui.as_jquery(nav_el);
13946
+ var $nav = func.runtime.ui._wrap_matches([nav_el]);
12929
13947
  func.UI.component.init_xu_nav($container, $nav);
12930
13948
  return $nav;
12931
13949
  };
@@ -13266,6 +14284,7 @@ func.runtime.ui.render_single_view_node = async function (options) {
13266
14284
  parent_infoP: options.parent_infoP,
13267
14285
  jobNoP: options.jobNoP,
13268
14286
  keyP: options.keyP,
14287
+ treeP: options.treeP,
13269
14288
  parent_nodeP: options.parent_nodeP,
13270
14289
  prop: options.prop,
13271
14290
  div_typeP: 'div',
@@ -13394,6 +14413,7 @@ func.runtime.ui.render_panel_node = async function (options) {
13394
14413
  parent_infoP: options.parent_infoP,
13395
14414
  jobNoP: options.jobNoP,
13396
14415
  keyP: options.keyP,
14416
+ treeP: options.treeP,
13397
14417
  parent_nodeP: options.parent_nodeP,
13398
14418
  prop: options.prop,
13399
14419
  $appendToP: $wrapper,
@@ -13723,6 +14743,14 @@ func.runtime.ui.screen_loading_done = async function (options) {
13723
14743
  });
13724
14744
 
13725
14745
  const _session = SESSION_OBJ[options.SESSION_ID];
14746
+ if (func.runtime.render.should_use_ssr_payload(options.SESSION_ID, options.paramsP)) {
14747
+ const root_node = func.runtime.ui.get_root_node(options.SESSION_ID);
14748
+ if (root_node) {
14749
+ func.runtime.ui.set_attr(root_node, 'data-xuda-client-activation', _session.opt.app_client_activation || 'none');
14750
+ func.runtime.ui.set_attr(root_node, 'data-xuda-ssr-status', _session.opt.app_client_activation === 'hydrate' ? 'hydrated' : 'taken-over');
14751
+ }
14752
+ func.runtime.render.mark_ssr_payload_consumed(options.SESSION_ID);
14753
+ }
13726
14754
  func.events.delete_job(options.SESSION_ID, options.jobNoP);
13727
14755
  func.UI.utils.screen_blocker(false, options.paramsP.prog_id + (options.paramsP.sourceScreenP ? '_' + options.paramsP.sourceScreenP : ''));
13728
14756
  if (_session.prog_id === options.paramsP.prog_id) {
@@ -13755,7 +14783,7 @@ func.runtime.ui._to_node_array = func.runtime.ui._to_node_array || function (inp
13755
14783
  if (!input) {
13756
14784
  return [];
13757
14785
  }
13758
- if (input.jquery) {
14786
+ if (typeof input?.toArray === 'function' && typeof input?.length === 'number' && !input?.nodeType) {
13759
14787
  return input.toArray();
13760
14788
  }
13761
14789
  if (Array.isArray(input)) {
@@ -15565,6 +16593,9 @@ func.runtime.render.get_screen_context = function (SESSION_ID, $container, param
15565
16593
  };
15566
16594
  func.runtime.render.get_node_attributes = function (nodeP) {
15567
16595
  try {
16596
+ if (func.runtime.render.is_tree_node?.(nodeP)) {
16597
+ return nodeP.attributes;
16598
+ }
15568
16599
  return nodeP?.attributes;
15569
16600
  } catch (error) {
15570
16601
  return undefined;
@@ -15682,6 +16713,7 @@ func.runtime.render.prepare_draw_context = async function (options) {
15682
16713
  parent_infoP: options.parent_infoP,
15683
16714
  jobNoP: options.jobNoP,
15684
16715
  keyP: options.keyP,
16716
+ treeP: options.treeP,
15685
16717
  parent_nodeP: options.parent_nodeP,
15686
16718
  prop: options.prop,
15687
16719
  div_typeP: options.element,
@@ -15704,6 +16736,7 @@ func.runtime.render.prepare_draw_context = async function (options) {
15704
16736
  parent_infoP: options.parent_infoP,
15705
16737
  jobNoP: options.jobNoP,
15706
16738
  keyP: options.keyP,
16739
+ treeP: options.treeP,
15707
16740
  parent_nodeP: options.parent_nodeP,
15708
16741
  prop: options.prop,
15709
16742
  div_typeP: options.element,
@@ -16560,18 +17593,28 @@ func.runtime.widgets.render_node = async function (options) {
16560
17593
  parent_infoP: options.parent_infoP,
16561
17594
  jobNoP: options.jobNoP,
16562
17595
  keyP: options.keyP,
17596
+ treeP: options.treeP,
16563
17597
  parent_nodeP: options.parent_nodeP,
16564
17598
  prop: options.prop,
16565
17599
  classP: 'widget_wrapper',
16566
17600
  });
16567
17601
 
17602
+ if (func.runtime.render.is_hydration_mode(SESSION_OBJ?.[options.SESSION_ID]) && func.runtime.render.should_use_ssr_payload(options.SESSION_ID, options.paramsP)) {
17603
+ func.runtime.ui.empty($div);
17604
+ }
17605
+
16568
17606
  const widget_context = func.runtime.widgets.create_context(options.SESSION_ID, options.paramsP, options.prop);
16569
17607
  const { plugin_name, method, propsP, plugin: _plugin } = widget_context;
16570
17608
  const report_error = function (descP, warn) {
16571
17609
  return func.runtime.widgets.report_error(widget_context, descP, warn);
16572
17610
  };
16573
17611
 
16574
- const methods = await func.runtime.widgets.get_methods(widget_context);
17612
+ const definition = await func.runtime.widgets.get_definition(widget_context);
17613
+ if (!func.runtime.widgets.supports_current_environment(definition)) {
17614
+ return report_error(`plugin ${plugin_name} is not available in the current environment`, true);
17615
+ }
17616
+
17617
+ const methods = definition?.methods || {};
16575
17618
  if (methods && !methods[method]) {
16576
17619
  return report_error('method not found');
16577
17620
  }
@@ -16632,7 +17675,13 @@ func.runtime.widgets.render_node = async function (options) {
16632
17675
  job_id: options.jobNoP,
16633
17676
  });
16634
17677
 
16635
- const params = func.runtime.widgets.build_params(widget_context, $div, plugin_setup_ret.data, api_utils);
17678
+ const params = func.runtime.widgets.build_params(
17679
+ widget_context,
17680
+ func.runtime.ui.get_first_node($div),
17681
+ func.runtime.ui.get_data($div),
17682
+ plugin_setup_ret.data,
17683
+ api_utils,
17684
+ );
16636
17685
  const fx = await func.runtime.widgets.get_resource(widget_context, 'runtime.mjs');
16637
17686
 
16638
17687
  await func.runtime.widgets.load_runtime_css(widget_context);
@@ -16656,9 +17705,306 @@ func.runtime.widgets = func.runtime.widgets || {};
16656
17705
 
16657
17706
  // Browser-only special node renderers live here so the core render tree can stay focused.
16658
17707
 
17708
+ const normalize_runtime_tag_name = function (tag_name) {
17709
+ return `${tag_name || ''}`.trim().toLowerCase();
17710
+ };
17711
+
17712
+ const get_runtime_node_attributes = function (nodeP) {
17713
+ if (!nodeP?.attributes || typeof nodeP.attributes !== 'object') {
17714
+ return {};
17715
+ }
17716
+
17717
+ return nodeP.attributes;
17718
+ };
17719
+
17720
+ const get_runtime_node_content = function (nodeP) {
17721
+ if (typeof nodeP?.content === 'string') {
17722
+ return nodeP.content;
17723
+ }
17724
+
17725
+ if (typeof nodeP?.text === 'string') {
17726
+ return nodeP.text;
17727
+ }
17728
+
17729
+ if (!Array.isArray(nodeP?.children)) {
17730
+ return '';
17731
+ }
17732
+
17733
+ return nodeP.children
17734
+ .map(function (child) {
17735
+ if (typeof child === 'string') {
17736
+ return child;
17737
+ }
17738
+ if (typeof child?.content === 'string') {
17739
+ return child.content;
17740
+ }
17741
+ if (typeof child?.text === 'string') {
17742
+ return child.text;
17743
+ }
17744
+ return '';
17745
+ })
17746
+ .join('');
17747
+ };
17748
+
17749
+ const get_runtime_asset_key = function (options, tag_name) {
17750
+ const source_node = options.treeP || options.nodeP || {};
17751
+ const parts = [
17752
+ 'xuda-html-asset',
17753
+ options.paramsP?.prog_id || '',
17754
+ tag_name,
17755
+ source_node.id || source_node.id_org || '',
17756
+ typeof options.keyP === 'undefined' || options.keyP === null ? '' : `${options.keyP}`,
17757
+ ].filter(function (part) {
17758
+ return `${part || ''}`.trim() !== '';
17759
+ });
17760
+
17761
+ return parts.join(':');
17762
+ };
17763
+
17764
+ const get_runtime_asset_signature = function (attributes, content) {
17765
+ const normalized_attributes = {};
17766
+ const attr_keys = Object.keys(attributes || {}).sort();
17767
+
17768
+ for (let index = 0; index < attr_keys.length; index++) {
17769
+ const key = attr_keys[index];
17770
+ normalized_attributes[key] = attributes[key];
17771
+ }
17772
+
17773
+ return JSON.stringify({
17774
+ attributes: normalized_attributes,
17775
+ content: `${content || ''}`,
17776
+ });
17777
+ };
17778
+
17779
+ const escape_runtime_asset_selector_value = function (value) {
17780
+ const win = func.runtime.platform.get_window?.();
17781
+ if (win?.CSS?.escape) {
17782
+ return win.CSS.escape(value);
17783
+ }
17784
+
17785
+ return `${value || ''}`.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
17786
+ };
17787
+
17788
+ const find_runtime_head_asset = function (head, tag_name, asset_key) {
17789
+ if (!head?.querySelector || !asset_key) {
17790
+ return null;
17791
+ }
17792
+
17793
+ return head.querySelector(`${tag_name}[data-xuda-asset-key="${escape_runtime_asset_selector_value(asset_key)}"]`);
17794
+ };
17795
+
17796
+ const find_runtime_head_asset_by_attr = function (head, tag_name, attr_name, attr_value) {
17797
+ if (!head?.querySelectorAll || !attr_name || !attr_value) {
17798
+ return null;
17799
+ }
17800
+
17801
+ const candidates = Array.from(head.querySelectorAll(tag_name));
17802
+ return (
17803
+ candidates.find(function (candidate) {
17804
+ return candidate.getAttribute(attr_name) === attr_value || candidate[attr_name] === attr_value;
17805
+ }) || null
17806
+ );
17807
+ };
17808
+
17809
+ const wait_for_runtime_asset_load = function (node) {
17810
+ if (!node?.addEventListener) {
17811
+ return Promise.resolve(node);
17812
+ }
17813
+
17814
+ if (node.getAttribute?.('data-xuda-loaded') === 'true') {
17815
+ return Promise.resolve(node);
17816
+ }
17817
+
17818
+ if (!node.getAttribute?.('data-xuda-asset-key')) {
17819
+ return Promise.resolve(node);
17820
+ }
17821
+
17822
+ const tag_name = normalize_runtime_tag_name(node.tagName);
17823
+ if (tag_name !== 'script' && !(tag_name === 'link' && normalize_runtime_tag_name(node.getAttribute?.('rel')) === 'stylesheet')) {
17824
+ return Promise.resolve(node);
17825
+ }
17826
+
17827
+ return new Promise(function (resolve) {
17828
+ const done = function () {
17829
+ node.setAttribute?.('data-xuda-loaded', 'true');
17830
+ resolve(node);
17831
+ };
17832
+
17833
+ node.addEventListener('load', done, { once: true });
17834
+ node.addEventListener('error', done, { once: true });
17835
+ });
17836
+ };
17837
+
17838
+ const remove_runtime_head_asset = function (node) {
17839
+ if (node?.parentNode?.removeChild) {
17840
+ node.parentNode.removeChild(node);
17841
+ }
17842
+ };
17843
+
17844
+ const apply_runtime_asset_metadata = function (node, asset_key, signature) {
17845
+ if (!node?.setAttribute) {
17846
+ return node;
17847
+ }
17848
+
17849
+ if (asset_key) {
17850
+ node.setAttribute('data-xuda-asset-key', asset_key);
17851
+ }
17852
+ node.setAttribute('data-xuda-asset-signature', signature);
17853
+ return node;
17854
+ };
17855
+
17856
+ const create_runtime_head_element = function (doc, tag_name, attributes, asset_key, signature) {
17857
+ const node = doc.createElement(tag_name);
17858
+ apply_runtime_asset_metadata(node, asset_key, signature);
17859
+ func.runtime.platform.apply_element_attributes(node, attributes);
17860
+ return node;
17861
+ };
17862
+
17863
+ const upsert_runtime_head_element = async function (options) {
17864
+ const doc = func.runtime.platform.get_document?.();
17865
+ const head = doc?.head;
17866
+ const tag_name = normalize_runtime_tag_name(options.tag_name);
17867
+
17868
+ if (!doc?.createElement || !head?.appendChild || !tag_name) {
17869
+ return null;
17870
+ }
17871
+
17872
+ const asset_key = options.asset_key || '';
17873
+ const signature = options.signature || '';
17874
+ const attributes = options.attributes || {};
17875
+ const content = typeof options.content === 'string' ? options.content : '';
17876
+
17877
+ const existing_by_key = find_runtime_head_asset(head, tag_name, asset_key);
17878
+ if (existing_by_key && existing_by_key.getAttribute('data-xuda-asset-signature') === signature) {
17879
+ return options.await_load ? await wait_for_runtime_asset_load(existing_by_key) : existing_by_key;
17880
+ }
17881
+
17882
+ if (existing_by_key) {
17883
+ remove_runtime_head_asset(existing_by_key);
17884
+ }
17885
+
17886
+ if (options.find_existing_attr?.name && options.find_existing_attr?.value) {
17887
+ const existing_by_attr = find_runtime_head_asset_by_attr(head, tag_name, options.find_existing_attr.name, options.find_existing_attr.value);
17888
+ if (existing_by_attr) {
17889
+ apply_runtime_asset_metadata(existing_by_attr, asset_key, signature);
17890
+ existing_by_attr.setAttribute?.('data-xuda-loaded', 'true');
17891
+ return existing_by_attr;
17892
+ }
17893
+ }
17894
+
17895
+ const node = create_runtime_head_element(doc, tag_name, attributes, asset_key, signature);
17896
+
17897
+ if (tag_name === 'script' && attributes.src && !Object.prototype.hasOwnProperty.call(attributes, 'async')) {
17898
+ const script_type = normalize_runtime_tag_name(attributes.type);
17899
+ if (script_type !== 'module') {
17900
+ node.async = false;
17901
+ }
17902
+ }
17903
+
17904
+ if (tag_name === 'style' || (tag_name === 'script' && !attributes.src)) {
17905
+ node.textContent = content;
17906
+ }
17907
+
17908
+ head.appendChild(node);
17909
+
17910
+ if (tag_name !== 'script' && tag_name !== 'link') {
17911
+ node.setAttribute('data-xuda-loaded', 'true');
17912
+ }
17913
+
17914
+ return options.await_load ? await wait_for_runtime_asset_load(node) : node;
17915
+ };
17916
+
17917
+ const render_runtime_html_asset = async function (options) {
17918
+ if (options.is_skeleton) {
17919
+ return options.$container;
17920
+ }
17921
+
17922
+ const nodeP = options.treeP || options.nodeP || {};
17923
+ const tag_name = normalize_runtime_tag_name(nodeP.tagName);
17924
+ const attributes = { ...get_runtime_node_attributes(nodeP) };
17925
+ const content = get_runtime_node_content(nodeP);
17926
+ const asset_key = get_runtime_asset_key(options, tag_name);
17927
+ const signature = get_runtime_asset_signature(attributes, content);
17928
+
17929
+ switch (tag_name) {
17930
+ case 'title':
17931
+ func.runtime.platform.set_title(content);
17932
+ return options.$container;
17933
+ case 'style':
17934
+ await upsert_runtime_head_element({
17935
+ tag_name,
17936
+ attributes,
17937
+ content,
17938
+ asset_key,
17939
+ signature,
17940
+ });
17941
+ return options.$container;
17942
+ case 'meta':
17943
+ if (!Object.keys(attributes).length) {
17944
+ return options.$container;
17945
+ }
17946
+ await upsert_runtime_head_element({
17947
+ tag_name,
17948
+ attributes,
17949
+ asset_key,
17950
+ signature,
17951
+ });
17952
+ return options.$container;
17953
+ case 'link': {
17954
+ const href = `${attributes.href || ''}`.trim();
17955
+ if (!href) {
17956
+ return options.$container;
17957
+ }
17958
+ await upsert_runtime_head_element({
17959
+ tag_name,
17960
+ attributes,
17961
+ asset_key,
17962
+ signature,
17963
+ find_existing_attr: {
17964
+ name: 'href',
17965
+ value: href,
17966
+ },
17967
+ await_load: normalize_runtime_tag_name(attributes.rel) === 'stylesheet',
17968
+ });
17969
+ return options.$container;
17970
+ }
17971
+ case 'script': {
17972
+ const src = `${attributes.src || ''}`.trim();
17973
+ if (!src && !content.trim()) {
17974
+ return options.$container;
17975
+ }
17976
+ await upsert_runtime_head_element({
17977
+ tag_name,
17978
+ attributes,
17979
+ content,
17980
+ asset_key,
17981
+ signature,
17982
+ find_existing_attr: src
17983
+ ? {
17984
+ name: 'src',
17985
+ value: src,
17986
+ }
17987
+ : null,
17988
+ await_load: !!src,
17989
+ });
17990
+ return options.$container;
17991
+ }
17992
+ default:
17993
+ return null;
17994
+ }
17995
+ };
17996
+
16659
17997
  func.runtime.render.render_special_node = async function (options) {
16660
- if (options.nodeP.content && options.nodeP.attributes) {
16661
- options.nodeP.attributes['xu-content'] = options.nodeP.content;
17998
+ const treeP = options.treeP || null;
17999
+ const nodeP = options.nodeP || func.runtime.render.get_tree_source_node(treeP);
18000
+ const render_tag_name = treeP?.tagName || nodeP?.tagName;
18001
+ const normalized_render_tag_name = normalize_runtime_tag_name(render_tag_name);
18002
+ const is_native_html_asset = ['title', 'style', 'meta', 'link', 'script'].includes(normalized_render_tag_name);
18003
+
18004
+ if (!is_native_html_asset && treeP?.content && nodeP?.attributes) {
18005
+ nodeP.attributes['xu-content'] = treeP.content;
18006
+ } else if (!is_native_html_asset && nodeP?.content && nodeP.attributes) {
18007
+ nodeP.attributes['xu-content'] = nodeP.content;
16662
18008
  }
16663
18009
 
16664
18010
  const renderers = {
@@ -16668,6 +18014,7 @@ func.runtime.render.render_special_node = async function (options) {
16668
18014
  SESSION_ID: options.SESSION_ID,
16669
18015
  $container: options.$container,
16670
18016
  $root_container: options.$root_container,
18017
+ treeP,
16671
18018
  nodeP: options.nodeP,
16672
18019
  parent_infoP: options.parent_infoP,
16673
18020
  paramsP: options.paramsP,
@@ -16684,6 +18031,7 @@ func.runtime.render.render_special_node = async function (options) {
16684
18031
  SESSION_ID: options.SESSION_ID,
16685
18032
  $container: options.$container,
16686
18033
  $root_container: options.$root_container,
18034
+ treeP,
16687
18035
  nodeP: options.nodeP,
16688
18036
  parent_infoP: options.parent_infoP,
16689
18037
  paramsP: options.paramsP,
@@ -16703,6 +18051,7 @@ func.runtime.render.render_special_node = async function (options) {
16703
18051
  SESSION_ID: options.SESSION_ID,
16704
18052
  $container: options.$container,
16705
18053
  $root_container: options.$root_container,
18054
+ treeP,
16706
18055
  nodeP: options.nodeP,
16707
18056
  parent_infoP: options.parent_infoP,
16708
18057
  paramsP: options.paramsP,
@@ -16719,6 +18068,7 @@ func.runtime.render.render_special_node = async function (options) {
16719
18068
  SESSION_ID: options.SESSION_ID,
16720
18069
  $container: options.$container,
16721
18070
  $root_container: options.$root_container,
18071
+ treeP,
16722
18072
  nodeP: options.nodeP,
16723
18073
  parent_infoP: options.parent_infoP,
16724
18074
  paramsP: options.paramsP,
@@ -16730,9 +18080,24 @@ func.runtime.render.render_special_node = async function (options) {
16730
18080
  refreshed_ds: options.refreshed_ds,
16731
18081
  });
16732
18082
  },
18083
+ title: async function () {
18084
+ return await render_runtime_html_asset(options);
18085
+ },
18086
+ style: async function () {
18087
+ return await render_runtime_html_asset(options);
18088
+ },
18089
+ meta: async function () {
18090
+ return await render_runtime_html_asset(options);
18091
+ },
18092
+ link: async function () {
18093
+ return await render_runtime_html_asset(options);
18094
+ },
18095
+ script: async function () {
18096
+ return await render_runtime_html_asset(options);
18097
+ },
16733
18098
  };
16734
18099
 
16735
- const renderer = renderers[options.nodeP.tagName];
18100
+ const renderer = renderers[normalized_render_tag_name];
16736
18101
  if (!renderer) {
16737
18102
  return { handled: false };
16738
18103
  }
@@ -16846,8 +18211,9 @@ func.runtime.widgets = func.runtime.widgets || {};
16846
18211
  // Browser-only render tree entrypoints live here so draw/cache helpers can stay focused.
16847
18212
 
16848
18213
  func.runtime.render.create_tree_runtime = function (options) {
18214
+ const render_node = options.nodeP || func.runtime.render.get_tree_source_node(options.treeP);
16849
18215
  const render_context = func.runtime.render.get_screen_context(options.SESSION_ID, options.$container, options.paramsP, options.is_skeleton);
16850
- const prop = func.runtime.render.get_node_attributes(options.nodeP);
18216
+ const prop = func.runtime.render.get_node_attributes(options.treeP || render_node);
16851
18217
  const is_mobile = render_context.is_mobile ? true : false;
16852
18218
  const hover_handlers = func.runtime.render.create_hover_handlers({
16853
18219
  SESSION_ID: options.SESSION_ID,
@@ -16862,13 +18228,22 @@ func.runtime.render.create_tree_runtime = function (options) {
16862
18228
  return await func.runtime.ui.close_modal_session(options.SESSION_ID, modal_id);
16863
18229
  };
16864
18230
  const iterate_child = async function ($divP, nodeP, parent_infoP, $root_container, before_record_function) {
18231
+ const child_tree = await func.runtime.render.ensure_tree_node({
18232
+ SESSION_ID: options.SESSION_ID,
18233
+ nodeP: nodeP || options.treeP || render_node,
18234
+ parent_infoP,
18235
+ paramsP: options.paramsP,
18236
+ keyP: options.keyP,
18237
+ parent_nodeP: render_node,
18238
+ pathP: options.treeP?.meta?.path || [],
18239
+ });
16865
18240
  return await func.runtime.render.iterate_children({
16866
18241
  $divP,
16867
- nodeP,
18242
+ nodeP: child_tree,
16868
18243
  is_mobile,
16869
18244
  before_record_function,
16870
18245
  render_child: async function (key, child) {
16871
- await options.render_child($divP, child, parent_infoP, key, nodeP, $root_container);
18246
+ await options.render_child($divP, child, parent_infoP, key, render_node, $root_container);
16872
18247
  },
16873
18248
  });
16874
18249
  };
@@ -16898,7 +18273,7 @@ func.runtime.render.draw_node = async function (options) {
16898
18273
  check_existP: options.check_existP,
16899
18274
  $root_container: options.$root_container,
16900
18275
  prop: options.prop,
16901
- element: options.nodeP.tagName,
18276
+ element: options.treeP?.tagName || options.nodeP.tagName,
16902
18277
  hover_handlers: options.hover_handlers,
16903
18278
  include_hover_click: options.include_hover_click,
16904
18279
  iterate_child: options.iterate_child,
@@ -16921,21 +18296,33 @@ func.runtime.render.draw_node = async function (options) {
16921
18296
  nodeP: options.nodeP,
16922
18297
  });
16923
18298
  };
16924
- 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) {
16925
- if (!nodeP) return;
16926
- const perf_end = func.runtime?.perf?.start?.(SESSION_ID, 'render_ui_tree');
16927
- func.runtime?.perf?.increment_map?.(SESSION_ID, 'render_node_counts', nodeP.id || nodeP.id_org || nodeP.tagName || 'unknown');
18299
+ func.runtime.render.render_tree = async function (treeP, renderer_context) {
18300
+ if (!treeP) return;
18301
+ const nodeP = func.runtime.render.get_tree_source_node(treeP);
18302
+ const perf_end = func.runtime?.perf?.start?.(renderer_context.SESSION_ID, 'render_ui_tree');
18303
+ func.runtime?.perf?.increment_map?.(renderer_context.SESSION_ID, 'render_node_counts', treeP.id || nodeP?.id || nodeP?.id_org || treeP.tagName || 'unknown');
16928
18304
  try {
16929
18305
  const tree_runtime = func.runtime.render.create_tree_runtime({
16930
- SESSION_ID,
16931
- $container,
18306
+ SESSION_ID: renderer_context.SESSION_ID,
18307
+ $container: renderer_context.$container,
18308
+ treeP,
16932
18309
  nodeP,
16933
- parent_infoP,
16934
- paramsP,
16935
- jobNoP,
16936
- is_skeleton,
18310
+ parent_infoP: renderer_context.parent_infoP,
18311
+ paramsP: renderer_context.paramsP,
18312
+ jobNoP: renderer_context.jobNoP,
18313
+ is_skeleton: renderer_context.is_skeleton,
18314
+ keyP: renderer_context.keyP,
16937
18315
  render_child: async function ($divP, child, parent_infoP, key, parentNodeP, rootContainerP) {
16938
- await func.runtime.render.render_ui_tree(SESSION_ID, $divP, child, parent_infoP, paramsP, jobNoP, is_skeleton, key, null, parentNodeP, null, rootContainerP);
18316
+ await func.runtime.render.render_tree(child, {
18317
+ ...renderer_context,
18318
+ $container: $divP,
18319
+ parent_infoP,
18320
+ keyP: key,
18321
+ refreshed_ds: null,
18322
+ parent_nodeP: parentNodeP,
18323
+ check_existP: null,
18324
+ $root_container: rootContainerP,
18325
+ });
16939
18326
  },
16940
18327
  });
16941
18328
  const render_context = tree_runtime.render_context;
@@ -16947,23 +18334,24 @@ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nod
16947
18334
  const iterate_child = tree_runtime.iterate_child;
16948
18335
 
16949
18336
  func.runtime.render.log_tree_debug({
16950
- SESSION_ID,
16951
- paramsP,
18337
+ SESSION_ID: renderer_context.SESSION_ID,
18338
+ paramsP: renderer_context.paramsP,
16952
18339
  nodeP,
16953
18340
  _ds,
16954
18341
  });
16955
18342
  const special_render = await func.runtime.render.render_special_node({
16956
- SESSION_ID,
16957
- $container,
16958
- $root_container,
18343
+ SESSION_ID: renderer_context.SESSION_ID,
18344
+ $container: renderer_context.$container,
18345
+ $root_container: renderer_context.$root_container,
18346
+ treeP,
16959
18347
  nodeP,
16960
- parent_infoP,
16961
- paramsP,
16962
- jobNoP,
16963
- is_skeleton,
16964
- keyP,
16965
- refreshed_ds,
16966
- parent_nodeP,
18348
+ parent_infoP: renderer_context.parent_infoP,
18349
+ paramsP: renderer_context.paramsP,
18350
+ jobNoP: renderer_context.jobNoP,
18351
+ is_skeleton: renderer_context.is_skeleton,
18352
+ keyP: renderer_context.keyP,
18353
+ refreshed_ds: renderer_context.refreshed_ds,
18354
+ parent_nodeP: renderer_context.parent_nodeP,
16967
18355
  prop,
16968
18356
  render_context,
16969
18357
  hover_handlers,
@@ -16971,22 +18359,23 @@ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nod
16971
18359
  close_modal,
16972
18360
  });
16973
18361
  if (special_render.handled) {
16974
- func.runtime?.perf?.increment?.(SESSION_ID, 'render_special_node_hits');
18362
+ func.runtime?.perf?.increment?.(renderer_context.SESSION_ID, 'render_special_node_hits');
16975
18363
  return special_render.result;
16976
18364
  }
16977
18365
  return await func.runtime.render.draw_node({
16978
- SESSION_ID,
16979
- $container,
16980
- $root_container,
18366
+ SESSION_ID: renderer_context.SESSION_ID,
18367
+ $container: renderer_context.$container,
18368
+ $root_container: renderer_context.$root_container,
18369
+ treeP,
16981
18370
  nodeP,
16982
- parent_infoP,
16983
- paramsP,
16984
- jobNoP,
16985
- is_skeleton,
16986
- keyP,
16987
- refreshed_ds,
16988
- parent_nodeP,
16989
- check_existP,
18371
+ parent_infoP: renderer_context.parent_infoP,
18372
+ paramsP: renderer_context.paramsP,
18373
+ jobNoP: renderer_context.jobNoP,
18374
+ is_skeleton: renderer_context.is_skeleton,
18375
+ keyP: renderer_context.keyP,
18376
+ refreshed_ds: renderer_context.refreshed_ds,
18377
+ parent_nodeP: renderer_context.parent_nodeP,
18378
+ check_existP: renderer_context.check_existP,
16990
18379
  prop,
16991
18380
  hover_handlers,
16992
18381
  include_hover_click,
@@ -16995,6 +18384,31 @@ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nod
16995
18384
  } finally {
16996
18385
  perf_end?.();
16997
18386
  }
18387
+ };
18388
+ 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) {
18389
+ if (!nodeP) return;
18390
+ const treeP = await func.runtime.render.ensure_tree_node({
18391
+ SESSION_ID,
18392
+ nodeP,
18393
+ parent_infoP,
18394
+ paramsP,
18395
+ keyP,
18396
+ parent_nodeP,
18397
+ });
18398
+
18399
+ return await func.runtime.render.render_tree(treeP, {
18400
+ SESSION_ID,
18401
+ $container,
18402
+ parent_infoP,
18403
+ paramsP,
18404
+ jobNoP,
18405
+ is_skeleton,
18406
+ keyP,
18407
+ refreshed_ds,
18408
+ parent_nodeP,
18409
+ check_existP,
18410
+ $root_container,
18411
+ });
16998
18412
  };
16999
18413
  func.runtime = func.runtime || {};
17000
18414
  func.runtime.ui = func.runtime.ui || {};
@@ -17662,7 +19076,7 @@ func.runtime.render.handle_xu_ref = async function (options) {
17662
19076
  func.runtime.render.handle_xu_bind = async function (options) {
17663
19077
  if (options.is_skeleton) return {};
17664
19078
 
17665
- 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);
19079
+ const $elm = func.runtime?.ui?.get_preferred_live_element ? func.runtime.ui.get_preferred_live_element(options.$elm) : options.$elm;
17666
19080
  const elm_data = func.runtime.ui.get_data($elm);
17667
19081
  const xuData = elm_data?.xuData;
17668
19082
  const bind_expression =
@@ -18518,6 +19932,7 @@ func.runtime.render.handle_xu_panel_program = async function (options) {
18518
19932
  parent_infoP: options.parent_infoP,
18519
19933
  jobNoP: options.jobNoP,
18520
19934
  keyP: options.keyP,
19935
+ treeP: options.treeP,
18521
19936
  parent_nodeP: options.parent_nodeP,
18522
19937
  prop: options.nodeP.attributes,
18523
19938
  $appendToP: $wrapper,
@@ -22531,6 +23946,12 @@ function xuda(...args) {
22531
23946
  if (typeof opt !== 'object') {
22532
23947
  return console.error('Xuda Error - opt argument is not an object');
22533
23948
  }
23949
+
23950
+ if (!opt.ssr_payload && func.runtime.platform.get_window()?.__XUDA_SSR__) {
23951
+ opt.ssr_payload = func.runtime.platform.get_window().__XUDA_SSR__;
23952
+ }
23953
+ func.runtime.render.apply_runtime_bootstrap_defaults(opt);
23954
+
22534
23955
  glb.URL_PARAMS = func.common.getJsonFromUrl(platform.get_url_href());
22535
23956
 
22536
23957
  glb.worker_type = 'Worker';