@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.
@@ -27,8 +27,8 @@
27
27
  return sprintf.apply(null, [fmt].concat(argv || []));
28
28
  }
29
29
  function sprintf_format(parse_tree, argv) {
30
- var cursor = 1, tree_length = parse_tree.length, arg, output = "", i, k, ph, pad, pad_character, pad_length, is_positive, sign;
31
- for (i = 0; i < tree_length; i++) if (typeof parse_tree[i] === "string") output += parse_tree[i];
30
+ var cursor = 1, tree_length = parse_tree.length, arg, output = "", i = 0, k, ph, pad, pad_character, pad_length, is_positive, sign;
31
+ for (; i < tree_length; i++) if (typeof parse_tree[i] === "string") output += parse_tree[i];
32
32
  else if (typeof parse_tree[i] === "object") {
33
33
  ph = parse_tree[i];
34
34
  if (ph.keys) {
@@ -161,7 +161,7 @@
161
161
  function build_command_response(gobj, result, comment, schema, data) {
162
162
  return {
163
163
  result,
164
- comment: comment || {},
164
+ comment: comment || "",
165
165
  schema: schema || {},
166
166
  data: data || []
167
167
  };
@@ -215,7 +215,7 @@
215
215
  var __global_stats_parser_fn__ = null;
216
216
  var dl_global_event_types = [];
217
217
  var _gclass_register = Object.create(null);
218
- var __jn_services__ = {};
218
+ var __jn_services__ = Object.create(null);
219
219
  var __yuno__ = null;
220
220
  var __default_service__ = null;
221
221
  var __inside__ = 0;
@@ -824,7 +824,8 @@
824
824
  if (!is_object(jn_value)) jn_value = {};
825
825
  break;
826
826
  case data_type_t.DTP_JSON:
827
- if (is_string(svalue)) try {
827
+ if (svalue === "" || svalue === void 0) jn_value = null;
828
+ else if (is_string(svalue)) try {
828
829
  jn_value = JSON.parse(svalue);
829
830
  } catch (error) {
830
831
  log_error(`JSON parse error: ${error.message}`);
@@ -859,11 +860,17 @@
859
860
  break;
860
861
  case data_type_t.DTP_INTEGER:
861
862
  jn_value2 = parseInt(jn_value_);
862
- if (isNaN(jn_value2)) log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
863
+ if (isNaN(jn_value2)) {
864
+ log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
865
+ return -1;
866
+ }
863
867
  break;
864
868
  case data_type_t.DTP_REAL:
865
869
  jn_value2 = Number(jn_value_);
866
- if (isNaN(jn_value2)) log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
870
+ if (isNaN(jn_value2)) {
871
+ log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
872
+ return -1;
873
+ }
867
874
  break;
868
875
  case data_type_t.DTP_LIST:
869
876
  if (is_string(jn_value_)) try {
@@ -890,15 +897,11 @@
890
897
  }
891
898
  break;
892
899
  case data_type_t.DTP_JSON:
900
+ jn_value2 = jn_value_;
893
901
  if (is_string(jn_value_)) try {
894
902
  jn_value2 = JSON.parse(jn_value_);
895
903
  } catch (error) {
896
- log_error(`JSON parse error: ${error.message}`);
897
- }
898
- else jn_value2 = jn_value_;
899
- if (!is_array(jn_value2) && !is_object(jn_value2)) {
900
- log_error(`attr must be a json dict/list: ${it.name}`);
901
- return -1;
904
+ jn_value2 = jn_value_;
902
905
  }
903
906
  break;
904
907
  case data_type_t.DTP_POINTER: jn_value2 = jn_value_;
@@ -1203,6 +1206,10 @@
1203
1206
  * find a service
1204
1207
  ************************************************************/
1205
1208
  function gobj_find_service(service_name, verbose) {
1209
+ if (!is_string(service_name)) {
1210
+ log_error(`gobj_find_service(): service name must be a string: ${service_name}`);
1211
+ return null;
1212
+ }
1206
1213
  service_name = service_name.toLowerCase();
1207
1214
  let service_gobj = __jn_services__[service_name];
1208
1215
  if (!service_gobj) {
@@ -1337,7 +1344,7 @@
1337
1344
  let new_kw = kw;
1338
1345
  if (json2sdata(hs, new_kw, -1, gobj.gclass.gclass_flag & gclass_flag_t.gcflag_ignore_unknown_attrs ? 0 : print_attr_not_found, gobj) < 0) {
1339
1346
  log_error(`${gobj_short_name(gobj)} json2data() FAILED`);
1340
- trace_json(new_kw);
1347
+ trace_json_masked(new_kw);
1341
1348
  }
1342
1349
  }
1343
1350
  /************************************************************
@@ -1360,9 +1367,10 @@
1360
1367
  return null;
1361
1368
  }
1362
1369
  if (is_string(parent)) {
1363
- parent = gobj_find_service(parent);
1370
+ const parent_name = parent;
1371
+ parent = gobj_find_service(parent_name);
1364
1372
  if (!parent) {
1365
- log_warning(`parent service not found: ${parent}`);
1373
+ log_warning(`parent service not found: ${parent_name}`);
1366
1374
  return null;
1367
1375
  }
1368
1376
  }
@@ -1475,6 +1483,7 @@
1475
1483
  parent.gclass.gmt.mt_child_removed(parent, gobj);
1476
1484
  }
1477
1485
  if (gobj.gobj_flag & gobj_flag_t.gobj_flag_service) _deregister_service(gobj);
1486
+ if (gobj === __default_service__) __default_service__ = null;
1478
1487
  gobj_unsubscribe_list(gobj, gobj.dl_subscriptions, true);
1479
1488
  gobj_unsubscribe_list(gobj, gobj.dl_subscribings, true);
1480
1489
  if (parent) {
@@ -1820,7 +1829,7 @@
1820
1829
  log_error(`gobj NULL of bad type`);
1821
1830
  return null;
1822
1831
  }
1823
- if (gobj.bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1832
+ if (gobj.bottom_gobj && bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1824
1833
  gobj.bottom_gobj = bottom_gobj;
1825
1834
  }
1826
1835
  /***************************************************************************
@@ -2235,7 +2244,7 @@
2235
2244
  jn_attrs[key] = value;
2236
2245
  if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(key)) gobj.priv[key] = value;
2237
2246
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2238
- if (gobj.mt_writing) gobj.mt_writing(gobj, key);
2247
+ if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2239
2248
  }
2240
2249
  return 0;
2241
2250
  }
@@ -2259,8 +2268,8 @@
2259
2268
  if (!it.name) continue;
2260
2269
  if (exclude_flag && it.flag & exclude_flag) continue;
2261
2270
  if (include_flag === -1 || it.flag & include_flag) {
2262
- const value = set_default(gobj, gobj.jn_attrs, it);
2263
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = value;
2271
+ set_default(gobj, gobj.jn_attrs, it);
2272
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = gobj.jn_attrs[it.name];
2264
2273
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, it.name);
2265
2274
  }
2266
2275
  }
@@ -2294,7 +2303,7 @@
2294
2303
  let hs = gobj_hsdata2(gobj, name, false);
2295
2304
  if (hs) {
2296
2305
  hs[name] = parseBoolean(value);
2297
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2306
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2298
2307
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2299
2308
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2300
2309
  }
@@ -2310,7 +2319,7 @@
2310
2319
  let hs = gobj_hsdata2(gobj, name, false);
2311
2320
  if (hs) {
2312
2321
  hs[name] = parseInt(value);
2313
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2322
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2314
2323
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2315
2324
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2316
2325
  }
@@ -2326,7 +2335,7 @@
2326
2335
  let hs = gobj_hsdata2(gobj, name, false);
2327
2336
  if (hs) {
2328
2337
  hs[name] = String(value);
2329
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2338
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2330
2339
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2331
2340
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2332
2341
  }
@@ -2380,15 +2389,15 @@
2380
2389
  log_error(`${gobj_short_name(gobj)}: gobj_change_parent() BAD TYPE of parent`);
2381
2390
  return -1;
2382
2391
  }
2383
- if (gobj.parent && gobj.parent.mt_child_removed) gobj.parent.mt_child_removed(gobj.parent, gobj);
2392
+ if (gobj.parent && gobj.parent.gclass.gmt.mt_child_removed) gobj.parent.gclass.gmt.mt_child_removed(gobj.parent, gobj);
2384
2393
  if (gobj.parent) {
2385
2394
  _remove_child(gobj.parent, gobj);
2386
2395
  gobj.parent = null;
2387
2396
  }
2388
2397
  _add_child(parent, gobj);
2389
- if (parent.mt_child_added) {
2398
+ if (parent.gclass.gmt.mt_child_added) {
2390
2399
  if (__trace_gobj_create_delete__(gobj)) log_debug(sprintf("👦👦🔵 child_added(%s): %s", gobj_full_name(parent), gobj_short_name(gobj)));
2391
- parent.mt_child_added(parent, gobj);
2400
+ parent.gclass.gmt.mt_child_added(parent, gobj);
2392
2401
  }
2393
2402
  return 0;
2394
2403
  }
@@ -2500,9 +2509,19 @@
2500
2509
  let snapshot = __dl_posted_events__;
2501
2510
  __dl_posted_events__ = [];
2502
2511
  let delivered = 0;
2503
- for (let msg of snapshot) {
2512
+ for (let i = 0; i < snapshot.length; i++) {
2513
+ const msg = snapshot[i];
2504
2514
  if (msg.dst.obflag & (obflag_t.obflag_destroyed | obflag_t.obflag_destroying)) continue;
2505
- gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2515
+ try {
2516
+ gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2517
+ } catch (e) {
2518
+ __dl_posted_events__ = snapshot.slice(i + 1).concat(__dl_posted_events__);
2519
+ if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
2520
+ __posted_events_scheduled__ = true;
2521
+ setTimeout(gobj_deliver_posted_events, 0);
2522
+ }
2523
+ throw e;
2524
+ }
2506
2525
  delivered++;
2507
2526
  }
2508
2527
  if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
@@ -2552,7 +2571,7 @@
2552
2571
  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)));
2553
2572
  if (kw) {
2554
2573
  if (__trace_gobj_ev_kw__(dst)) {
2555
- if (json_object_size(kw)) trace_json(kw);
2574
+ if (json_object_size(kw)) trace_json_masked(kw);
2556
2575
  }
2557
2576
  }
2558
2577
  }
@@ -2572,7 +2591,7 @@
2572
2591
  }
2573
2592
  if (kw) {
2574
2593
  if (__trace_gobj_ev_kw__(dst)) {
2575
- if (json_object_size(kw)) trace_json(kw);
2594
+ if (json_object_size(kw)) trace_json_masked(kw);
2576
2595
  }
2577
2596
  }
2578
2597
  }
@@ -2580,15 +2599,40 @@
2580
2599
  let ret = -1;
2581
2600
  try {
2582
2601
  if (event_action.action) ret = event_action.action(dst, event, kw, src);
2583
- } finally {}
2602
+ } catch (e) {
2603
+ __inside__--;
2604
+ if (!(e && e.__gobj_logged__)) {
2605
+ log_error(`${gobj_short_name(dst)}: action of ${event} in ${dst.current_state.state_name} THREW: ${e && e.stack ? e.stack : e}`);
2606
+ try {
2607
+ e.__gobj_logged__ = true;
2608
+ } catch (e2) {}
2609
+ }
2610
+ throw e;
2611
+ }
2584
2612
  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));
2585
2613
  __inside__--;
2586
2614
  return ret;
2587
2615
  }
2588
2616
  var subs_flag_t = Object.freeze({
2589
- __hard_subscription__: 1,
2590
- __own_event__: 2
2617
+ __rename_event_name__: 1,
2618
+ __hard_subscription__: 2,
2619
+ __own_event__: 4
2591
2620
  });
2621
+ /***************************************************************************
2622
+ * The event type of `event_name` in any registered gclass, null if none.
2623
+ * The C kernel's gobj_find_event_type(): any case, gclasses only.
2624
+ ***************************************************************************/
2625
+ function _find_event_type_in_gclasses(event_name) {
2626
+ let lower = String(event_name).toLowerCase();
2627
+ for (let gclass_name in _gclass_register) {
2628
+ let dl = _gclass_register[gclass_name].dl_events;
2629
+ for (let i = 0; i < dl.length; i++) {
2630
+ let event_type = dl[i];
2631
+ if (event_type.event_name && event_type.event_name.toLowerCase() === lower) return event_type;
2632
+ }
2633
+ }
2634
+ return null;
2635
+ }
2592
2636
  var subscription_desc = [
2593
2637
  SDATA(data_type_t.DTP_POINTER, "publisher", 0, null, "publisher gobj"),
2594
2638
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "subscriber gobj"),
@@ -2621,6 +2665,21 @@
2621
2665
  if (json_size(__config__) > 0) {
2622
2666
  let kw_clone = json_deep_copy(__config__);
2623
2667
  json_object_set_new(subs, "__config__", kw_clone);
2668
+ let renamed_event = kw_get_str(publisher, kw_clone, "__rename_event_name__", "");
2669
+ if (!empty_string(renamed_event)) {
2670
+ let event_type = _find_event_type_in_gclasses(renamed_event);
2671
+ if (event_type) {
2672
+ json_object_set_new(subs, "renamed_event", event_type.event_name);
2673
+ delete kw_clone.__rename_event_name__;
2674
+ subs_flag |= subs_flag_t.__rename_event_name__;
2675
+ let kw_global = kw_get_dict(publisher, subs, "__global__", null);
2676
+ if (!is_object(kw_global)) {
2677
+ kw_global = {};
2678
+ json_object_set_new(subs, "__global__", kw_global);
2679
+ }
2680
+ kw_global.__original_event_name__ = event;
2681
+ } else log_error(`${gobj_short_name(publisher)}: EVENT NOT FOUND (__rename_event_name__: ${renamed_event})`);
2682
+ }
2624
2683
  if (kw_has_key(kw_clone, "__hard_subscription__")) {
2625
2684
  let hard_subscription = kw_get_bool(publisher, kw_clone, "__hard_subscription__", false);
2626
2685
  json_object_del(kw_clone, "__hard_subscription__");
@@ -2640,9 +2699,52 @@
2640
2699
  return subs;
2641
2700
  }
2642
2701
  /***************************************************************************
2702
+ * The kw to find the subscriptions a (un)subscription `kw` names: `kw` as
2703
+ * _create_subscription() stores it, as the C kernel's
2704
+ * _subscription_match_kw(). The three `__config__` keys the framework
2705
+ * reads (__hard_subscription__, __own_event__, __rename_event_name__)
2706
+ * become the subs_flag and are taken out of the stored `__config__`, and
2707
+ * a renamed event adds `__original_event_name__` to the stored
2708
+ * `__global__`: with `kw` as it came, the same kw matched no subscription,
2709
+ * a repeat of it was made twice (the subscriber got each event twice) and
2710
+ * its withdrawal found nothing. A known renamed event is returned in
2711
+ * `renamed_event`, which _match_subscription() compares: a plain and a
2712
+ * renamed subscription, or two renames of one event, are two. A rename
2713
+ * that no gclass declares stays in the stored `__config__`, and in this
2714
+ * one. Up to gobj-js 7.25.8 the kw was compared as it came, and
2715
+ * `__rename_event_name__` always stayed in the stored `__config__`: a
2716
+ * plain kw with the rest of that __config__ found no repeat (two
2717
+ * subscriptions where C has one).
2718
+ * Return {kw, renamed_event}; renamed_event is "" when kw does not rename.
2719
+ ***************************************************************************/
2720
+ function _subscription_match_kw(kw, event) {
2721
+ let __config__ = kw ? kw.__config__ : null;
2722
+ if (!is_object(__config__) || !kw_has_key(__config__, "__hard_subscription__") && !kw_has_key(__config__, "__own_event__") && !kw_has_key(__config__, "__rename_event_name__")) return {
2723
+ kw,
2724
+ renamed_event: ""
2725
+ };
2726
+ let kw_match = Object.assign({}, kw);
2727
+ let config_match = Object.assign({}, __config__);
2728
+ kw_match.__config__ = config_match;
2729
+ let renamed_event = "";
2730
+ let rename = config_match.__rename_event_name__;
2731
+ let event_type = typeof rename === "string" && !empty_string(rename) ? _find_event_type_in_gclasses(rename) : null;
2732
+ if (event_type) {
2733
+ renamed_event = event_type.event_name;
2734
+ delete config_match.__rename_event_name__;
2735
+ if (event && json_size(kw_match.__global__) > 0) kw_match.__global__ = Object.assign({}, kw_match.__global__, { __original_event_name__: event });
2736
+ }
2737
+ delete config_match.__hard_subscription__;
2738
+ delete config_match.__own_event__;
2739
+ return {
2740
+ kw: kw_match,
2741
+ renamed_event
2742
+ };
2743
+ }
2744
+ /***************************************************************************
2643
2745
  * Match subscription
2644
2746
  ***************************************************************************/
2645
- function _match_subscription(subs, publisher, event, kw, subscriber) {
2747
+ function _match_subscription(subs, publisher, event, renamed_event, kw, subscriber) {
2646
2748
  let __config__ = kw_get_dict(publisher, kw, "__config__", null);
2647
2749
  let __global__ = kw_get_dict(publisher, kw, "__global__", null);
2648
2750
  let __local__ = kw_get_dict(publisher, kw, "__local__", null);
@@ -2657,6 +2759,9 @@
2657
2759
  if (event) {
2658
2760
  if (event !== kw_get_str(null, subs, "event", "")) match = false;
2659
2761
  }
2762
+ if (renamed_event) {
2763
+ if (renamed_event !== kw_get_str(null, subs, "renamed_event", "")) match = false;
2764
+ }
2660
2765
  if (json_size(__config__) > 0) {
2661
2766
  let kw_config = kw_get_dict(null, subs, "__config__", null);
2662
2767
  if (json_size(kw_config) > 0) {
@@ -2691,26 +2796,29 @@
2691
2796
  * kw (__config__, __global__, __local__, __filter__),
2692
2797
  * subscriber
2693
2798
  ***************************************************************************/
2694
- function _find_subscriptions(dl_subs, publisher, event, kw, subscriber) {
2799
+ function _find_subscriptions(dl_subs, publisher, event, renamed_event, kw, subscriber) {
2695
2800
  let iter = [];
2696
2801
  for (let i = 0; i < dl_subs.length; i++) {
2697
2802
  let subs = dl_subs[i];
2698
- if (_match_subscription(subs, publisher, event, kw, subscriber)) json_array_append(iter, subs);
2803
+ if (_match_subscription(subs, publisher, event, renamed_event, kw, subscriber)) json_array_append(iter, subs);
2699
2804
  }
2700
2805
  return iter;
2701
2806
  }
2702
2807
  /***************************************************************************
2703
- * Find idx of subscription in dl_subs, -1 not found
2808
+ * Find idx of the subscription `subs` itself in dl_subs, -1 not found.
2809
+ * Up to gobj-js 7.25.8 it was the first entry whose fields matched `subs`:
2810
+ * a subscription already removed took a live one that matched it (a plain
2811
+ * one matches every other of its event and subscriber).
2704
2812
  ***************************************************************************/
2705
- function _get_subs_idx(dl_subs, publisher, event, kw, subscriber) {
2706
- for (let i = 0; i < dl_subs.length; i++) {
2707
- let subs = dl_subs[i];
2708
- if (_match_subscription(subs, publisher, event, kw, subscriber)) return i;
2709
- }
2710
- return -1;
2813
+ function _get_subs_idx(dl_subs, subs) {
2814
+ return dl_subs.indexOf(subs);
2711
2815
  }
2712
2816
  /***************************************************************************
2713
- * Delete subscription in publisher and subscriber
2817
+ * Delete subscription in publisher and subscriber.
2818
+ * Return 0 removed, -1 a hard one kept (only `force` removes it), 1 it
2819
+ * was not there any more: already removed, as a stale reference is, or by
2820
+ * the mt_subscription_deleted() of an earlier entry of the same list. The
2821
+ * caller says which is an error. As in C.
2714
2822
  ***************************************************************************/
2715
2823
  function _delete_subscription(gobj, subs, force, not_inform) {
2716
2824
  let publisher = kw_get_pointer(gobj, subs, "publisher", null, kw_flag_t.KW_REQUIRED);
@@ -2719,7 +2827,12 @@
2719
2827
  if (kw_get_int(gobj, subs, "subs_flag", null, kw_flag_t.KW_REQUIRED) & subs_flag_t.__hard_subscription__ ? 1 : 0) {
2720
2828
  if (!force) return -1;
2721
2829
  }
2722
- if (__trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher)) {
2830
+ let tracea = __trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher);
2831
+ if (_get_subs_idx(publisher.dl_subscriptions, subs) < 0) {
2832
+ if (tracea) trace_machine(sprintf("💜💜👎 unsubscribing event '%s': publisher %s, subscriber'%s', ALREADY REMOVED", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2833
+ return 1;
2834
+ }
2835
+ if (tracea) {
2723
2836
  trace_machine(sprintf("💜💜👎 unsubscribing event '%s': publisher %s, subscriber'%s'", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2724
2837
  trace_json(subs);
2725
2838
  }
@@ -2728,13 +2841,13 @@
2728
2841
  if (publisher.gclass.gmt.mt_subscription_deleted) publisher.gclass.gmt.mt_subscription_deleted(publisher, subs);
2729
2842
  }
2730
2843
  }
2731
- let idx = _get_subs_idx(publisher.dl_subscriptions, publisher, event, subs, subscriber);
2844
+ let idx = _get_subs_idx(publisher.dl_subscriptions, subs);
2732
2845
  if (idx >= 0) json_array_remove(publisher.dl_subscriptions, idx);
2733
2846
  else {
2734
2847
  log_error(`${gobj_short_name(gobj)}: subscription in publisher not found`);
2735
2848
  trace_json(subs);
2736
2849
  }
2737
- idx = _get_subs_idx(subscriber.dl_subscribings, publisher, event, subs, subscriber);
2850
+ idx = _get_subs_idx(subscriber.dl_subscribings, subs);
2738
2851
  if (idx >= 0) json_array_remove(subscriber.dl_subscribings, idx);
2739
2852
  else {
2740
2853
  log_error(`${gobj_short_name(gobj)}: subscription in subscriber not found`);
@@ -2777,7 +2890,17 @@
2777
2890
  }
2778
2891
  }
2779
2892
  } else event = "";
2780
- let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, kw, subscriber);
2893
+ let match = _subscription_match_kw(kw, event);
2894
+ let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, match.renamed_event, match.kw, subscriber);
2895
+ let subs_hard = null;
2896
+ 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__) {
2897
+ subs_hard = dl_subs[i];
2898
+ break;
2899
+ }
2900
+ if (subs_hard) {
2901
+ log_warning(`${gobj_short_name(publisher)}: Hard subscription REPEATED, the one there is kept and returned (event: ${event}, subscriber: ${gobj_short_name(subscriber)})`);
2902
+ return subs_hard;
2903
+ }
2781
2904
  if (json_array_size(dl_subs) > 0) {
2782
2905
  log_error(`${gobj_short_name(publisher)}: ${json_array_size(dl_subs)} subscription(s) REPEATED, will be deleted and override`);
2783
2906
  trace_json(dl_subs, "subscription REPEATED");
@@ -2792,7 +2915,7 @@
2792
2915
  json_array_append(subscriber.dl_subscribings, subs);
2793
2916
  if (__trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher)) {
2794
2917
  trace_machine(sprintf("💜💜👍 subscribing event '%s', publisher '%s', subscriber %s", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2795
- if (kw && (__trace_gobj_ev_kw__(subscriber) || __trace_gobj_ev_kw__(publisher))) trace_json(kw);
2918
+ if (kw && (__trace_gobj_ev_kw__(subscriber) || __trace_gobj_ev_kw__(publisher))) trace_json_masked(kw);
2796
2919
  }
2797
2920
  if (publisher.gclass.gmt.mt_subscription_added) {
2798
2921
  let result = publisher.gclass.gmt.mt_subscription_added(publisher, subs);
@@ -2834,16 +2957,22 @@
2834
2957
  }
2835
2958
  }
2836
2959
  } else event = "";
2837
- let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, kw, subscriber);
2960
+ let match = _subscription_match_kw(kw, event);
2961
+ let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, match.renamed_event, match.kw, subscriber);
2838
2962
  let deleted = 0;
2963
+ let kept_hard = 0;
2964
+ let already_removed = 0;
2839
2965
  for (let i = 0; i < dl_subs.length; i++) {
2840
2966
  let subs = dl_subs[i];
2841
- _delete_subscription(publisher, subs, false, false);
2842
- deleted++;
2967
+ let ret = _delete_subscription(publisher, subs, false, false);
2968
+ if (ret === 0) deleted++;
2969
+ else if (ret < 0) kept_hard++;
2970
+ else already_removed++;
2843
2971
  }
2844
- if (!deleted) {
2972
+ 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})`);
2973
+ else if (!deleted && !already_removed) {
2845
2974
  log_warning(`${gobj_short_name(publisher)}: No subscription found`);
2846
- trace_json(kw, "No subscription found");
2975
+ trace_json_masked(kw, "No subscription found");
2847
2976
  }
2848
2977
  return 0;
2849
2978
  }
@@ -2852,10 +2981,12 @@
2852
2981
  ***************************************************************************/
2853
2982
  function gobj_unsubscribe_list(gobj, dl_subs, force) {
2854
2983
  let dl = [...dl_subs];
2984
+ let already_removed = 0;
2855
2985
  for (let i = 0; i < dl.length; i++) {
2856
2986
  let subs = dl[i];
2857
- _delete_subscription(gobj, subs, force, false);
2987
+ if (_delete_subscription(gobj, subs, force, false) > 0) already_removed++;
2858
2988
  }
2989
+ if (already_removed) log_warning(`${gobj_short_name(gobj)}: Subscription(s) already removed, nothing to remove (count: ${already_removed})`);
2859
2990
  return 0;
2860
2991
  }
2861
2992
  /***************************************************************************
@@ -2867,7 +2998,7 @@
2867
2998
  ***************************************************************************/
2868
2999
  function gobj_find_subscriptions(publisher, event, kw, subscriber) {
2869
3000
  if (kw == null) kw = {};
2870
- return _find_subscriptions(publisher.dl_subscriptions, publisher, event, kw, subscriber);
3001
+ return _find_subscriptions(publisher.dl_subscriptions, publisher, event, null, kw, subscriber);
2871
3002
  }
2872
3003
  /***************************************************************************
2873
3004
  *
@@ -2906,7 +3037,7 @@
2906
3037
  * subscriber
2907
3038
  ***************************************************************************/
2908
3039
  function gobj_find_subscribings(subscriber, event, kw, publisher) {
2909
- return _find_subscriptions(subscriber.dl_subscribings, publisher, event, kw, subscriber);
3040
+ return _find_subscriptions(subscriber.dl_subscribings, publisher, event, null, kw, subscriber);
2910
3041
  }
2911
3042
  /***************************************************************************
2912
3043
  * Return the sum of returns of gobj_send_event
@@ -2932,7 +3063,7 @@
2932
3063
  if (tracea) {
2933
3064
  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));
2934
3065
  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 : "", ""));
2935
- if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw)) trace_json(kw);
3066
+ if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw)) trace_json_masked(kw);
2936
3067
  }
2937
3068
  if (publisher.gclass.gmt.mt_publish_event) {
2938
3069
  let topublish = publisher.gclass.gmt.mt_publish_event(publisher, event, kw);
@@ -2957,6 +3088,8 @@
2957
3088
  let __global__ = kw_get_dict(publisher, subs, "__global__", null, 0);
2958
3089
  let __local__ = kw_get_dict(publisher, subs, "__local__", null, 0);
2959
3090
  let __filter__ = kw_get_dict_value(publisher, subs, "__filter__", null, 0);
3091
+ let event_name = subs.renamed_event;
3092
+ if (empty_string(event_name)) event_name = event;
2960
3093
  let topublish = 1;
2961
3094
  if (publisher.gclass.gmt.mt_publication_filter) topublish = publisher.gclass.gmt.mt_publication_filter(publisher, event, kw, subscriber);
2962
3095
  else if (json_size(__filter__) > 0) {
@@ -2980,11 +3113,11 @@
2980
3113
  if (__config__) {}
2981
3114
  if (json_size(__global__) > 0) json_object_update(kw2publish, json_deep_copy(__global__));
2982
3115
  if (tracea) {
2983
- if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event ? event : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
2984
- 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)));
2985
- if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json(kw2publish);
3116
+ if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event_name ? event_name : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
3117
+ 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)));
3118
+ if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json_masked(kw2publish);
2986
3119
  }
2987
- let ret_ = gobj_send_event(subscriber, event, kw2publish, publisher);
3120
+ let ret_ = gobj_send_event(subscriber, event_name, kw2publish, publisher);
2988
3121
  if (ret_ < 0 && subs_flag & subs_flag_t.__own_event__) {
2989
3122
  sent_count = -1;
2990
3123
  break;
@@ -3008,8 +3141,9 @@
3008
3141
  return -1;
3009
3142
  }
3010
3143
  if (is_commands_tracing(gobj) || is_machine_tracing(gobj, null) && !is_machine_not_tracing(src, null)) {
3011
- trace_machine(sprintf("🌀🌀 mach(%s%s), cmd: %s, src: %s", !gobj_is_running(gobj) ? "!!" : "", gobj_short_name(gobj), command, gobj_short_name(src)));
3012
- if (__trace_gobj_ev_kw__(gobj)) trace_json(kw);
3144
+ let command_shown = mask_secrets_inline(command);
3145
+ 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)));
3146
+ if (__trace_gobj_ev_kw__(gobj)) trace_json_masked(kw);
3013
3147
  }
3014
3148
  if (gobj.gclass.gmt.mt_command_parser) return gobj.gclass.gmt.mt_command_parser(gobj, command, kw, src);
3015
3149
  if (gobj.gclass.command_table) {
@@ -3145,10 +3279,22 @@
3145
3279
  f_warning = _console.warn;
3146
3280
  }
3147
3281
  }
3282
+ /************************************************************
3283
+ * JSON.stringify for a log line: a circular object (a gobj,
3284
+ * a DOM node) must not make the log call itself throw.
3285
+ ************************************************************/
3286
+ function log_stringify(v) {
3287
+ try {
3288
+ return JSON.stringify(v);
3289
+ } catch (e) {
3290
+ return String(v);
3291
+ }
3292
+ }
3148
3293
  function format_log(format) {
3149
3294
  const args = Array.prototype.slice.call(arguments);
3150
3295
  let output = "";
3151
3296
  if (typeof format === "string") {
3297
+ if (args.length === 1) return format;
3152
3298
  let i = 1;
3153
3299
  output = format.replace(/%[sdifoO]/g, (match) => {
3154
3300
  const val = args[i++];
@@ -3158,17 +3304,17 @@
3158
3304
  case "%i": return parseInt(val);
3159
3305
  case "%f": return parseFloat(val);
3160
3306
  case "%o":
3161
- case "%O": return typeof val === "object" ? JSON.stringify(val) : String(val);
3307
+ case "%O": return typeof val === "object" ? log_stringify(val) : String(val);
3162
3308
  default: return match;
3163
3309
  }
3164
3310
  });
3165
- const rest = args.slice(i).map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg));
3311
+ const rest = args.slice(i).map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg));
3166
3312
  if (rest.length > 0) output += " " + rest.join(" ");
3167
- } else output = args.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)).join(" ");
3313
+ } else output = args.map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg)).join(" ");
3168
3314
  return output;
3169
3315
  }
3170
- function log_error(format) {
3171
- let msg = format_log(format);
3316
+ function log_error(format, ...args) {
3317
+ let msg = format_log(format, ...args);
3172
3318
  let hora = current_timestamp();
3173
3319
  if (f_error) {
3174
3320
  if (f_error === _console.error) {
@@ -3180,8 +3326,8 @@
3180
3326
  }
3181
3327
  emit_log_callback("error", msg, hora);
3182
3328
  }
3183
- function log_warning(format) {
3184
- let msg = format_log(format);
3329
+ function log_warning(format, ...args) {
3330
+ let msg = format_log(format, ...args);
3185
3331
  let hora = current_timestamp();
3186
3332
  if (f_warning) {
3187
3333
  if (f_warning === _console.warn) {
@@ -3193,24 +3339,24 @@
3193
3339
  }
3194
3340
  emit_log_callback("warning", msg, hora);
3195
3341
  }
3196
- function log_info(format) {
3197
- let msg = format_log(format);
3342
+ function log_info(format, ...args) {
3343
+ let msg = format_log(format, ...args);
3198
3344
  let hora = current_timestamp();
3199
3345
  if (f_info) {
3200
3346
  if (console_wants("info", msg)) f_info("%c" + hora + " INFO: " + String(msg), "color:cyan");
3201
3347
  }
3202
3348
  emit_log_callback("info", msg, hora);
3203
3349
  }
3204
- function log_debug(format) {
3205
- let msg = format_log(format);
3350
+ function log_debug(format, ...args) {
3351
+ let msg = format_log(format, ...args);
3206
3352
  let hora = current_timestamp();
3207
3353
  if (f_debug) {
3208
3354
  if (console_wants("debug", msg)) f_debug("%c" + hora + " DEBUG: " + String(msg), "color:silver");
3209
3355
  }
3210
3356
  emit_log_callback("debug", msg, hora);
3211
3357
  }
3212
- function trace_msg(format) {
3213
- let msg = format_log(format);
3358
+ function trace_msg(format, ...args) {
3359
+ let msg = format_log(format, ...args);
3214
3360
  let hora = current_timestamp();
3215
3361
  if (f_debug) {
3216
3362
  if (console_wants("msg", msg)) f_debug("%c" + hora + " MSG: " + String(msg), "color:cyan");
@@ -3225,6 +3371,268 @@
3225
3371
  emit_log_callback("json", jn, current_timestamp());
3226
3372
  }
3227
3373
  /************************************************************
3374
+ * The names of the secrets (is_secret_name()). The same lists
3375
+ * as the C kernel's helpers.c: a name that holds one of these
3376
+ * (any case) is a secret.
3377
+ ************************************************************/
3378
+ var SECRET_NAME_PARTS = [
3379
+ "passw",
3380
+ "pwd",
3381
+ "passphrase",
3382
+ "secret",
3383
+ "token",
3384
+ "jwt",
3385
+ "bearer",
3386
+ "authorization",
3387
+ "cookie",
3388
+ "credential",
3389
+ "salt"
3390
+ ];
3391
+ var SECRET_NAME_JOINED = [
3392
+ "apikey",
3393
+ "sessionid",
3394
+ "sessionkey",
3395
+ "authdata"
3396
+ ];
3397
+ var NOT_SECRET_SEGMENTS = /* @__PURE__ */ new Set([
3398
+ "endpoint",
3399
+ "url",
3400
+ "uri",
3401
+ "domain",
3402
+ "path",
3403
+ "file",
3404
+ "public",
3405
+ "pub",
3406
+ "count",
3407
+ "counts",
3408
+ "type",
3409
+ "name",
3410
+ "len",
3411
+ "length",
3412
+ "size",
3413
+ "max",
3414
+ "min",
3415
+ "ttl",
3416
+ "timeout",
3417
+ "expiry",
3418
+ "expires",
3419
+ "mode"
3420
+ ]);
3421
+ /************************************************************
3422
+ * TRUE if `name` is the name of a secret: a password, a token,
3423
+ * a key... Any case. As the C kernel's is_secret_name().
3424
+ ************************************************************/
3425
+ function is_secret_name(name) {
3426
+ if (typeof name !== "string" || name.length < 3) return false;
3427
+ let lower = name.toLowerCase();
3428
+ let hit = false;
3429
+ for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
3430
+ hit = true;
3431
+ break;
3432
+ }
3433
+ if (!hit) {
3434
+ let joined = lower.replace(/[_\-. \t]/g, "");
3435
+ for (const part of SECRET_NAME_JOINED) if (joined.indexOf(part) >= 0) {
3436
+ hit = true;
3437
+ break;
3438
+ }
3439
+ }
3440
+ if (!hit) hit = lower.indexOf("priv") >= 0 && lower.indexOf("key") >= 0;
3441
+ if (!hit) return false;
3442
+ for (const segment of lower.split(/[_\-. \t]/)) if (NOT_SECRET_SEGMENTS.has(segment)) return false;
3443
+ return true;
3444
+ }
3445
+ /************************************************************
3446
+ * Where an unquoted secret value that begins at `p` ends: at
3447
+ * the end, at the quote that closes an outer quoted value, or
3448
+ * before the next "word=" -- a plain word after a blank is still
3449
+ * part of it ("password=correct horse battery", "password=
3450
+ * hunter2"). As the C kernel's inline_secret_value_end().
3451
+ ************************************************************/
3452
+ function inline_secret_value_end(str, p, outer) {
3453
+ const blank = (c) => c === " " || c === " ";
3454
+ const closes_outer = (i) => outer && str[i] === outer && (i + 1 >= str.length || blank(str[i + 1]));
3455
+ let end = p;
3456
+ while (true) {
3457
+ while (end < str.length && !blank(str[end])) {
3458
+ if (closes_outer(end)) return end;
3459
+ end++;
3460
+ }
3461
+ if (end >= str.length) return end;
3462
+ let w = end;
3463
+ while (w < str.length && blank(str[w])) w++;
3464
+ if (w >= str.length || closes_outer(w)) return end;
3465
+ for (let e = w; e < str.length && !blank(str[e]) && !closes_outer(e); e++) if (str[e] === "=" && e > w) return end;
3466
+ end = w;
3467
+ }
3468
+ }
3469
+ /************************************************************
3470
+ * A text (a command line) with the value of every "name=value"
3471
+ * whose name is a secret's written as "********", quoted or not
3472
+ * (and the "value" of a write-attr whose attribute names a
3473
+ * secret). The rest as it is. Return null when there was nothing
3474
+ * to mask. As the C kernel's mask_secrets_inline().
3475
+ ************************************************************/
3476
+ var MASK_MAX_NAME = 128;
3477
+ var MASK_MAX_BYTES = 4194304;
3478
+ var MASK_TOO_LARGE = "<not shown: too large to mask>";
3479
+ function mask_secrets_inline(str) {
3480
+ if (typeof str !== "string" || str.indexOf("=") < 0) return null;
3481
+ if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3482
+ let value_is_secret = false;
3483
+ let re_attr = /(^|[ \t])attribute=["']?([^ \t"']*)/g;
3484
+ let m;
3485
+ while ((m = re_attr.exec(str)) !== null) if (is_secret_name(m[2].slice(-128))) value_is_secret = true;
3486
+ let parts = [];
3487
+ let copied = 0;
3488
+ let changed = false;
3489
+ let outer = "";
3490
+ let word = 0;
3491
+ let p = 0;
3492
+ const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " ";
3493
+ while (p < str.length) {
3494
+ let c = str[p];
3495
+ if (c === " " || c === " ") {
3496
+ p++;
3497
+ word = p;
3498
+ continue;
3499
+ }
3500
+ if (c === "\"" || c === "'") {
3501
+ if (!outer && (p === 0 || str[p - 1] === " " || str[p - 1] === " " || str[p - 1] === "=")) outer = c;
3502
+ else if (c === outer && is_blank_or_end(p + 1)) outer = "";
3503
+ }
3504
+ if (c !== "=") {
3505
+ p++;
3506
+ continue;
3507
+ }
3508
+ let name = str.substring(Math.max(word, p - MASK_MAX_NAME), p);
3509
+ word = p + 1;
3510
+ if (!(is_secret_name(name) || value_is_secret && name.toLowerCase() === "value")) {
3511
+ p++;
3512
+ continue;
3513
+ }
3514
+ p++;
3515
+ let quote = str[p] === "\"" || str[p] === "'" ? str[p] : "";
3516
+ let end;
3517
+ if (quote) {
3518
+ end = p + 1;
3519
+ while (end < str.length && str[end] !== quote) end++;
3520
+ if (str[end] === quote) end++;
3521
+ } else end = inline_secret_value_end(str, p, outer);
3522
+ if (end > p) {
3523
+ parts.push(str.substring(copied, p));
3524
+ parts.push("********");
3525
+ copied = end;
3526
+ changed = true;
3527
+ }
3528
+ p = end;
3529
+ word = p;
3530
+ }
3531
+ if (!changed) return null;
3532
+ parts.push(str.substring(copied));
3533
+ return parts.join("");
3534
+ }
3535
+ /************************************************************
3536
+ * A json as a log or a trace may show it: at any depth, the
3537
+ * value of a key with a secret's name is "********", whatever
3538
+ * its type (not an absent one, a null or an empty string); so is
3539
+ * the "value" of a dict whose "attribute" names a secret; and a
3540
+ * string is masked as mask_secrets_inline(). Return a masked
3541
+ * copy, or `jn` itself when there was nothing to mask. As the C
3542
+ * kernel's json_mask_secrets().
3543
+ *
3544
+ * Only JSON is walked: plain objects (their prototype is
3545
+ * Object.prototype or null) and arrays. Anything else -- a gobj,
3546
+ * a DOM node, a class instance, a typed array, a function -- is
3547
+ * passed as it is, never walked, as trace_json() always showed it
3548
+ * (a kw may carry a gobj: its parent and children make a cycle).
3549
+ * A plain object met twice is masked once, the same everywhere; a
3550
+ * cycle back to one is "<cycle>"; below MASK_MAX_DEPTH levels
3551
+ * (64, as in C) nothing is shown.
3552
+ * It never throws: a failure is the placeholder below.
3553
+ ************************************************************/
3554
+ var MASK_MAX_DEPTH = 64;
3555
+ var MASK_FAILED = "<not shown: the masking failed>";
3556
+ function is_plain_object(value) {
3557
+ if (value === null || typeof value !== "object") return false;
3558
+ let proto = Object.getPrototypeOf(value);
3559
+ return proto === Object.prototype || proto === null;
3560
+ }
3561
+ var MASK_IN_PROGRESS = Symbol("mask-in-progress");
3562
+ function _json_mask_secrets(jn, memo, depth, budget) {
3563
+ if (typeof jn === "string") {
3564
+ if (jn.length > budget.left) {
3565
+ budget.left = 0;
3566
+ return MASK_TOO_LARGE;
3567
+ }
3568
+ budget.left -= jn.length;
3569
+ let masked = mask_secrets_inline(jn);
3570
+ return masked !== null ? masked : jn;
3571
+ }
3572
+ let is_array = Array.isArray(jn);
3573
+ if (!is_array && !is_plain_object(jn)) return jn;
3574
+ if (budget.left <= 0) return MASK_TOO_LARGE;
3575
+ if (memo.has(jn)) {
3576
+ let seen = memo.get(jn);
3577
+ return seen === MASK_IN_PROGRESS ? "<cycle>" : seen;
3578
+ }
3579
+ if (depth >= MASK_MAX_DEPTH) return "<deeper not shown>";
3580
+ budget.left -= 1;
3581
+ memo.set(jn, MASK_IN_PROGRESS);
3582
+ let changed = false;
3583
+ let truncated = false;
3584
+ let result;
3585
+ if (is_array) {
3586
+ result = [];
3587
+ for (let i = 0; i < jn.length; i++) {
3588
+ if (budget.left <= 0) {
3589
+ truncated = true;
3590
+ break;
3591
+ }
3592
+ budget.left -= 1;
3593
+ let shown = _json_mask_secrets(jn[i], memo, depth + 1, budget);
3594
+ if (shown !== jn[i]) changed = true;
3595
+ result.push(shown);
3596
+ }
3597
+ if (truncated) result.push(MASK_TOO_LARGE);
3598
+ } else {
3599
+ result = Object.create(Object.getPrototypeOf(jn));
3600
+ let value_is_secret = typeof jn.attribute === "string" && is_secret_name(jn.attribute.slice(-128));
3601
+ for (const key in jn) {
3602
+ if (!Object.prototype.hasOwnProperty.call(jn, key)) continue;
3603
+ if (budget.left <= 0) {
3604
+ truncated = true;
3605
+ break;
3606
+ }
3607
+ budget.left -= 1 + key.length;
3608
+ let value = jn[key];
3609
+ let shown;
3610
+ if ((is_secret_name(key.slice(-128)) || value_is_secret && key === "value") && value !== void 0 && value !== null && value !== "") shown = "********";
3611
+ else shown = _json_mask_secrets(value, memo, depth + 1, budget);
3612
+ if (shown !== value) changed = true;
3613
+ result[key] = shown;
3614
+ }
3615
+ if (truncated) result["<more>"] = MASK_TOO_LARGE;
3616
+ }
3617
+ if (!changed && !truncated) result = jn;
3618
+ memo.set(jn, result);
3619
+ return result;
3620
+ }
3621
+ function json_mask_secrets(jn) {
3622
+ try {
3623
+ return _json_mask_secrets(jn, /* @__PURE__ */ new WeakMap(), 0, { left: MASK_MAX_BYTES });
3624
+ } catch (e) {
3625
+ return MASK_FAILED;
3626
+ }
3627
+ }
3628
+ /************************************************************
3629
+ * trace_json() of a json that can hold a credential: what
3630
+ * json_mask_secrets() masks is shown masked
3631
+ ************************************************************/
3632
+ function trace_json_masked(jn, msg) {
3633
+ trace_json(json_mask_secrets(jn), msg);
3634
+ }
3635
+ /************************************************************
3228
3636
  * Duplicate an object (new references)
3229
3637
  * using the modern structuredClone (Deep copy of a single object)
3230
3638
  ************************************************************/
@@ -3497,6 +3905,8 @@
3497
3905
  if (is_number(jn_var1) || is_number(jn_var2)) {
3498
3906
  let val1 = Number(jn_var1);
3499
3907
  let val2 = Number(jn_var2);
3908
+ if (Number.isNaN(val1)) val1 = 0;
3909
+ if (Number.isNaN(val2)) val2 = 0;
3500
3910
  if (val1 > val2) return 1;
3501
3911
  else if (val1 < val2) return -1;
3502
3912
  else return 0;
@@ -3526,8 +3936,8 @@
3526
3936
  ***************************************************************************/
3527
3937
  function is_metadata_key(key) {
3528
3938
  if (!is_string(key)) return false;
3529
- let i;
3530
- for (i = 0; i < key.length; i++) {
3939
+ let i = 0;
3940
+ for (; i < key.length; i++) {
3531
3941
  if (key[i] !== "_") break;
3532
3942
  if (i > 2) break;
3533
3943
  }
@@ -3538,8 +3948,8 @@
3538
3948
  ***************************************************************************/
3539
3949
  function is_private_key(key) {
3540
3950
  if (!is_string(key)) return false;
3541
- let i;
3542
- for (i = 0; i < key.length; i++) {
3951
+ let i = 0;
3952
+ for (; i < key.length; i++) {
3543
3953
  if (key[i] !== "_") break;
3544
3954
  if (i > 2) break;
3545
3955
  }
@@ -3564,6 +3974,28 @@
3564
3974
  return false;
3565
3975
  }
3566
3976
  /************************************************************
3977
+ * A key that reaches the prototype chain instead of the data:
3978
+ * "__proto__", "constructor" or "prototype" when the object
3979
+ * does not hold it as its OWN key. A walk through one of them
3980
+ * read, or WROTE, Object.prototype: the flat id
3981
+ * "__proto__`polluted" set a key on every object of the page.
3982
+ * In C they are keys like any other, so here too: read them
3983
+ * only as own keys, and write "__proto__" as an own key.
3984
+ ************************************************************/
3985
+ function is_prototype_segment(obj, key) {
3986
+ if (key === "__proto__" || key === "constructor" || key === "prototype") return !Object.prototype.hasOwnProperty.call(obj, key);
3987
+ return false;
3988
+ }
3989
+ function set_own_key(obj, key, value) {
3990
+ if (key === "__proto__") Object.defineProperty(obj, key, {
3991
+ value,
3992
+ writable: true,
3993
+ enumerable: true,
3994
+ configurable: true
3995
+ });
3996
+ else obj[key] = value;
3997
+ }
3998
+ /************************************************************
3567
3999
  * The value at `path` (keys joined by a backtick), as the C
3568
4000
  * kw_find_path() finds it: undefined when it is not there.
3569
4001
  * A kw that is not a dict or a list is LOGGED ("kw must be list
@@ -3590,7 +4022,12 @@
3590
4022
  }
3591
4023
  let ss = path.split("`");
3592
4024
  let len = ss.length;
3593
- for (let i = 0; i < len - 1; i++) {
4025
+ for (let i = 0; i < len; i++) {
4026
+ if (is_prototype_segment(kw, ss[i])) {
4027
+ if (verbose) log_error(`${prefix}path not found: '${path}'`);
4028
+ return;
4029
+ }
4030
+ if (i === len - 1) break;
3594
4031
  kw = kw[ss[i]];
3595
4032
  if (kw === void 0 || kw === null) {
3596
4033
  if (verbose) log_error(`${prefix}path not found: '${path}'`);
@@ -3650,7 +4087,7 @@
3650
4087
  return v;
3651
4088
  } else if (required) {
3652
4089
  log_error(`path not found: '${path}'`);
3653
- trace_json(kw);
4090
+ trace_json_masked(kw);
3654
4091
  }
3655
4092
  return Boolean(default_value);
3656
4093
  }
@@ -3664,11 +4101,11 @@
3664
4101
  } else if (wild && b === null) value = false;
3665
4102
  else if (wild) {
3666
4103
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3667
- trace_json(kw);
4104
+ trace_json_masked(kw);
3668
4105
  return false;
3669
4106
  } else {
3670
4107
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json boolean: '${path}'`);
3671
- trace_json(kw);
4108
+ trace_json_masked(kw);
3672
4109
  return Boolean(default_value);
3673
4110
  }
3674
4111
  if (extract) kw_delete(gobj, kw, path);
@@ -3718,7 +4155,7 @@
3718
4155
  return v;
3719
4156
  } else if (required) {
3720
4157
  log_error(`path not found: '${path}'`);
3721
- trace_json(kw);
4158
+ trace_json_masked(kw);
3722
4159
  }
3723
4160
  return parseInt(default_value);
3724
4161
  }
@@ -3726,14 +4163,14 @@
3726
4163
  if (is_number(v)) value = Math.trunc(v);
3727
4164
  else if (!wild) {
3728
4165
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json integer: '${path}'`);
3729
- trace_json(kw);
4166
+ trace_json_masked(kw);
3730
4167
  return parseInt(default_value);
3731
4168
  } else if (is_boolean(v)) value = v ? 1 : 0;
3732
4169
  else if (is_string(v)) value = strtoll_base0(v);
3733
4170
  else if (v === null) value = 0;
3734
4171
  else {
3735
4172
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3736
- trace_json(kw);
4173
+ trace_json_masked(kw);
3737
4174
  return 0;
3738
4175
  }
3739
4176
  if (extract) kw_delete(gobj, kw, path);
@@ -3765,7 +4202,7 @@
3765
4202
  return v;
3766
4203
  } else if (required) {
3767
4204
  log_error(`path not found: '${path}'`);
3768
- trace_json(kw);
4205
+ trace_json_masked(kw);
3769
4206
  }
3770
4207
  return Number(default_value);
3771
4208
  }
@@ -3773,7 +4210,7 @@
3773
4210
  if (is_number(v)) value = v;
3774
4211
  else if (!wild) {
3775
4212
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json real: '${path}'`);
3776
- trace_json(kw);
4213
+ trace_json_masked(kw);
3777
4214
  return Number(default_value);
3778
4215
  } else if (is_boolean(v)) value = v ? 1 : 0;
3779
4216
  else if (is_string(v)) {
@@ -3782,7 +4219,7 @@
3782
4219
  } else if (v === null) value = 0;
3783
4220
  else {
3784
4221
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3785
- trace_json(kw);
4222
+ trace_json_masked(kw);
3786
4223
  return 0;
3787
4224
  }
3788
4225
  if (extract) kw_delete(gobj, kw, path);
@@ -3834,14 +4271,14 @@
3834
4271
  return default_value;
3835
4272
  } else if (required && !kw_path_holds_key(kw, path)) {
3836
4273
  log_error(`path not found: '${path}'`);
3837
- trace_json(kw);
4274
+ trace_json_masked(kw);
3838
4275
  }
3839
4276
  return default_value;
3840
4277
  }
3841
4278
  if (!is_string(v)) {
3842
4279
  if (v !== null) {
3843
4280
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json str: '${path}'`);
3844
- trace_json(kw);
4281
+ trace_json_masked(kw);
3845
4282
  }
3846
4283
  return default_value;
3847
4284
  }
@@ -3864,7 +4301,7 @@
3864
4301
  return v;
3865
4302
  } else if (required) {
3866
4303
  log_error(`path not found: '${path}'`);
3867
- trace_json(kw);
4304
+ trace_json_masked(kw);
3868
4305
  }
3869
4306
  return default_value;
3870
4307
  }
@@ -3891,14 +4328,14 @@
3891
4328
  }
3892
4329
  if (required) {
3893
4330
  log_error(`path not found: '${path}'`);
3894
- trace_json(kw);
4331
+ trace_json_masked(kw);
3895
4332
  }
3896
4333
  return default_value;
3897
4334
  }
3898
4335
  if (!is_object(v)) {
3899
4336
  if (required) {
3900
4337
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json dict: '${path}'`);
3901
- trace_json(kw);
4338
+ trace_json_masked(kw);
3902
4339
  }
3903
4340
  return default_value;
3904
4341
  }
@@ -3921,7 +4358,7 @@
3921
4358
  return v;
3922
4359
  } else if (required) {
3923
4360
  log_error(`path not found: '${path}'`);
3924
- trace_json(kw);
4361
+ trace_json_masked(kw);
3925
4362
  }
3926
4363
  return default_value;
3927
4364
  }
@@ -3949,14 +4386,14 @@
3949
4386
  }
3950
4387
  if (required) {
3951
4388
  log_error(`path not found: '${path}'`);
3952
- trace_json(kw);
4389
+ trace_json_masked(kw);
3953
4390
  }
3954
4391
  return default_value;
3955
4392
  }
3956
4393
  if (!is_array(v)) {
3957
4394
  if (required) {
3958
4395
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json list: '${path}'`);
3959
- trace_json(kw);
4396
+ trace_json_masked(kw);
3960
4397
  }
3961
4398
  return default_value;
3962
4399
  }
@@ -3973,17 +4410,13 @@
3973
4410
  return -1;
3974
4411
  }
3975
4412
  let ss = path.split("`");
3976
- if (ss.length <= 1) {
3977
- kw[path] = value;
3978
- return 0;
3979
- }
3980
4413
  let len = ss.length;
3981
4414
  for (let i = 0; i < len; i++) {
3982
4415
  let key = ss[i];
3983
- if (i === len - 1) kw[key] = value;
4416
+ if (i === len - 1) set_own_key(kw, key, value);
3984
4417
  else {
3985
- let seg = kw[key];
3986
- if (seg === void 0) kw[key] = {};
4418
+ let seg = is_prototype_segment(kw, key) ? void 0 : kw[key];
4419
+ if (seg === void 0) set_own_key(kw, key, {});
3987
4420
  else if (!is_object(seg) && !is_array(seg)) {
3988
4421
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}kw_set_dict_value(): segment '${key}' is not a dict or a list: '${path}'`);
3989
4422
  return -1;
@@ -4038,9 +4471,9 @@
4038
4471
  if (!matched) break;
4039
4472
  continue;
4040
4473
  }
4041
- let jn_record_value = kw_get_dict_value(0, kw, path, 0, 0);
4042
- if (!jn_record_value) jn_record_value = json_object_get(kw, path);
4043
- if (!jn_record_value) {
4474
+ let jn_record_value = kw_get_dict_value(0, kw, path, void 0, 0);
4475
+ if (jn_record_value === void 0) jn_record_value = json_object_get(kw, path);
4476
+ if (jn_record_value === void 0) {
4044
4477
  matched = false;
4045
4478
  break;
4046
4479
  }
@@ -4159,10 +4592,11 @@
4159
4592
  ***************************************************************************/
4160
4593
  function kw_clone_by_not_keys(gobj, kw, keys, verbose) {
4161
4594
  let kw_clone = {};
4595
+ if (!is_object(kw)) return kw_clone;
4162
4596
  json_object_update(kw_clone, kw);
4163
4597
  if (is_string(keys) && !empty_string(keys)) {
4164
4598
  let key = keys;
4165
- if (kw_get_dict_value(gobj, kw, key, 0, 0)) json_object_del(kw_clone, key);
4599
+ if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
4166
4600
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4167
4601
  } else if (is_object(keys) && json_object_size(keys) > 0) {
4168
4602
  for (let key of Object.keys(keys)) if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
@@ -5122,9 +5556,15 @@
5122
5556
  * will be 'data-i18n' attr
5123
5557
  *
5124
5558
  * content -> string | [createElement2() or parameters of createElement2]
5559
+ * | HTMLElement | SVGElement | Text
5125
5560
  * if string begins with '<' then
5126
5561
  * it will be converted to HTMLElement
5127
5562
  * content = createOneHtml(string)
5563
+ * ⚠️ So a string is MARKUP when it starts with '<': never
5564
+ * pass DATA (a value from a backend, a name a user typed)
5565
+ * as a bare string, or "<img src=x onerror=...>" runs.
5566
+ * Pass it as a Text node, which is always text:
5567
+ * ['span', {}, document.createTextNode(value)]
5128
5568
  * if attrs['i18n'] is not empty then (or 'data-i18n' or 'data_i18next')
5129
5569
  * it will be overridden with the translation
5130
5570
  * content = translate(attrs['i18n']));
@@ -5563,7 +6003,7 @@
5563
6003
  current[seg] = flat[key];
5564
6004
  } else {
5565
6005
  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`);
5566
- current[seg] = flat[key];
6006
+ set_own_key(current, seg, flat[key]);
5567
6007
  }
5568
6008
  break;
5569
6009
  }
@@ -5576,10 +6016,10 @@
5576
6016
  current[seg] = child;
5577
6017
  }
5578
6018
  } else {
5579
- child = current[seg];
6019
+ child = is_prototype_segment(current, seg) ? void 0 : current[seg];
5580
6020
  if (child === void 0) {
5581
6021
  child = next_is_index ? [] : {};
5582
- current[seg] = child;
6022
+ set_own_key(current, seg, child);
5583
6023
  }
5584
6024
  }
5585
6025
  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`);
@@ -6342,6 +6782,7 @@
6342
6782
  ***************************************************************/
6343
6783
  function mt_play(gobj) {
6344
6784
  let default_service = gobj_default_service();
6785
+ if (!default_service) return 0;
6345
6786
  if (!gobj_is_running(default_service)) gobj_start(default_service);
6346
6787
  if (!gobj_is_playing(default_service)) gobj_play(default_service);
6347
6788
  return 0;
@@ -6350,7 +6791,8 @@
6350
6791
  * Framework Method: Pause
6351
6792
  ***************************************************************/
6352
6793
  function mt_pause(gobj) {
6353
- gobj_pause(gobj_default_service());
6794
+ let default_service = gobj_default_service();
6795
+ if (default_service && gobj_is_playing(default_service)) gobj_pause(default_service);
6354
6796
  return 0;
6355
6797
  }
6356
6798
  /***************************************************************
@@ -6606,6 +7048,7 @@
6606
7048
  function mt_create(gobj) {
6607
7049
  let priv = gobj.priv;
6608
7050
  priv.gobj_timer = gobj_create_pure_child(gobj_name(gobj), "C_TIMER", {}, gobj);
7051
+ priv.ws_seq = 0;
6609
7052
  priv.url = gobj_read_str_attr(gobj, "url");
6610
7053
  priv.remote_yuno_name = gobj_read_str_attr(gobj, "remote_yuno_name");
6611
7054
  priv.remote_yuno_role = gobj_read_str_attr(gobj, "remote_yuno_role");
@@ -6640,6 +7083,16 @@
6640
7083
  function mt_start(gobj) {
6641
7084
  let priv = gobj.priv;
6642
7085
  priv.retry_ms = 0;
7086
+ if (gobj_current_state(gobj) !== "ST_DISCONNECTED") {
7087
+ gobj_send_event(gobj, "EV_ON_CLOSE", {
7088
+ url: priv.url,
7089
+ ws_seq: priv.ws_seq,
7090
+ code: 1e3,
7091
+ reason: "restarted",
7092
+ was_clean: true
7093
+ }, gobj);
7094
+ clear_timeout(priv.gobj_timer);
7095
+ }
6643
7096
  priv.websocket = setup_websocket(gobj);
6644
7097
  return 0;
6645
7098
  }
@@ -6774,10 +7227,16 @@
6774
7227
  log_error(`${gobj_short_name(gobj)}: Failed to initialize WebSocket.`);
6775
7228
  return null;
6776
7229
  }
7230
+ const ws_seq = ++gobj.priv.ws_seq;
6777
7231
  websocket.onopen = () => {
6778
- gobj_send_event(gobj, "EV_ON_OPEN", { url }, gobj);
7232
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
7233
+ gobj_send_event(gobj, "EV_ON_OPEN", {
7234
+ url,
7235
+ ws_seq
7236
+ }, gobj);
6779
7237
  };
6780
7238
  websocket.onmessage = (e) => {
7239
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
6781
7240
  gobj_send_event(gobj, "EV_ON_MESSAGE", {
6782
7241
  url,
6783
7242
  data: e.data
@@ -6785,9 +7244,13 @@
6785
7244
  };
6786
7245
  websocket.onclose = (e) => {
6787
7246
  if (gobj_is_destroying(gobj)) return;
7247
+ if (ws_seq !== gobj.priv.ws_seq) return;
6788
7248
  if (!gobj_read_bool_attr(gobj_yuno(), "browser_beforeunload")) gobj_send_event(gobj, "EV_ON_CLOSE", {
6789
7249
  url,
6790
- e
7250
+ ws_seq,
7251
+ code: e.code,
7252
+ reason: e.reason,
7253
+ was_clean: e.wasClean
6791
7254
  }, gobj);
6792
7255
  else log_warning("Close websocket by unload page");
6793
7256
  };
@@ -6808,7 +7271,7 @@
6808
7271
  try {
6809
7272
  websocket.close(code, reason);
6810
7273
  } catch (e) {
6811
- log_error("Error closing WebSocket:", e.message);
7274
+ log_error(`${gobj_short_name(gobj)}: Error closing WebSocket: ${e.message}`);
6812
7275
  }
6813
7276
  }
6814
7277
  /***************************************************************
@@ -6818,7 +7281,7 @@
6818
7281
  let hora = current_timestamp();
6819
7282
  try {
6820
7283
  log_debug("\n" + hora + " " + prefix + "\n");
6821
- trace_json(iev);
7284
+ trace_json_masked(iev);
6822
7285
  } catch (e) {
6823
7286
  log_debug("ERROR in trace_inter_event: " + e);
6824
7287
  }
@@ -6934,29 +7397,29 @@
6934
7397
  try {
6935
7398
  x = JSON.parse(data);
6936
7399
  } catch (e) {
6937
- log_error("parsing inter_event json: " + e);
7400
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: ${e}`);
6938
7401
  return null;
6939
7402
  }
6940
7403
  if (!(x instanceof Object)) {
6941
- log_error("parsing inter_event: websocket data not a json object");
7404
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: websocket data not a json object`);
6942
7405
  return null;
6943
7406
  }
6944
7407
  let event = x["event"];
6945
7408
  if (!event) {
6946
- log_error("parsing inter_event: no event");
7409
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: no event`);
6947
7410
  return null;
6948
7411
  }
6949
7412
  if (typeof event !== "string") {
6950
- log_error("parsing inter_event: event not a string");
7413
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: event not a string`);
6951
7414
  return null;
6952
7415
  }
6953
7416
  let kw = x["kw"];
6954
7417
  if (!kw) {
6955
- log_error("parsing inter_kw: no kw");
7418
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_kw: no kw`);
6956
7419
  return null;
6957
7420
  }
6958
7421
  if (!(kw instanceof Object)) {
6959
- log_error("parsing inter_event: kw not a json object");
7422
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: kw not a json object`);
6960
7423
  return null;
6961
7424
  }
6962
7425
  return {
@@ -7016,10 +7479,9 @@
7016
7479
  ***************************************************************/
7017
7480
  function ac_on_close(gobj, event, kw, src) {
7018
7481
  let priv = gobj.priv;
7019
- let e = kw.e;
7020
7482
  let url = kw.url;
7021
7483
  set_remote_log_functions();
7022
- log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7484
+ log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7023
7485
  if (priv.websocket) {
7024
7486
  priv.websocket.onopen = null;
7025
7487
  priv.websocket.onmessage = null;
@@ -7036,15 +7498,15 @@
7036
7498
  remote_yuno_service: priv.remote_yuno_service
7037
7499
  });
7038
7500
  } else {
7039
- log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7501
+ log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7040
7502
  gobj_publish_event(gobj, "EV_ON_OPEN_ERROR", {
7041
7503
  url,
7042
7504
  remote_yuno_name: priv.remote_yuno_name,
7043
7505
  remote_yuno_role: priv.remote_yuno_role,
7044
7506
  remote_yuno_service: priv.remote_yuno_service,
7045
- code: e.code,
7046
- reason: e.reason,
7047
- was_clean: e.wasClean
7507
+ code: kw.code,
7508
+ reason: kw.reason,
7509
+ was_clean: kw.was_clean
7048
7510
  });
7049
7511
  }
7050
7512
  if (gobj_is_running(gobj)) set_timeout(priv.gobj_timer, next_retry(gobj));
@@ -7142,6 +7604,10 @@
7142
7604
  function ac_on_message(gobj, event, kw, src) {
7143
7605
  let url = gobj.priv.url;
7144
7606
  let iev_msg = iev_create_from_json(gobj, kw.data);
7607
+ if (!iev_msg) {
7608
+ log_warning(`${gobj_short_name(gobj)}: frame dropped from ${url}: ${String(kw.data).substring(0, 256)}`);
7609
+ return -1;
7610
+ }
7145
7611
  if (is_traffic_tracing(gobj)) {
7146
7612
  let prefix = gobj_name(gobj_yuno()) + " <== " + url;
7147
7613
  let size = kw.data.length;
@@ -7167,8 +7633,14 @@
7167
7633
  return 0;
7168
7634
  }
7169
7635
  let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
7170
- if (msg_type === "__subscribing__") return -1;
7171
- if (msg_type === "__unsubscribing__") return -1;
7636
+ if (msg_type === "__subscribing__") {
7637
+ log_warning(`${gobj_short_name(gobj)}: subscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7638
+ return -1;
7639
+ }
7640
+ if (msg_type === "__unsubscribing__") {
7641
+ log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7642
+ return -1;
7643
+ }
7172
7644
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
7173
7645
  gobj_send_event(gobj, iev_event, iev_kw, gobj);
7174
7646
  return 0;
@@ -7500,6 +7972,7 @@
7500
7972
  exports.is_object = is_object;
7501
7973
  exports.is_private_key = is_private_key;
7502
7974
  exports.is_pure_number = is_pure_number;
7975
+ exports.is_secret_name = is_secret_name;
7503
7976
  exports.is_string = is_string;
7504
7977
  exports.jdb_delete = jdb_delete;
7505
7978
  exports.jdb_get = jdb_get;
@@ -7515,6 +7988,7 @@
7515
7988
  exports.json_array_size = json_array_size;
7516
7989
  exports.json_deep_copy = json_deep_copy;
7517
7990
  exports.json_is_identical = json_is_identical;
7991
+ exports.json_mask_secrets = json_mask_secrets;
7518
7992
  exports.json_object_del = json_object_del;
7519
7993
  exports.json_object_get = json_object_get;
7520
7994
  exports.json_object_set = json_object_set;
@@ -7562,6 +8036,7 @@
7562
8036
  exports.log_error = log_error;
7563
8037
  exports.log_info = log_info;
7564
8038
  exports.log_warning = log_warning;
8039
+ exports.mask_secrets_inline = mask_secrets_inline;
7565
8040
  exports.msg_iev_get_msg_type = msg_iev_get_msg_type;
7566
8041
  exports.msg_iev_get_stack = msg_iev_get_stack;
7567
8042
  exports.msg_iev_push_stack = msg_iev_push_stack;
@@ -7595,6 +8070,7 @@
7595
8070
  exports.template_get_field_desc = template_get_field_desc;
7596
8071
  exports.timeTracker = timeTracker;
7597
8072
  exports.trace_json = trace_json;
8073
+ exports.trace_json_masked = trace_json_masked;
7598
8074
  exports.trace_level_t = trace_level_t;
7599
8075
  exports.trace_msg = trace_msg;
7600
8076
  exports.treedb_decoder_fkey = treedb_decoder_fkey;