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