@yuneta/gobj-js 7.25.8 → 7.26.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.
@@ -24,8 +24,8 @@ function vsprintf(fmt, argv) {
24
24
  return sprintf.apply(null, [fmt].concat(argv || []));
25
25
  }
26
26
  function sprintf_format(parse_tree, argv) {
27
- var cursor = 1, tree_length = parse_tree.length, arg, output = "", i, k, ph, pad, pad_character, pad_length, is_positive, sign;
28
- for (i = 0; i < tree_length; i++) if (typeof parse_tree[i] === "string") output += parse_tree[i];
27
+ var cursor = 1, tree_length = parse_tree.length, arg, output = "", i = 0, k, ph, pad, pad_character, pad_length, is_positive, sign;
28
+ for (; i < tree_length; i++) if (typeof parse_tree[i] === "string") output += parse_tree[i];
29
29
  else if (typeof parse_tree[i] === "object") {
30
30
  ph = parse_tree[i];
31
31
  if (ph.keys) {
@@ -158,7 +158,7 @@ function command_parser(gobj, command, kw, src) {}
158
158
  function build_command_response(gobj, result, comment, schema, data) {
159
159
  return {
160
160
  result,
161
- comment: comment || {},
161
+ comment: comment || "",
162
162
  schema: schema || {},
163
163
  data: data || []
164
164
  };
@@ -212,7 +212,7 @@ var __global_command_parser_fn__ = null;
212
212
  var __global_stats_parser_fn__ = null;
213
213
  var dl_global_event_types = [];
214
214
  var _gclass_register = Object.create(null);
215
- var __jn_services__ = {};
215
+ var __jn_services__ = Object.create(null);
216
216
  var __yuno__ = null;
217
217
  var __default_service__ = null;
218
218
  var __inside__ = 0;
@@ -821,7 +821,8 @@ function set_default(gobj, sdata, it) {
821
821
  if (!is_object(jn_value)) jn_value = {};
822
822
  break;
823
823
  case data_type_t.DTP_JSON:
824
- if (is_string(svalue)) try {
824
+ if (svalue === "" || svalue === void 0) jn_value = null;
825
+ else if (is_string(svalue)) try {
825
826
  jn_value = JSON.parse(svalue);
826
827
  } catch (error) {
827
828
  log_error(`JSON parse error: ${error.message}`);
@@ -856,11 +857,17 @@ function json2item(gobj, sdata, it, jn_value_) {
856
857
  break;
857
858
  case data_type_t.DTP_INTEGER:
858
859
  jn_value2 = parseInt(jn_value_);
859
- if (isNaN(jn_value2)) log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
860
+ if (isNaN(jn_value2)) {
861
+ log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
862
+ return -1;
863
+ }
860
864
  break;
861
865
  case data_type_t.DTP_REAL:
862
866
  jn_value2 = Number(jn_value_);
863
- if (isNaN(jn_value2)) log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
867
+ if (isNaN(jn_value2)) {
868
+ log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
869
+ return -1;
870
+ }
864
871
  break;
865
872
  case data_type_t.DTP_LIST:
866
873
  if (is_string(jn_value_)) try {
@@ -887,15 +894,11 @@ function json2item(gobj, sdata, it, jn_value_) {
887
894
  }
888
895
  break;
889
896
  case data_type_t.DTP_JSON:
897
+ jn_value2 = jn_value_;
890
898
  if (is_string(jn_value_)) try {
891
899
  jn_value2 = JSON.parse(jn_value_);
892
900
  } catch (error) {
893
- log_error(`JSON parse error: ${error.message}`);
894
- }
895
- else jn_value2 = jn_value_;
896
- if (!is_array(jn_value2) && !is_object(jn_value2)) {
897
- log_error(`attr must be a json dict/list: ${it.name}`);
898
- return -1;
901
+ jn_value2 = jn_value_;
899
902
  }
900
903
  break;
901
904
  case data_type_t.DTP_POINTER: jn_value2 = jn_value_;
@@ -1200,6 +1203,10 @@ function gobj_default_service() {
1200
1203
  * find a service
1201
1204
  ************************************************************/
1202
1205
  function gobj_find_service(service_name, verbose) {
1206
+ if (!is_string(service_name)) {
1207
+ log_error(`gobj_find_service(): service name must be a string: ${service_name}`);
1208
+ return null;
1209
+ }
1203
1210
  service_name = service_name.toLowerCase();
1204
1211
  let service_gobj = __jn_services__[service_name];
1205
1212
  if (!service_gobj) {
@@ -1334,7 +1341,7 @@ function write_json_parameters(gobj, kw, jn_global) {
1334
1341
  let new_kw = kw;
1335
1342
  if (json2sdata(hs, new_kw, -1, gobj.gclass.gclass_flag & gclass_flag_t.gcflag_ignore_unknown_attrs ? 0 : print_attr_not_found, gobj) < 0) {
1336
1343
  log_error(`${gobj_short_name(gobj)} json2data() FAILED`);
1337
- trace_json(new_kw);
1344
+ trace_json_masked(new_kw);
1338
1345
  }
1339
1346
  }
1340
1347
  /************************************************************
@@ -1357,9 +1364,10 @@ function gobj_create2(gobj_name, gclass_name, kw, parent, gobj_flag) {
1357
1364
  return null;
1358
1365
  }
1359
1366
  if (is_string(parent)) {
1360
- parent = gobj_find_service(parent);
1367
+ const parent_name = parent;
1368
+ parent = gobj_find_service(parent_name);
1361
1369
  if (!parent) {
1362
- log_warning(`parent service not found: ${parent}`);
1370
+ log_warning(`parent service not found: ${parent_name}`);
1363
1371
  return null;
1364
1372
  }
1365
1373
  }
@@ -1472,6 +1480,7 @@ function gobj_destroy(gobj) {
1472
1480
  parent.gclass.gmt.mt_child_removed(parent, gobj);
1473
1481
  }
1474
1482
  if (gobj.gobj_flag & gobj_flag_t.gobj_flag_service) _deregister_service(gobj);
1483
+ if (gobj === __default_service__) __default_service__ = null;
1475
1484
  gobj_unsubscribe_list(gobj, gobj.dl_subscriptions, true);
1476
1485
  gobj_unsubscribe_list(gobj, gobj.dl_subscribings, true);
1477
1486
  if (parent) {
@@ -1817,7 +1826,7 @@ function gobj_set_bottom_gobj(gobj, bottom_gobj) {
1817
1826
  log_error(`gobj NULL of bad type`);
1818
1827
  return null;
1819
1828
  }
1820
- if (gobj.bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1829
+ if (gobj.bottom_gobj && bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1821
1830
  gobj.bottom_gobj = bottom_gobj;
1822
1831
  }
1823
1832
  /***************************************************************************
@@ -2232,7 +2241,7 @@ function gobj_write_attr(gobj, path, value, src) {
2232
2241
  jn_attrs[key] = value;
2233
2242
  if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(key)) gobj.priv[key] = value;
2234
2243
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2235
- if (gobj.mt_writing) gobj.mt_writing(gobj, key);
2244
+ if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2236
2245
  }
2237
2246
  return 0;
2238
2247
  }
@@ -2256,8 +2265,8 @@ function sdata_write_default_values(gobj, include_flag, exclude_flag) {
2256
2265
  if (!it.name) continue;
2257
2266
  if (exclude_flag && it.flag & exclude_flag) continue;
2258
2267
  if (include_flag === -1 || it.flag & include_flag) {
2259
- const value = set_default(gobj, gobj.jn_attrs, it);
2260
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = value;
2268
+ set_default(gobj, gobj.jn_attrs, it);
2269
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = gobj.jn_attrs[it.name];
2261
2270
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, it.name);
2262
2271
  }
2263
2272
  }
@@ -2291,7 +2300,7 @@ function gobj_write_bool_attr(gobj, name, value) {
2291
2300
  let hs = gobj_hsdata2(gobj, name, false);
2292
2301
  if (hs) {
2293
2302
  hs[name] = parseBoolean(value);
2294
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2303
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2295
2304
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2296
2305
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2297
2306
  }
@@ -2307,7 +2316,7 @@ function gobj_write_integer_attr(gobj, name, value) {
2307
2316
  let hs = gobj_hsdata2(gobj, name, false);
2308
2317
  if (hs) {
2309
2318
  hs[name] = parseInt(value);
2310
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2319
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2311
2320
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2312
2321
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2313
2322
  }
@@ -2323,7 +2332,7 @@ function gobj_write_str_attr(gobj, name, value) {
2323
2332
  let hs = gobj_hsdata2(gobj, name, false);
2324
2333
  if (hs) {
2325
2334
  hs[name] = String(value);
2326
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2335
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2327
2336
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2328
2337
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2329
2338
  }
@@ -2377,15 +2386,15 @@ function gobj_change_parent(gobj, parent) {
2377
2386
  log_error(`${gobj_short_name(gobj)}: gobj_change_parent() BAD TYPE of parent`);
2378
2387
  return -1;
2379
2388
  }
2380
- if (gobj.parent && gobj.parent.mt_child_removed) gobj.parent.mt_child_removed(gobj.parent, gobj);
2389
+ if (gobj.parent && gobj.parent.gclass.gmt.mt_child_removed) gobj.parent.gclass.gmt.mt_child_removed(gobj.parent, gobj);
2381
2390
  if (gobj.parent) {
2382
2391
  _remove_child(gobj.parent, gobj);
2383
2392
  gobj.parent = null;
2384
2393
  }
2385
2394
  _add_child(parent, gobj);
2386
- if (parent.mt_child_added) {
2395
+ if (parent.gclass.gmt.mt_child_added) {
2387
2396
  if (__trace_gobj_create_delete__(gobj)) log_debug(sprintf("👦👦🔵 child_added(%s): %s", gobj_full_name(parent), gobj_short_name(gobj)));
2388
- parent.mt_child_added(parent, gobj);
2397
+ parent.gclass.gmt.mt_child_added(parent, gobj);
2389
2398
  }
2390
2399
  return 0;
2391
2400
  }
@@ -2497,9 +2506,19 @@ function gobj_deliver_posted_events() {
2497
2506
  let snapshot = __dl_posted_events__;
2498
2507
  __dl_posted_events__ = [];
2499
2508
  let delivered = 0;
2500
- for (let msg of snapshot) {
2509
+ for (let i = 0; i < snapshot.length; i++) {
2510
+ const msg = snapshot[i];
2501
2511
  if (msg.dst.obflag & (obflag_t.obflag_destroyed | obflag_t.obflag_destroying)) continue;
2502
- gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2512
+ try {
2513
+ gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2514
+ } catch (e) {
2515
+ __dl_posted_events__ = snapshot.slice(i + 1).concat(__dl_posted_events__);
2516
+ if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
2517
+ __posted_events_scheduled__ = true;
2518
+ setTimeout(gobj_deliver_posted_events, 0);
2519
+ }
2520
+ throw e;
2521
+ }
2503
2522
  delivered++;
2504
2523
  }
2505
2524
  if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
@@ -2549,7 +2568,7 @@ function gobj_send_event(dst, event, kw, src) {
2549
2568
  else trace_machine(sprintf("🔃 mach(%s%s), st: %s, ev: %s, from(%s%s)", !dst.running ? "!!" : "", gobj_short_name(dst), state.state_name, event ? event : "", src && !src.running ? "!!" : "", gobj_short_name(src)));
2550
2569
  if (kw) {
2551
2570
  if (__trace_gobj_ev_kw__(dst)) {
2552
- if (json_object_size(kw)) trace_json(kw);
2571
+ if (json_object_size(kw)) trace_json_masked(kw);
2553
2572
  }
2554
2573
  }
2555
2574
  }
@@ -2569,7 +2588,7 @@ function gobj_send_event(dst, event, kw, src) {
2569
2588
  }
2570
2589
  if (kw) {
2571
2590
  if (__trace_gobj_ev_kw__(dst)) {
2572
- if (json_object_size(kw)) trace_json(kw);
2591
+ if (json_object_size(kw)) trace_json_masked(kw);
2573
2592
  }
2574
2593
  }
2575
2594
  }
@@ -2577,15 +2596,40 @@ function gobj_send_event(dst, event, kw, src) {
2577
2596
  let ret = -1;
2578
2597
  try {
2579
2598
  if (event_action.action) ret = event_action.action(dst, event, kw, src);
2580
- } finally {}
2599
+ } catch (e) {
2600
+ __inside__--;
2601
+ if (!(e && e.__gobj_logged__)) {
2602
+ log_error(`${gobj_short_name(dst)}: action of ${event} in ${dst.current_state.state_name} THREW: ${e && e.stack ? e.stack : e}`);
2603
+ try {
2604
+ e.__gobj_logged__ = true;
2605
+ } catch (e2) {}
2606
+ }
2607
+ throw e;
2608
+ }
2581
2609
  if (tracea && __trace_machine_format__ !== 1 && !(dst.obflag & obflag_t.obflag_destroyed)) trace_machine(sprintf("<- mach(%s%s^%s), st: %s, ev: %s, ret: %d", !dst.running ? "!!" : "", gobj_gclass_name(dst), gobj_name(dst), dst.current_state.state_name, event ? event : "", is_number(ret) ? ret : 0));
2582
2610
  __inside__--;
2583
2611
  return ret;
2584
2612
  }
2585
2613
  var subs_flag_t = Object.freeze({
2586
- __hard_subscription__: 1,
2587
- __own_event__: 2
2614
+ __rename_event_name__: 1,
2615
+ __hard_subscription__: 2,
2616
+ __own_event__: 4
2588
2617
  });
2618
+ /***************************************************************************
2619
+ * The event type of `event_name` in any registered gclass, null if none.
2620
+ * The C kernel's gobj_find_event_type(): any case, gclasses only.
2621
+ ***************************************************************************/
2622
+ function _find_event_type_in_gclasses(event_name) {
2623
+ let lower = String(event_name).toLowerCase();
2624
+ for (let gclass_name in _gclass_register) {
2625
+ let dl = _gclass_register[gclass_name].dl_events;
2626
+ for (let i = 0; i < dl.length; i++) {
2627
+ let event_type = dl[i];
2628
+ if (event_type.event_name && event_type.event_name.toLowerCase() === lower) return event_type;
2629
+ }
2630
+ }
2631
+ return null;
2632
+ }
2589
2633
  var subscription_desc = [
2590
2634
  SDATA(data_type_t.DTP_POINTER, "publisher", 0, null, "publisher gobj"),
2591
2635
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "subscriber gobj"),
@@ -2618,6 +2662,21 @@ function _create_subscription(publisher, event, kw, subscriber) {
2618
2662
  if (json_size(__config__) > 0) {
2619
2663
  let kw_clone = json_deep_copy(__config__);
2620
2664
  json_object_set_new(subs, "__config__", kw_clone);
2665
+ let renamed_event = kw_get_str(publisher, kw_clone, "__rename_event_name__", "");
2666
+ if (!empty_string(renamed_event)) {
2667
+ let event_type = _find_event_type_in_gclasses(renamed_event);
2668
+ if (event_type) {
2669
+ json_object_set_new(subs, "renamed_event", event_type.event_name);
2670
+ delete kw_clone.__rename_event_name__;
2671
+ subs_flag |= subs_flag_t.__rename_event_name__;
2672
+ let kw_global = kw_get_dict(publisher, subs, "__global__", null);
2673
+ if (!is_object(kw_global)) {
2674
+ kw_global = {};
2675
+ json_object_set_new(subs, "__global__", kw_global);
2676
+ }
2677
+ kw_global.__original_event_name__ = event;
2678
+ } else log_error(`${gobj_short_name(publisher)}: EVENT NOT FOUND (__rename_event_name__: ${renamed_event})`);
2679
+ }
2621
2680
  if (kw_has_key(kw_clone, "__hard_subscription__")) {
2622
2681
  let hard_subscription = kw_get_bool(publisher, kw_clone, "__hard_subscription__", false);
2623
2682
  json_object_del(kw_clone, "__hard_subscription__");
@@ -2637,9 +2696,52 @@ function _create_subscription(publisher, event, kw, subscriber) {
2637
2696
  return subs;
2638
2697
  }
2639
2698
  /***************************************************************************
2699
+ * The kw to find the subscriptions a (un)subscription `kw` names: `kw` as
2700
+ * _create_subscription() stores it, as the C kernel's
2701
+ * _subscription_match_kw(). The three `__config__` keys the framework
2702
+ * reads (__hard_subscription__, __own_event__, __rename_event_name__)
2703
+ * become the subs_flag and are taken out of the stored `__config__`, and
2704
+ * a renamed event adds `__original_event_name__` to the stored
2705
+ * `__global__`: with `kw` as it came, the same kw matched no subscription,
2706
+ * a repeat of it was made twice (the subscriber got each event twice) and
2707
+ * its withdrawal found nothing. A known renamed event is returned in
2708
+ * `renamed_event`, which _match_subscription() compares: a plain and a
2709
+ * renamed subscription, or two renames of one event, are two. A rename
2710
+ * that no gclass declares stays in the stored `__config__`, and in this
2711
+ * one. Up to gobj-js 7.25.8 the kw was compared as it came, and
2712
+ * `__rename_event_name__` always stayed in the stored `__config__`: a
2713
+ * plain kw with the rest of that __config__ found no repeat (two
2714
+ * subscriptions where C has one).
2715
+ * Return {kw, renamed_event}; renamed_event is "" when kw does not rename.
2716
+ ***************************************************************************/
2717
+ function _subscription_match_kw(kw, event) {
2718
+ let __config__ = kw ? kw.__config__ : null;
2719
+ if (!is_object(__config__) || !kw_has_key(__config__, "__hard_subscription__") && !kw_has_key(__config__, "__own_event__") && !kw_has_key(__config__, "__rename_event_name__")) return {
2720
+ kw,
2721
+ renamed_event: ""
2722
+ };
2723
+ let kw_match = Object.assign({}, kw);
2724
+ let config_match = Object.assign({}, __config__);
2725
+ kw_match.__config__ = config_match;
2726
+ let renamed_event = "";
2727
+ let rename = config_match.__rename_event_name__;
2728
+ let event_type = typeof rename === "string" && !empty_string(rename) ? _find_event_type_in_gclasses(rename) : null;
2729
+ if (event_type) {
2730
+ renamed_event = event_type.event_name;
2731
+ delete config_match.__rename_event_name__;
2732
+ if (event && json_size(kw_match.__global__) > 0) kw_match.__global__ = Object.assign({}, kw_match.__global__, { __original_event_name__: event });
2733
+ }
2734
+ delete config_match.__hard_subscription__;
2735
+ delete config_match.__own_event__;
2736
+ return {
2737
+ kw: kw_match,
2738
+ renamed_event
2739
+ };
2740
+ }
2741
+ /***************************************************************************
2640
2742
  * Match subscription
2641
2743
  ***************************************************************************/
2642
- function _match_subscription(subs, publisher, event, kw, subscriber) {
2744
+ function _match_subscription(subs, publisher, event, renamed_event, kw, subscriber) {
2643
2745
  let __config__ = kw_get_dict(publisher, kw, "__config__", null);
2644
2746
  let __global__ = kw_get_dict(publisher, kw, "__global__", null);
2645
2747
  let __local__ = kw_get_dict(publisher, kw, "__local__", null);
@@ -2654,6 +2756,9 @@ function _match_subscription(subs, publisher, event, kw, subscriber) {
2654
2756
  if (event) {
2655
2757
  if (event !== kw_get_str(null, subs, "event", "")) match = false;
2656
2758
  }
2759
+ if (renamed_event) {
2760
+ if (renamed_event !== kw_get_str(null, subs, "renamed_event", "")) match = false;
2761
+ }
2657
2762
  if (json_size(__config__) > 0) {
2658
2763
  let kw_config = kw_get_dict(null, subs, "__config__", null);
2659
2764
  if (json_size(kw_config) > 0) {
@@ -2688,26 +2793,29 @@ function _match_subscription(subs, publisher, event, kw, subscriber) {
2688
2793
  * kw (__config__, __global__, __local__, __filter__),
2689
2794
  * subscriber
2690
2795
  ***************************************************************************/
2691
- function _find_subscriptions(dl_subs, publisher, event, kw, subscriber) {
2796
+ function _find_subscriptions(dl_subs, publisher, event, renamed_event, kw, subscriber) {
2692
2797
  let iter = [];
2693
2798
  for (let i = 0; i < dl_subs.length; i++) {
2694
2799
  let subs = dl_subs[i];
2695
- if (_match_subscription(subs, publisher, event, kw, subscriber)) json_array_append(iter, subs);
2800
+ if (_match_subscription(subs, publisher, event, renamed_event, kw, subscriber)) json_array_append(iter, subs);
2696
2801
  }
2697
2802
  return iter;
2698
2803
  }
2699
2804
  /***************************************************************************
2700
- * Find idx of subscription in dl_subs, -1 not found
2805
+ * Find idx of the subscription `subs` itself in dl_subs, -1 not found.
2806
+ * Up to gobj-js 7.25.8 it was the first entry whose fields matched `subs`:
2807
+ * a subscription already removed took a live one that matched it (a plain
2808
+ * one matches every other of its event and subscriber).
2701
2809
  ***************************************************************************/
2702
- function _get_subs_idx(dl_subs, publisher, event, kw, subscriber) {
2703
- for (let i = 0; i < dl_subs.length; i++) {
2704
- let subs = dl_subs[i];
2705
- if (_match_subscription(subs, publisher, event, kw, subscriber)) return i;
2706
- }
2707
- return -1;
2810
+ function _get_subs_idx(dl_subs, subs) {
2811
+ return dl_subs.indexOf(subs);
2708
2812
  }
2709
2813
  /***************************************************************************
2710
- * Delete subscription in publisher and subscriber
2814
+ * Delete subscription in publisher and subscriber.
2815
+ * Return 0 removed, -1 a hard one kept (only `force` removes it), 1 it
2816
+ * was not there any more: already removed, as a stale reference is, or by
2817
+ * the mt_subscription_deleted() of an earlier entry of the same list. The
2818
+ * caller says which is an error. As in C.
2711
2819
  ***************************************************************************/
2712
2820
  function _delete_subscription(gobj, subs, force, not_inform) {
2713
2821
  let publisher = kw_get_pointer(gobj, subs, "publisher", null, kw_flag_t.KW_REQUIRED);
@@ -2716,7 +2824,12 @@ function _delete_subscription(gobj, subs, force, not_inform) {
2716
2824
  if (kw_get_int(gobj, subs, "subs_flag", null, kw_flag_t.KW_REQUIRED) & subs_flag_t.__hard_subscription__ ? 1 : 0) {
2717
2825
  if (!force) return -1;
2718
2826
  }
2719
- if (__trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher)) {
2827
+ let tracea = __trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher);
2828
+ if (_get_subs_idx(publisher.dl_subscriptions, subs) < 0) {
2829
+ if (tracea) trace_machine(sprintf("💜💜👎 unsubscribing event '%s': publisher %s, subscriber'%s', ALREADY REMOVED", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2830
+ return 1;
2831
+ }
2832
+ if (tracea) {
2720
2833
  trace_machine(sprintf("💜💜👎 unsubscribing event '%s': publisher %s, subscriber'%s'", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2721
2834
  trace_json(subs);
2722
2835
  }
@@ -2725,13 +2838,13 @@ function _delete_subscription(gobj, subs, force, not_inform) {
2725
2838
  if (publisher.gclass.gmt.mt_subscription_deleted) publisher.gclass.gmt.mt_subscription_deleted(publisher, subs);
2726
2839
  }
2727
2840
  }
2728
- let idx = _get_subs_idx(publisher.dl_subscriptions, publisher, event, subs, subscriber);
2841
+ let idx = _get_subs_idx(publisher.dl_subscriptions, subs);
2729
2842
  if (idx >= 0) json_array_remove(publisher.dl_subscriptions, idx);
2730
2843
  else {
2731
2844
  log_error(`${gobj_short_name(gobj)}: subscription in publisher not found`);
2732
2845
  trace_json(subs);
2733
2846
  }
2734
- idx = _get_subs_idx(subscriber.dl_subscribings, publisher, event, subs, subscriber);
2847
+ idx = _get_subs_idx(subscriber.dl_subscribings, subs);
2735
2848
  if (idx >= 0) json_array_remove(subscriber.dl_subscribings, idx);
2736
2849
  else {
2737
2850
  log_error(`${gobj_short_name(gobj)}: subscription in subscriber not found`);
@@ -2774,7 +2887,17 @@ function gobj_subscribe_event(publisher, event, kw, subscriber) {
2774
2887
  }
2775
2888
  }
2776
2889
  } else event = "";
2777
- let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, kw, subscriber);
2890
+ let match = _subscription_match_kw(kw, event);
2891
+ let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, match.renamed_event, match.kw, subscriber);
2892
+ let subs_hard = null;
2893
+ for (let i = 0; i < dl_subs.length; i++) if (kw_get_int(publisher, dl_subs[i], "subs_flag", 0, kw_flag_t.KW_REQUIRED) & subs_flag_t.__hard_subscription__) {
2894
+ subs_hard = dl_subs[i];
2895
+ break;
2896
+ }
2897
+ if (subs_hard) {
2898
+ log_warning(`${gobj_short_name(publisher)}: Hard subscription REPEATED, the one there is kept and returned (event: ${event}, subscriber: ${gobj_short_name(subscriber)})`);
2899
+ return subs_hard;
2900
+ }
2778
2901
  if (json_array_size(dl_subs) > 0) {
2779
2902
  log_error(`${gobj_short_name(publisher)}: ${json_array_size(dl_subs)} subscription(s) REPEATED, will be deleted and override`);
2780
2903
  trace_json(dl_subs, "subscription REPEATED");
@@ -2789,7 +2912,7 @@ function gobj_subscribe_event(publisher, event, kw, subscriber) {
2789
2912
  json_array_append(subscriber.dl_subscribings, subs);
2790
2913
  if (__trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher)) {
2791
2914
  trace_machine(sprintf("💜💜👍 subscribing event '%s', publisher '%s', subscriber %s", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2792
- if (kw && (__trace_gobj_ev_kw__(subscriber) || __trace_gobj_ev_kw__(publisher))) trace_json(kw);
2915
+ if (kw && (__trace_gobj_ev_kw__(subscriber) || __trace_gobj_ev_kw__(publisher))) trace_json_masked(kw);
2793
2916
  }
2794
2917
  if (publisher.gclass.gmt.mt_subscription_added) {
2795
2918
  let result = publisher.gclass.gmt.mt_subscription_added(publisher, subs);
@@ -2831,16 +2954,22 @@ function gobj_unsubscribe_event(publisher, event, kw, subscriber) {
2831
2954
  }
2832
2955
  }
2833
2956
  } else event = "";
2834
- let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, kw, subscriber);
2957
+ let match = _subscription_match_kw(kw, event);
2958
+ let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, match.renamed_event, match.kw, subscriber);
2835
2959
  let deleted = 0;
2960
+ let kept_hard = 0;
2961
+ let already_removed = 0;
2836
2962
  for (let i = 0; i < dl_subs.length; i++) {
2837
2963
  let subs = dl_subs[i];
2838
- _delete_subscription(publisher, subs, false, false);
2839
- deleted++;
2964
+ let ret = _delete_subscription(publisher, subs, false, false);
2965
+ if (ret === 0) deleted++;
2966
+ else if (ret < 0) kept_hard++;
2967
+ else already_removed++;
2840
2968
  }
2841
- if (!deleted) {
2969
+ if (kept_hard) log_warning(`${gobj_short_name(publisher)}: Hard subscription not removed, only gobj_unsubscribe_list() with force removes it (event: ${event}, hard: ${kept_hard})`);
2970
+ else if (!deleted && !already_removed) {
2842
2971
  log_warning(`${gobj_short_name(publisher)}: No subscription found`);
2843
- trace_json(kw, "No subscription found");
2972
+ trace_json_masked(kw, "No subscription found");
2844
2973
  }
2845
2974
  return 0;
2846
2975
  }
@@ -2849,10 +2978,12 @@ function gobj_unsubscribe_event(publisher, event, kw, subscriber) {
2849
2978
  ***************************************************************************/
2850
2979
  function gobj_unsubscribe_list(gobj, dl_subs, force) {
2851
2980
  let dl = [...dl_subs];
2981
+ let already_removed = 0;
2852
2982
  for (let i = 0; i < dl.length; i++) {
2853
2983
  let subs = dl[i];
2854
- _delete_subscription(gobj, subs, force, false);
2984
+ if (_delete_subscription(gobj, subs, force, false) > 0) already_removed++;
2855
2985
  }
2986
+ if (already_removed) log_warning(`${gobj_short_name(gobj)}: Subscription(s) already removed, nothing to remove (count: ${already_removed})`);
2856
2987
  return 0;
2857
2988
  }
2858
2989
  /***************************************************************************
@@ -2864,7 +2995,7 @@ function gobj_unsubscribe_list(gobj, dl_subs, force) {
2864
2995
  ***************************************************************************/
2865
2996
  function gobj_find_subscriptions(publisher, event, kw, subscriber) {
2866
2997
  if (kw == null) kw = {};
2867
- return _find_subscriptions(publisher.dl_subscriptions, publisher, event, kw, subscriber);
2998
+ return _find_subscriptions(publisher.dl_subscriptions, publisher, event, null, kw, subscriber);
2868
2999
  }
2869
3000
  /***************************************************************************
2870
3001
  *
@@ -2903,7 +3034,7 @@ function gobj_list_subscriptions(gobj2view) {
2903
3034
  * subscriber
2904
3035
  ***************************************************************************/
2905
3036
  function gobj_find_subscribings(subscriber, event, kw, publisher) {
2906
- return _find_subscriptions(subscriber.dl_subscribings, publisher, event, kw, subscriber);
3037
+ return _find_subscriptions(subscriber.dl_subscribings, publisher, event, null, kw, subscriber);
2907
3038
  }
2908
3039
  /***************************************************************************
2909
3040
  * Return the sum of returns of gobj_send_event
@@ -2929,7 +3060,7 @@ function gobj_publish_event(publisher, event, kw) {
2929
3060
  if (tracea) {
2930
3061
  if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔝 %s %s%s %s", event ? event : "", !publisher.running ? "!!" : "", gobj_short_name(publisher), publisher.current_state.state_name));
2931
3062
  else trace_machine(sprintf("🔝🔝 mach(%s%s^%s), st: %s, ev: %s%s%s", !publisher.running ? "!!" : "", gobj_gclass_name(publisher), gobj_name(publisher), publisher.current_state.state_name, "", event ? event : "", ""));
2932
- if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw)) trace_json(kw);
3063
+ if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw)) trace_json_masked(kw);
2933
3064
  }
2934
3065
  if (publisher.gclass.gmt.mt_publish_event) {
2935
3066
  let topublish = publisher.gclass.gmt.mt_publish_event(publisher, event, kw);
@@ -2954,6 +3085,8 @@ function gobj_publish_event(publisher, event, kw) {
2954
3085
  let __global__ = kw_get_dict(publisher, subs, "__global__", null, 0);
2955
3086
  let __local__ = kw_get_dict(publisher, subs, "__local__", null, 0);
2956
3087
  let __filter__ = kw_get_dict_value(publisher, subs, "__filter__", null, 0);
3088
+ let event_name = subs.renamed_event;
3089
+ if (empty_string(event_name)) event_name = event;
2957
3090
  let topublish = 1;
2958
3091
  if (publisher.gclass.gmt.mt_publication_filter) topublish = publisher.gclass.gmt.mt_publication_filter(publisher, event, kw, subscriber);
2959
3092
  else if (json_size(__filter__) > 0) {
@@ -2977,11 +3110,11 @@ function gobj_publish_event(publisher, event, kw) {
2977
3110
  if (__config__) {}
2978
3111
  if (json_size(__global__) > 0) json_object_update(kw2publish, json_deep_copy(__global__));
2979
3112
  if (tracea) {
2980
- if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event ? event : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
2981
- else trace_machine(sprintf("🔝🔄 mach(%s%s), st: %s, ev: %s, from(%s%s)", !subscriber.running ? "!!" : "", gobj_short_name(subscriber), gobj_current_state(subscriber), event ? event : "", publisher && !publisher.running ? "!!" : "", gobj_short_name(publisher)));
2982
- if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json(kw2publish);
3113
+ if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event_name ? event_name : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
3114
+ else trace_machine(sprintf("🔝🔄 mach(%s%s), st: %s, ev: %s (%s), from(%s%s)", !subscriber.running ? "!!" : "", gobj_short_name(subscriber), gobj_current_state(subscriber), event ? event : "", event_name ? event_name : "", publisher && !publisher.running ? "!!" : "", gobj_short_name(publisher)));
3115
+ if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json_masked(kw2publish);
2983
3116
  }
2984
- let ret_ = gobj_send_event(subscriber, event, kw2publish, publisher);
3117
+ let ret_ = gobj_send_event(subscriber, event_name, kw2publish, publisher);
2985
3118
  if (ret_ < 0 && subs_flag & subs_flag_t.__own_event__) {
2986
3119
  sent_count = -1;
2987
3120
  break;
@@ -3005,8 +3138,9 @@ function gobj_command(gobj, command, kw, src) {
3005
3138
  return -1;
3006
3139
  }
3007
3140
  if (is_commands_tracing(gobj) || is_machine_tracing(gobj, null) && !is_machine_not_tracing(src, null)) {
3008
- trace_machine(sprintf("🌀🌀 mach(%s%s), cmd: %s, src: %s", !gobj_is_running(gobj) ? "!!" : "", gobj_short_name(gobj), command, gobj_short_name(src)));
3009
- if (__trace_gobj_ev_kw__(gobj)) trace_json(kw);
3141
+ let command_shown = mask_secrets_inline(command);
3142
+ trace_machine(sprintf("🌀🌀 mach(%s%s), cmd: %s, src: %s", !gobj_is_running(gobj) ? "!!" : "", gobj_short_name(gobj), command_shown !== null ? command_shown : command, gobj_short_name(src)));
3143
+ if (__trace_gobj_ev_kw__(gobj)) trace_json_masked(kw);
3010
3144
  }
3011
3145
  if (gobj.gclass.gmt.mt_command_parser) return gobj.gclass.gmt.mt_command_parser(gobj, command, kw, src);
3012
3146
  if (gobj.gclass.command_table) {
@@ -3142,10 +3276,22 @@ function set_remote_log_functions(remote_log_fn) {
3142
3276
  f_warning = _console.warn;
3143
3277
  }
3144
3278
  }
3279
+ /************************************************************
3280
+ * JSON.stringify for a log line: a circular object (a gobj,
3281
+ * a DOM node) must not make the log call itself throw.
3282
+ ************************************************************/
3283
+ function log_stringify(v) {
3284
+ try {
3285
+ return JSON.stringify(v);
3286
+ } catch (e) {
3287
+ return String(v);
3288
+ }
3289
+ }
3145
3290
  function format_log(format) {
3146
3291
  const args = Array.prototype.slice.call(arguments);
3147
3292
  let output = "";
3148
3293
  if (typeof format === "string") {
3294
+ if (args.length === 1) return format;
3149
3295
  let i = 1;
3150
3296
  output = format.replace(/%[sdifoO]/g, (match) => {
3151
3297
  const val = args[i++];
@@ -3155,17 +3301,17 @@ function format_log(format) {
3155
3301
  case "%i": return parseInt(val);
3156
3302
  case "%f": return parseFloat(val);
3157
3303
  case "%o":
3158
- case "%O": return typeof val === "object" ? JSON.stringify(val) : String(val);
3304
+ case "%O": return typeof val === "object" ? log_stringify(val) : String(val);
3159
3305
  default: return match;
3160
3306
  }
3161
3307
  });
3162
- const rest = args.slice(i).map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg));
3308
+ const rest = args.slice(i).map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg));
3163
3309
  if (rest.length > 0) output += " " + rest.join(" ");
3164
- } else output = args.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)).join(" ");
3310
+ } else output = args.map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg)).join(" ");
3165
3311
  return output;
3166
3312
  }
3167
- function log_error(format) {
3168
- let msg = format_log(format);
3313
+ function log_error(format, ...args) {
3314
+ let msg = format_log(format, ...args);
3169
3315
  let hora = current_timestamp();
3170
3316
  if (f_error) {
3171
3317
  if (f_error === _console.error) {
@@ -3177,8 +3323,8 @@ function log_error(format) {
3177
3323
  }
3178
3324
  emit_log_callback("error", msg, hora);
3179
3325
  }
3180
- function log_warning(format) {
3181
- let msg = format_log(format);
3326
+ function log_warning(format, ...args) {
3327
+ let msg = format_log(format, ...args);
3182
3328
  let hora = current_timestamp();
3183
3329
  if (f_warning) {
3184
3330
  if (f_warning === _console.warn) {
@@ -3190,24 +3336,24 @@ function log_warning(format) {
3190
3336
  }
3191
3337
  emit_log_callback("warning", msg, hora);
3192
3338
  }
3193
- function log_info(format) {
3194
- let msg = format_log(format);
3339
+ function log_info(format, ...args) {
3340
+ let msg = format_log(format, ...args);
3195
3341
  let hora = current_timestamp();
3196
3342
  if (f_info) {
3197
3343
  if (console_wants("info", msg)) f_info("%c" + hora + " INFO: " + String(msg), "color:cyan");
3198
3344
  }
3199
3345
  emit_log_callback("info", msg, hora);
3200
3346
  }
3201
- function log_debug(format) {
3202
- let msg = format_log(format);
3347
+ function log_debug(format, ...args) {
3348
+ let msg = format_log(format, ...args);
3203
3349
  let hora = current_timestamp();
3204
3350
  if (f_debug) {
3205
3351
  if (console_wants("debug", msg)) f_debug("%c" + hora + " DEBUG: " + String(msg), "color:silver");
3206
3352
  }
3207
3353
  emit_log_callback("debug", msg, hora);
3208
3354
  }
3209
- function trace_msg(format) {
3210
- let msg = format_log(format);
3355
+ function trace_msg(format, ...args) {
3356
+ let msg = format_log(format, ...args);
3211
3357
  let hora = current_timestamp();
3212
3358
  if (f_debug) {
3213
3359
  if (console_wants("msg", msg)) f_debug("%c" + hora + " MSG: " + String(msg), "color:cyan");
@@ -3222,6 +3368,268 @@ function trace_json(jn, msg) {
3222
3368
  emit_log_callback("json", jn, current_timestamp());
3223
3369
  }
3224
3370
  /************************************************************
3371
+ * The names of the secrets (is_secret_name()). The same lists
3372
+ * as the C kernel's helpers.c: a name that holds one of these
3373
+ * (any case) is a secret.
3374
+ ************************************************************/
3375
+ var SECRET_NAME_PARTS = [
3376
+ "passw",
3377
+ "pwd",
3378
+ "passphrase",
3379
+ "secret",
3380
+ "token",
3381
+ "jwt",
3382
+ "bearer",
3383
+ "authorization",
3384
+ "cookie",
3385
+ "credential",
3386
+ "salt"
3387
+ ];
3388
+ var SECRET_NAME_JOINED = [
3389
+ "apikey",
3390
+ "sessionid",
3391
+ "sessionkey",
3392
+ "authdata"
3393
+ ];
3394
+ var NOT_SECRET_SEGMENTS = /* @__PURE__ */ new Set([
3395
+ "endpoint",
3396
+ "url",
3397
+ "uri",
3398
+ "domain",
3399
+ "path",
3400
+ "file",
3401
+ "public",
3402
+ "pub",
3403
+ "count",
3404
+ "counts",
3405
+ "type",
3406
+ "name",
3407
+ "len",
3408
+ "length",
3409
+ "size",
3410
+ "max",
3411
+ "min",
3412
+ "ttl",
3413
+ "timeout",
3414
+ "expiry",
3415
+ "expires",
3416
+ "mode"
3417
+ ]);
3418
+ /************************************************************
3419
+ * TRUE if `name` is the name of a secret: a password, a token,
3420
+ * a key... Any case. As the C kernel's is_secret_name().
3421
+ ************************************************************/
3422
+ function is_secret_name(name) {
3423
+ if (typeof name !== "string" || name.length < 3) return false;
3424
+ let lower = name.toLowerCase();
3425
+ let hit = false;
3426
+ for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
3427
+ hit = true;
3428
+ break;
3429
+ }
3430
+ if (!hit) {
3431
+ let joined = lower.replace(/[_\-. \t]/g, "");
3432
+ for (const part of SECRET_NAME_JOINED) if (joined.indexOf(part) >= 0) {
3433
+ hit = true;
3434
+ break;
3435
+ }
3436
+ }
3437
+ if (!hit) hit = lower.indexOf("priv") >= 0 && lower.indexOf("key") >= 0;
3438
+ if (!hit) return false;
3439
+ for (const segment of lower.split(/[_\-. \t]/)) if (NOT_SECRET_SEGMENTS.has(segment)) return false;
3440
+ return true;
3441
+ }
3442
+ /************************************************************
3443
+ * Where an unquoted secret value that begins at `p` ends: at
3444
+ * the end, at the quote that closes an outer quoted value, or
3445
+ * before the next "word=" -- a plain word after a blank is still
3446
+ * part of it ("password=correct horse battery", "password=
3447
+ * hunter2"). As the C kernel's inline_secret_value_end().
3448
+ ************************************************************/
3449
+ function inline_secret_value_end(str, p, outer) {
3450
+ const blank = (c) => c === " " || c === " ";
3451
+ const closes_outer = (i) => outer && str[i] === outer && (i + 1 >= str.length || blank(str[i + 1]));
3452
+ let end = p;
3453
+ while (true) {
3454
+ while (end < str.length && !blank(str[end])) {
3455
+ if (closes_outer(end)) return end;
3456
+ end++;
3457
+ }
3458
+ if (end >= str.length) return end;
3459
+ let w = end;
3460
+ while (w < str.length && blank(str[w])) w++;
3461
+ if (w >= str.length || closes_outer(w)) return end;
3462
+ for (let e = w; e < str.length && !blank(str[e]) && !closes_outer(e); e++) if (str[e] === "=" && e > w) return end;
3463
+ end = w;
3464
+ }
3465
+ }
3466
+ /************************************************************
3467
+ * A text (a command line) with the value of every "name=value"
3468
+ * whose name is a secret's written as "********", quoted or not
3469
+ * (and the "value" of a write-attr whose attribute names a
3470
+ * secret). The rest as it is. Return null when there was nothing
3471
+ * to mask. As the C kernel's mask_secrets_inline().
3472
+ ************************************************************/
3473
+ var MASK_MAX_NAME = 128;
3474
+ var MASK_MAX_BYTES = 4194304;
3475
+ var MASK_TOO_LARGE = "<not shown: too large to mask>";
3476
+ function mask_secrets_inline(str) {
3477
+ if (typeof str !== "string" || str.indexOf("=") < 0) return null;
3478
+ if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3479
+ let value_is_secret = false;
3480
+ let re_attr = /(^|[ \t])attribute=["']?([^ \t"']*)/g;
3481
+ let m;
3482
+ while ((m = re_attr.exec(str)) !== null) if (is_secret_name(m[2].slice(-128))) value_is_secret = true;
3483
+ let parts = [];
3484
+ let copied = 0;
3485
+ let changed = false;
3486
+ let outer = "";
3487
+ let word = 0;
3488
+ let p = 0;
3489
+ const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " ";
3490
+ while (p < str.length) {
3491
+ let c = str[p];
3492
+ if (c === " " || c === " ") {
3493
+ p++;
3494
+ word = p;
3495
+ continue;
3496
+ }
3497
+ if (c === "\"" || c === "'") {
3498
+ if (!outer && (p === 0 || str[p - 1] === " " || str[p - 1] === " " || str[p - 1] === "=")) outer = c;
3499
+ else if (c === outer && is_blank_or_end(p + 1)) outer = "";
3500
+ }
3501
+ if (c !== "=") {
3502
+ p++;
3503
+ continue;
3504
+ }
3505
+ let name = str.substring(Math.max(word, p - MASK_MAX_NAME), p);
3506
+ word = p + 1;
3507
+ if (!(is_secret_name(name) || value_is_secret && name.toLowerCase() === "value")) {
3508
+ p++;
3509
+ continue;
3510
+ }
3511
+ p++;
3512
+ let quote = str[p] === "\"" || str[p] === "'" ? str[p] : "";
3513
+ let end;
3514
+ if (quote) {
3515
+ end = p + 1;
3516
+ while (end < str.length && str[end] !== quote) end++;
3517
+ if (str[end] === quote) end++;
3518
+ } else end = inline_secret_value_end(str, p, outer);
3519
+ if (end > p) {
3520
+ parts.push(str.substring(copied, p));
3521
+ parts.push("********");
3522
+ copied = end;
3523
+ changed = true;
3524
+ }
3525
+ p = end;
3526
+ word = p;
3527
+ }
3528
+ if (!changed) return null;
3529
+ parts.push(str.substring(copied));
3530
+ return parts.join("");
3531
+ }
3532
+ /************************************************************
3533
+ * A json as a log or a trace may show it: at any depth, the
3534
+ * value of a key with a secret's name is "********", whatever
3535
+ * its type (not an absent one, a null or an empty string); so is
3536
+ * the "value" of a dict whose "attribute" names a secret; and a
3537
+ * string is masked as mask_secrets_inline(). Return a masked
3538
+ * copy, or `jn` itself when there was nothing to mask. As the C
3539
+ * kernel's json_mask_secrets().
3540
+ *
3541
+ * Only JSON is walked: plain objects (their prototype is
3542
+ * Object.prototype or null) and arrays. Anything else -- a gobj,
3543
+ * a DOM node, a class instance, a typed array, a function -- is
3544
+ * passed as it is, never walked, as trace_json() always showed it
3545
+ * (a kw may carry a gobj: its parent and children make a cycle).
3546
+ * A plain object met twice is masked once, the same everywhere; a
3547
+ * cycle back to one is "<cycle>"; below MASK_MAX_DEPTH levels
3548
+ * (64, as in C) nothing is shown.
3549
+ * It never throws: a failure is the placeholder below.
3550
+ ************************************************************/
3551
+ var MASK_MAX_DEPTH = 64;
3552
+ var MASK_FAILED = "<not shown: the masking failed>";
3553
+ function is_plain_object(value) {
3554
+ if (value === null || typeof value !== "object") return false;
3555
+ let proto = Object.getPrototypeOf(value);
3556
+ return proto === Object.prototype || proto === null;
3557
+ }
3558
+ var MASK_IN_PROGRESS = Symbol("mask-in-progress");
3559
+ function _json_mask_secrets(jn, memo, depth, budget) {
3560
+ if (typeof jn === "string") {
3561
+ if (jn.length > budget.left) {
3562
+ budget.left = 0;
3563
+ return MASK_TOO_LARGE;
3564
+ }
3565
+ budget.left -= jn.length;
3566
+ let masked = mask_secrets_inline(jn);
3567
+ return masked !== null ? masked : jn;
3568
+ }
3569
+ let is_array = Array.isArray(jn);
3570
+ if (!is_array && !is_plain_object(jn)) return jn;
3571
+ if (budget.left <= 0) return MASK_TOO_LARGE;
3572
+ if (memo.has(jn)) {
3573
+ let seen = memo.get(jn);
3574
+ return seen === MASK_IN_PROGRESS ? "<cycle>" : seen;
3575
+ }
3576
+ if (depth >= MASK_MAX_DEPTH) return "<deeper not shown>";
3577
+ budget.left -= 1;
3578
+ memo.set(jn, MASK_IN_PROGRESS);
3579
+ let changed = false;
3580
+ let truncated = false;
3581
+ let result;
3582
+ if (is_array) {
3583
+ result = [];
3584
+ for (let i = 0; i < jn.length; i++) {
3585
+ if (budget.left <= 0) {
3586
+ truncated = true;
3587
+ break;
3588
+ }
3589
+ budget.left -= 1;
3590
+ let shown = _json_mask_secrets(jn[i], memo, depth + 1, budget);
3591
+ if (shown !== jn[i]) changed = true;
3592
+ result.push(shown);
3593
+ }
3594
+ if (truncated) result.push(MASK_TOO_LARGE);
3595
+ } else {
3596
+ result = Object.create(Object.getPrototypeOf(jn));
3597
+ let value_is_secret = typeof jn.attribute === "string" && is_secret_name(jn.attribute.slice(-128));
3598
+ for (const key in jn) {
3599
+ if (!Object.prototype.hasOwnProperty.call(jn, key)) continue;
3600
+ if (budget.left <= 0) {
3601
+ truncated = true;
3602
+ break;
3603
+ }
3604
+ budget.left -= 1 + key.length;
3605
+ let value = jn[key];
3606
+ let shown;
3607
+ if ((is_secret_name(key.slice(-128)) || value_is_secret && key === "value") && value !== void 0 && value !== null && value !== "") shown = "********";
3608
+ else shown = _json_mask_secrets(value, memo, depth + 1, budget);
3609
+ if (shown !== value) changed = true;
3610
+ result[key] = shown;
3611
+ }
3612
+ if (truncated) result["<more>"] = MASK_TOO_LARGE;
3613
+ }
3614
+ if (!changed && !truncated) result = jn;
3615
+ memo.set(jn, result);
3616
+ return result;
3617
+ }
3618
+ function json_mask_secrets(jn) {
3619
+ try {
3620
+ return _json_mask_secrets(jn, /* @__PURE__ */ new WeakMap(), 0, { left: MASK_MAX_BYTES });
3621
+ } catch (e) {
3622
+ return MASK_FAILED;
3623
+ }
3624
+ }
3625
+ /************************************************************
3626
+ * trace_json() of a json that can hold a credential: what
3627
+ * json_mask_secrets() masks is shown masked
3628
+ ************************************************************/
3629
+ function trace_json_masked(jn, msg) {
3630
+ trace_json(json_mask_secrets(jn), msg);
3631
+ }
3632
+ /************************************************************
3225
3633
  * Duplicate an object (new references)
3226
3634
  * using the modern structuredClone (Deep copy of a single object)
3227
3635
  ************************************************************/
@@ -3494,6 +3902,8 @@ function cmp_two_simple_json(jn_var1, jn_var2) {
3494
3902
  if (is_number(jn_var1) || is_number(jn_var2)) {
3495
3903
  let val1 = Number(jn_var1);
3496
3904
  let val2 = Number(jn_var2);
3905
+ if (Number.isNaN(val1)) val1 = 0;
3906
+ if (Number.isNaN(val2)) val2 = 0;
3497
3907
  if (val1 > val2) return 1;
3498
3908
  else if (val1 < val2) return -1;
3499
3909
  else return 0;
@@ -3523,8 +3933,8 @@ function strstr(haystack, needle, bool) {
3523
3933
  ***************************************************************************/
3524
3934
  function is_metadata_key(key) {
3525
3935
  if (!is_string(key)) return false;
3526
- let i;
3527
- for (i = 0; i < key.length; i++) {
3936
+ let i = 0;
3937
+ for (; i < key.length; i++) {
3528
3938
  if (key[i] !== "_") break;
3529
3939
  if (i > 2) break;
3530
3940
  }
@@ -3535,8 +3945,8 @@ function is_metadata_key(key) {
3535
3945
  ***************************************************************************/
3536
3946
  function is_private_key(key) {
3537
3947
  if (!is_string(key)) return false;
3538
- let i;
3539
- for (i = 0; i < key.length; i++) {
3948
+ let i = 0;
3949
+ for (; i < key.length; i++) {
3540
3950
  if (key[i] !== "_") break;
3541
3951
  if (i > 2) break;
3542
3952
  }
@@ -3561,6 +3971,28 @@ function kw_has_key(kw, key) {
3561
3971
  return false;
3562
3972
  }
3563
3973
  /************************************************************
3974
+ * A key that reaches the prototype chain instead of the data:
3975
+ * "__proto__", "constructor" or "prototype" when the object
3976
+ * does not hold it as its OWN key. A walk through one of them
3977
+ * read, or WROTE, Object.prototype: the flat id
3978
+ * "__proto__`polluted" set a key on every object of the page.
3979
+ * In C they are keys like any other, so here too: read them
3980
+ * only as own keys, and write "__proto__" as an own key.
3981
+ ************************************************************/
3982
+ function is_prototype_segment(obj, key) {
3983
+ if (key === "__proto__" || key === "constructor" || key === "prototype") return !Object.prototype.hasOwnProperty.call(obj, key);
3984
+ return false;
3985
+ }
3986
+ function set_own_key(obj, key, value) {
3987
+ if (key === "__proto__") Object.defineProperty(obj, key, {
3988
+ value,
3989
+ writable: true,
3990
+ enumerable: true,
3991
+ configurable: true
3992
+ });
3993
+ else obj[key] = value;
3994
+ }
3995
+ /************************************************************
3564
3996
  * The value at `path` (keys joined by a backtick), as the C
3565
3997
  * kw_find_path() finds it: undefined when it is not there.
3566
3998
  * A kw that is not a dict or a list is LOGGED ("kw must be list
@@ -3587,7 +4019,12 @@ function kw_find_path(gobj, kw, path, verbose) {
3587
4019
  }
3588
4020
  let ss = path.split("`");
3589
4021
  let len = ss.length;
3590
- for (let i = 0; i < len - 1; i++) {
4022
+ for (let i = 0; i < len; i++) {
4023
+ if (is_prototype_segment(kw, ss[i])) {
4024
+ if (verbose) log_error(`${prefix}path not found: '${path}'`);
4025
+ return;
4026
+ }
4027
+ if (i === len - 1) break;
3591
4028
  kw = kw[ss[i]];
3592
4029
  if (kw === void 0 || kw === null) {
3593
4030
  if (verbose) log_error(`${prefix}path not found: '${path}'`);
@@ -3647,7 +4084,7 @@ function kw_get_bool(gobj, kw, path, default_value, flag) {
3647
4084
  return v;
3648
4085
  } else if (required) {
3649
4086
  log_error(`path not found: '${path}'`);
3650
- trace_json(kw);
4087
+ trace_json_masked(kw);
3651
4088
  }
3652
4089
  return Boolean(default_value);
3653
4090
  }
@@ -3661,11 +4098,11 @@ function kw_get_bool(gobj, kw, path, default_value, flag) {
3661
4098
  } else if (wild && b === null) value = false;
3662
4099
  else if (wild) {
3663
4100
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3664
- trace_json(kw);
4101
+ trace_json_masked(kw);
3665
4102
  return false;
3666
4103
  } else {
3667
4104
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json boolean: '${path}'`);
3668
- trace_json(kw);
4105
+ trace_json_masked(kw);
3669
4106
  return Boolean(default_value);
3670
4107
  }
3671
4108
  if (extract) kw_delete(gobj, kw, path);
@@ -3715,7 +4152,7 @@ function kw_get_int(gobj, kw, path, default_value, flag) {
3715
4152
  return v;
3716
4153
  } else if (required) {
3717
4154
  log_error(`path not found: '${path}'`);
3718
- trace_json(kw);
4155
+ trace_json_masked(kw);
3719
4156
  }
3720
4157
  return parseInt(default_value);
3721
4158
  }
@@ -3723,14 +4160,14 @@ function kw_get_int(gobj, kw, path, default_value, flag) {
3723
4160
  if (is_number(v)) value = Math.trunc(v);
3724
4161
  else if (!wild) {
3725
4162
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json integer: '${path}'`);
3726
- trace_json(kw);
4163
+ trace_json_masked(kw);
3727
4164
  return parseInt(default_value);
3728
4165
  } else if (is_boolean(v)) value = v ? 1 : 0;
3729
4166
  else if (is_string(v)) value = strtoll_base0(v);
3730
4167
  else if (v === null) value = 0;
3731
4168
  else {
3732
4169
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3733
- trace_json(kw);
4170
+ trace_json_masked(kw);
3734
4171
  return 0;
3735
4172
  }
3736
4173
  if (extract) kw_delete(gobj, kw, path);
@@ -3762,7 +4199,7 @@ function kw_get_real(gobj, kw, path, default_value, flag) {
3762
4199
  return v;
3763
4200
  } else if (required) {
3764
4201
  log_error(`path not found: '${path}'`);
3765
- trace_json(kw);
4202
+ trace_json_masked(kw);
3766
4203
  }
3767
4204
  return Number(default_value);
3768
4205
  }
@@ -3770,7 +4207,7 @@ function kw_get_real(gobj, kw, path, default_value, flag) {
3770
4207
  if (is_number(v)) value = v;
3771
4208
  else if (!wild) {
3772
4209
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json real: '${path}'`);
3773
- trace_json(kw);
4210
+ trace_json_masked(kw);
3774
4211
  return Number(default_value);
3775
4212
  } else if (is_boolean(v)) value = v ? 1 : 0;
3776
4213
  else if (is_string(v)) {
@@ -3779,7 +4216,7 @@ function kw_get_real(gobj, kw, path, default_value, flag) {
3779
4216
  } else if (v === null) value = 0;
3780
4217
  else {
3781
4218
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3782
- trace_json(kw);
4219
+ trace_json_masked(kw);
3783
4220
  return 0;
3784
4221
  }
3785
4222
  if (extract) kw_delete(gobj, kw, path);
@@ -3831,14 +4268,14 @@ function kw_get_str(gobj, kw, path, default_value, flag) {
3831
4268
  return default_value;
3832
4269
  } else if (required && !kw_path_holds_key(kw, path)) {
3833
4270
  log_error(`path not found: '${path}'`);
3834
- trace_json(kw);
4271
+ trace_json_masked(kw);
3835
4272
  }
3836
4273
  return default_value;
3837
4274
  }
3838
4275
  if (!is_string(v)) {
3839
4276
  if (v !== null) {
3840
4277
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json str: '${path}'`);
3841
- trace_json(kw);
4278
+ trace_json_masked(kw);
3842
4279
  }
3843
4280
  return default_value;
3844
4281
  }
@@ -3861,7 +4298,7 @@ function kw_get_pointer(gobj, kw, path, default_value, flag) {
3861
4298
  return v;
3862
4299
  } else if (required) {
3863
4300
  log_error(`path not found: '${path}'`);
3864
- trace_json(kw);
4301
+ trace_json_masked(kw);
3865
4302
  }
3866
4303
  return default_value;
3867
4304
  }
@@ -3888,14 +4325,14 @@ function kw_get_dict(gobj, kw, path, default_value, flag) {
3888
4325
  }
3889
4326
  if (required) {
3890
4327
  log_error(`path not found: '${path}'`);
3891
- trace_json(kw);
4328
+ trace_json_masked(kw);
3892
4329
  }
3893
4330
  return default_value;
3894
4331
  }
3895
4332
  if (!is_object(v)) {
3896
4333
  if (required) {
3897
4334
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json dict: '${path}'`);
3898
- trace_json(kw);
4335
+ trace_json_masked(kw);
3899
4336
  }
3900
4337
  return default_value;
3901
4338
  }
@@ -3918,7 +4355,7 @@ function kw_get_dict_value(gobj, kw, path, default_value, flag) {
3918
4355
  return v;
3919
4356
  } else if (required) {
3920
4357
  log_error(`path not found: '${path}'`);
3921
- trace_json(kw);
4358
+ trace_json_masked(kw);
3922
4359
  }
3923
4360
  return default_value;
3924
4361
  }
@@ -3946,14 +4383,14 @@ function kw_get_list(gobj, kw, path, default_value, flag) {
3946
4383
  }
3947
4384
  if (required) {
3948
4385
  log_error(`path not found: '${path}'`);
3949
- trace_json(kw);
4386
+ trace_json_masked(kw);
3950
4387
  }
3951
4388
  return default_value;
3952
4389
  }
3953
4390
  if (!is_array(v)) {
3954
4391
  if (required) {
3955
4392
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json list: '${path}'`);
3956
- trace_json(kw);
4393
+ trace_json_masked(kw);
3957
4394
  }
3958
4395
  return default_value;
3959
4396
  }
@@ -3970,17 +4407,13 @@ function kw_set_dict_value(gobj, kw, path, value) {
3970
4407
  return -1;
3971
4408
  }
3972
4409
  let ss = path.split("`");
3973
- if (ss.length <= 1) {
3974
- kw[path] = value;
3975
- return 0;
3976
- }
3977
4410
  let len = ss.length;
3978
4411
  for (let i = 0; i < len; i++) {
3979
4412
  let key = ss[i];
3980
- if (i === len - 1) kw[key] = value;
4413
+ if (i === len - 1) set_own_key(kw, key, value);
3981
4414
  else {
3982
- let seg = kw[key];
3983
- if (seg === void 0) kw[key] = {};
4415
+ let seg = is_prototype_segment(kw, key) ? void 0 : kw[key];
4416
+ if (seg === void 0) set_own_key(kw, key, {});
3984
4417
  else if (!is_object(seg) && !is_array(seg)) {
3985
4418
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}kw_set_dict_value(): segment '${key}' is not a dict or a list: '${path}'`);
3986
4419
  return -1;
@@ -4035,9 +4468,9 @@ function _kw_match_simple(kw, jn_filter, prefix_path) {
4035
4468
  if (!matched) break;
4036
4469
  continue;
4037
4470
  }
4038
- let jn_record_value = kw_get_dict_value(0, kw, path, 0, 0);
4039
- if (!jn_record_value) jn_record_value = json_object_get(kw, path);
4040
- if (!jn_record_value) {
4471
+ let jn_record_value = kw_get_dict_value(0, kw, path, void 0, 0);
4472
+ if (jn_record_value === void 0) jn_record_value = json_object_get(kw, path);
4473
+ if (jn_record_value === void 0) {
4041
4474
  matched = false;
4042
4475
  break;
4043
4476
  }
@@ -4156,10 +4589,11 @@ If paths are empty return empty
4156
4589
  ***************************************************************************/
4157
4590
  function kw_clone_by_not_keys(gobj, kw, keys, verbose) {
4158
4591
  let kw_clone = {};
4592
+ if (!is_object(kw)) return kw_clone;
4159
4593
  json_object_update(kw_clone, kw);
4160
4594
  if (is_string(keys) && !empty_string(keys)) {
4161
4595
  let key = keys;
4162
- if (kw_get_dict_value(gobj, kw, key, 0, 0)) json_object_del(kw_clone, key);
4596
+ if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
4163
4597
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4164
4598
  } else if (is_object(keys) && json_object_size(keys) > 0) {
4165
4599
  for (let key of Object.keys(keys)) if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
@@ -5119,9 +5553,15 @@ parent: document.body
5119
5553
  * will be 'data-i18n' attr
5120
5554
  *
5121
5555
  * content -> string | [createElement2() or parameters of createElement2]
5556
+ * | HTMLElement | SVGElement | Text
5122
5557
  * if string begins with '<' then
5123
5558
  * it will be converted to HTMLElement
5124
5559
  * content = createOneHtml(string)
5560
+ * ⚠️ So a string is MARKUP when it starts with '<': never
5561
+ * pass DATA (a value from a backend, a name a user typed)
5562
+ * as a bare string, or "<img src=x onerror=...>" runs.
5563
+ * Pass it as a Text node, which is always text:
5564
+ * ['span', {}, document.createTextNode(value)]
5125
5565
  * if attrs['i18n'] is not empty then (or 'data-i18n' or 'data_i18next')
5126
5566
  * it will be overridden with the translation
5127
5567
  * content = translate(attrs['i18n']));
@@ -5560,7 +6000,7 @@ function flat2json(flat) {
5560
6000
  current[seg] = flat[key];
5561
6001
  } else {
5562
6002
  if (Object.prototype.hasOwnProperty.call(current, seg)) throw new Error(`flat2json: '${key}' is written twice, or once as a value and once as a container`);
5563
- current[seg] = flat[key];
6003
+ set_own_key(current, seg, flat[key]);
5564
6004
  }
5565
6005
  break;
5566
6006
  }
@@ -5573,10 +6013,10 @@ function flat2json(flat) {
5573
6013
  current[seg] = child;
5574
6014
  }
5575
6015
  } else {
5576
- child = current[seg];
6016
+ child = is_prototype_segment(current, seg) ? void 0 : current[seg];
5577
6017
  if (child === void 0) {
5578
6018
  child = next_is_index ? [] : {};
5579
- current[seg] = child;
6019
+ set_own_key(current, seg, child);
5580
6020
  }
5581
6021
  }
5582
6022
  if (child === null || typeof child !== "object") throw new Error(`flat2json: '${key}' walks through a value: the same id is a leaf and a container at once`);
@@ -6339,6 +6779,7 @@ function mt_stop$1(gobj) {
6339
6779
  ***************************************************************/
6340
6780
  function mt_play(gobj) {
6341
6781
  let default_service = gobj_default_service();
6782
+ if (!default_service) return 0;
6342
6783
  if (!gobj_is_running(default_service)) gobj_start(default_service);
6343
6784
  if (!gobj_is_playing(default_service)) gobj_play(default_service);
6344
6785
  return 0;
@@ -6347,7 +6788,8 @@ function mt_play(gobj) {
6347
6788
  * Framework Method: Pause
6348
6789
  ***************************************************************/
6349
6790
  function mt_pause(gobj) {
6350
- gobj_pause(gobj_default_service());
6791
+ let default_service = gobj_default_service();
6792
+ if (default_service && gobj_is_playing(default_service)) gobj_pause(default_service);
6351
6793
  return 0;
6352
6794
  }
6353
6795
  /***************************************************************
@@ -6603,6 +7045,7 @@ var __gclass__ = null;
6603
7045
  function mt_create(gobj) {
6604
7046
  let priv = gobj.priv;
6605
7047
  priv.gobj_timer = gobj_create_pure_child(gobj_name(gobj), "C_TIMER", {}, gobj);
7048
+ priv.ws_seq = 0;
6606
7049
  priv.url = gobj_read_str_attr(gobj, "url");
6607
7050
  priv.remote_yuno_name = gobj_read_str_attr(gobj, "remote_yuno_name");
6608
7051
  priv.remote_yuno_role = gobj_read_str_attr(gobj, "remote_yuno_role");
@@ -6637,6 +7080,16 @@ function mt_writing(gobj, path) {
6637
7080
  function mt_start(gobj) {
6638
7081
  let priv = gobj.priv;
6639
7082
  priv.retry_ms = 0;
7083
+ if (gobj_current_state(gobj) !== "ST_DISCONNECTED") {
7084
+ gobj_send_event(gobj, "EV_ON_CLOSE", {
7085
+ url: priv.url,
7086
+ ws_seq: priv.ws_seq,
7087
+ code: 1e3,
7088
+ reason: "restarted",
7089
+ was_clean: true
7090
+ }, gobj);
7091
+ clear_timeout(priv.gobj_timer);
7092
+ }
6640
7093
  priv.websocket = setup_websocket(gobj);
6641
7094
  return 0;
6642
7095
  }
@@ -6771,10 +7224,16 @@ function setup_websocket(gobj) {
6771
7224
  log_error(`${gobj_short_name(gobj)}: Failed to initialize WebSocket.`);
6772
7225
  return null;
6773
7226
  }
7227
+ const ws_seq = ++gobj.priv.ws_seq;
6774
7228
  websocket.onopen = () => {
6775
- gobj_send_event(gobj, "EV_ON_OPEN", { url }, gobj);
7229
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
7230
+ gobj_send_event(gobj, "EV_ON_OPEN", {
7231
+ url,
7232
+ ws_seq
7233
+ }, gobj);
6776
7234
  };
6777
7235
  websocket.onmessage = (e) => {
7236
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
6778
7237
  gobj_send_event(gobj, "EV_ON_MESSAGE", {
6779
7238
  url,
6780
7239
  data: e.data
@@ -6782,9 +7241,13 @@ function setup_websocket(gobj) {
6782
7241
  };
6783
7242
  websocket.onclose = (e) => {
6784
7243
  if (gobj_is_destroying(gobj)) return;
7244
+ if (ws_seq !== gobj.priv.ws_seq) return;
6785
7245
  if (!gobj_read_bool_attr(gobj_yuno(), "browser_beforeunload")) gobj_send_event(gobj, "EV_ON_CLOSE", {
6786
7246
  url,
6787
- e
7247
+ ws_seq,
7248
+ code: e.code,
7249
+ reason: e.reason,
7250
+ was_clean: e.wasClean
6788
7251
  }, gobj);
6789
7252
  else log_warning("Close websocket by unload page");
6790
7253
  };
@@ -6805,7 +7268,7 @@ function close_websocket(gobj, code = 1e3, reason = "") {
6805
7268
  try {
6806
7269
  websocket.close(code, reason);
6807
7270
  } catch (e) {
6808
- log_error("Error closing WebSocket:", e.message);
7271
+ log_error(`${gobj_short_name(gobj)}: Error closing WebSocket: ${e.message}`);
6809
7272
  }
6810
7273
  }
6811
7274
  /***************************************************************
@@ -6815,7 +7278,7 @@ function trace_inter_event(gobj, prefix, iev) {
6815
7278
  let hora = current_timestamp();
6816
7279
  try {
6817
7280
  log_debug("\n" + hora + " " + prefix + "\n");
6818
- trace_json(iev);
7281
+ trace_json_masked(iev);
6819
7282
  } catch (e) {
6820
7283
  log_debug("ERROR in trace_inter_event: " + e);
6821
7284
  }
@@ -6931,29 +7394,29 @@ function iev_create_from_json(gobj, data) {
6931
7394
  try {
6932
7395
  x = JSON.parse(data);
6933
7396
  } catch (e) {
6934
- log_error("parsing inter_event json: " + e);
7397
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: ${e}`);
6935
7398
  return null;
6936
7399
  }
6937
7400
  if (!(x instanceof Object)) {
6938
- log_error("parsing inter_event: websocket data not a json object");
7401
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: websocket data not a json object`);
6939
7402
  return null;
6940
7403
  }
6941
7404
  let event = x["event"];
6942
7405
  if (!event) {
6943
- log_error("parsing inter_event: no event");
7406
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: no event`);
6944
7407
  return null;
6945
7408
  }
6946
7409
  if (typeof event !== "string") {
6947
- log_error("parsing inter_event: event not a string");
7410
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: event not a string`);
6948
7411
  return null;
6949
7412
  }
6950
7413
  let kw = x["kw"];
6951
7414
  if (!kw) {
6952
- log_error("parsing inter_kw: no kw");
7415
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_kw: no kw`);
6953
7416
  return null;
6954
7417
  }
6955
7418
  if (!(kw instanceof Object)) {
6956
- log_error("parsing inter_event: kw not a json object");
7419
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: kw not a json object`);
6957
7420
  return null;
6958
7421
  }
6959
7422
  return {
@@ -7013,10 +7476,9 @@ function ac_on_open(gobj, event, kw, src) {
7013
7476
  ***************************************************************/
7014
7477
  function ac_on_close(gobj, event, kw, src) {
7015
7478
  let priv = gobj.priv;
7016
- let e = kw.e;
7017
7479
  let url = kw.url;
7018
7480
  set_remote_log_functions();
7019
- log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7481
+ log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7020
7482
  if (priv.websocket) {
7021
7483
  priv.websocket.onopen = null;
7022
7484
  priv.websocket.onmessage = null;
@@ -7033,15 +7495,15 @@ function ac_on_close(gobj, event, kw, src) {
7033
7495
  remote_yuno_service: priv.remote_yuno_service
7034
7496
  });
7035
7497
  } else {
7036
- log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7498
+ log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7037
7499
  gobj_publish_event(gobj, "EV_ON_OPEN_ERROR", {
7038
7500
  url,
7039
7501
  remote_yuno_name: priv.remote_yuno_name,
7040
7502
  remote_yuno_role: priv.remote_yuno_role,
7041
7503
  remote_yuno_service: priv.remote_yuno_service,
7042
- code: e.code,
7043
- reason: e.reason,
7044
- was_clean: e.wasClean
7504
+ code: kw.code,
7505
+ reason: kw.reason,
7506
+ was_clean: kw.was_clean
7045
7507
  });
7046
7508
  }
7047
7509
  if (gobj_is_running(gobj)) set_timeout(priv.gobj_timer, next_retry(gobj));
@@ -7139,6 +7601,10 @@ function ac_identity_card_ack(gobj, event, kw, src) {
7139
7601
  function ac_on_message(gobj, event, kw, src) {
7140
7602
  let url = gobj.priv.url;
7141
7603
  let iev_msg = iev_create_from_json(gobj, kw.data);
7604
+ if (!iev_msg) {
7605
+ log_warning(`${gobj_short_name(gobj)}: frame dropped from ${url}: ${String(kw.data).substring(0, 256)}`);
7606
+ return -1;
7607
+ }
7142
7608
  if (is_traffic_tracing(gobj)) {
7143
7609
  let prefix = gobj_name(gobj_yuno()) + " <== " + url;
7144
7610
  let size = kw.data.length;
@@ -7164,8 +7630,14 @@ function ac_on_message(gobj, event, kw, src) {
7164
7630
  return 0;
7165
7631
  }
7166
7632
  let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
7167
- if (msg_type === "__subscribing__") return -1;
7168
- if (msg_type === "__unsubscribing__") return -1;
7633
+ if (msg_type === "__subscribing__") {
7634
+ log_warning(`${gobj_short_name(gobj)}: subscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7635
+ return -1;
7636
+ }
7637
+ if (msg_type === "__unsubscribing__") {
7638
+ log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7639
+ return -1;
7640
+ }
7169
7641
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
7170
7642
  gobj_send_event(gobj, iev_event, iev_kw, gobj);
7171
7643
  return 0;
@@ -7497,6 +7969,7 @@ exports.is_number = is_number;
7497
7969
  exports.is_object = is_object;
7498
7970
  exports.is_private_key = is_private_key;
7499
7971
  exports.is_pure_number = is_pure_number;
7972
+ exports.is_secret_name = is_secret_name;
7500
7973
  exports.is_string = is_string;
7501
7974
  exports.jdb_delete = jdb_delete;
7502
7975
  exports.jdb_get = jdb_get;
@@ -7512,6 +7985,7 @@ exports.json_array_remove = json_array_remove;
7512
7985
  exports.json_array_size = json_array_size;
7513
7986
  exports.json_deep_copy = json_deep_copy;
7514
7987
  exports.json_is_identical = json_is_identical;
7988
+ exports.json_mask_secrets = json_mask_secrets;
7515
7989
  exports.json_object_del = json_object_del;
7516
7990
  exports.json_object_get = json_object_get;
7517
7991
  exports.json_object_set = json_object_set;
@@ -7559,6 +8033,7 @@ exports.log_debug = log_debug;
7559
8033
  exports.log_error = log_error;
7560
8034
  exports.log_info = log_info;
7561
8035
  exports.log_warning = log_warning;
8036
+ exports.mask_secrets_inline = mask_secrets_inline;
7562
8037
  exports.msg_iev_get_msg_type = msg_iev_get_msg_type;
7563
8038
  exports.msg_iev_get_stack = msg_iev_get_stack;
7564
8039
  exports.msg_iev_push_stack = msg_iev_push_stack;
@@ -7592,6 +8067,7 @@ exports.strstr = strstr;
7592
8067
  exports.template_get_field_desc = template_get_field_desc;
7593
8068
  exports.timeTracker = timeTracker;
7594
8069
  exports.trace_json = trace_json;
8070
+ exports.trace_json_masked = trace_json_masked;
7595
8071
  exports.trace_level_t = trace_level_t;
7596
8072
  exports.trace_msg = trace_msg;
7597
8073
  exports.treedb_decoder_fkey = treedb_decoder_fkey;