@yuneta/gobj-js 7.26.27 → 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,18 +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];
2396
- if (gobj_is_destroying(child)) continue;
2439
+ if (walk_steps_over(child, owner, seen)) continue;
2440
+ seen.add(child);
2397
2441
  let ret = walk_ret(cb_walking(child, user_data, user_data2));
2398
2442
  if (ret < 0) return ret;
2399
2443
  }
2400
2444
  else for (let i = 0; i < iter.length; i++) {
2401
2445
  child = iter[i];
2402
- if (gobj_is_destroying(child)) continue;
2446
+ if (walk_steps_over(child, owner, seen)) continue;
2447
+ seen.add(child);
2403
2448
  let ret = walk_ret(cb_walking(child, user_data, user_data2));
2404
2449
  if (ret < 0) return ret;
2405
2450
  }
@@ -2414,24 +2459,23 @@ var gobjJs = (function(exports) {
2414
2459
  * or
2415
2460
  * WALK_LAST2FIRST
2416
2461
  ***************************************************************/
2417
- function rc_walk_by_level(iter_, walk_type, cb_walking, user_data, user_data2) {
2418
- let iter = [...iter_];
2419
- let ret = rc_walk_by_list(iter, walk_type, cb_walking, user_data, user_data2);
2420
- if (ret < 0) return ret;
2421
- let child;
2422
- if (walk_type & walk_type_t.WALK_LAST2FIRST) for (let i = iter.length; i > 0; i--) {
2423
- child = iter[i - 1];
2424
- if (gobj_is_destroying(child)) continue;
2425
- 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));
2426
2471
  if (ret < 0) return ret;
2427
2472
  }
2428
- else for (let i = 0; i < iter.length; i++) {
2429
- child = iter[i];
2473
+ for (const child of handed) {
2430
2474
  if (gobj_is_destroying(child)) continue;
2431
- ret = rc_walk_by_level(child.dl_children, walk_type, cb_walking, user_data, user_data2);
2475
+ let ret = rc_walk_by_level(child, seen, walk_type, cb_walking, user_data, user_data2);
2432
2476
  if (ret < 0) return ret;
2433
2477
  }
2434
- return ret;
2478
+ return 0;
2435
2479
  }
2436
2480
  /***************************************************************
2437
2481
  * Walking
@@ -2448,27 +2492,29 @@ var gobjJs = (function(exports) {
2448
2492
  * or
2449
2493
  * WALK_LAST2FIRST
2450
2494
  ***************************************************************/
2451
- function rc_walk_by_tree(iter_, walk_type, cb_walking, user_data, user_data2) {
2452
- if (walk_type & walk_type_t.WALK_BYLEVEL) return rc_walk_by_level(iter_, walk_type, cb_walking, user_data, user_data2);
2453
- let iter = [...iter_];
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];
2454
2498
  let ret;
2455
2499
  let child;
2456
2500
  if (walk_type & walk_type_t.WALK_BOTTOM2TOP) for (let i = iter.length; i > 0; i--) {
2457
2501
  child = iter[i - 1];
2458
- if (gobj_is_destroying(child)) continue;
2459
- 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);
2460
2504
  if (ret < 0) return ret;
2461
2505
  if (gobj_is_destroying(child)) continue;
2506
+ seen.add(child);
2462
2507
  ret = walk_ret(cb_walking(child, user_data, user_data2));
2463
2508
  if (ret < 0) return ret;
2464
2509
  }
2465
2510
  else for (let i = 0; i < iter.length; i++) {
2466
2511
  child = iter[i];
2467
- if (gobj_is_destroying(child)) continue;
2512
+ if (walk_steps_over(child, owner, seen)) continue;
2513
+ seen.add(child);
2468
2514
  ret = walk_ret(cb_walking(child, user_data, user_data2));
2469
2515
  if (ret < 0) return ret;
2470
2516
  else if (ret === 0 && !gobj_is_destroying(child)) {
2471
- ret = rc_walk_by_tree(child.dl_children, walk_type, cb_walking, user_data, user_data2);
2517
+ ret = rc_walk_by_tree(child, seen, walk_type, cb_walking, user_data, user_data2);
2472
2518
  if (ret < 0) return ret;
2473
2519
  }
2474
2520
  }
@@ -6101,7 +6147,10 @@ var gobjJs = (function(exports) {
6101
6147
  * for ever; a new corrupt entry is a new WARNING. Its text is never
6102
6148
  * shown (JSON.parse() quotes it in its error, and an entry can hold a
6103
6149
  * token). A store that cannot be read (blocked) is warned once per
6104
- * key, and the default is NOT written there. Outside a browser: the
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
6105
6154
  * default.
6106
6155
  ********************************************/
6107
6156
  function kw_get_local_storage_value(key, default_value, create = false) {
@@ -6115,6 +6164,7 @@ var gobjJs = (function(exports) {
6115
6164
  warn_local_storage_once(key, `${where} cannot be read (storage blocked?): the default is used`);
6116
6165
  return default_value;
6117
6166
  }
6167
+ let went_well = true;
6118
6168
  let value = null;
6119
6169
  if (text !== null) try {
6120
6170
  value = JSON.parse(text);
@@ -6123,17 +6173,25 @@ var gobjJs = (function(exports) {
6123
6173
  try {
6124
6174
  store.removeItem(key);
6125
6175
  log_warning(`${where} does not hold json: removed, the default is used`);
6126
- local_storage_warned.delete(key);
6127
6176
  } catch (e2) {
6177
+ went_well = false;
6128
6178
  warn_local_storage_once(key, `${where} does not hold json and cannot be removed: the default is used`);
6129
6179
  }
6130
- text = void 0;
6131
6180
  }
6132
- if (text !== void 0) local_storage_warned.delete(key);
6133
6181
  if (value === null) {
6134
- if (create) kw_set_local_storage_value(key, default_value);
6135
- 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
+ }
6136
6193
  }
6194
+ if (went_well) local_storage_warned.delete(key);
6137
6195
  return value;
6138
6196
  }
6139
6197
  /********************************************
@@ -7373,29 +7431,62 @@ var gobjJs = (function(exports) {
7373
7431
  return -1;
7374
7432
  }
7375
7433
  /************************************************************
7434
+ * A new uuid v4 (RFC 9562).
7376
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.
7377
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;
7378
7465
  function node_uuid() {
7379
- let uuid = "";
7466
+ if (uuid_of_this_page) return uuid_of_this_page;
7380
7467
  const store = local_store();
7381
- if (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();
7382
7482
  try {
7383
- uuid = store.getItem("host_uuid");
7483
+ store.setItem("host_uuid", uuid);
7384
7484
  } catch (e) {
7385
- uuid = null;
7386
- warn_local_storage_once("host_uuid", "node_uuid(): localStorage cannot be read (storage blocked?): a uuid of this page only");
7485
+ warn_local_storage_once("host_uuid", "node_uuid(): localStorage cannot be written (storage full or blocked?): a uuid of this page only");
7387
7486
  }
7388
- if (!uuid && window.crypto?.randomUUID) {
7389
- uuid = window.crypto.randomUUID();
7390
- try {
7391
- store.setItem("host_uuid", uuid);
7392
- } catch (e) {
7393
- warn_local_storage_once("host_uuid", "node_uuid(): localStorage cannot be written (storage full or blocked?): a uuid of this page only");
7394
- }
7395
- }
7396
- } else if (typeof globalThis !== "undefined" && globalThis.crypto?.randomUUID) uuid = globalThis.crypto.randomUUID();
7397
- else uuid = "00000000-0000-0000-0000-000000000000";
7398
- return uuid;
7487
+ }
7488
+ uuid_of_this_page = uuid;
7489
+ return uuid_of_this_page;
7399
7490
  }
7400
7491
  /************************************************************
7401
7492
  * Build a cleaned name
@@ -8680,7 +8771,7 @@ var gobjJs = (function(exports) {
8680
8771
  SDATA(data_type_t.DTP_STRING, "cert_pem", sdata_flag_t.SDF_PERSIST, "", "SSL server certification, PEM str format"),
8681
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."),
8682
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."),
8683
- SDATA(data_type_t.DTP_INTEGER, "timeout_retry", sdata_flag_t.SDF_RD, "5000", "First reconnect delay (ms), at least 1000. 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"),
8684
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"),
8685
8776
  SDATA(data_type_t.DTP_INTEGER, "timeout_idack", sdata_flag_t.SDF_RD, "5000", "timeout waiting idAck"),
8686
8777
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, 0, "subscriber of output-events. If null then subscriber is the parent"),