@yuneta/gobj-js 7.26.26 → 7.26.28

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.
@@ -1952,6 +1952,31 @@
1952
1952
  }
1953
1953
  }
1954
1954
  /************************************************************
1955
+ * The SDF_REQUIRED attributes with no value, or null when there
1956
+ * is none: what is empty is asked type by type, as in C.
1957
+ ************************************************************/
1958
+ function gobj_check_required_attrs(gobj) {
1959
+ const hs = gobj_hsdata(gobj);
1960
+ const missing = [];
1961
+ if (!is_array(gobj.gclass.attrs_table)) return null;
1962
+ for (const it of gobj.gclass.attrs_table) {
1963
+ if (!it.name || !(it.flag & sdata_flag_t.SDF_REQUIRED)) continue;
1964
+ const value = hs[it.name];
1965
+ switch (it.type) {
1966
+ case data_type_t.DTP_STRING:
1967
+ if (!is_string(value) || empty_string(value)) missing.push(it.name);
1968
+ break;
1969
+ case data_type_t.DTP_LIST:
1970
+ case data_type_t.DTP_DICT:
1971
+ case data_type_t.DTP_JSON:
1972
+ if (empty_json(value)) missing.push(it.name);
1973
+ break;
1974
+ case data_type_t.DTP_POINTER: if (!value) missing.push(it.name);
1975
+ }
1976
+ }
1977
+ return missing.length ? missing : null;
1978
+ }
1979
+ /************************************************************
1955
1980
  *
1956
1981
  ************************************************************/
1957
1982
  function gobj_start(gobj) {
@@ -1967,6 +1992,11 @@
1967
1992
  log_error(`${gobj_short_name(gobj)}: GObj DISABLED`);
1968
1993
  return -1;
1969
1994
  }
1995
+ const required_attrs = gobj_check_required_attrs(gobj);
1996
+ if (required_attrs) {
1997
+ log_error(`${gobj_short_name(gobj)}: Cannot start without all required attributes: ` + JSON.stringify(required_attrs));
1998
+ return -1;
1999
+ }
1970
2000
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏺ ⏺ start: %s", gobj_full_name(gobj)));
1971
2001
  gobj.running = true;
1972
2002
  let ret = 0;
@@ -2353,7 +2383,7 @@
2353
2383
  log_error(`gobj NULL, destroying, or bad type`);
2354
2384
  return 0;
2355
2385
  }
2356
- return rc_walk_by_list(gobj.dl_children, walk_type, cb_walking, user_data, user_data2);
2386
+ return rc_walk_by_list(gobj, /* @__PURE__ */ new Set(), walk_type, cb_walking, user_data, user_data2);
2357
2387
  }
2358
2388
  /***************************************************************************
2359
2389
  *
@@ -2363,7 +2393,7 @@
2363
2393
  log_error(`gobj NULL, destroying, or bad type`);
2364
2394
  return 0;
2365
2395
  }
2366
- return rc_walk_by_tree(gobj.dl_children, walk_type, cb_walking, user_data, user_data2);
2396
+ return rc_walk_by_tree(gobj, /* @__PURE__ */ new Set(), walk_type, cb_walking, user_data, user_data2);
2367
2397
  }
2368
2398
  /***************************************************************
2369
2399
  * Walking
@@ -2390,16 +2420,33 @@
2390
2420
  log_error(`walk callback must return a NUMBER (-1 stop, 0 descend, >0 skip children), got ${typeof ret}: taken as 0`);
2391
2421
  return 0;
2392
2422
  }
2393
- function rc_walk_by_list(iter_, walk_type, cb_walking, user_data, user_data2) {
2394
- let iter = [...iter_];
2423
+ /***************************************************************
2424
+ * Whether a walk steps over this gobj of the list of `owner`.
2425
+ *
2426
+ * Each list is walked over a COPY of it, because a callback may destroy
2427
+ * any gobj of the list, the current one included, or move one with
2428
+ * gobj_change_parent(). One it destroyed is stepped over, as the C walk
2429
+ * does; one it moved is no longer in this list, and is handed where the
2430
+ * walk finds it now (7.26.27 handed it here AND there); and a gobj is
2431
+ * never handed twice in one walk (`seen`), wherever it was moved.
2432
+ ***************************************************************/
2433
+ function walk_steps_over(child, owner, seen) {
2434
+ return gobj_is_destroying(child) || child.parent !== owner || seen.has(child);
2435
+ }
2436
+ function rc_walk_by_list(owner, seen, walk_type, cb_walking, user_data, user_data2) {
2437
+ let iter = [...owner.dl_children];
2395
2438
  let child;
2396
2439
  if (walk_type & walk_type_t.WALK_LAST2FIRST) for (let i = iter.length; i > 0; i--) {
2397
2440
  child = iter[i - 1];
2441
+ if (walk_steps_over(child, owner, seen)) continue;
2442
+ seen.add(child);
2398
2443
  let ret = walk_ret(cb_walking(child, user_data, user_data2));
2399
2444
  if (ret < 0) return ret;
2400
2445
  }
2401
2446
  else for (let i = 0; i < iter.length; i++) {
2402
2447
  child = iter[i];
2448
+ if (walk_steps_over(child, owner, seen)) continue;
2449
+ seen.add(child);
2403
2450
  let ret = walk_ret(cb_walking(child, user_data, user_data2));
2404
2451
  if (ret < 0) return ret;
2405
2452
  }
@@ -2414,21 +2461,23 @@
2414
2461
  * or
2415
2462
  * WALK_LAST2FIRST
2416
2463
  ***************************************************************/
2417
- function rc_walk_by_level(iter, walk_type, cb_walking, user_data, user_data2) {
2418
- let ret = rc_walk_by_list(iter, walk_type, cb_walking, user_data, user_data2);
2419
- if (ret < 0) return ret;
2420
- let child;
2421
- if (walk_type & walk_type_t.WALK_LAST2FIRST) for (let i = iter.length; i > 0; i--) {
2422
- child = iter[i - 1];
2423
- ret = rc_walk_by_level([...child.dl_children], walk_type, cb_walking, user_data, user_data2);
2464
+ function rc_walk_by_level(owner, seen, walk_type, cb_walking, user_data, user_data2) {
2465
+ let iter = [...owner.dl_children];
2466
+ if (walk_type & walk_type_t.WALK_LAST2FIRST) iter.reverse();
2467
+ let handed = [];
2468
+ for (const child of iter) {
2469
+ if (walk_steps_over(child, owner, seen)) continue;
2470
+ seen.add(child);
2471
+ handed.push(child);
2472
+ let ret = walk_ret(cb_walking(child, user_data, user_data2));
2424
2473
  if (ret < 0) return ret;
2425
2474
  }
2426
- else for (let i = 0; i < iter.length; i++) {
2427
- child = iter[i];
2428
- ret = rc_walk_by_level([...child.dl_children], walk_type, cb_walking, user_data, user_data2);
2475
+ for (const child of handed) {
2476
+ if (gobj_is_destroying(child)) continue;
2477
+ let ret = rc_walk_by_level(child, seen, walk_type, cb_walking, user_data, user_data2);
2429
2478
  if (ret < 0) return ret;
2430
2479
  }
2431
- return ret;
2480
+ return 0;
2432
2481
  }
2433
2482
  /***************************************************************
2434
2483
  * Walking
@@ -2445,23 +2494,29 @@
2445
2494
  * or
2446
2495
  * WALK_LAST2FIRST
2447
2496
  ***************************************************************/
2448
- function rc_walk_by_tree(iter, walk_type, cb_walking, user_data, user_data2) {
2449
- if (walk_type & walk_type_t.WALK_BYLEVEL) return rc_walk_by_level(iter, walk_type, cb_walking, user_data, user_data2);
2497
+ function rc_walk_by_tree(owner, seen, walk_type, cb_walking, user_data, user_data2) {
2498
+ if (walk_type & walk_type_t.WALK_BYLEVEL) return rc_walk_by_level(owner, seen, walk_type, cb_walking, user_data, user_data2);
2499
+ let iter = [...owner.dl_children];
2450
2500
  let ret;
2451
2501
  let child;
2452
2502
  if (walk_type & walk_type_t.WALK_BOTTOM2TOP) for (let i = iter.length; i > 0; i--) {
2453
2503
  child = iter[i - 1];
2454
- ret = rc_walk_by_tree([...child.dl_children], walk_type, cb_walking, user_data, user_data2);
2504
+ if (walk_steps_over(child, owner, seen)) continue;
2505
+ ret = rc_walk_by_tree(child, seen, walk_type, cb_walking, user_data, user_data2);
2455
2506
  if (ret < 0) return ret;
2507
+ if (gobj_is_destroying(child)) continue;
2508
+ seen.add(child);
2456
2509
  ret = walk_ret(cb_walking(child, user_data, user_data2));
2457
2510
  if (ret < 0) return ret;
2458
2511
  }
2459
2512
  else for (let i = 0; i < iter.length; i++) {
2460
2513
  child = iter[i];
2514
+ if (walk_steps_over(child, owner, seen)) continue;
2515
+ seen.add(child);
2461
2516
  ret = walk_ret(cb_walking(child, user_data, user_data2));
2462
2517
  if (ret < 0) return ret;
2463
- else if (ret === 0) {
2464
- ret = rc_walk_by_tree([...child.dl_children], walk_type, cb_walking, user_data, user_data2);
2518
+ else if (ret === 0 && !gobj_is_destroying(child)) {
2519
+ ret = rc_walk_by_tree(child, seen, walk_type, cb_walking, user_data, user_data2);
2465
2520
  if (ret < 0) return ret;
2466
2521
  }
2467
2522
  }
@@ -6046,44 +6101,99 @@
6046
6101
  return kw_clone;
6047
6102
  }
6048
6103
  /********************************************
6104
+ * The browser's localStorage, or null when there is none (outside a
6105
+ * browser).
6106
+ *
6107
+ * With the site's data blocked (Chrome with cookies blocked, a sandboxed
6108
+ * iframe), READING the window.localStorage property already throws a
6109
+ * SecurityError, not only its getItem(): every app reads its locale at
6110
+ * start, so the app did not start. Such a store is answered as one whose
6111
+ * every call throws that error, so each helper fails as it fails for any
6112
+ * store that refuses it: a read gives the default, a write -1.
6113
+ ********************************************/
6114
+ function local_store() {
6115
+ if (typeof window === "undefined") return null;
6116
+ let store;
6117
+ try {
6118
+ store = window.localStorage;
6119
+ } catch (e) {
6120
+ const refuse = () => {
6121
+ throw e;
6122
+ };
6123
+ return {
6124
+ getItem: refuse,
6125
+ setItem: refuse,
6126
+ removeItem: refuse
6127
+ };
6128
+ }
6129
+ return store || null;
6130
+ }
6131
+ /********************************************
6132
+ * A failure of the store that would repeat on every read is warned
6133
+ * once per key, until a read of the key goes well again.
6134
+ ********************************************/
6135
+ var local_storage_warned = /* @__PURE__ */ new Set();
6136
+ function warn_local_storage_once(key, msg) {
6137
+ if (local_storage_warned.has(key)) return;
6138
+ local_storage_warned.add(key);
6139
+ log_warning(msg);
6140
+ }
6141
+ /********************************************
6049
6142
  * Get a local attribute
6050
6143
  *
6051
6144
  * The json the entry holds, or `default_value` when there is none
6052
6145
  * (written there too with `create`). A json null is no value, as
6053
6146
  * json_absent() in C: the default. An entry that is not json (a
6054
- * corrupt or foreign write) is one WARNING and is REMOVED, so the
6147
+ * corrupt or foreign write) is a WARNING and is REMOVED, so the
6055
6148
  * next read finds no entry: it used to console.warn on every read,
6056
- * for ever. Its text is never shown (JSON.parse() quotes it in its
6057
- * error, and an entry can hold a token). A store that cannot be read
6058
- * is warned once per key. Outside a browser: the default.
6149
+ * for ever; a new corrupt entry is a new WARNING. Its text is never
6150
+ * shown (JSON.parse() quotes it in its error, and an entry can hold a
6151
+ * token). A store that cannot be read (blocked) is warned once per
6152
+ * key, and the default is NOT written there; one that cannot be
6153
+ * written (full) is warned once per key too: with `create` it was an
6154
+ * ERROR on every read, the default tried again each time. A key is
6155
+ * warned again after a read that goes well. Outside a browser: the
6156
+ * default.
6059
6157
  ********************************************/
6060
- var local_storage_warned = /* @__PURE__ */ new Set();
6061
- function warn_local_storage_once(key, what) {
6062
- if (local_storage_warned.has(key)) return;
6063
- local_storage_warned.add(key);
6064
- log_warning(`kw_get_local_storage_value(): localStorage key "${key}" ${what}`);
6065
- }
6066
6158
  function kw_get_local_storage_value(key, default_value, create = false) {
6067
- if (typeof window === "undefined" || !window.localStorage) return default_value;
6159
+ const store = local_store();
6160
+ if (!store) return default_value;
6161
+ const where = `kw_get_local_storage_value(): localStorage key "${key}"`;
6068
6162
  let text;
6069
6163
  try {
6070
- text = window.localStorage.getItem(key);
6164
+ text = store.getItem(key);
6071
6165
  } catch (e) {
6072
- warn_local_storage_once(key, "cannot be read (storage blocked?): the default is used");
6166
+ warn_local_storage_once(key, `${where} cannot be read (storage blocked?): the default is used`);
6073
6167
  return default_value;
6074
6168
  }
6169
+ let went_well = true;
6075
6170
  let value = null;
6076
6171
  if (text !== null) try {
6077
6172
  value = JSON.parse(text);
6078
6173
  } catch (e) {
6079
- warn_local_storage_once(key, "does not hold json: removed, the default is used");
6080
- kw_remove_local_storage_value(key);
6081
6174
  value = null;
6175
+ try {
6176
+ store.removeItem(key);
6177
+ log_warning(`${where} does not hold json: removed, the default is used`);
6178
+ } catch (e2) {
6179
+ went_well = false;
6180
+ warn_local_storage_once(key, `${where} does not hold json and cannot be removed: the default is used`);
6181
+ }
6082
6182
  }
6083
6183
  if (value === null) {
6084
- if (create) kw_set_local_storage_value(key, default_value);
6085
- return default_value;
6184
+ value = default_value;
6185
+ if (create) {
6186
+ const default_text = json_text_of(default_value);
6187
+ if (default_text === void 0) log_error(`${where}: cannot write the default: a value with no json text`);
6188
+ else try {
6189
+ store.setItem(key, default_text);
6190
+ } catch (e) {
6191
+ went_well = false;
6192
+ warn_local_storage_once(key, `${where} cannot be written (storage full or blocked?): the default is used, not kept`);
6193
+ }
6194
+ }
6086
6195
  }
6196
+ if (went_well) local_storage_warned.delete(key);
6087
6197
  return value;
6088
6198
  }
6089
6199
  /********************************************
@@ -6095,15 +6205,26 @@
6095
6205
  * reported success on a store that had rejected the write: a full or blocked
6096
6206
  * localStorage (quota, private mode) silently discarded the change while the
6097
6207
  * UI showed it as saved.
6208
+ *
6209
+ * A value with no json text is refused with an ERROR, as a typed writer
6210
+ * refuses it (json_text_of()): a function was stored as the text
6211
+ * "undefined", and a NaN, a Date or a Map as a text that reads back as
6212
+ * another value. Checked in Node.js too, where there is no store.
6098
6213
  ********************************************/
6099
6214
  function kw_set_local_storage_value(key, value) {
6100
- if (typeof window === "undefined" || !window.localStorage) return 0;
6101
- if (!key || value === void 0) {
6102
- log_error(`kw_set_local_storage_value(): invalid key or value: key=${key}`);
6215
+ if (!key) {
6216
+ log_error(`kw_set_local_storage_value(): invalid key: key=${key}`);
6217
+ return -1;
6218
+ }
6219
+ const text = json_text_of(value);
6220
+ if (text === void 0) {
6221
+ log_error(`kw_set_local_storage_value(): cannot save "${key}": a value with no json text`);
6103
6222
  return -1;
6104
6223
  }
6224
+ const store = local_store();
6225
+ if (!store) return 0;
6105
6226
  try {
6106
- window.localStorage.setItem(key, JSON.stringify(value));
6227
+ store.setItem(key, text);
6107
6228
  return 0;
6108
6229
  } catch (e) {
6109
6230
  log_error(`kw_set_local_storage_value(): cannot save "${key}" (storage full or blocked?): ${e && e.message ? e.message : e}`);
@@ -6114,15 +6235,16 @@
6114
6235
  * Remove local attribute
6115
6236
  ********************************************/
6116
6237
  function kw_remove_local_storage_value(key) {
6117
- if (typeof window === "undefined" || !window.localStorage) return;
6238
+ const store = local_store();
6239
+ if (!store) return;
6118
6240
  if (!key) {
6119
- window.console.warn(`Invalid key for localStorage removal: ${key}`);
6241
+ log_error(`kw_remove_local_storage_value(): invalid key: key=${key}`);
6120
6242
  return;
6121
6243
  }
6122
6244
  try {
6123
- window.localStorage.removeItem(key);
6245
+ store.removeItem(key);
6124
6246
  } catch (e) {
6125
- window.console.warn(`Error removing localStorage key "${key}":`, e);
6247
+ log_warning(`kw_remove_local_storage_value(): cannot remove "${key}" (storage blocked?): ${e && e.message ? e.message : e}`);
6126
6248
  }
6127
6249
  }
6128
6250
  /*************************************************************
@@ -7311,19 +7433,62 @@
7311
7433
  return -1;
7312
7434
  }
7313
7435
  /************************************************************
7436
+ * A new uuid v4 (RFC 9562).
7314
7437
  *
7438
+ * crypto.randomUUID() exists only in a secure context, so a page
7439
+ * served over http without TLS has none; crypto.getRandomValues()
7440
+ * exists there too. With no crypto at all, Math.random(): not fit
7441
+ * for a secret, and a uuid that names a node is not one.
7315
7442
  ************************************************************/
7443
+ function new_uuid() {
7444
+ const c = typeof window !== "undefined" && window.crypto || globalThis.crypto;
7445
+ if (c && typeof c.randomUUID === "function") return c.randomUUID();
7446
+ const b = /* @__PURE__ */ new Uint8Array(16);
7447
+ if (c && typeof c.getRandomValues === "function") c.getRandomValues(b);
7448
+ else for (let i = 0; i < b.length; i++) b[i] = Math.floor(Math.random() * 256);
7449
+ b[6] = b[6] & 15 | 64;
7450
+ b[8] = b[8] & 63 | 128;
7451
+ const h = Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
7452
+ return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
7453
+ }
7454
+ /************************************************************
7455
+ * The uuid of this node: ONE for the life of the page.
7456
+ *
7457
+ * The one the store keeps ("host_uuid"), made and written there when
7458
+ * there is none. When the store cannot keep it (blocked, full, no
7459
+ * localStorage, outside a browser) the one this page made, warned
7460
+ * once. It goes in the node_uuid attribute of the yuno, the id of the
7461
+ * identity card and the `host` of every message the client sends, so
7462
+ * the first answer is kept and answered again: 7.26.27 made a new
7463
+ * uuid on every call when the store could not keep it, and answered
7464
+ * null without crypto.randomUUID().
7465
+ ************************************************************/
7466
+ var uuid_of_this_page = null;
7316
7467
  function node_uuid() {
7317
- let uuid = "";
7318
- if (typeof window !== "undefined" && window.localStorage) {
7319
- uuid = window.localStorage.getItem("host_uuid");
7320
- if (!uuid && window.crypto?.randomUUID) {
7321
- uuid = window.crypto.randomUUID();
7322
- window.localStorage.setItem("host_uuid", uuid);
7468
+ if (uuid_of_this_page) return uuid_of_this_page;
7469
+ const store = local_store();
7470
+ if (!store) {
7471
+ uuid_of_this_page = new_uuid();
7472
+ return uuid_of_this_page;
7473
+ }
7474
+ let uuid = null;
7475
+ try {
7476
+ uuid = store.getItem("host_uuid");
7477
+ } catch (e) {
7478
+ warn_local_storage_once("host_uuid", "node_uuid(): localStorage cannot be read (storage blocked?): a uuid of this page only");
7479
+ uuid_of_this_page = new_uuid();
7480
+ return uuid_of_this_page;
7481
+ }
7482
+ if (!uuid) {
7483
+ uuid = new_uuid();
7484
+ try {
7485
+ store.setItem("host_uuid", uuid);
7486
+ } catch (e) {
7487
+ warn_local_storage_once("host_uuid", "node_uuid(): localStorage cannot be written (storage full or blocked?): a uuid of this page only");
7323
7488
  }
7324
- } else if (typeof globalThis !== "undefined" && globalThis.crypto?.randomUUID) uuid = globalThis.crypto.randomUUID();
7325
- else uuid = "00000000-0000-0000-0000-000000000000";
7326
- return uuid;
7489
+ }
7490
+ uuid_of_this_page = uuid;
7491
+ return uuid_of_this_page;
7327
7492
  }
7328
7493
  /************************************************************
7329
7494
  * Build a cleaned name
@@ -8608,7 +8773,7 @@
8608
8773
  SDATA(data_type_t.DTP_STRING, "cert_pem", sdata_flag_t.SDF_PERSIST, "", "SSL server certification, PEM str format"),
8609
8774
  SDATA(data_type_t.DTP_JSON, "extra_info", sdata_flag_t.SDF_RD, "{}", "dict data set by user, added to the identity card msg."),
8610
8775
  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."),
8611
- 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"),
8776
+ SDATA(data_type_t.DTP_INTEGER, "timeout_retry", sdata_flag_t.SDF_RD, "5000", "Base of the first reconnect delay (ms, ±20% jitter), at least 1000. It BACKS OFF from here, doubling up to timeout_retry_max, and resets when a session is reached"),
8612
8777
  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"),
8613
8778
  SDATA(data_type_t.DTP_INTEGER, "timeout_idack", sdata_flag_t.SDF_RD, "5000", "timeout waiting idAck"),
8614
8779
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, 0, "subscriber of output-events. If null then subscriber is the parent"),
@@ -8633,7 +8798,8 @@
8633
8798
  inside_on_open: false,
8634
8799
  retry_ms: 0,
8635
8800
  retry_ms_before_session: 0,
8636
- session_since: 0
8801
+ session_since: 0,
8802
+ retry_floor_warned: false
8637
8803
  };
8638
8804
  var __gclass__ = null;
8639
8805
  /******************************
@@ -8905,7 +9071,7 @@
8905
9071
  function close_for_peer_fault(gobj, reason) {
8906
9072
  let priv = gobj.priv;
8907
9073
  if (gobj_current_state(gobj) === "ST_SESSION") {
8908
- if (performance.now() - priv.session_since < Math.max(gobj_read_integer_attr(gobj, "timeout_retry"), gobj_read_integer_attr(gobj, "timeout_retry_max"))) priv.retry_ms = priv.retry_ms_before_session;
9074
+ if (performance.now() - priv.session_since < retry_bounds(gobj).max) priv.retry_ms = priv.retry_ms_before_session;
8909
9075
  priv.retry_ms_before_session = 0;
8910
9076
  }
8911
9077
  close_websocket(gobj, CLOSE_PEER_FAULT, reason);
@@ -9177,6 +9343,32 @@
9177
9343
  return 0;
9178
9344
  }
9179
9345
  /***************************************************************
9346
+ * The first delay of the backoff and its cap.
9347
+ *
9348
+ * A first delay below RETRY_FLOOR_MS is raised to it, with a WARNING
9349
+ * once: `timeout_retry` 0 never left 0 (0 doubled is 0), so a server
9350
+ * that closed before the ack was reconnected in a loop with no wait,
9351
+ * hundreds of connections a second.
9352
+ ***************************************************************/
9353
+ var RETRY_FLOOR_MS = 1e3;
9354
+ function retry_bounds(gobj) {
9355
+ let priv = gobj.priv;
9356
+ let base = gobj_read_integer_attr(gobj, "timeout_retry");
9357
+ let max = gobj_read_integer_attr(gobj, "timeout_retry_max");
9358
+ if (!(base >= RETRY_FLOOR_MS)) {
9359
+ if (!priv.retry_floor_warned) {
9360
+ priv.retry_floor_warned = true;
9361
+ log_warning(`${gobj_short_name(gobj)}: timeout_retry ${base} ms is below ${RETRY_FLOOR_MS} ms: ${RETRY_FLOOR_MS} ms is used`);
9362
+ }
9363
+ base = RETRY_FLOOR_MS;
9364
+ }
9365
+ if (!(max >= base)) max = base;
9366
+ return {
9367
+ base,
9368
+ max
9369
+ };
9370
+ }
9371
+ /***************************************************************
9180
9372
  * How long to wait before the next reconnect attempt.
9181
9373
  *
9182
9374
  * The delay used to be a constant (`timeout_retry`, 5s), for ever. A
@@ -9197,9 +9389,7 @@
9197
9389
  ***************************************************************/
9198
9390
  function next_retry(gobj) {
9199
9391
  let priv = gobj.priv;
9200
- let base = gobj_read_integer_attr(gobj, "timeout_retry");
9201
- let max = gobj_read_integer_attr(gobj, "timeout_retry_max");
9202
- if (max < base) max = base;
9392
+ const { base, max } = retry_bounds(gobj);
9203
9393
  priv.retry_ms = priv.retry_ms ? Math.min(priv.retry_ms * 2, max) : base;
9204
9394
  let jitter = priv.retry_ms * .2;
9205
9395
  let ms = priv.retry_ms - jitter + Math.random() * 2 * jitter;
@@ -9697,6 +9887,7 @@
9697
9887
  exports.json_object_update_existing = json_object_update_existing;
9698
9888
  exports.json_object_update_missing = json_object_update_missing;
9699
9889
  exports.json_size = json_size;
9890
+ exports.json_text_of = json_text_of;
9700
9891
  exports.jwt2json = jwt2json;
9701
9892
  exports.jwtDecode = jwtDecode;
9702
9893
  exports.kw_clone_by_keys = kw_clone_by_keys;