@xuda.io/runtime-bundle 1.0.1436 → 1.0.1438

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.
@@ -23706,7 +23706,30 @@ func.runtime.platform.emit = function (name, data) {
23706
23706
  };
23707
23707
 
23708
23708
  // ── Platform helpers for DOM-independent resource loading ──
23709
- func.runtime.platform.load_script = function (url, type, callback) {
23709
+ func.runtime.platform.apply_element_attributes = function (node, attributes, excluded_keys = []) {
23710
+ if (!node?.setAttribute || !attributes) {
23711
+ return node;
23712
+ }
23713
+
23714
+ const excluded = new Set(excluded_keys || []);
23715
+ const attr_keys = Object.keys(attributes);
23716
+ for (let index = 0; index < attr_keys.length; index++) {
23717
+ const key = attr_keys[index];
23718
+ if (!key || excluded.has(key)) {
23719
+ continue;
23720
+ }
23721
+
23722
+ const value = attributes[key];
23723
+ if (value === false || typeof value === 'undefined') {
23724
+ continue;
23725
+ }
23726
+
23727
+ node.setAttribute(key, value === null ? '' : `${value}`);
23728
+ }
23729
+
23730
+ return node;
23731
+ };
23732
+ func.runtime.platform.load_script = function (url, type, callback, attributes) {
23710
23733
  const doc = func.runtime.platform.get_document();
23711
23734
  if (!doc?.createElement || !doc?.head?.appendChild) {
23712
23735
  if (callback) {
@@ -23714,19 +23737,63 @@ func.runtime.platform.load_script = function (url, type, callback) {
23714
23737
  }
23715
23738
  return;
23716
23739
  }
23740
+ const find_existing_script = function () {
23741
+ const asset_key = attributes?.['data-xuda-asset-key'];
23742
+ const scripts = doc.querySelectorAll ? Array.from(doc.querySelectorAll('script')) : [];
23743
+ return scripts.find(function (script) {
23744
+ if (asset_key && script.getAttribute('data-xuda-asset-key') === asset_key) {
23745
+ return true;
23746
+ }
23747
+ return !!(url && script.getAttribute('src') === url);
23748
+ }) || null;
23749
+ };
23750
+
23751
+ const existing_script = find_existing_script();
23752
+ if (existing_script) {
23753
+ if (callback) {
23754
+ if (existing_script.getAttribute('data-xuda-loaded') === 'true' || !url) {
23755
+ callback();
23756
+ } else {
23757
+ existing_script.addEventListener('load', callback, { once: true });
23758
+ existing_script.addEventListener('error', callback, { once: true });
23759
+ }
23760
+ }
23761
+ return existing_script;
23762
+ }
23763
+
23717
23764
  const script = doc.createElement('script');
23718
23765
  script.src = url;
23719
23766
  if (type) script.type = type;
23720
- script.onload = callback;
23767
+ func.runtime.platform.apply_element_attributes(script, attributes, ['src', 'type']);
23768
+ script.onload = function () {
23769
+ script.setAttribute('data-xuda-loaded', 'true');
23770
+ if (callback) {
23771
+ callback();
23772
+ }
23773
+ };
23774
+ script.onerror = function () {
23775
+ if (callback) {
23776
+ callback();
23777
+ }
23778
+ };
23721
23779
  doc.head.appendChild(script);
23780
+ return script;
23722
23781
  };
23723
- func.runtime.platform.load_css = function (href) {
23782
+ func.runtime.platform.load_css = function (href, attributes) {
23724
23783
  const doc = func.runtime.platform.get_document();
23725
23784
  if (!doc?.createElement || !doc?.head) {
23726
23785
  return;
23727
23786
  }
23728
23787
  try {
23729
- if (doc.querySelector('link[href="' + href + '"]')) return;
23788
+ const asset_key = attributes?.['data-xuda-asset-key'];
23789
+ const existing_links = doc.querySelectorAll ? Array.from(doc.querySelectorAll('link')) : [];
23790
+ const existing = existing_links.find(function (link) {
23791
+ if (asset_key && link.getAttribute('data-xuda-asset-key') === asset_key) {
23792
+ return true;
23793
+ }
23794
+ return !!(href && link.getAttribute('href') === href);
23795
+ });
23796
+ if (existing) return existing;
23730
23797
  } catch (err) {
23731
23798
  return;
23732
23799
  }
@@ -23734,7 +23801,9 @@ func.runtime.platform.load_css = function (href) {
23734
23801
  link.rel = 'stylesheet';
23735
23802
  link.type = 'text/css';
23736
23803
  link.href = href;
23804
+ func.runtime.platform.apply_element_attributes(link, attributes, ['href']);
23737
23805
  doc.head.insertBefore(link, doc.head.firstChild);
23806
+ return link;
23738
23807
  };
23739
23808
  func.runtime.platform.remove_js_css = function (filename, filetype) {
23740
23809
  const doc = func.runtime.platform.get_document();
@@ -23768,6 +23837,128 @@ func.runtime.platform.set_cursor = function (element, cursor) {
23768
23837
  node.style.cursor = cursor;
23769
23838
  }
23770
23839
  };
23840
+ func.runtime.program.normalize_doc_for_runtime = function (doc) {
23841
+ if (!doc || doc.__xudaRuntimeNormalized || !Array.isArray(doc.progUi) || !doc.progUi.length) {
23842
+ return doc;
23843
+ }
23844
+
23845
+ const normalize_tag_name = function (tag_name) {
23846
+ return `${tag_name || ''}`.trim().toLowerCase();
23847
+ };
23848
+ const merge_attributes = function (target, source) {
23849
+ const merged = { ...(target || {}) };
23850
+ const source_attributes = source || {};
23851
+ const keys = Object.keys(source_attributes);
23852
+
23853
+ for (let index = 0; index < keys.length; index++) {
23854
+ const key = keys[index];
23855
+ const value = source_attributes[key];
23856
+ if (typeof value === 'undefined') {
23857
+ continue;
23858
+ }
23859
+
23860
+ if (key === 'class' && merged.class && value) {
23861
+ const next_value = `${merged.class} ${value}`.trim();
23862
+ merged.class = Array.from(new Set(next_value.split(/\s+/).filter(Boolean))).join(' ');
23863
+ continue;
23864
+ }
23865
+
23866
+ if (key === 'style' && merged.style && value) {
23867
+ merged.style = `${merged.style}; ${value}`.trim();
23868
+ continue;
23869
+ }
23870
+
23871
+ if (typeof merged[key] === 'undefined') {
23872
+ merged[key] = value;
23873
+ }
23874
+ }
23875
+
23876
+ return merged;
23877
+ };
23878
+ const normalize_nodes = function (nodes, state) {
23879
+ const normalized_nodes = [];
23880
+
23881
+ for (let index = 0; index < (nodes || []).length; index++) {
23882
+ const node = nodes[index];
23883
+ if (!node || typeof node !== 'object') {
23884
+ continue;
23885
+ }
23886
+
23887
+ const tag_name = normalize_tag_name(node.tagName);
23888
+
23889
+ if (tag_name === '!doctype') {
23890
+ state.changed = true;
23891
+ continue;
23892
+ }
23893
+
23894
+ if (tag_name === 'html') {
23895
+ state.changed = true;
23896
+ state.root_attributes = merge_attributes(state.root_attributes, node.attributes);
23897
+ normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
23898
+ continue;
23899
+ }
23900
+
23901
+ if (tag_name === 'head') {
23902
+ state.changed = true;
23903
+ normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
23904
+ continue;
23905
+ }
23906
+
23907
+ if (tag_name === 'body') {
23908
+ state.changed = true;
23909
+ state.root_attributes = merge_attributes(state.root_attributes, node.attributes);
23910
+ normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
23911
+ continue;
23912
+ }
23913
+
23914
+ let next_node = node;
23915
+ if (Array.isArray(node.children) && node.children.length) {
23916
+ const next_children = normalize_nodes(node.children, state);
23917
+ if (next_children !== node.children) {
23918
+ next_node = {
23919
+ ...node,
23920
+ children: next_children,
23921
+ };
23922
+ state.changed = true;
23923
+ }
23924
+ }
23925
+
23926
+ normalized_nodes.push(next_node);
23927
+ }
23928
+
23929
+ return normalized_nodes;
23930
+ };
23931
+
23932
+ const [root_node, ...extra_nodes] = doc.progUi;
23933
+ if (!root_node || typeof root_node !== 'object') {
23934
+ return doc;
23935
+ }
23936
+
23937
+ const state = {
23938
+ changed: false,
23939
+ root_attributes: {},
23940
+ };
23941
+
23942
+ const normalized_children = normalize_nodes([...(root_node.children || []), ...extra_nodes], state);
23943
+ const merged_attributes = merge_attributes(root_node.attributes, state.root_attributes);
23944
+
23945
+ if (!state.changed && !Object.keys(state.root_attributes).length) {
23946
+ doc.__xudaRuntimeNormalized = true;
23947
+ return doc;
23948
+ }
23949
+
23950
+ return {
23951
+ ...doc,
23952
+ __xudaRuntimeNormalized: true,
23953
+ progUi: [
23954
+ {
23955
+ ...root_node,
23956
+ attributes: merged_attributes,
23957
+ children: normalized_children,
23958
+ },
23959
+ ],
23960
+ };
23961
+ };
23771
23962
 
23772
23963
  func.runtime.env = {
23773
23964
  get_url_params: function () {
@@ -24025,6 +24216,147 @@ func.runtime.workers.delete_promise = function (SESSION_ID, worker_id, promise_q
24025
24216
  delete registry_entry.promise_queue[promise_queue_id];
24026
24217
  return true;
24027
24218
  };
24219
+ func.runtime.render.clone_runtime_options = function (value) {
24220
+ if (typeof structuredClone === 'function') {
24221
+ try {
24222
+ return structuredClone(value);
24223
+ } catch (_) {}
24224
+ }
24225
+
24226
+ if (Array.isArray(value)) {
24227
+ return value.map(function (item) {
24228
+ return func.runtime.render.clone_runtime_options(item);
24229
+ });
24230
+ }
24231
+
24232
+ if (value && typeof value === 'object') {
24233
+ const cloned = {};
24234
+ const keys = Object.keys(value);
24235
+ for (let index = 0; index < keys.length; index++) {
24236
+ const key = keys[index];
24237
+ cloned[key] = func.runtime.render.clone_runtime_options(value[key]);
24238
+ }
24239
+ return cloned;
24240
+ }
24241
+
24242
+ return value;
24243
+ };
24244
+ func.runtime.render.normalize_runtime_bootstrap = function (raw_options = {}) {
24245
+ const options = raw_options || {};
24246
+ let app_computing_mode = options.app_computing_mode || '';
24247
+ let app_render_mode = options.app_render_mode || '';
24248
+ let app_client_activation = options.app_client_activation || '';
24249
+ let ssr_payload = options.ssr_payload || null;
24250
+
24251
+ if (typeof ssr_payload === 'string') {
24252
+ try {
24253
+ ssr_payload = JSON.parse(ssr_payload);
24254
+ } catch (_) {
24255
+ ssr_payload = null;
24256
+ }
24257
+ }
24258
+
24259
+ if (ssr_payload && typeof ssr_payload === 'object') {
24260
+ ssr_payload = func.runtime.render.clone_runtime_options(ssr_payload);
24261
+ }
24262
+
24263
+ if (!app_computing_mode) {
24264
+ if (app_render_mode === 'ssr_first_page' || app_render_mode === 'ssr_full') {
24265
+ app_computing_mode = 'server';
24266
+ } else {
24267
+ app_computing_mode = 'main';
24268
+ }
24269
+ }
24270
+
24271
+ switch (app_computing_mode) {
24272
+ case 'main':
24273
+ app_render_mode = 'csr';
24274
+ app_client_activation = 'none';
24275
+ break;
24276
+
24277
+ case 'worker':
24278
+ app_render_mode = 'csr';
24279
+ app_client_activation = 'none';
24280
+ break;
24281
+
24282
+ default:
24283
+ app_computing_mode = 'server';
24284
+ if (app_render_mode !== 'ssr_full') {
24285
+ app_render_mode = 'ssr_first_page';
24286
+ }
24287
+ app_client_activation = app_render_mode === 'ssr_full' ? 'hydrate' : 'takeover';
24288
+ break;
24289
+ }
24290
+
24291
+ if (ssr_payload && typeof ssr_payload === 'object') {
24292
+ if (!ssr_payload.app_render_mode) {
24293
+ ssr_payload.app_render_mode = app_render_mode;
24294
+ }
24295
+ if (!ssr_payload.app_client_activation) {
24296
+ ssr_payload.app_client_activation = app_client_activation;
24297
+ }
24298
+ if (!ssr_payload.app_computing_mode) {
24299
+ ssr_payload.app_computing_mode = app_computing_mode;
24300
+ }
24301
+ }
24302
+
24303
+ return {
24304
+ app_computing_mode,
24305
+ app_render_mode,
24306
+ app_client_activation,
24307
+ ssr_payload,
24308
+ };
24309
+ };
24310
+ func.runtime.render.apply_runtime_bootstrap_defaults = function (target = {}) {
24311
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target);
24312
+ target.app_computing_mode = normalized.app_computing_mode;
24313
+ target.app_render_mode = normalized.app_render_mode;
24314
+ target.app_client_activation = normalized.app_client_activation;
24315
+ target.ssr_payload = normalized.ssr_payload;
24316
+ return normalized;
24317
+ };
24318
+ func.runtime.render.is_server_render_mode = function (target = {}) {
24319
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
24320
+ return normalized.app_computing_mode === 'server' && normalized.app_render_mode !== 'csr';
24321
+ };
24322
+ func.runtime.render.is_takeover_mode = function (target = {}) {
24323
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
24324
+ return normalized.app_client_activation === 'takeover';
24325
+ };
24326
+ func.runtime.render.is_hydration_mode = function (target = {}) {
24327
+ const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
24328
+ return normalized.app_client_activation === 'hydrate';
24329
+ };
24330
+ func.runtime.render.get_ssr_payload = function (target = {}) {
24331
+ if (target?.opt?.ssr_payload) {
24332
+ return target.opt.ssr_payload;
24333
+ }
24334
+ if (target?.ssr_payload) {
24335
+ return target.ssr_payload;
24336
+ }
24337
+ const win = func.runtime.platform.get_window();
24338
+ return win?.__XUDA_SSR__ || null;
24339
+ };
24340
+ func.runtime.render.should_use_ssr_payload = function (SESSION_ID, paramsP) {
24341
+ const session = SESSION_OBJ?.[SESSION_ID];
24342
+ const payload = func.runtime.render.get_ssr_payload(session);
24343
+ if (!payload || payload._consumed) {
24344
+ return false;
24345
+ }
24346
+ if (paramsP?.prog_id && payload.prog_id && payload.prog_id !== paramsP.prog_id) {
24347
+ return false;
24348
+ }
24349
+ return true;
24350
+ };
24351
+ func.runtime.render.mark_ssr_payload_consumed = function (SESSION_ID) {
24352
+ const session = SESSION_OBJ?.[SESSION_ID];
24353
+ const payload = func.runtime.render.get_ssr_payload(session);
24354
+ if (!payload || typeof payload !== 'object') {
24355
+ return false;
24356
+ }
24357
+ payload._consumed = true;
24358
+ return true;
24359
+ };
24028
24360
  func.runtime.render.get_root_data_system = function (SESSION_ID) {
24029
24361
  return SESSION_OBJ[SESSION_ID]?.DS_GLB?.[0]?.data_system || null;
24030
24362
  };
@@ -24428,10 +24760,10 @@ func.runtime.resources.load_cdn = async function (SESSION_ID, resource) {
24428
24760
  await func.utils.load_js_on_demand(normalized_resource.src);
24429
24761
  break;
24430
24762
  case 'css':
24431
- await func.utils.load_js_on_demand(normalized_resource.src);
24763
+ func.runtime.platform.load_css(normalized_resource.src);
24432
24764
  break;
24433
24765
  case 'module':
24434
- func.utils.load_js_on_demand(normalized_resource.src, 'module');
24766
+ await func.utils.load_js_on_demand(normalized_resource.src, 'module');
24435
24767
  break;
24436
24768
  default:
24437
24769
  await func.utils.load_js_on_demand(normalized_resource.src);
@@ -25767,7 +26099,692 @@ func.common.fastHash = function (inputString) {
25767
26099
  return ((hash >>> 0).toString(36) + '0000000000').slice(0, 10);
25768
26100
  };
25769
26101
 
25770
- glb.new_xu_render = false;
26102
+ glb.new_xu_render = false;
26103
+ func.runtime = func.runtime || {};
26104
+ func.runtime.ui = func.runtime.ui || {};
26105
+ func.runtime.render = func.runtime.render || {};
26106
+ func.runtime.widgets = func.runtime.widgets || {};
26107
+
26108
+ // Shared render-tree contract helpers live here so browser and headless runtimes can resolve the same UI structure.
26109
+
26110
+ func.runtime.render.TREE_CONTRACT_VERSION = func.runtime.render.TREE_CONTRACT_VERSION || 'xuda.render_tree.v1';
26111
+ func.runtime.render._tree_widget_capability_cache = func.runtime.render._tree_widget_capability_cache || {};
26112
+
26113
+ func.runtime.render.safe_clone_tree_value = function (value) {
26114
+ if (typeof structuredClone === 'function') {
26115
+ try {
26116
+ return structuredClone(value);
26117
+ } catch (_) {
26118
+ // Fall through to the recursive clone below.
26119
+ }
26120
+ }
26121
+
26122
+ if (Array.isArray(value)) {
26123
+ return value.map(function (item) {
26124
+ return func.runtime.render.safe_clone_tree_value(item);
26125
+ });
26126
+ }
26127
+
26128
+ if (value && typeof value === 'object') {
26129
+ const cloned = {};
26130
+ const keys = Object.keys(value);
26131
+ for (let index = 0; index < keys.length; index++) {
26132
+ const key = keys[index];
26133
+ cloned[key] = func.runtime.render.safe_clone_tree_value(value[key]);
26134
+ }
26135
+ return cloned;
26136
+ }
26137
+
26138
+ return value;
26139
+ };
26140
+ func.runtime.render.sort_tree_debug_value = function (value) {
26141
+ if (Array.isArray(value)) {
26142
+ return value.map(function (item) {
26143
+ return func.runtime.render.sort_tree_debug_value(item);
26144
+ });
26145
+ }
26146
+
26147
+ if (value && typeof value === 'object') {
26148
+ const sorted = {};
26149
+ const keys = Object.keys(value).sort();
26150
+ for (let index = 0; index < keys.length; index++) {
26151
+ const key = keys[index];
26152
+ sorted[key] = func.runtime.render.sort_tree_debug_value(value[key]);
26153
+ }
26154
+ return sorted;
26155
+ }
26156
+
26157
+ return value;
26158
+ };
26159
+ func.runtime.render.is_tree_node = function (nodeP) {
26160
+ return !!nodeP?.contract && nodeP.contract === func.runtime.render.TREE_CONTRACT_VERSION;
26161
+ };
26162
+ func.runtime.render.get_tree_source_node = function (nodeP) {
26163
+ if (!func.runtime.render.is_tree_node(nodeP)) {
26164
+ return nodeP || null;
26165
+ }
26166
+ return nodeP?.meta?.source_node || null;
26167
+ };
26168
+ func.runtime.render.get_tree_source_snapshot = function (nodeP) {
26169
+ if (!func.runtime.render.is_tree_node(nodeP)) {
26170
+ return func.runtime.render.safe_clone_tree_value(nodeP);
26171
+ }
26172
+ return nodeP?.meta?.source_snapshot || null;
26173
+ };
26174
+ func.runtime.render.get_tree_node_kind = function (nodeP) {
26175
+ const tag_name = typeof nodeP?.tagName === 'string' ? nodeP.tagName.toLowerCase() : '';
26176
+ const node_type = typeof nodeP?.type === 'string' ? nodeP.type.toLowerCase() : '';
26177
+
26178
+ if (tag_name === 'xu-widget') return 'widget';
26179
+ if (tag_name === 'xu-single-view') return 'single_view';
26180
+ if (tag_name === 'xu-multi-view') return 'multi_view';
26181
+ if (tag_name === 'xu-panel') return 'panel';
26182
+ if (tag_name === 'xu-teleport') return 'teleport';
26183
+ if (tag_name === 'xurender') return 'placeholder';
26184
+ if (tag_name === '#text' || node_type === 'text') return 'text';
26185
+ if (!tag_name && typeof nodeP?.content === 'string' && !Array.isArray(nodeP?.children)) return 'text';
26186
+ return 'element';
26187
+ };
26188
+ func.runtime.render.get_tree_node_id = function (nodeP, pathP) {
26189
+ if (nodeP?.id) {
26190
+ return nodeP.id;
26191
+ }
26192
+ if (nodeP?.id_org) {
26193
+ return nodeP.id_org;
26194
+ }
26195
+ const normalized_path = Array.isArray(pathP) && pathP.length ? pathP.join('.') : 'root';
26196
+ return `tree-node-${normalized_path}`;
26197
+ };
26198
+ func.runtime.render.get_tree_controls = function (attributes) {
26199
+ const attrs = attributes || {};
26200
+ const get_first_defined = function (keys) {
26201
+ for (let index = 0; index < keys.length; index++) {
26202
+ const key = keys[index];
26203
+ if (Object.prototype.hasOwnProperty.call(attrs, key)) {
26204
+ return attrs[key];
26205
+ }
26206
+ }
26207
+ return null;
26208
+ };
26209
+ return {
26210
+ xu_for: get_first_defined(['xu-for', 'xu-exp:xu-for']),
26211
+ xu_if: get_first_defined(['xu-if', 'xu-exp:xu-if']),
26212
+ xu_render: get_first_defined(['xu-render', 'xu-exp:xu-render']),
26213
+ };
26214
+ };
26215
+ func.runtime.render.get_tree_node_capabilities = async function (options) {
26216
+ const attributes = options?.attributes || {};
26217
+ const plugin_name = attributes['xu-widget'];
26218
+ if (!plugin_name) {
26219
+ return null;
26220
+ }
26221
+
26222
+ const cache = func.runtime.render._tree_widget_capability_cache;
26223
+ if (cache[plugin_name]) {
26224
+ return func.runtime.render.safe_clone_tree_value(cache[plugin_name]);
26225
+ }
26226
+
26227
+ let capabilities = {
26228
+ browser: true,
26229
+ headless: false,
26230
+ };
26231
+
26232
+ try {
26233
+ if (options.SESSION_ID && options.paramsP && func.runtime.widgets?.create_context && func.runtime.widgets?.get_definition) {
26234
+ const widget_context = func.runtime.widgets.create_context(options.SESSION_ID, options.paramsP, attributes);
26235
+ const definition = await func.runtime.widgets.get_definition(widget_context);
26236
+ capabilities = func.runtime.widgets.normalize_capabilities(definition);
26237
+ }
26238
+ } catch (_) {
26239
+ // Keep the safe browser-only default when the widget definition is unavailable.
26240
+ }
26241
+
26242
+ cache[plugin_name] = capabilities;
26243
+ return func.runtime.render.safe_clone_tree_value(capabilities);
26244
+ };
26245
+ func.runtime.render.ensure_tree_node = async function (options) {
26246
+ if (!options?.nodeP) {
26247
+ return null;
26248
+ }
26249
+ if (func.runtime.render.is_tree_node(options.nodeP)) {
26250
+ return options.nodeP;
26251
+ }
26252
+ return await func.runtime.render.build_tree(options);
26253
+ };
26254
+ func.runtime.render.build_tree = async function (options) {
26255
+ if (Array.isArray(options?.nodeP)) {
26256
+ return await func.runtime.render.build_tree_list({
26257
+ ...options,
26258
+ nodesP: options.nodeP,
26259
+ });
26260
+ }
26261
+
26262
+ const nodeP = options?.nodeP;
26263
+ if (!nodeP) {
26264
+ return null;
26265
+ }
26266
+ if (func.runtime.render.is_tree_node(nodeP)) {
26267
+ return nodeP;
26268
+ }
26269
+
26270
+ const pathP = Array.isArray(options?.pathP) ? options.pathP.slice() : [];
26271
+ const tree_path = pathP.length ? pathP.slice() : [0];
26272
+ const attributes = func.runtime.render.safe_clone_tree_value(nodeP.attributes || {});
26273
+ if (typeof nodeP.content !== 'undefined' && typeof attributes['xu-content'] === 'undefined') {
26274
+ attributes['xu-content'] = func.runtime.render.safe_clone_tree_value(nodeP.content);
26275
+ }
26276
+
26277
+ const widget_capabilities = await func.runtime.render.get_tree_node_capabilities({
26278
+ SESSION_ID: options?.SESSION_ID,
26279
+ paramsP: options?.paramsP,
26280
+ attributes,
26281
+ });
26282
+ const children = [];
26283
+ const child_nodes = Array.isArray(nodeP.children) ? nodeP.children : [];
26284
+ const parent_tree_id = tree_path.join('.');
26285
+
26286
+ for (let index = 0; index < child_nodes.length; index++) {
26287
+ const child_tree = await func.runtime.render.build_tree({
26288
+ ...options,
26289
+ nodeP: child_nodes[index],
26290
+ pathP: tree_path.concat(index),
26291
+ parent_tree_id: parent_tree_id,
26292
+ keyP: index,
26293
+ parent_nodeP: nodeP,
26294
+ });
26295
+ if (child_tree) {
26296
+ children.push(child_tree);
26297
+ }
26298
+ }
26299
+
26300
+ const tree = {
26301
+ contract: func.runtime.render.TREE_CONTRACT_VERSION,
26302
+ id: func.runtime.render.get_tree_node_id(nodeP, tree_path),
26303
+ xu_tree_id: `tree.${tree_path.join('.')}`,
26304
+ kind: func.runtime.render.get_tree_node_kind(nodeP),
26305
+ tagName: nodeP.tagName || null,
26306
+ attributes,
26307
+ text: typeof nodeP.text !== 'undefined' ? func.runtime.render.safe_clone_tree_value(nodeP.text) : null,
26308
+ content: typeof nodeP.content !== 'undefined' ? func.runtime.render.safe_clone_tree_value(nodeP.content) : null,
26309
+ children,
26310
+ meta: {
26311
+ tree_id: tree_path.join('.'),
26312
+ path: tree_path,
26313
+ parent_tree_id: options?.parent_tree_id || null,
26314
+ key: typeof options?.keyP === 'undefined' ? null : options.keyP,
26315
+ recordid: nodeP?.recordid || null,
26316
+ dependency_fields: func.runtime.render.safe_clone_tree_value(nodeP?.dependency_fields || null),
26317
+ iterate_info: func.runtime.render.safe_clone_tree_value(options?.parent_infoP?.iterate_info || nodeP?.iterate_info || null),
26318
+ controls: func.runtime.render.get_tree_controls(attributes),
26319
+ capabilities: widget_capabilities,
26320
+ widget: attributes['xu-widget']
26321
+ ? {
26322
+ plugin_name: attributes['xu-widget'],
26323
+ method: attributes['xu-method'] || '_default',
26324
+ capabilities: widget_capabilities,
26325
+ }
26326
+ : null,
26327
+ source_node_id: nodeP?.id || nodeP?.id_org || null,
26328
+ source_node: nodeP,
26329
+ source_snapshot: func.runtime.ui?.get_node_snapshot
26330
+ ? func.runtime.ui.get_node_snapshot(nodeP)
26331
+ : func.runtime.render.safe_clone_tree_value(nodeP),
26332
+ },
26333
+ };
26334
+
26335
+ return tree;
26336
+ };
26337
+ func.runtime.render.build_tree_list = async function (options) {
26338
+ const nodes = Array.isArray(options?.nodesP) ? options.nodesP : [];
26339
+ const trees = [];
26340
+
26341
+ for (let index = 0; index < nodes.length; index++) {
26342
+ const tree = await func.runtime.render.build_tree({
26343
+ ...options,
26344
+ nodeP: nodes[index],
26345
+ pathP: Array.isArray(options?.pathP) && options.pathP.length ? options.pathP.concat(index) : [index],
26346
+ keyP: index,
26347
+ });
26348
+ if (tree) {
26349
+ trees.push(tree);
26350
+ }
26351
+ }
26352
+
26353
+ return trees;
26354
+ };
26355
+ func.runtime.render.sanitize_tree_for_debug = function (treeP) {
26356
+ if (Array.isArray(treeP)) {
26357
+ return treeP.map(function (child) {
26358
+ return func.runtime.render.sanitize_tree_for_debug(child);
26359
+ });
26360
+ }
26361
+
26362
+ if (!func.runtime.render.is_tree_node(treeP)) {
26363
+ return func.runtime.render.sort_tree_debug_value(func.runtime.render.safe_clone_tree_value(treeP));
26364
+ }
26365
+
26366
+ return {
26367
+ contract: treeP.contract,
26368
+ id: treeP.id,
26369
+ xu_tree_id: treeP.xu_tree_id || null,
26370
+ kind: treeP.kind,
26371
+ tagName: treeP.tagName,
26372
+ attributes: func.runtime.render.sort_tree_debug_value(treeP.attributes || {}),
26373
+ text: treeP.text,
26374
+ content: treeP.content,
26375
+ children: treeP.children.map(function (child) {
26376
+ return func.runtime.render.sanitize_tree_for_debug(child);
26377
+ }),
26378
+ meta: {
26379
+ tree_id: treeP.meta?.tree_id || null,
26380
+ path: func.runtime.render.safe_clone_tree_value(treeP.meta?.path || []),
26381
+ parent_tree_id: treeP.meta?.parent_tree_id || null,
26382
+ key: typeof treeP.meta?.key === 'undefined' ? null : treeP.meta.key,
26383
+ recordid: treeP.meta?.recordid || null,
26384
+ dependency_fields: func.runtime.render.sort_tree_debug_value(treeP.meta?.dependency_fields || null),
26385
+ iterate_info: func.runtime.render.sort_tree_debug_value(treeP.meta?.iterate_info || null),
26386
+ controls: func.runtime.render.sort_tree_debug_value(treeP.meta?.controls || null),
26387
+ capabilities: func.runtime.render.sort_tree_debug_value(treeP.meta?.capabilities || null),
26388
+ widget: treeP.meta?.widget
26389
+ ? {
26390
+ plugin_name: treeP.meta.widget.plugin_name,
26391
+ method: treeP.meta.widget.method,
26392
+ capabilities: func.runtime.render.sort_tree_debug_value(treeP.meta.widget.capabilities || null),
26393
+ }
26394
+ : null,
26395
+ source_node_id: treeP.meta?.source_node_id || null,
26396
+ },
26397
+ };
26398
+ };
26399
+ func.runtime.render.serialize_tree = function (treeP, spacing = 2) {
26400
+ return JSON.stringify(func.runtime.render.sanitize_tree_for_debug(treeP), null, spacing);
26401
+ };
26402
+ func.runtime = func.runtime || {};
26403
+ func.runtime.ui = func.runtime.ui || {};
26404
+ func.runtime.render = func.runtime.render || {};
26405
+ func.runtime.widgets = func.runtime.widgets || {};
26406
+
26407
+ // Shared string-renderer helpers live here so headless/server runtimes can materialize the render tree without a DOM.
26408
+
26409
+ func.runtime.render.HTML_VOID_TAGS = func.runtime.render.HTML_VOID_TAGS || {
26410
+ area: true,
26411
+ base: true,
26412
+ br: true,
26413
+ col: true,
26414
+ embed: true,
26415
+ hr: true,
26416
+ img: true,
26417
+ input: true,
26418
+ link: true,
26419
+ meta: true,
26420
+ param: true,
26421
+ source: true,
26422
+ track: true,
26423
+ wbr: true,
26424
+ };
26425
+ func.runtime.render.escape_html = function (value) {
26426
+ return `${value ?? ''}`
26427
+ .replaceAll('&', '&amp;')
26428
+ .replaceAll('<', '&lt;')
26429
+ .replaceAll('>', '&gt;')
26430
+ .replaceAll('"', '&quot;')
26431
+ .replaceAll("'", '&#039;');
26432
+ };
26433
+ func.runtime.render.escape_html_attribute = function (value) {
26434
+ return func.runtime.render.escape_html(value);
26435
+ };
26436
+ func.runtime.render.is_html_void_tag = function (tag_name) {
26437
+ return !!func.runtime.render.HTML_VOID_TAGS[(tag_name || '').toLowerCase()];
26438
+ };
26439
+ func.runtime.render.is_falsey_render_value = function (value) {
26440
+ if (value === false || value === null || typeof value === 'undefined') {
26441
+ return true;
26442
+ }
26443
+ if (typeof value === 'number') {
26444
+ return value === 0;
26445
+ }
26446
+ if (typeof value === 'string') {
26447
+ const normalized = value.trim().toLowerCase();
26448
+ return normalized === '' || normalized === 'false' || normalized === '0' || normalized === 'null' || normalized === 'undefined' || normalized === 'off' || normalized === 'no';
26449
+ }
26450
+ return false;
26451
+ };
26452
+ func.runtime.render.should_render_tree_node = function (treeP) {
26453
+ const controls = treeP?.meta?.controls || {};
26454
+ if (controls.xu_if !== null && controls.xu_if !== undefined && func.runtime.render.is_falsey_render_value(controls.xu_if)) {
26455
+ return false;
26456
+ }
26457
+ if (controls.xu_render !== null && controls.xu_render !== undefined && func.runtime.render.is_falsey_render_value(controls.xu_render)) {
26458
+ return false;
26459
+ }
26460
+ return true;
26461
+ };
26462
+ func.runtime.render.is_tree_control_attribute = function (key) {
26463
+ if (!key) {
26464
+ return false;
26465
+ }
26466
+ return (
26467
+ key.startsWith('xu-exp:') ||
26468
+ key === 'xu-widget' ||
26469
+ key === 'xu-method' ||
26470
+ key === 'xu-for' ||
26471
+ key === 'xu-for-key' ||
26472
+ key === 'xu-for-val' ||
26473
+ key === 'xu-if' ||
26474
+ key === 'xu-render' ||
26475
+ key === 'xu-bind' ||
26476
+ key === 'xu-content' ||
26477
+ key === 'xu-text' ||
26478
+ key === 'xu-html' ||
26479
+ key === 'xu-show' ||
26480
+ key === 'xu-panel-program' ||
26481
+ key === 'xu-teleport'
26482
+ );
26483
+ };
26484
+ func.runtime.render.get_string_renderer_tag_name = function (treeP) {
26485
+ switch (treeP?.kind) {
26486
+ case 'widget':
26487
+ case 'single_view':
26488
+ case 'multi_view':
26489
+ case 'panel':
26490
+ case 'teleport':
26491
+ return 'div';
26492
+ case 'placeholder':
26493
+ return null;
26494
+ case 'text':
26495
+ return null;
26496
+ default:
26497
+ return treeP?.tagName || 'div';
26498
+ }
26499
+ };
26500
+ func.runtime.render.get_tree_terminal_content = function (treeP) {
26501
+ const attributes = treeP?.attributes || {};
26502
+ if (typeof attributes['xu-html'] !== 'undefined' && attributes['xu-html'] !== null) {
26503
+ return {
26504
+ value: `${attributes['xu-html']}`,
26505
+ mode: 'html',
26506
+ };
26507
+ }
26508
+ if (typeof attributes['xu-content'] !== 'undefined' && attributes['xu-content'] !== null) {
26509
+ return {
26510
+ value: `${attributes['xu-content']}`,
26511
+ mode: 'html',
26512
+ };
26513
+ }
26514
+ if (typeof attributes['xu-text'] !== 'undefined' && attributes['xu-text'] !== null) {
26515
+ return {
26516
+ value: `${attributes['xu-text']}`,
26517
+ mode: 'text',
26518
+ };
26519
+ }
26520
+ if (treeP?.kind === 'text') {
26521
+ return {
26522
+ value: typeof treeP?.text !== 'undefined' && treeP?.text !== null ? `${treeP.text}` : `${treeP?.content || ''}`,
26523
+ mode: 'text',
26524
+ };
26525
+ }
26526
+ return null;
26527
+ };
26528
+ func.runtime.render.render_tree_terminal_content = function (treeP) {
26529
+ const terminal = func.runtime.render.get_tree_terminal_content(treeP);
26530
+ if (!terminal) {
26531
+ return null;
26532
+ }
26533
+ if (terminal.mode === 'html') {
26534
+ return terminal.value;
26535
+ }
26536
+ return func.runtime.render.escape_html(terminal.value);
26537
+ };
26538
+ func.runtime.render.get_widget_fallback_markup = function (treeP) {
26539
+ const widget_meta = treeP?.meta?.widget || {};
26540
+ const capability_state = widget_meta?.capabilities?.headless ? 'headless-capable' : 'browser-only';
26541
+ return `<!--xuda-widget:${func.runtime.render.escape_html(widget_meta.plugin_name || 'unknown')}:${capability_state}-->`;
26542
+ };
26543
+ func.runtime.render.get_tree_string_attributes = function (treeP, renderer_context) {
26544
+ const attributes = func.runtime.render.safe_clone_tree_value(treeP?.attributes || {});
26545
+ const attr_pairs = [];
26546
+ const keys = Object.keys(attributes);
26547
+
26548
+ for (let index = 0; index < keys.length; index++) {
26549
+ const key = keys[index];
26550
+ if (func.runtime.render.is_tree_control_attribute(key)) {
26551
+ continue;
26552
+ }
26553
+ const value = attributes[key];
26554
+ if (value === false || value === null || typeof value === 'undefined') {
26555
+ continue;
26556
+ }
26557
+ if (value === true) {
26558
+ attr_pairs.push(key);
26559
+ continue;
26560
+ }
26561
+ const normalized_value = typeof value === 'object' ? JSON.stringify(value) : `${value}`;
26562
+ attr_pairs.push(`${key}="${func.runtime.render.escape_html_attribute(normalized_value)}"`);
26563
+ }
26564
+
26565
+ attr_pairs.push(`data-xuda-kind="${func.runtime.render.escape_html_attribute(treeP?.kind || 'element')}"`);
26566
+ attr_pairs.push(`data-xuda-node-id="${func.runtime.render.escape_html_attribute(treeP?.id || treeP?.meta?.source_node_id || '')}"`);
26567
+ attr_pairs.push(`data-xuda-tree-id="${func.runtime.render.escape_html_attribute(treeP?.meta?.tree_id || '')}"`);
26568
+
26569
+ if (treeP?.kind === 'widget' && treeP?.meta?.widget) {
26570
+ attr_pairs.push(`data-xuda-widget="${func.runtime.render.escape_html_attribute(treeP.meta.widget.plugin_name || '')}"`);
26571
+ attr_pairs.push(`data-xuda-widget-method="${func.runtime.render.escape_html_attribute(treeP.meta.widget.method || '_default')}"`);
26572
+ attr_pairs.push(`data-xuda-widget-capability="${func.runtime.render.escape_html_attribute(treeP.meta.widget.capabilities?.headless ? 'headless' : 'browser')}"`);
26573
+ }
26574
+
26575
+ if (treeP?.kind === 'teleport' && treeP?.attributes?.['xu-teleport']) {
26576
+ attr_pairs.push(`data-xuda-teleport-target="${func.runtime.render.escape_html_attribute(treeP.attributes['xu-teleport'])}"`);
26577
+ }
26578
+
26579
+ if ((treeP?.meta?.controls?.xu_for !== null && treeP?.meta?.controls?.xu_for !== undefined) && !renderer_context?.strip_iteration_markers) {
26580
+ attr_pairs.push('data-xuda-xu-for="pending"');
26581
+ }
26582
+
26583
+ return attr_pairs.length ? ' ' + attr_pairs.join(' ') : '';
26584
+ };
26585
+ func.runtime.render.render_tree_children_to_string = async function (treeP, renderer_context) {
26586
+ if (!Array.isArray(treeP?.children) || !treeP.children.length) {
26587
+ return '';
26588
+ }
26589
+ let html = '';
26590
+ for (let index = 0; index < treeP.children.length; index++) {
26591
+ html += await func.runtime.render.render_tree_to_string(treeP.children[index], {
26592
+ ...renderer_context,
26593
+ parent_tree: treeP,
26594
+ });
26595
+ }
26596
+ return html;
26597
+ };
26598
+ func.runtime.render.render_tree_to_string = async function (treeP, renderer_context = {}) {
26599
+ if (!treeP) {
26600
+ return '';
26601
+ }
26602
+ if (Array.isArray(treeP)) {
26603
+ let html = '';
26604
+ for (let index = 0; index < treeP.length; index++) {
26605
+ html += await func.runtime.render.render_tree_to_string(treeP[index], renderer_context);
26606
+ }
26607
+ return html;
26608
+ }
26609
+
26610
+ const ensured_tree = await func.runtime.render.ensure_tree_node({
26611
+ SESSION_ID: renderer_context?.SESSION_ID,
26612
+ nodeP: treeP,
26613
+ paramsP: renderer_context?.paramsP,
26614
+ parent_infoP: renderer_context?.parent_infoP,
26615
+ keyP: renderer_context?.keyP,
26616
+ parent_nodeP: renderer_context?.parent_nodeP,
26617
+ });
26618
+
26619
+ if (!ensured_tree || !func.runtime.render.should_render_tree_node(ensured_tree)) {
26620
+ return '';
26621
+ }
26622
+
26623
+ if (ensured_tree.kind === 'placeholder') {
26624
+ if (renderer_context?.include_placeholders) {
26625
+ return `<!--xuda-placeholder:${func.runtime.render.escape_html(ensured_tree.id || '')}-->`;
26626
+ }
26627
+ return '';
26628
+ }
26629
+
26630
+ if (ensured_tree.kind === 'text') {
26631
+ return func.runtime.render.render_tree_terminal_content(ensured_tree) || '';
26632
+ }
26633
+
26634
+ const tag_name = func.runtime.render.get_string_renderer_tag_name(ensured_tree);
26635
+ if (!tag_name || tag_name.toLowerCase() === 'script') {
26636
+ return '';
26637
+ }
26638
+
26639
+ const attributes = func.runtime.render.get_tree_string_attributes(ensured_tree, renderer_context);
26640
+ const terminal_content = func.runtime.render.render_tree_terminal_content(ensured_tree);
26641
+ let children_html = terminal_content !== null ? terminal_content : await func.runtime.render.render_tree_children_to_string(ensured_tree, renderer_context);
26642
+
26643
+ if (ensured_tree.kind === 'widget' && !children_html) {
26644
+ children_html = func.runtime.render.get_widget_fallback_markup(ensured_tree);
26645
+ }
26646
+
26647
+ if (func.runtime.render.is_html_void_tag(tag_name)) {
26648
+ return `<${tag_name}${attributes}>`;
26649
+ }
26650
+
26651
+ return `<${tag_name}${attributes}>${children_html}</${tag_name}>`;
26652
+ };
26653
+ func.runtime.render.render_to_string = async function (options = {}) {
26654
+ const treeP = await func.runtime.render.ensure_tree_node({
26655
+ SESSION_ID: options.SESSION_ID,
26656
+ nodeP: options.treeP || options.nodeP,
26657
+ paramsP: options.paramsP,
26658
+ parent_infoP: options.parent_infoP,
26659
+ keyP: options.keyP,
26660
+ parent_nodeP: options.parent_nodeP,
26661
+ });
26662
+
26663
+ return await func.runtime.render.render_tree_to_string(treeP, options);
26664
+ };
26665
+ func.runtime.render.get_server_render_mode = function (options = {}) {
26666
+ const normalized = func.runtime.render.normalize_runtime_bootstrap({
26667
+ app_computing_mode: options.app_computing_mode,
26668
+ app_render_mode: options.app_render_mode,
26669
+ app_client_activation: options.app_client_activation,
26670
+ });
26671
+
26672
+ return normalized;
26673
+ };
26674
+ func.runtime.render.build_server_render_params = async function (options = {}) {
26675
+ const SESSION_ID = options.SESSION_ID;
26676
+ const prog_id = options.prog_id;
26677
+ const dsSessionP = options.dsSessionP;
26678
+ const _session = SESSION_OBJ?.[SESSION_ID] || {};
26679
+ const _ds = _session?.DS_GLB?.[dsSessionP] || {};
26680
+ const viewDoc = options.viewDoc || (await func.utils?.VIEWS_OBJ?.get?.(SESSION_ID, prog_id));
26681
+
26682
+ if (!viewDoc?.properties) {
26683
+ throw new Error(`view document not found for ${prog_id}`);
26684
+ }
26685
+
26686
+ const base_params = _ds?.screen_params ? func.runtime.render.safe_clone_tree_value(_ds.screen_params) : {};
26687
+ const screenId = options.screenId || base_params.screenId || `ssr_${prog_id}_${dsSessionP || '0'}`;
26688
+ const paramsP = {
26689
+ ...base_params,
26690
+ prog_id,
26691
+ sourceScreenP: null,
26692
+ $callingContainerP: null,
26693
+ triggerIdP: null,
26694
+ callingDataSource_objP: _ds,
26695
+ rowIdP: typeof options.rowIdP !== 'undefined' ? options.rowIdP : (_ds?.currentRecordId || null),
26696
+ renderType: viewDoc.properties?.renderType,
26697
+ parameters_obj_inP: options.parameters_obj_inP || base_params.parameters_obj_inP || options.parameters_raw_obj || {},
26698
+ source_functionP: options.source_functionP || base_params.source_functionP || 'render_string',
26699
+ is_panelP: false,
26700
+ screen_type: options.screen_type || base_params.screen_type || 'render_string',
26701
+ screenInfo: viewDoc,
26702
+ call_screen_propertiesP: base_params.call_screen_propertiesP,
26703
+ parentDataSourceNoP: typeof _ds?.parentDataSourceNo === 'undefined' || _ds?.parentDataSourceNo === null ? 0 : _ds.parentDataSourceNo,
26704
+ parameters_raw_obj: options.parameters_raw_obj || base_params.parameters_raw_obj || {},
26705
+ dsSessionP,
26706
+ screenId,
26707
+ containerIdP: base_params.containerIdP || `ssr_container_${screenId}`,
26708
+ };
26709
+
26710
+ if (_ds) {
26711
+ _ds.screen_params = paramsP;
26712
+ }
26713
+
26714
+ return paramsP;
26715
+ };
26716
+ func.runtime.render.build_prog_tree = async function (options = {}) {
26717
+ const SESSION_ID = options.SESSION_ID;
26718
+ const prog_id = options.prog_id;
26719
+ const viewDoc = options.viewDoc || (await func.utils?.VIEWS_OBJ?.get?.(SESSION_ID, prog_id));
26720
+
26721
+ if (!viewDoc?.progUi?.length) {
26722
+ throw new Error(`progUi not found for ${prog_id}`);
26723
+ }
26724
+
26725
+ const paramsP = options.paramsP || (await func.runtime.render.build_server_render_params({
26726
+ ...options,
26727
+ SESSION_ID,
26728
+ prog_id,
26729
+ viewDoc,
26730
+ }));
26731
+ const root_index = typeof options.root_index === 'number' ? options.root_index : 0;
26732
+ const root_node = func.runtime.render.safe_clone_tree_value(viewDoc.progUi[root_index]);
26733
+ const tree = await func.runtime.render.build_tree({
26734
+ SESSION_ID,
26735
+ nodeP: root_node,
26736
+ paramsP,
26737
+ });
26738
+
26739
+ return {
26740
+ tree,
26741
+ paramsP,
26742
+ viewDoc,
26743
+ };
26744
+ };
26745
+ func.runtime.render.build_ssr_payload = function (render_program, options = {}) {
26746
+ const runtime_profile = func.runtime.render.get_server_render_mode(options);
26747
+ return {
26748
+ contract: 'xuda.ssr.v1',
26749
+ prog_id: options.prog_id,
26750
+ screenId: render_program.paramsP.screenId,
26751
+ containerId: render_program.paramsP.containerIdP,
26752
+ app_computing_mode: runtime_profile.app_computing_mode,
26753
+ app_render_mode: runtime_profile.app_render_mode,
26754
+ app_client_activation: runtime_profile.app_client_activation,
26755
+ tree_contract: func.runtime.render.TREE_CONTRACT_VERSION,
26756
+ };
26757
+ };
26758
+ func.runtime.render.build_ssr_screen_html = function (html, render_program, options = {}) {
26759
+ const payload = func.runtime.render.build_ssr_payload(render_program, options);
26760
+ const screenId = func.runtime.render.escape_html_attribute(payload.screenId || '');
26761
+ const containerId = func.runtime.render.escape_html_attribute(payload.containerId || '');
26762
+ const activation = func.runtime.render.escape_html_attribute(payload.app_client_activation || 'takeover');
26763
+
26764
+ 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>`;
26765
+ };
26766
+ func.runtime.render.render_prog_to_string = async function (options = {}) {
26767
+ const render_program = await func.runtime.render.build_prog_tree(options);
26768
+ const html = await func.runtime.render.render_to_string({
26769
+ ...options,
26770
+ SESSION_ID: options.SESSION_ID,
26771
+ treeP: render_program.tree,
26772
+ paramsP: render_program.paramsP,
26773
+ });
26774
+ const ssr_payload = func.runtime.render.build_ssr_payload(render_program, options);
26775
+ const screen_html = func.runtime.render.build_ssr_screen_html(html, render_program, options);
26776
+
26777
+ return {
26778
+ prog_id: options.prog_id,
26779
+ dsSessionP: render_program.paramsP.dsSessionP,
26780
+ screenId: render_program.paramsP.screenId,
26781
+ html,
26782
+ screen_html,
26783
+ tree_json: func.runtime.render.serialize_tree(render_program.tree),
26784
+ paramsP: render_program.paramsP,
26785
+ ssr_payload,
26786
+ };
26787
+ };
25771
26788
  func.runtime = func.runtime || {};
25772
26789
  func.runtime.platform = func.runtime.platform || {};
25773
26790
 
@@ -26005,6 +27022,13 @@ func.utils.DOCS_OBJ = {};
26005
27022
  func.utils.DOCS_OBJ.get = async function (SESSION_ID, idP) {
26006
27023
  if (!idP || idP === '0') return;
26007
27024
 
27025
+ const normalize_runtime_doc = function (doc) {
27026
+ if (!doc || !func.runtime.program?.normalize_doc_for_runtime) {
27027
+ return doc;
27028
+ }
27029
+ return func.runtime.program.normalize_doc_for_runtime(doc);
27030
+ };
27031
+
26008
27032
  var _session = SESSION_OBJ[SESSION_ID];
26009
27033
  const _app_id = _session.app_id;
26010
27034
  if (!DOCS_OBJ[_app_id]) {
@@ -26026,7 +27050,8 @@ func.utils.DOCS_OBJ.get = async function (SESSION_ID, idP) {
26026
27050
  }
26027
27051
  let val = _session.project_data?.programs?.[idP];
26028
27052
  if (val) {
26029
- return (DOCS_OBJ[_app_id][idP] = val);
27053
+ DOCS_OBJ[_app_id][idP] = normalize_runtime_doc(val);
27054
+ return DOCS_OBJ[_app_id][idP];
26030
27055
  }
26031
27056
  }
26032
27057
 
@@ -26034,7 +27059,7 @@ func.utils.DOCS_OBJ.get = async function (SESSION_ID, idP) {
26034
27059
  const module = await func.common.get_module(SESSION_ID, `xuda-progs-loader-module.mjs`);
26035
27060
 
26036
27061
  if (idP !== 'system') {
26037
- DOCS_OBJ[_app_id][idP] = await module.DOCS_OBJ_get(SESSION_ID, idP);
27062
+ DOCS_OBJ[_app_id][idP] = normalize_runtime_doc(await module.DOCS_OBJ_get(SESSION_ID, idP));
26038
27063
  if (DOCS_OBJ[_app_id][idP] && xu_isEmpty(DOCS_OBJ[_app_id][idP])) {
26039
27064
  await func.utils.remove_cached_objects(SESSION_ID);
26040
27065
 
@@ -28105,6 +29130,15 @@ func.runtime.ui.ensure_embed_container = function (SESSION_ID) {
28105
29130
  const $root_element = func.runtime.ui.get_root_element(SESSION_ID);
28106
29131
  let $embed_container = func.runtime.ui.find_by_selector($root_element, `#embed_${SESSION_ID}`, true);
28107
29132
 
29133
+ if (!$embed_container.length) {
29134
+ const $ssr_embed_container = func.runtime.ui.find_by_selector($root_element, `[data-xuda-ssr-embed="true"]`, true);
29135
+ const ssr_embed_node = func.runtime.ui.get_first_node($ssr_embed_container);
29136
+ if (ssr_embed_node) {
29137
+ ssr_embed_node.id = 'embed_' + SESSION_ID;
29138
+ $embed_container = func.runtime.ui._wrap_matches([ssr_embed_node]);
29139
+ }
29140
+ }
29141
+
28108
29142
  if (!$embed_container.length) {
28109
29143
  const embed_node = document.createElement('div');
28110
29144
  embed_node.id = 'embed_' + SESSION_ID;
@@ -28140,12 +29174,44 @@ func.runtime.ui.get_root_tag_name = function () {
28140
29174
  }
28141
29175
  return root_tag_name;
28142
29176
  };
29177
+ func.runtime.ui.find_ssr_screen_host = function ($container, screenId, containerId) {
29178
+ const container_node = func.runtime.ui.get_first_node($container);
29179
+ if (!container_node || !screenId) {
29180
+ return null;
29181
+ }
29182
+
29183
+ const dialog_node =
29184
+ container_node.querySelector?.(`#${CSS?.escape ? CSS.escape(screenId) : screenId}`) ||
29185
+ container_node.querySelector?.(`[data-xuda-ssr-screen-id="${screenId}"]`);
29186
+ if (!dialog_node) {
29187
+ return null;
29188
+ }
29189
+
29190
+ const root_frame_node =
29191
+ (containerId ? dialog_node.querySelector?.(`#${CSS?.escape ? CSS.escape(containerId) : containerId}`) : null) ||
29192
+ dialog_node.querySelector?.('[data-xuda-ssr-root-frame="true"]');
29193
+ if (!root_frame_node) {
29194
+ return null;
29195
+ }
29196
+
29197
+ return {
29198
+ $dialogDiv: func.runtime.ui._wrap_matches([dialog_node]),
29199
+ $rootFrame: func.runtime.ui._wrap_matches([root_frame_node]),
29200
+ reused_ssr_host: true,
29201
+ };
29202
+ };
28143
29203
  func.runtime.ui.create_screen_host = function (SESSION_ID, screen_type, params, $callingContainerP, screenId) {
28144
29204
  var $dialogDiv;
28145
29205
  var $rootFrame;
29206
+ let reused_ssr_host = false;
28146
29207
 
28147
29208
  switch (screen_type) {
28148
29209
  case 'embed': {
29210
+ const ssr_host = func.runtime.ui.find_ssr_screen_host($callingContainerP, screenId, params?.containerIdP);
29211
+ if (ssr_host) {
29212
+ return ssr_host;
29213
+ }
29214
+
28149
29215
  const dialogNode = document.createElement('div');
28150
29216
  dialogNode.id = screenId;
28151
29217
  dialogNode.setAttribute('ui_engine', UI_FRAMEWORK_INSTALLED);
@@ -28195,6 +29261,7 @@ func.runtime.ui.create_screen_host = function (SESSION_ID, screen_type, params,
28195
29261
  return {
28196
29262
  $dialogDiv,
28197
29263
  $rootFrame,
29264
+ reused_ssr_host,
28198
29265
  };
28199
29266
  };
28200
29267
  func.runtime.ui.find_xu_ui_in_root = function (SESSION_ID, xu_ui_id) {
@@ -28759,6 +29826,11 @@ func.runtime.ui.build_container_xu_data = function (options) {
28759
29826
  func.runtime.ui.apply_container_meta = function ($div, options) {
28760
29827
  const div_node = func.runtime.ui.get_first_node($div);
28761
29828
  func.runtime.ui.set_attr(div_node, 'xu-ui-id', options.ui_id);
29829
+ func.runtime.ui.set_attr(div_node, 'data-xuda-kind', options.treeP?.kind || options.nodeP?.tagName || 'element');
29830
+ func.runtime.ui.set_attr(div_node, 'data-xuda-node-id', options.nodeP?.id || options.nodeP?.id_org || '');
29831
+ if (options.treeP?.meta?.tree_id !== null && typeof options.treeP?.meta?.tree_id !== 'undefined') {
29832
+ func.runtime.ui.set_attr(div_node, 'data-xuda-tree-id', options.treeP.meta.tree_id);
29833
+ }
28762
29834
  const xuData = func.runtime.ui.build_container_xu_data(options);
28763
29835
  if (options.parent_infoP?.iterate_info) {
28764
29836
  xuData.iterate_info = options.parent_infoP.iterate_info;
@@ -28853,6 +29925,38 @@ func.runtime.ui.create_container_element = function (div_typeP, attr_str, prop,
28853
29925
  }
28854
29926
  return func.runtime.ui.create_element(div, attr_str);
28855
29927
  };
29928
+ func.runtime.ui.find_hydration_candidate = function (options) {
29929
+ if (!func.runtime.render.is_hydration_mode(SESSION_OBJ?.[options.SESSION_ID])) {
29930
+ return null;
29931
+ }
29932
+ if (!func.runtime.render.should_use_ssr_payload(options.SESSION_ID, options.paramsP)) {
29933
+ return null;
29934
+ }
29935
+ if (options.is_placeholder || !options.treeP?.meta?.tree_id) {
29936
+ return null;
29937
+ }
29938
+
29939
+ const append_node = func.runtime.ui.get_first_node(options.$appendTo || options.$container);
29940
+ if (!append_node) {
29941
+ return null;
29942
+ }
29943
+
29944
+ const children = func.runtime.ui.get_children(append_node);
29945
+ for (let index = 0; index < children.length; index++) {
29946
+ const child = children[index];
29947
+ if (child?.__xuda_hydration_claimed) {
29948
+ continue;
29949
+ }
29950
+ if (func.runtime.ui.get_attr(child, 'data-xuda-tree-id') !== `${options.treeP.meta.tree_id}`) {
29951
+ continue;
29952
+ }
29953
+ child.__xuda_hydration_claimed = true;
29954
+ func.runtime.ui.set_attr(child, 'data-xuda-client-activation', 'hydrate');
29955
+ return child;
29956
+ }
29957
+
29958
+ return null;
29959
+ };
28856
29960
  func.runtime.ui.build_xu_ui_id_seed = function (nodeP, dsSessionP, key_path, currentRecordId) {
28857
29961
  const nodeId = nodeP.xu_tree_id || nodeP.id;
28858
29962
  const elem_key = `${nodeId}-${key_path}-${currentRecordId}`;
@@ -28905,7 +30009,11 @@ func.runtime.ui.create_container = async function (options) {
28905
30009
  try {
28906
30010
  const key_path = func.runtime.ui.build_container_key_path(container_xu_data, options.keyP, options.parent_infoP, options.nodeP, options.parent_nodeP);
28907
30011
  const elem_key = `${options.nodeP.xu_tree_id || options.nodeP.id}-${key_path}-${currentRecordId}`;
28908
- const $div = func.runtime.ui.create_container_element(options.div_typeP, options.attr_str, options.prop, options.nodeP, $appendTo);
30012
+ const hydration_candidate = func.runtime.ui.find_hydration_candidate({
30013
+ ...options,
30014
+ $appendTo,
30015
+ });
30016
+ const $div = hydration_candidate || func.runtime.ui.create_container_element(options.div_typeP, options.attr_str, options.prop, options.nodeP, $appendTo);
28909
30017
  const new_ui_id = await func.runtime.ui.generate_xu_ui_id(options.SESSION_ID, options.nodeP, options.$container, options.paramsP, options.keyP, {
28910
30018
  container_xu_data,
28911
30019
  currentRecordId,
@@ -28931,9 +30039,10 @@ func.runtime.ui.create_container = async function (options) {
28931
30039
  parent_infoP: options.parent_infoP,
28932
30040
  is_placeholder: options.is_placeholder,
28933
30041
  classP: options.classP,
30042
+ treeP: options.treeP,
28934
30043
  });
28935
30044
 
28936
- if (options.div_typeP !== 'svg') {
30045
+ if (!hydration_candidate && options.div_typeP !== 'svg') {
28937
30046
  func.runtime.ui.append_to($div, $appendTo);
28938
30047
  }
28939
30048
  return $div;
@@ -31614,9 +32723,10 @@ func.runtime.ui.init_screen = async function (options) {
31614
32723
 
31615
32724
  const _session = SESSION_OBJ[SESSION_ID];
31616
32725
  const screenInfo = structuredClone(screen_ret);
32726
+ const ssr_payload = func.runtime.render.should_use_ssr_payload(SESSION_ID, { prog_id }) ? func.runtime.render.get_ssr_payload(_session) : null;
31617
32727
 
31618
32728
  const screen_type = source_functionP?.split('_')?.[1];
31619
- const screenId = (glb.screen_num++).toString();
32729
+ const screenId = ssr_payload?.screenId || (glb.screen_num++).toString();
31620
32730
 
31621
32731
  if (SCREEN_BLOCKER_OBJ[prog_id + (sourceScreenP ? '_' + sourceScreenP : '')]) {
31622
32732
  const wait_for_SCREEN_BLOCKER_release = function () {
@@ -31658,6 +32768,8 @@ func.runtime.ui.init_screen = async function (options) {
31658
32768
  call_screen_propertiesP,
31659
32769
  parentDataSourceNoP: _session.DS_GLB?.[callingDataSource_objP?.dsSession]?.dsSession || callingDataSource_objP?.parentDataSourceNo || 0,
31660
32770
  parameters_raw_obj,
32771
+ containerIdP: ssr_payload?.containerId || null,
32772
+ ssr_payload,
31661
32773
  };
31662
32774
 
31663
32775
  const screen_host = func.runtime.ui.create_screen_host(SESSION_ID, screen_type, params, $callingContainerP, screenId);
@@ -31690,6 +32802,10 @@ func.runtime.ui.init_screen = async function (options) {
31690
32802
  func.runtime.ui.set_style($rootFrame, 'display', 'contents');
31691
32803
  }
31692
32804
 
32805
+ if (screen_host.reused_ssr_host && func.runtime.render.is_takeover_mode(_session)) {
32806
+ func.runtime.ui.empty($rootFrame);
32807
+ }
32808
+
31693
32809
  if (!is_panelP) func.UI.utils.indicator.screen.busy();
31694
32810
 
31695
32811
  const ret = await func.datasource.create(
@@ -31731,7 +32847,24 @@ func.runtime.ui.init_screen = async function (options) {
31731
32847
  }
31732
32848
  let node = structuredClone(viewDoc.progUi);
31733
32849
  if (!node.length) return console.warn('ui node empty');
31734
- 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);
32850
+ const root_tree = await func.runtime.render.build_tree({
32851
+ SESSION_ID,
32852
+ nodeP: node[0],
32853
+ paramsP: params,
32854
+ });
32855
+ const ret_render_$container = await func.runtime.render.render_tree(root_tree, {
32856
+ SESSION_ID,
32857
+ $container: $rootFrame,
32858
+ parent_infoP: null,
32859
+ paramsP: params,
32860
+ jobNoP,
32861
+ is_skeleton: null,
32862
+ keyP: null,
32863
+ refreshed_ds: null,
32864
+ parent_nodeP: null,
32865
+ check_existP: null,
32866
+ $root_container: $rootFrame,
32867
+ });
31735
32868
 
31736
32869
  if (!is_panelP) func.UI.utils.indicator.screen.normal();
31737
32870
 
@@ -32156,6 +33289,7 @@ func.runtime.ui.render_single_view_node = async function (options) {
32156
33289
  parent_infoP: options.parent_infoP,
32157
33290
  jobNoP: options.jobNoP,
32158
33291
  keyP: options.keyP,
33292
+ treeP: options.treeP,
32159
33293
  parent_nodeP: options.parent_nodeP,
32160
33294
  prop: options.prop,
32161
33295
  div_typeP: 'div',
@@ -32284,6 +33418,7 @@ func.runtime.ui.render_panel_node = async function (options) {
32284
33418
  parent_infoP: options.parent_infoP,
32285
33419
  jobNoP: options.jobNoP,
32286
33420
  keyP: options.keyP,
33421
+ treeP: options.treeP,
32287
33422
  parent_nodeP: options.parent_nodeP,
32288
33423
  prop: options.prop,
32289
33424
  $appendToP: $wrapper,
@@ -32613,6 +33748,14 @@ func.runtime.ui.screen_loading_done = async function (options) {
32613
33748
  });
32614
33749
 
32615
33750
  const _session = SESSION_OBJ[options.SESSION_ID];
33751
+ if (func.runtime.render.should_use_ssr_payload(options.SESSION_ID, options.paramsP)) {
33752
+ const root_node = func.runtime.ui.get_root_node(options.SESSION_ID);
33753
+ if (root_node) {
33754
+ func.runtime.ui.set_attr(root_node, 'data-xuda-client-activation', _session.opt.app_client_activation || 'none');
33755
+ func.runtime.ui.set_attr(root_node, 'data-xuda-ssr-status', _session.opt.app_client_activation === 'hydrate' ? 'hydrated' : 'taken-over');
33756
+ }
33757
+ func.runtime.render.mark_ssr_payload_consumed(options.SESSION_ID);
33758
+ }
32616
33759
  func.events.delete_job(options.SESSION_ID, options.jobNoP);
32617
33760
  func.UI.utils.screen_blocker(false, options.paramsP.prog_id + (options.paramsP.sourceScreenP ? '_' + options.paramsP.sourceScreenP : ''));
32618
33761
  if (_session.prog_id === options.paramsP.prog_id) {
@@ -34455,6 +35598,9 @@ func.runtime.render.get_screen_context = function (SESSION_ID, $container, param
34455
35598
  };
34456
35599
  func.runtime.render.get_node_attributes = function (nodeP) {
34457
35600
  try {
35601
+ if (func.runtime.render.is_tree_node?.(nodeP)) {
35602
+ return nodeP.attributes;
35603
+ }
34458
35604
  return nodeP?.attributes;
34459
35605
  } catch (error) {
34460
35606
  return undefined;
@@ -34572,6 +35718,7 @@ func.runtime.render.prepare_draw_context = async function (options) {
34572
35718
  parent_infoP: options.parent_infoP,
34573
35719
  jobNoP: options.jobNoP,
34574
35720
  keyP: options.keyP,
35721
+ treeP: options.treeP,
34575
35722
  parent_nodeP: options.parent_nodeP,
34576
35723
  prop: options.prop,
34577
35724
  div_typeP: options.element,
@@ -34594,6 +35741,7 @@ func.runtime.render.prepare_draw_context = async function (options) {
34594
35741
  parent_infoP: options.parent_infoP,
34595
35742
  jobNoP: options.jobNoP,
34596
35743
  keyP: options.keyP,
35744
+ treeP: options.treeP,
34597
35745
  parent_nodeP: options.parent_nodeP,
34598
35746
  prop: options.prop,
34599
35747
  div_typeP: options.element,
@@ -35450,11 +36598,16 @@ func.runtime.widgets.render_node = async function (options) {
35450
36598
  parent_infoP: options.parent_infoP,
35451
36599
  jobNoP: options.jobNoP,
35452
36600
  keyP: options.keyP,
36601
+ treeP: options.treeP,
35453
36602
  parent_nodeP: options.parent_nodeP,
35454
36603
  prop: options.prop,
35455
36604
  classP: 'widget_wrapper',
35456
36605
  });
35457
36606
 
36607
+ if (func.runtime.render.is_hydration_mode(SESSION_OBJ?.[options.SESSION_ID]) && func.runtime.render.should_use_ssr_payload(options.SESSION_ID, options.paramsP)) {
36608
+ func.runtime.ui.empty($div);
36609
+ }
36610
+
35458
36611
  const widget_context = func.runtime.widgets.create_context(options.SESSION_ID, options.paramsP, options.prop);
35459
36612
  const { plugin_name, method, propsP, plugin: _plugin } = widget_context;
35460
36613
  const report_error = function (descP, warn) {
@@ -35557,9 +36710,370 @@ func.runtime.widgets = func.runtime.widgets || {};
35557
36710
 
35558
36711
  // Browser-only special node renderers live here so the core render tree can stay focused.
35559
36712
 
36713
+ const normalize_runtime_tag_name = function (tag_name) {
36714
+ return `${tag_name || ''}`.trim().toLowerCase();
36715
+ };
36716
+
36717
+ const get_runtime_node_attributes = function (nodeP) {
36718
+ if (!nodeP?.attributes || typeof nodeP.attributes !== 'object') {
36719
+ return {};
36720
+ }
36721
+
36722
+ return nodeP.attributes;
36723
+ };
36724
+
36725
+ const get_runtime_node_content = function (nodeP) {
36726
+ if (typeof nodeP?.content === 'string') {
36727
+ return nodeP.content;
36728
+ }
36729
+
36730
+ if (typeof nodeP?.text === 'string') {
36731
+ return nodeP.text;
36732
+ }
36733
+
36734
+ if (!Array.isArray(nodeP?.children)) {
36735
+ return '';
36736
+ }
36737
+
36738
+ return nodeP.children
36739
+ .map(function (child) {
36740
+ if (typeof child === 'string') {
36741
+ return child;
36742
+ }
36743
+ if (typeof child?.content === 'string') {
36744
+ return child.content;
36745
+ }
36746
+ if (typeof child?.text === 'string') {
36747
+ return child.text;
36748
+ }
36749
+ return '';
36750
+ })
36751
+ .join('');
36752
+ };
36753
+
36754
+ const get_runtime_asset_key = function (options, tag_name) {
36755
+ const source_node = options.treeP || options.nodeP || {};
36756
+ const parts = [
36757
+ 'xuda-html-asset',
36758
+ options.paramsP?.prog_id || '',
36759
+ tag_name,
36760
+ source_node.id || source_node.id_org || '',
36761
+ typeof options.keyP === 'undefined' || options.keyP === null ? '' : `${options.keyP}`,
36762
+ ].filter(function (part) {
36763
+ return `${part || ''}`.trim() !== '';
36764
+ });
36765
+
36766
+ return parts.join(':');
36767
+ };
36768
+
36769
+ const get_runtime_asset_signature = function (attributes, content) {
36770
+ const normalized_attributes = {};
36771
+ const attr_keys = Object.keys(attributes || {}).sort();
36772
+
36773
+ for (let index = 0; index < attr_keys.length; index++) {
36774
+ const key = attr_keys[index];
36775
+ normalized_attributes[key] = attributes[key];
36776
+ }
36777
+
36778
+ return JSON.stringify({
36779
+ attributes: normalized_attributes,
36780
+ content: `${content || ''}`,
36781
+ });
36782
+ };
36783
+
36784
+ const escape_runtime_asset_selector_value = function (value) {
36785
+ const win = func.runtime.platform.get_window?.();
36786
+ if (win?.CSS?.escape) {
36787
+ return win.CSS.escape(value);
36788
+ }
36789
+
36790
+ return `${value || ''}`.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
36791
+ };
36792
+
36793
+ const find_runtime_head_asset = function (head, tag_name, asset_key) {
36794
+ if (!head?.querySelector || !asset_key) {
36795
+ return null;
36796
+ }
36797
+
36798
+ return head.querySelector(`${tag_name}[data-xuda-asset-key="${escape_runtime_asset_selector_value(asset_key)}"]`);
36799
+ };
36800
+
36801
+ const find_runtime_head_asset_by_attr = function (head, tag_name, attr_name, attr_value) {
36802
+ if (!head?.querySelectorAll || !attr_name || !attr_value) {
36803
+ return null;
36804
+ }
36805
+
36806
+ const candidates = Array.from(head.querySelectorAll(tag_name));
36807
+ return (
36808
+ candidates.find(function (candidate) {
36809
+ return candidate.getAttribute(attr_name) === attr_value || candidate[attr_name] === attr_value;
36810
+ }) || null
36811
+ );
36812
+ };
36813
+
36814
+ const wait_for_runtime_asset_load = function (node) {
36815
+ if (!node?.addEventListener) {
36816
+ return Promise.resolve(node);
36817
+ }
36818
+
36819
+ if (node.getAttribute?.('data-xuda-loaded') === 'true') {
36820
+ return Promise.resolve(node);
36821
+ }
36822
+
36823
+ if (!node.getAttribute?.('data-xuda-asset-key')) {
36824
+ return Promise.resolve(node);
36825
+ }
36826
+
36827
+ const tag_name = normalize_runtime_tag_name(node.tagName);
36828
+ if (tag_name !== 'script' && !(tag_name === 'link' && normalize_runtime_tag_name(node.getAttribute?.('rel')) === 'stylesheet')) {
36829
+ return Promise.resolve(node);
36830
+ }
36831
+
36832
+ return new Promise(function (resolve) {
36833
+ const done = function () {
36834
+ node.setAttribute?.('data-xuda-loaded', 'true');
36835
+ resolve(node);
36836
+ };
36837
+
36838
+ node.addEventListener('load', done, { once: true });
36839
+ node.addEventListener('error', done, { once: true });
36840
+ });
36841
+ };
36842
+
36843
+ const remove_runtime_head_asset = function (node) {
36844
+ if (node?.parentNode?.removeChild) {
36845
+ node.parentNode.removeChild(node);
36846
+ }
36847
+ };
36848
+
36849
+ const apply_runtime_asset_metadata = function (node, asset_key, signature) {
36850
+ if (!node?.setAttribute) {
36851
+ return node;
36852
+ }
36853
+
36854
+ if (asset_key) {
36855
+ node.setAttribute('data-xuda-asset-key', asset_key);
36856
+ }
36857
+ node.setAttribute('data-xuda-asset-signature', signature);
36858
+ return node;
36859
+ };
36860
+
36861
+ const create_runtime_head_element = function (doc, tag_name, attributes, asset_key, signature) {
36862
+ const node = doc.createElement(tag_name);
36863
+ apply_runtime_asset_metadata(node, asset_key, signature);
36864
+ func.runtime.platform.apply_element_attributes(node, attributes);
36865
+ return node;
36866
+ };
36867
+
36868
+ const queue_runtime_html_script_task = function (task) {
36869
+ const queue =
36870
+ func.runtime.render._html_script_queue ||
36871
+ (func.runtime.render._html_script_queue = {
36872
+ items: [],
36873
+ scheduled: false,
36874
+ running: false,
36875
+ });
36876
+
36877
+ queue.items.push(task);
36878
+
36879
+ if (queue.scheduled || queue.running) {
36880
+ return;
36881
+ }
36882
+
36883
+ queue.scheduled = true;
36884
+
36885
+ const drain_queue = async function () {
36886
+ if (queue.running) {
36887
+ return;
36888
+ }
36889
+
36890
+ queue.running = true;
36891
+ queue.scheduled = false;
36892
+
36893
+ try {
36894
+ while (queue.items.length) {
36895
+ const next_task = queue.items.shift();
36896
+ await next_task();
36897
+ }
36898
+ } catch (error) {
36899
+ console.error(error);
36900
+ } finally {
36901
+ queue.running = false;
36902
+ if (queue.items.length && !queue.scheduled) {
36903
+ queue.scheduled = true;
36904
+ schedule_runtime_html_script_queue();
36905
+ }
36906
+ }
36907
+ };
36908
+
36909
+ const schedule_runtime_html_script_queue = function () {
36910
+ const win = func.runtime.platform.get_window?.();
36911
+ const run_later = function () {
36912
+ setTimeout(function () {
36913
+ drain_queue();
36914
+ }, 0);
36915
+ };
36916
+
36917
+ if (win?.requestAnimationFrame) {
36918
+ win.requestAnimationFrame(function () {
36919
+ win.requestAnimationFrame(run_later);
36920
+ });
36921
+ return;
36922
+ }
36923
+
36924
+ run_later();
36925
+ };
36926
+
36927
+ schedule_runtime_html_script_queue();
36928
+ };
36929
+
36930
+ const upsert_runtime_head_element = async function (options) {
36931
+ const doc = func.runtime.platform.get_document?.();
36932
+ const head = doc?.head;
36933
+ const tag_name = normalize_runtime_tag_name(options.tag_name);
36934
+
36935
+ if (!doc?.createElement || !head?.appendChild || !tag_name) {
36936
+ return null;
36937
+ }
36938
+
36939
+ const asset_key = options.asset_key || '';
36940
+ const signature = options.signature || '';
36941
+ const attributes = options.attributes || {};
36942
+ const content = typeof options.content === 'string' ? options.content : '';
36943
+
36944
+ const existing_by_key = find_runtime_head_asset(head, tag_name, asset_key);
36945
+ if (existing_by_key && existing_by_key.getAttribute('data-xuda-asset-signature') === signature) {
36946
+ return options.await_load ? await wait_for_runtime_asset_load(existing_by_key) : existing_by_key;
36947
+ }
36948
+
36949
+ if (existing_by_key) {
36950
+ remove_runtime_head_asset(existing_by_key);
36951
+ }
36952
+
36953
+ if (options.find_existing_attr?.name && options.find_existing_attr?.value) {
36954
+ const existing_by_attr = find_runtime_head_asset_by_attr(head, tag_name, options.find_existing_attr.name, options.find_existing_attr.value);
36955
+ if (existing_by_attr) {
36956
+ apply_runtime_asset_metadata(existing_by_attr, asset_key, signature);
36957
+ existing_by_attr.setAttribute?.('data-xuda-loaded', 'true');
36958
+ return existing_by_attr;
36959
+ }
36960
+ }
36961
+
36962
+ const node = create_runtime_head_element(doc, tag_name, attributes, asset_key, signature);
36963
+
36964
+ if (tag_name === 'script' && attributes.src && !Object.prototype.hasOwnProperty.call(attributes, 'async')) {
36965
+ const script_type = normalize_runtime_tag_name(attributes.type);
36966
+ if (script_type !== 'module') {
36967
+ node.async = false;
36968
+ }
36969
+ }
36970
+
36971
+ if (tag_name === 'style' || (tag_name === 'script' && !attributes.src)) {
36972
+ node.textContent = content;
36973
+ }
36974
+
36975
+ head.appendChild(node);
36976
+
36977
+ if (tag_name !== 'script' && tag_name !== 'link') {
36978
+ node.setAttribute('data-xuda-loaded', 'true');
36979
+ }
36980
+
36981
+ return options.await_load ? await wait_for_runtime_asset_load(node) : node;
36982
+ };
36983
+
36984
+ const render_runtime_html_asset = async function (options) {
36985
+ if (options.is_skeleton) {
36986
+ return options.$container;
36987
+ }
36988
+
36989
+ const nodeP = options.treeP || options.nodeP || {};
36990
+ const tag_name = normalize_runtime_tag_name(nodeP.tagName);
36991
+ const attributes = { ...get_runtime_node_attributes(nodeP) };
36992
+ const content = get_runtime_node_content(nodeP);
36993
+ const asset_key = get_runtime_asset_key(options, tag_name);
36994
+ const signature = get_runtime_asset_signature(attributes, content);
36995
+
36996
+ switch (tag_name) {
36997
+ case 'title':
36998
+ func.runtime.platform.set_title(content);
36999
+ return options.$container;
37000
+ case 'style':
37001
+ await upsert_runtime_head_element({
37002
+ tag_name,
37003
+ attributes,
37004
+ content,
37005
+ asset_key,
37006
+ signature,
37007
+ });
37008
+ return options.$container;
37009
+ case 'meta':
37010
+ if (!Object.keys(attributes).length) {
37011
+ return options.$container;
37012
+ }
37013
+ await upsert_runtime_head_element({
37014
+ tag_name,
37015
+ attributes,
37016
+ asset_key,
37017
+ signature,
37018
+ });
37019
+ return options.$container;
37020
+ case 'link': {
37021
+ const href = `${attributes.href || ''}`.trim();
37022
+ if (!href) {
37023
+ return options.$container;
37024
+ }
37025
+ await upsert_runtime_head_element({
37026
+ tag_name,
37027
+ attributes,
37028
+ asset_key,
37029
+ signature,
37030
+ find_existing_attr: {
37031
+ name: 'href',
37032
+ value: href,
37033
+ },
37034
+ await_load: normalize_runtime_tag_name(attributes.rel) === 'stylesheet',
37035
+ });
37036
+ return options.$container;
37037
+ }
37038
+ case 'script': {
37039
+ const src = `${attributes.src || ''}`.trim();
37040
+ if (!src && !content.trim()) {
37041
+ return options.$container;
37042
+ }
37043
+ queue_runtime_html_script_task(async function () {
37044
+ await upsert_runtime_head_element({
37045
+ tag_name,
37046
+ attributes,
37047
+ content,
37048
+ asset_key,
37049
+ signature,
37050
+ find_existing_attr: src
37051
+ ? {
37052
+ name: 'src',
37053
+ value: src,
37054
+ }
37055
+ : null,
37056
+ await_load: !!src,
37057
+ });
37058
+ });
37059
+ return options.$container;
37060
+ }
37061
+ default:
37062
+ return null;
37063
+ }
37064
+ };
37065
+
35560
37066
  func.runtime.render.render_special_node = async function (options) {
35561
- if (options.nodeP.content && options.nodeP.attributes) {
35562
- options.nodeP.attributes['xu-content'] = options.nodeP.content;
37067
+ const treeP = options.treeP || null;
37068
+ const nodeP = options.nodeP || func.runtime.render.get_tree_source_node(treeP);
37069
+ const render_tag_name = treeP?.tagName || nodeP?.tagName;
37070
+ const normalized_render_tag_name = normalize_runtime_tag_name(render_tag_name);
37071
+ const is_native_html_asset = ['title', 'style', 'meta', 'link', 'script'].includes(normalized_render_tag_name);
37072
+
37073
+ if (!is_native_html_asset && treeP?.content && nodeP?.attributes) {
37074
+ nodeP.attributes['xu-content'] = treeP.content;
37075
+ } else if (!is_native_html_asset && nodeP?.content && nodeP.attributes) {
37076
+ nodeP.attributes['xu-content'] = nodeP.content;
35563
37077
  }
35564
37078
 
35565
37079
  const renderers = {
@@ -35569,6 +37083,7 @@ func.runtime.render.render_special_node = async function (options) {
35569
37083
  SESSION_ID: options.SESSION_ID,
35570
37084
  $container: options.$container,
35571
37085
  $root_container: options.$root_container,
37086
+ treeP,
35572
37087
  nodeP: options.nodeP,
35573
37088
  parent_infoP: options.parent_infoP,
35574
37089
  paramsP: options.paramsP,
@@ -35585,6 +37100,7 @@ func.runtime.render.render_special_node = async function (options) {
35585
37100
  SESSION_ID: options.SESSION_ID,
35586
37101
  $container: options.$container,
35587
37102
  $root_container: options.$root_container,
37103
+ treeP,
35588
37104
  nodeP: options.nodeP,
35589
37105
  parent_infoP: options.parent_infoP,
35590
37106
  paramsP: options.paramsP,
@@ -35604,6 +37120,7 @@ func.runtime.render.render_special_node = async function (options) {
35604
37120
  SESSION_ID: options.SESSION_ID,
35605
37121
  $container: options.$container,
35606
37122
  $root_container: options.$root_container,
37123
+ treeP,
35607
37124
  nodeP: options.nodeP,
35608
37125
  parent_infoP: options.parent_infoP,
35609
37126
  paramsP: options.paramsP,
@@ -35620,6 +37137,7 @@ func.runtime.render.render_special_node = async function (options) {
35620
37137
  SESSION_ID: options.SESSION_ID,
35621
37138
  $container: options.$container,
35622
37139
  $root_container: options.$root_container,
37140
+ treeP,
35623
37141
  nodeP: options.nodeP,
35624
37142
  parent_infoP: options.parent_infoP,
35625
37143
  paramsP: options.paramsP,
@@ -35631,9 +37149,24 @@ func.runtime.render.render_special_node = async function (options) {
35631
37149
  refreshed_ds: options.refreshed_ds,
35632
37150
  });
35633
37151
  },
37152
+ title: async function () {
37153
+ return await render_runtime_html_asset(options);
37154
+ },
37155
+ style: async function () {
37156
+ return await render_runtime_html_asset(options);
37157
+ },
37158
+ meta: async function () {
37159
+ return await render_runtime_html_asset(options);
37160
+ },
37161
+ link: async function () {
37162
+ return await render_runtime_html_asset(options);
37163
+ },
37164
+ script: async function () {
37165
+ return await render_runtime_html_asset(options);
37166
+ },
35634
37167
  };
35635
37168
 
35636
- const renderer = renderers[options.nodeP.tagName];
37169
+ const renderer = renderers[normalized_render_tag_name];
35637
37170
  if (!renderer) {
35638
37171
  return { handled: false };
35639
37172
  }
@@ -35747,8 +37280,9 @@ func.runtime.widgets = func.runtime.widgets || {};
35747
37280
  // Browser-only render tree entrypoints live here so draw/cache helpers can stay focused.
35748
37281
 
35749
37282
  func.runtime.render.create_tree_runtime = function (options) {
37283
+ const render_node = options.nodeP || func.runtime.render.get_tree_source_node(options.treeP);
35750
37284
  const render_context = func.runtime.render.get_screen_context(options.SESSION_ID, options.$container, options.paramsP, options.is_skeleton);
35751
- const prop = func.runtime.render.get_node_attributes(options.nodeP);
37285
+ const prop = func.runtime.render.get_node_attributes(options.treeP || render_node);
35752
37286
  const is_mobile = render_context.is_mobile ? true : false;
35753
37287
  const hover_handlers = func.runtime.render.create_hover_handlers({
35754
37288
  SESSION_ID: options.SESSION_ID,
@@ -35763,13 +37297,22 @@ func.runtime.render.create_tree_runtime = function (options) {
35763
37297
  return await func.runtime.ui.close_modal_session(options.SESSION_ID, modal_id);
35764
37298
  };
35765
37299
  const iterate_child = async function ($divP, nodeP, parent_infoP, $root_container, before_record_function) {
37300
+ const child_tree = await func.runtime.render.ensure_tree_node({
37301
+ SESSION_ID: options.SESSION_ID,
37302
+ nodeP: nodeP || options.treeP || render_node,
37303
+ parent_infoP,
37304
+ paramsP: options.paramsP,
37305
+ keyP: options.keyP,
37306
+ parent_nodeP: render_node,
37307
+ pathP: options.treeP?.meta?.path || [],
37308
+ });
35766
37309
  return await func.runtime.render.iterate_children({
35767
37310
  $divP,
35768
- nodeP,
37311
+ nodeP: child_tree,
35769
37312
  is_mobile,
35770
37313
  before_record_function,
35771
37314
  render_child: async function (key, child) {
35772
- await options.render_child($divP, child, parent_infoP, key, nodeP, $root_container);
37315
+ await options.render_child($divP, child, parent_infoP, key, render_node, $root_container);
35773
37316
  },
35774
37317
  });
35775
37318
  };
@@ -35799,7 +37342,7 @@ func.runtime.render.draw_node = async function (options) {
35799
37342
  check_existP: options.check_existP,
35800
37343
  $root_container: options.$root_container,
35801
37344
  prop: options.prop,
35802
- element: options.nodeP.tagName,
37345
+ element: options.treeP?.tagName || options.nodeP.tagName,
35803
37346
  hover_handlers: options.hover_handlers,
35804
37347
  include_hover_click: options.include_hover_click,
35805
37348
  iterate_child: options.iterate_child,
@@ -35822,21 +37365,33 @@ func.runtime.render.draw_node = async function (options) {
35822
37365
  nodeP: options.nodeP,
35823
37366
  });
35824
37367
  };
35825
- 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) {
35826
- if (!nodeP) return;
35827
- const perf_end = func.runtime?.perf?.start?.(SESSION_ID, 'render_ui_tree');
35828
- func.runtime?.perf?.increment_map?.(SESSION_ID, 'render_node_counts', nodeP.id || nodeP.id_org || nodeP.tagName || 'unknown');
37368
+ func.runtime.render.render_tree = async function (treeP, renderer_context) {
37369
+ if (!treeP) return;
37370
+ const nodeP = func.runtime.render.get_tree_source_node(treeP);
37371
+ const perf_end = func.runtime?.perf?.start?.(renderer_context.SESSION_ID, 'render_ui_tree');
37372
+ func.runtime?.perf?.increment_map?.(renderer_context.SESSION_ID, 'render_node_counts', treeP.id || nodeP?.id || nodeP?.id_org || treeP.tagName || 'unknown');
35829
37373
  try {
35830
37374
  const tree_runtime = func.runtime.render.create_tree_runtime({
35831
- SESSION_ID,
35832
- $container,
37375
+ SESSION_ID: renderer_context.SESSION_ID,
37376
+ $container: renderer_context.$container,
37377
+ treeP,
35833
37378
  nodeP,
35834
- parent_infoP,
35835
- paramsP,
35836
- jobNoP,
35837
- is_skeleton,
37379
+ parent_infoP: renderer_context.parent_infoP,
37380
+ paramsP: renderer_context.paramsP,
37381
+ jobNoP: renderer_context.jobNoP,
37382
+ is_skeleton: renderer_context.is_skeleton,
37383
+ keyP: renderer_context.keyP,
35838
37384
  render_child: async function ($divP, child, parent_infoP, key, parentNodeP, rootContainerP) {
35839
- await func.runtime.render.render_ui_tree(SESSION_ID, $divP, child, parent_infoP, paramsP, jobNoP, is_skeleton, key, null, parentNodeP, null, rootContainerP);
37385
+ await func.runtime.render.render_tree(child, {
37386
+ ...renderer_context,
37387
+ $container: $divP,
37388
+ parent_infoP,
37389
+ keyP: key,
37390
+ refreshed_ds: null,
37391
+ parent_nodeP: parentNodeP,
37392
+ check_existP: null,
37393
+ $root_container: rootContainerP,
37394
+ });
35840
37395
  },
35841
37396
  });
35842
37397
  const render_context = tree_runtime.render_context;
@@ -35848,23 +37403,24 @@ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nod
35848
37403
  const iterate_child = tree_runtime.iterate_child;
35849
37404
 
35850
37405
  func.runtime.render.log_tree_debug({
35851
- SESSION_ID,
35852
- paramsP,
37406
+ SESSION_ID: renderer_context.SESSION_ID,
37407
+ paramsP: renderer_context.paramsP,
35853
37408
  nodeP,
35854
37409
  _ds,
35855
37410
  });
35856
37411
  const special_render = await func.runtime.render.render_special_node({
35857
- SESSION_ID,
35858
- $container,
35859
- $root_container,
37412
+ SESSION_ID: renderer_context.SESSION_ID,
37413
+ $container: renderer_context.$container,
37414
+ $root_container: renderer_context.$root_container,
37415
+ treeP,
35860
37416
  nodeP,
35861
- parent_infoP,
35862
- paramsP,
35863
- jobNoP,
35864
- is_skeleton,
35865
- keyP,
35866
- refreshed_ds,
35867
- parent_nodeP,
37417
+ parent_infoP: renderer_context.parent_infoP,
37418
+ paramsP: renderer_context.paramsP,
37419
+ jobNoP: renderer_context.jobNoP,
37420
+ is_skeleton: renderer_context.is_skeleton,
37421
+ keyP: renderer_context.keyP,
37422
+ refreshed_ds: renderer_context.refreshed_ds,
37423
+ parent_nodeP: renderer_context.parent_nodeP,
35868
37424
  prop,
35869
37425
  render_context,
35870
37426
  hover_handlers,
@@ -35872,22 +37428,23 @@ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nod
35872
37428
  close_modal,
35873
37429
  });
35874
37430
  if (special_render.handled) {
35875
- func.runtime?.perf?.increment?.(SESSION_ID, 'render_special_node_hits');
37431
+ func.runtime?.perf?.increment?.(renderer_context.SESSION_ID, 'render_special_node_hits');
35876
37432
  return special_render.result;
35877
37433
  }
35878
37434
  return await func.runtime.render.draw_node({
35879
- SESSION_ID,
35880
- $container,
35881
- $root_container,
37435
+ SESSION_ID: renderer_context.SESSION_ID,
37436
+ $container: renderer_context.$container,
37437
+ $root_container: renderer_context.$root_container,
37438
+ treeP,
35882
37439
  nodeP,
35883
- parent_infoP,
35884
- paramsP,
35885
- jobNoP,
35886
- is_skeleton,
35887
- keyP,
35888
- refreshed_ds,
35889
- parent_nodeP,
35890
- check_existP,
37440
+ parent_infoP: renderer_context.parent_infoP,
37441
+ paramsP: renderer_context.paramsP,
37442
+ jobNoP: renderer_context.jobNoP,
37443
+ is_skeleton: renderer_context.is_skeleton,
37444
+ keyP: renderer_context.keyP,
37445
+ refreshed_ds: renderer_context.refreshed_ds,
37446
+ parent_nodeP: renderer_context.parent_nodeP,
37447
+ check_existP: renderer_context.check_existP,
35891
37448
  prop,
35892
37449
  hover_handlers,
35893
37450
  include_hover_click,
@@ -35896,6 +37453,31 @@ func.runtime.render.render_ui_tree = async function (SESSION_ID, $container, nod
35896
37453
  } finally {
35897
37454
  perf_end?.();
35898
37455
  }
37456
+ };
37457
+ 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) {
37458
+ if (!nodeP) return;
37459
+ const treeP = await func.runtime.render.ensure_tree_node({
37460
+ SESSION_ID,
37461
+ nodeP,
37462
+ parent_infoP,
37463
+ paramsP,
37464
+ keyP,
37465
+ parent_nodeP,
37466
+ });
37467
+
37468
+ return await func.runtime.render.render_tree(treeP, {
37469
+ SESSION_ID,
37470
+ $container,
37471
+ parent_infoP,
37472
+ paramsP,
37473
+ jobNoP,
37474
+ is_skeleton,
37475
+ keyP,
37476
+ refreshed_ds,
37477
+ parent_nodeP,
37478
+ check_existP,
37479
+ $root_container,
37480
+ });
35899
37481
  };
35900
37482
  func.runtime = func.runtime || {};
35901
37483
  func.runtime.ui = func.runtime.ui || {};
@@ -37419,6 +39001,7 @@ func.runtime.render.handle_xu_panel_program = async function (options) {
37419
39001
  parent_infoP: options.parent_infoP,
37420
39002
  jobNoP: options.jobNoP,
37421
39003
  keyP: options.keyP,
39004
+ treeP: options.treeP,
37422
39005
  parent_nodeP: options.parent_nodeP,
37423
39006
  prop: options.nodeP.attributes,
37424
39007
  $appendToP: $wrapper,
@@ -44969,6 +46552,12 @@ function xuda(...args) {
44969
46552
  if (typeof opt !== 'object') {
44970
46553
  return console.error('Xuda Error - opt argument is not an object');
44971
46554
  }
46555
+
46556
+ if (!opt.ssr_payload && func.runtime.platform.get_window()?.__XUDA_SSR__) {
46557
+ opt.ssr_payload = func.runtime.platform.get_window().__XUDA_SSR__;
46558
+ }
46559
+ func.runtime.render.apply_runtime_bootstrap_defaults(opt);
46560
+
44972
46561
  glb.URL_PARAMS = func.common.getJsonFromUrl(platform.get_url_href());
44973
46562
 
44974
46563
  glb.worker_type = 'Worker';