@yuneta/gobj-js 7.7.2 → 7.8.0

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.
@@ -2633,6 +2633,10 @@
2633
2633
  function set_log_callback(fn) {
2634
2634
  __log_callback__ = typeof fn === "function" ? fn : null;
2635
2635
  }
2636
+ var __console_output_enabled__ = true;
2637
+ function set_console_log_enabled(enabled) {
2638
+ __console_output_enabled__ = enabled ? true : false;
2639
+ }
2636
2640
  var __in_log_callback__ = false;
2637
2641
  function emit_log_callback(level, msg, hora) {
2638
2642
  if (__log_callback__ && !__in_log_callback__) {
@@ -2678,9 +2682,10 @@
2678
2682
  function log_error(format) {
2679
2683
  let msg = format_log(format);
2680
2684
  let hora = current_timestamp();
2681
- if (f_error) if (f_error === window.console.error) f_error("%c" + hora + " ERROR: " + String(msg), "color:red");
2682
- else {
2683
- window.console.error("%c" + hora + " ERROR: " + String(msg), "color:red");
2685
+ if (f_error) if (f_error === window.console.error) {
2686
+ if (__console_output_enabled__) f_error("%c" + hora + " ERROR: " + String(msg), "color:red");
2687
+ } else {
2688
+ if (__console_output_enabled__) window.console.error("%c" + hora + " ERROR: " + String(msg), "color:red");
2684
2689
  f_error(`${hora} ERROR: ${String(msg)}`);
2685
2690
  }
2686
2691
  emit_log_callback("error", msg, hora);
@@ -2688,9 +2693,10 @@
2688
2693
  function log_warning(format) {
2689
2694
  let msg = format_log(format);
2690
2695
  let hora = current_timestamp();
2691
- if (f_warning) if (f_warning === window.console.warn) f_warning("%c" + hora + " WARNING: " + String(msg), "color:yellow");
2692
- else {
2693
- window.console.warn("%c" + hora + " WARNING: " + String(msg), "color:yellow");
2696
+ if (f_warning) if (f_warning === window.console.warn) {
2697
+ if (__console_output_enabled__) f_warning("%c" + hora + " WARNING: " + String(msg), "color:yellow");
2698
+ } else {
2699
+ if (__console_output_enabled__) window.console.warn("%c" + hora + " WARNING: " + String(msg), "color:yellow");
2694
2700
  f_warning(`${hora} WARNING: ${String(msg)}`);
2695
2701
  }
2696
2702
  emit_log_callback("warning", msg, hora);
@@ -2698,24 +2704,32 @@
2698
2704
  function log_info(format) {
2699
2705
  let msg = format_log(format);
2700
2706
  let hora = current_timestamp();
2701
- if (f_info) f_info("%c" + hora + " INFO: " + String(msg), "color:cyan");
2707
+ if (f_info) {
2708
+ if (__console_output_enabled__) f_info("%c" + hora + " INFO: " + String(msg), "color:cyan");
2709
+ }
2702
2710
  emit_log_callback("info", msg, hora);
2703
2711
  }
2704
2712
  function log_debug(format) {
2705
2713
  let msg = format_log(format);
2706
2714
  let hora = current_timestamp();
2707
- if (f_debug) f_debug("%c" + hora + " DEBUG: " + String(msg), "color:silver");
2715
+ if (f_debug) {
2716
+ if (__console_output_enabled__) f_debug("%c" + hora + " DEBUG: " + String(msg), "color:silver");
2717
+ }
2708
2718
  emit_log_callback("debug", msg, hora);
2709
2719
  }
2710
2720
  function trace_msg(format) {
2711
2721
  let msg = format_log(format);
2712
2722
  let hora = current_timestamp();
2713
- if (f_debug) f_debug("%c" + hora + " MSG: " + String(msg), "color:cyan");
2723
+ if (f_debug) {
2724
+ if (__console_output_enabled__) f_debug("%c" + hora + " MSG: " + String(msg), "color:cyan");
2725
+ }
2714
2726
  emit_log_callback("msg", msg, hora);
2715
2727
  }
2716
2728
  function trace_json(jn, msg) {
2717
- if (msg) window.console.warn("=====> " + msg);
2718
- window.console.dir(jn);
2729
+ if (__console_output_enabled__) {
2730
+ if (msg) window.console.warn("=====> " + msg);
2731
+ window.console.dir(jn);
2732
+ }
2719
2733
  emit_log_callback("json", jn, current_timestamp());
2720
2734
  }
2721
2735
  /************************************************************
@@ -3528,17 +3542,26 @@
3528
3542
  }
3529
3543
  /********************************************
3530
3544
  * Save a local attribute
3545
+ *
3546
+ * Returns 0 on success, -1 when the value did NOT reach the store.
3547
+ * It used to return nothing and only console.warn, so every caller up the
3548
+ * chain (db_save_persistent_attrs -> gobj_save_persistent_attrs -> the app)
3549
+ * reported success on a store that had rejected the write: a full or blocked
3550
+ * localStorage (quota, private mode) silently discarded the change while the
3551
+ * UI showed it as saved.
3531
3552
  ********************************************/
3532
3553
  function kw_set_local_storage_value(key, value) {
3533
- if (typeof window === "undefined" || !window.localStorage) return;
3554
+ if (typeof window === "undefined" || !window.localStorage) return 0;
3534
3555
  if (!key || value === void 0) {
3535
- window.console.warn(`Invalid key or value for localStorage: key=${key}, value=${value}`);
3536
- return;
3556
+ log_error(`kw_set_local_storage_value(): invalid key or value: key=${key}`);
3557
+ return -1;
3537
3558
  }
3538
3559
  try {
3539
3560
  window.localStorage.setItem(key, JSON.stringify(value));
3561
+ return 0;
3540
3562
  } catch (e) {
3541
- window.console.warn(`Error saving localStorage key "${key}":`, e);
3563
+ log_error(`kw_set_local_storage_value(): cannot save "${key}" (storage full or blocked?): ${e && e.message ? e.message : e}`);
3564
+ return -1;
3542
3565
  }
3543
3566
  }
3544
3567
  /********************************************
@@ -3625,7 +3648,7 @@
3625
3648
  if (is_object(kw)) new_dict = kw;
3626
3649
  else if (is_array(kw)) for (let i = 0; i < kw.length; i++) {
3627
3650
  let kv = kw[i];
3628
- let id = kw_get_str(kv, "id", null, false);
3651
+ let id = kw_get_str(gobj, kv, "id", "", 0);
3629
3652
  if (!empty_string(id)) new_dict[id] = kv;
3630
3653
  }
3631
3654
  else log_error(`${gobj_short_name(gobj)} kwid_new_dict: data type unknown`);
@@ -4513,6 +4536,10 @@
4513
4536
  let value = elem.getAttribute("data-i18n-aria-label");
4514
4537
  if (value) elem.setAttribute("aria-label", t(value));
4515
4538
  });
4539
+ element.querySelectorAll("[data-i18n-placeholder]").forEach(function(elem) {
4540
+ let value = elem.getAttribute("data-i18n-placeholder");
4541
+ if (value) elem.setAttribute("placeholder", t(value));
4542
+ });
4516
4543
  }
4517
4544
  /************************************************************
4518
4545
  * Example Usage
@@ -4694,13 +4721,16 @@
4694
4721
  }
4695
4722
  }
4696
4723
  /************************************************************
4697
- *
4724
+ * Returns 0 on success, -1 when the attrs did NOT reach the store
4725
+ * (quota exceeded, private mode, storage disabled). Callers must check
4726
+ * it: a discarded write with a UI that shows the value as saved is the
4727
+ * worst of both worlds.
4698
4728
  ************************************************************/
4699
4729
  function db_save_persistent_attrs(gobj, keys) {
4700
4730
  let attrs = kw_clone_by_keys(gobj, gobj_read_attrs(gobj, sdata_flag_t.SDF_PERSIST, 0), keys, false);
4701
4731
  let jn_file = kw_get_local_storage_value(_get_persistent_path(gobj), null, false);
4702
4732
  if (jn_file && is_object(jn_file)) json_object_update_missing(attrs, jn_file);
4703
- kw_set_local_storage_value(_get_persistent_path(gobj), attrs);
4733
+ return kw_set_local_storage_value(_get_persistent_path(gobj), attrs);
4704
4734
  }
4705
4735
  /************************************************************
4706
4736
  *
@@ -4916,6 +4946,7 @@
4916
4946
  * is_writable // field writable (no readonly)
4917
4947
  * default_value
4918
4948
  * placeholder
4949
+ * fkey // fkey mapping {topic_name: hook_name}, null if none
4919
4950
  * ]
4920
4951
  *
4921
4952
  * The template is recursive, in values you can set dictionaries, or arrays,
@@ -4985,7 +5016,8 @@
4985
5016
  is_hidden: false,
4986
5017
  default_value: col.default,
4987
5018
  placeholder: col.placeholder,
4988
- fillspace: col.fillspace || 4
5019
+ fillspace: col.fillspace || 4,
5020
+ fkey: col.fkey || null
4989
5021
  };
4990
5022
  if (!col.flag) return field_desc;
4991
5023
  for (let i = 0; i < col.flag.length; i++) {
@@ -5031,7 +5063,8 @@
5031
5063
  is_writable: false,
5032
5064
  is_hidden: false,
5033
5065
  default_value: void 0,
5034
- placeholder: void 0
5066
+ placeholder: void 0,
5067
+ fkey: null
5035
5068
  };
5036
5069
  if (is_string(value)) {
5037
5070
  let values = value.split(".");
@@ -5075,7 +5108,9 @@
5075
5108
  let new_record = {};
5076
5109
  if (!kw) kw = {};
5077
5110
  Object.entries(template).forEach(([field, col]) => {
5078
- new_record[field] = kw_get_dict_value(null, kw, field, kw_get_dict_value(col, "default"));
5111
+ let default_value;
5112
+ if (is_object(col)) default_value = col.default;
5113
+ new_record[field] = kw_get_dict_value(null, kw, field, default_value);
5079
5114
  });
5080
5115
  return new_record;
5081
5116
  }
@@ -5444,7 +5479,9 @@
5444
5479
  SDATA(data_type_t.DTP_STRING, "jwt", sdata_flag_t.SDF_PERSIST, "", "JWT"),
5445
5480
  SDATA(data_type_t.DTP_STRING, "cert_pem", sdata_flag_t.SDF_PERSIST, "", "SSL server certification, PEM str format"),
5446
5481
  SDATA(data_type_t.DTP_JSON, "extra_info", sdata_flag_t.SDF_RD, "{}", "dict data set by user, added to the identity card msg."),
5447
- SDATA(data_type_t.DTP_INTEGER, "timeout_retry", sdata_flag_t.SDF_RD, "5000", "timeout waiting idAck"),
5482
+ SDATA(data_type_t.DTP_LIST, "required_services", sdata_flag_t.SDF_RD, "[]", "Services this LINK requires (identity card). Empty = the yuno's `required_services`, as before. A yuno with several links to DIFFERENT backends must set it per link: the yuno-wide list is the union, so every backend is told the service names of all the others."),
5483
+ SDATA(data_type_t.DTP_INTEGER, "timeout_retry", sdata_flag_t.SDF_RD, "5000", "First reconnect delay (ms). It BACKS OFF from here, doubling up to timeout_retry_max, and resets when a session is reached"),
5484
+ SDATA(data_type_t.DTP_INTEGER, "timeout_retry_max", sdata_flag_t.SDF_RD, "60000", "Cap of the reconnect backoff (ms). Set it equal to timeout_retry for the old fixed-interval behaviour"),
5448
5485
  SDATA(data_type_t.DTP_INTEGER, "timeout_idack", sdata_flag_t.SDF_RD, "5000", "timeout waiting idAck"),
5449
5486
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, 0, "subscriber of output-events. If null then subscriber is the parent"),
5450
5487
  SDATA_END()
@@ -5457,7 +5494,8 @@
5457
5494
  gobj_timer: null,
5458
5495
  inform_on_close: false,
5459
5496
  websocket: null,
5460
- inside_on_open: false
5497
+ inside_on_open: false,
5498
+ retry_ms: 0
5461
5499
  };
5462
5500
  var __gclass__ = null;
5463
5501
  /******************************
@@ -5505,6 +5543,7 @@
5505
5543
  function mt_start(gobj) {
5506
5544
  let priv = gobj.priv;
5507
5545
  gobj_start(priv.gobj_timer);
5546
+ priv.retry_ms = 0;
5508
5547
  priv.websocket = setup_websocket(gobj);
5509
5548
  return 0;
5510
5549
  }
@@ -5763,6 +5802,8 @@
5763
5802
  let yuno_version = gobj_read_str_attr(gobj_yuno(), "yuno_version");
5764
5803
  let yuno_release = gobj_read_str_attr(gobj_yuno(), "yuno_release");
5765
5804
  let yuno_tag = gobj_read_str_attr(gobj_yuno(), "yuno_tag");
5805
+ let required_services = gobj_read_attr(gobj, "required_services");
5806
+ if (!required_services || !required_services.length) required_services = gobj_read_attr(gobj_yuno(), "required_services");
5766
5807
  let kw = {
5767
5808
  "yuno_role": gobj_yuno_role(),
5768
5809
  "yuno_id": gobj_yuno_id(),
@@ -5781,7 +5822,7 @@
5781
5822
  "id": node_uuid(),
5782
5823
  "user_agent": typeof window !== "undefined" ? window.navigator.userAgent : "",
5783
5824
  "language": typeof window !== "undefined" ? window.navigator.language : "",
5784
- "required_services": gobj_read_attr(gobj_yuno(), "required_services")
5825
+ "required_services": required_services
5785
5826
  };
5786
5827
  msg_iev_push_stack(gobj, kw, IEVENT_STACK_ID, build_cli_ievent_request(gobj, gobj_name(gobj_parent(gobj)), null));
5787
5828
  msg_iev_set_msg_type(gobj, kw, "__identity__");
@@ -5910,9 +5951,37 @@
5910
5951
  was_clean: e.wasClean
5911
5952
  });
5912
5953
  }
5913
- if (gobj_is_running(gobj)) set_timeout(priv.gobj_timer, gobj_read_integer_attr(gobj, "timeout_retry"));
5954
+ if (gobj_is_running(gobj)) set_timeout(priv.gobj_timer, next_retry(gobj));
5914
5955
  return 0;
5915
5956
  }
5957
+ /***************************************************************
5958
+ * How long to wait before the next reconnect attempt.
5959
+ *
5960
+ * The delay used to be a constant (`timeout_retry`, 5s), for ever. A
5961
+ * backend that is down — or a URL with a typo, which never comes back —
5962
+ * was therefore hit every 5 seconds for the whole life of the tab, by
5963
+ * EVERY link pointed at it, all in lockstep. It backs off now: from
5964
+ * `timeout_retry`, doubling, capped at `timeout_retry_max`.
5965
+ *
5966
+ * The jitter (±20%) is what breaks the lockstep. Without it, N links that
5967
+ * went down together retry together for ever — a thundering herd against a
5968
+ * backend that is just coming back up. It is the one place a random number
5969
+ * earns its keep.
5970
+ *
5971
+ * Reset to zero when a session is actually reached
5972
+ * (ac_identity_card_ack), so a link that flaps once does not carry a
5973
+ * minute-long penalty into its next real drop.
5974
+ ***************************************************************/
5975
+ function next_retry(gobj) {
5976
+ let priv = gobj.priv;
5977
+ let base = gobj_read_integer_attr(gobj, "timeout_retry");
5978
+ let max = gobj_read_integer_attr(gobj, "timeout_retry_max");
5979
+ if (max < base) max = base;
5980
+ priv.retry_ms = priv.retry_ms ? Math.min(priv.retry_ms * 2, max) : base;
5981
+ let jitter = priv.retry_ms * .2;
5982
+ let ms = priv.retry_ms - jitter + Math.random() * 2 * jitter;
5983
+ return Math.max(1, Math.round(ms));
5984
+ }
5916
5985
  /********************************************
5917
5986
  *
5918
5987
  ********************************************/
@@ -5953,6 +6022,7 @@
5953
6022
  } else {
5954
6023
  gobj_change_state(gobj, "ST_SESSION");
5955
6024
  priv.inside_on_open = true;
6025
+ priv.retry_ms = 0;
5956
6026
  if (!priv.inform_on_close) {
5957
6027
  priv.inform_on_close = true;
5958
6028
  let kw_on_open = {
@@ -6376,6 +6446,7 @@
6376
6446
  exports.safeSrc = safeSrc;
6377
6447
  exports.sdata_flag_t = sdata_flag_t;
6378
6448
  exports.send_http_json_post = send_http_json_post;
6449
+ exports.set_console_log_enabled = set_console_log_enabled;
6379
6450
  exports.set_log_callback = set_log_callback;
6380
6451
  exports.set_remote_log_functions = set_remote_log_functions;
6381
6452
  exports.set_timeout = set_timeout;