@yuneta/gobj-js 7.25.7 → 7.25.9

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) {
@@ -1337,7 +1337,7 @@
1337
1337
  let new_kw = kw;
1338
1338
  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
1339
  log_error(`${gobj_short_name(gobj)} json2data() FAILED`);
1340
- trace_json(new_kw);
1340
+ trace_json_masked(new_kw);
1341
1341
  }
1342
1342
  }
1343
1343
  /************************************************************
@@ -2552,7 +2552,7 @@
2552
2552
  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
2553
  if (kw) {
2554
2554
  if (__trace_gobj_ev_kw__(dst)) {
2555
- if (json_object_size(kw)) trace_json(kw);
2555
+ if (json_object_size(kw)) trace_json_masked(kw);
2556
2556
  }
2557
2557
  }
2558
2558
  }
@@ -2572,7 +2572,7 @@
2572
2572
  }
2573
2573
  if (kw) {
2574
2574
  if (__trace_gobj_ev_kw__(dst)) {
2575
- if (json_object_size(kw)) trace_json(kw);
2575
+ if (json_object_size(kw)) trace_json_masked(kw);
2576
2576
  }
2577
2577
  }
2578
2578
  }
@@ -2586,9 +2586,25 @@
2586
2586
  return ret;
2587
2587
  }
2588
2588
  var subs_flag_t = Object.freeze({
2589
- __hard_subscription__: 1,
2590
- __own_event__: 2
2589
+ __rename_event_name__: 1,
2590
+ __hard_subscription__: 2,
2591
+ __own_event__: 4
2591
2592
  });
2593
+ /***************************************************************************
2594
+ * The event type of `event_name` in any registered gclass, null if none.
2595
+ * The C kernel's gobj_find_event_type(): any case, gclasses only.
2596
+ ***************************************************************************/
2597
+ function _find_event_type_in_gclasses(event_name) {
2598
+ let lower = String(event_name).toLowerCase();
2599
+ for (let gclass_name in _gclass_register) {
2600
+ let dl = _gclass_register[gclass_name].dl_events;
2601
+ for (let i = 0; i < dl.length; i++) {
2602
+ let event_type = dl[i];
2603
+ if (event_type.event_name && event_type.event_name.toLowerCase() === lower) return event_type;
2604
+ }
2605
+ }
2606
+ return null;
2607
+ }
2592
2608
  var subscription_desc = [
2593
2609
  SDATA(data_type_t.DTP_POINTER, "publisher", 0, null, "publisher gobj"),
2594
2610
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "subscriber gobj"),
@@ -2621,6 +2637,21 @@
2621
2637
  if (json_size(__config__) > 0) {
2622
2638
  let kw_clone = json_deep_copy(__config__);
2623
2639
  json_object_set_new(subs, "__config__", kw_clone);
2640
+ let renamed_event = kw_get_str(publisher, kw_clone, "__rename_event_name__", "");
2641
+ if (!empty_string(renamed_event)) {
2642
+ let event_type = _find_event_type_in_gclasses(renamed_event);
2643
+ if (event_type) {
2644
+ json_object_set_new(subs, "renamed_event", event_type.event_name);
2645
+ delete kw_clone.__rename_event_name__;
2646
+ subs_flag |= subs_flag_t.__rename_event_name__;
2647
+ let kw_global = kw_get_dict(publisher, subs, "__global__", null);
2648
+ if (!is_object(kw_global)) {
2649
+ kw_global = {};
2650
+ json_object_set_new(subs, "__global__", kw_global);
2651
+ }
2652
+ kw_global.__original_event_name__ = event;
2653
+ } else log_error(`${gobj_short_name(publisher)}: EVENT NOT FOUND (__rename_event_name__: ${renamed_event})`);
2654
+ }
2624
2655
  if (kw_has_key(kw_clone, "__hard_subscription__")) {
2625
2656
  let hard_subscription = kw_get_bool(publisher, kw_clone, "__hard_subscription__", false);
2626
2657
  json_object_del(kw_clone, "__hard_subscription__");
@@ -2640,9 +2671,52 @@
2640
2671
  return subs;
2641
2672
  }
2642
2673
  /***************************************************************************
2674
+ * The kw to find the subscriptions a (un)subscription `kw` names: `kw` as
2675
+ * _create_subscription() stores it, as the C kernel's
2676
+ * _subscription_match_kw(). The three `__config__` keys the framework
2677
+ * reads (__hard_subscription__, __own_event__, __rename_event_name__)
2678
+ * become the subs_flag and are taken out of the stored `__config__`, and
2679
+ * a renamed event adds `__original_event_name__` to the stored
2680
+ * `__global__`: with `kw` as it came, the same kw matched no subscription,
2681
+ * a repeat of it was made twice (the subscriber got each event twice) and
2682
+ * its withdrawal found nothing. A known renamed event is returned in
2683
+ * `renamed_event`, which _match_subscription() compares: a plain and a
2684
+ * renamed subscription, or two renames of one event, are two. A rename
2685
+ * that no gclass declares stays in the stored `__config__`, and in this
2686
+ * one. Up to gobj-js 7.25.8 the kw was compared as it came, and
2687
+ * `__rename_event_name__` always stayed in the stored `__config__`: a
2688
+ * plain kw with the rest of that __config__ found no repeat (two
2689
+ * subscriptions where C has one).
2690
+ * Return {kw, renamed_event}; renamed_event is "" when kw does not rename.
2691
+ ***************************************************************************/
2692
+ function _subscription_match_kw(kw, event) {
2693
+ let __config__ = kw ? kw.__config__ : null;
2694
+ if (!is_object(__config__) || !kw_has_key(__config__, "__hard_subscription__") && !kw_has_key(__config__, "__own_event__") && !kw_has_key(__config__, "__rename_event_name__")) return {
2695
+ kw,
2696
+ renamed_event: ""
2697
+ };
2698
+ let kw_match = Object.assign({}, kw);
2699
+ let config_match = Object.assign({}, __config__);
2700
+ kw_match.__config__ = config_match;
2701
+ let renamed_event = "";
2702
+ let rename = config_match.__rename_event_name__;
2703
+ let event_type = typeof rename === "string" && !empty_string(rename) ? _find_event_type_in_gclasses(rename) : null;
2704
+ if (event_type) {
2705
+ renamed_event = event_type.event_name;
2706
+ delete config_match.__rename_event_name__;
2707
+ if (event && json_size(kw_match.__global__) > 0) kw_match.__global__ = Object.assign({}, kw_match.__global__, { __original_event_name__: event });
2708
+ }
2709
+ delete config_match.__hard_subscription__;
2710
+ delete config_match.__own_event__;
2711
+ return {
2712
+ kw: kw_match,
2713
+ renamed_event
2714
+ };
2715
+ }
2716
+ /***************************************************************************
2643
2717
  * Match subscription
2644
2718
  ***************************************************************************/
2645
- function _match_subscription(subs, publisher, event, kw, subscriber) {
2719
+ function _match_subscription(subs, publisher, event, renamed_event, kw, subscriber) {
2646
2720
  let __config__ = kw_get_dict(publisher, kw, "__config__", null);
2647
2721
  let __global__ = kw_get_dict(publisher, kw, "__global__", null);
2648
2722
  let __local__ = kw_get_dict(publisher, kw, "__local__", null);
@@ -2657,6 +2731,9 @@
2657
2731
  if (event) {
2658
2732
  if (event !== kw_get_str(null, subs, "event", "")) match = false;
2659
2733
  }
2734
+ if (renamed_event) {
2735
+ if (renamed_event !== kw_get_str(null, subs, "renamed_event", "")) match = false;
2736
+ }
2660
2737
  if (json_size(__config__) > 0) {
2661
2738
  let kw_config = kw_get_dict(null, subs, "__config__", null);
2662
2739
  if (json_size(kw_config) > 0) {
@@ -2691,26 +2768,29 @@
2691
2768
  * kw (__config__, __global__, __local__, __filter__),
2692
2769
  * subscriber
2693
2770
  ***************************************************************************/
2694
- function _find_subscriptions(dl_subs, publisher, event, kw, subscriber) {
2771
+ function _find_subscriptions(dl_subs, publisher, event, renamed_event, kw, subscriber) {
2695
2772
  let iter = [];
2696
2773
  for (let i = 0; i < dl_subs.length; i++) {
2697
2774
  let subs = dl_subs[i];
2698
- if (_match_subscription(subs, publisher, event, kw, subscriber)) json_array_append(iter, subs);
2775
+ if (_match_subscription(subs, publisher, event, renamed_event, kw, subscriber)) json_array_append(iter, subs);
2699
2776
  }
2700
2777
  return iter;
2701
2778
  }
2702
2779
  /***************************************************************************
2703
- * Find idx of subscription in dl_subs, -1 not found
2780
+ * Find idx of the subscription `subs` itself in dl_subs, -1 not found.
2781
+ * Up to gobj-js 7.25.8 it was the first entry whose fields matched `subs`:
2782
+ * a subscription already removed took a live one that matched it (a plain
2783
+ * one matches every other of its event and subscriber).
2704
2784
  ***************************************************************************/
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;
2785
+ function _get_subs_idx(dl_subs, subs) {
2786
+ return dl_subs.indexOf(subs);
2711
2787
  }
2712
2788
  /***************************************************************************
2713
- * Delete subscription in publisher and subscriber
2789
+ * Delete subscription in publisher and subscriber.
2790
+ * Return 0 removed, -1 a hard one kept (only `force` removes it), 1 it
2791
+ * was not there any more: already removed, as a stale reference is, or by
2792
+ * the mt_subscription_deleted() of an earlier entry of the same list. The
2793
+ * caller says which is an error. As in C.
2714
2794
  ***************************************************************************/
2715
2795
  function _delete_subscription(gobj, subs, force, not_inform) {
2716
2796
  let publisher = kw_get_pointer(gobj, subs, "publisher", null, kw_flag_t.KW_REQUIRED);
@@ -2719,7 +2799,12 @@
2719
2799
  if (kw_get_int(gobj, subs, "subs_flag", null, kw_flag_t.KW_REQUIRED) & subs_flag_t.__hard_subscription__ ? 1 : 0) {
2720
2800
  if (!force) return -1;
2721
2801
  }
2722
- if (__trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher)) {
2802
+ let tracea = __trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher);
2803
+ if (_get_subs_idx(publisher.dl_subscriptions, subs) < 0) {
2804
+ if (tracea) trace_machine(sprintf("💜💜👎 unsubscribing event '%s': publisher %s, subscriber'%s', ALREADY REMOVED", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2805
+ return 1;
2806
+ }
2807
+ if (tracea) {
2723
2808
  trace_machine(sprintf("💜💜👎 unsubscribing event '%s': publisher %s, subscriber'%s'", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2724
2809
  trace_json(subs);
2725
2810
  }
@@ -2728,13 +2813,13 @@
2728
2813
  if (publisher.gclass.gmt.mt_subscription_deleted) publisher.gclass.gmt.mt_subscription_deleted(publisher, subs);
2729
2814
  }
2730
2815
  }
2731
- let idx = _get_subs_idx(publisher.dl_subscriptions, publisher, event, subs, subscriber);
2816
+ let idx = _get_subs_idx(publisher.dl_subscriptions, subs);
2732
2817
  if (idx >= 0) json_array_remove(publisher.dl_subscriptions, idx);
2733
2818
  else {
2734
2819
  log_error(`${gobj_short_name(gobj)}: subscription in publisher not found`);
2735
2820
  trace_json(subs);
2736
2821
  }
2737
- idx = _get_subs_idx(subscriber.dl_subscribings, publisher, event, subs, subscriber);
2822
+ idx = _get_subs_idx(subscriber.dl_subscribings, subs);
2738
2823
  if (idx >= 0) json_array_remove(subscriber.dl_subscribings, idx);
2739
2824
  else {
2740
2825
  log_error(`${gobj_short_name(gobj)}: subscription in subscriber not found`);
@@ -2777,7 +2862,17 @@
2777
2862
  }
2778
2863
  }
2779
2864
  } else event = "";
2780
- let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, kw, subscriber);
2865
+ let match = _subscription_match_kw(kw, event);
2866
+ let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, match.renamed_event, match.kw, subscriber);
2867
+ let subs_hard = null;
2868
+ 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__) {
2869
+ subs_hard = dl_subs[i];
2870
+ break;
2871
+ }
2872
+ if (subs_hard) {
2873
+ log_warning(`${gobj_short_name(publisher)}: Hard subscription REPEATED, the one there is kept and returned (event: ${event}, subscriber: ${gobj_short_name(subscriber)})`);
2874
+ return subs_hard;
2875
+ }
2781
2876
  if (json_array_size(dl_subs) > 0) {
2782
2877
  log_error(`${gobj_short_name(publisher)}: ${json_array_size(dl_subs)} subscription(s) REPEATED, will be deleted and override`);
2783
2878
  trace_json(dl_subs, "subscription REPEATED");
@@ -2792,7 +2887,7 @@
2792
2887
  json_array_append(subscriber.dl_subscribings, subs);
2793
2888
  if (__trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher)) {
2794
2889
  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);
2890
+ if (kw && (__trace_gobj_ev_kw__(subscriber) || __trace_gobj_ev_kw__(publisher))) trace_json_masked(kw);
2796
2891
  }
2797
2892
  if (publisher.gclass.gmt.mt_subscription_added) {
2798
2893
  let result = publisher.gclass.gmt.mt_subscription_added(publisher, subs);
@@ -2834,16 +2929,22 @@
2834
2929
  }
2835
2930
  }
2836
2931
  } else event = "";
2837
- let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, kw, subscriber);
2932
+ let match = _subscription_match_kw(kw, event);
2933
+ let dl_subs = _find_subscriptions(publisher.dl_subscriptions, publisher, event, match.renamed_event, match.kw, subscriber);
2838
2934
  let deleted = 0;
2935
+ let kept_hard = 0;
2936
+ let already_removed = 0;
2839
2937
  for (let i = 0; i < dl_subs.length; i++) {
2840
2938
  let subs = dl_subs[i];
2841
- _delete_subscription(publisher, subs, false, false);
2842
- deleted++;
2939
+ let ret = _delete_subscription(publisher, subs, false, false);
2940
+ if (ret === 0) deleted++;
2941
+ else if (ret < 0) kept_hard++;
2942
+ else already_removed++;
2843
2943
  }
2844
- if (!deleted) {
2944
+ 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})`);
2945
+ else if (!deleted && !already_removed) {
2845
2946
  log_warning(`${gobj_short_name(publisher)}: No subscription found`);
2846
- trace_json(kw, "No subscription found");
2947
+ trace_json_masked(kw, "No subscription found");
2847
2948
  }
2848
2949
  return 0;
2849
2950
  }
@@ -2852,10 +2953,12 @@
2852
2953
  ***************************************************************************/
2853
2954
  function gobj_unsubscribe_list(gobj, dl_subs, force) {
2854
2955
  let dl = [...dl_subs];
2956
+ let already_removed = 0;
2855
2957
  for (let i = 0; i < dl.length; i++) {
2856
2958
  let subs = dl[i];
2857
- _delete_subscription(gobj, subs, force, false);
2959
+ if (_delete_subscription(gobj, subs, force, false) > 0) already_removed++;
2858
2960
  }
2961
+ if (already_removed) log_warning(`${gobj_short_name(gobj)}: Subscription(s) already removed, nothing to remove (count: ${already_removed})`);
2859
2962
  return 0;
2860
2963
  }
2861
2964
  /***************************************************************************
@@ -2867,7 +2970,7 @@
2867
2970
  ***************************************************************************/
2868
2971
  function gobj_find_subscriptions(publisher, event, kw, subscriber) {
2869
2972
  if (kw == null) kw = {};
2870
- return _find_subscriptions(publisher.dl_subscriptions, publisher, event, kw, subscriber);
2973
+ return _find_subscriptions(publisher.dl_subscriptions, publisher, event, null, kw, subscriber);
2871
2974
  }
2872
2975
  /***************************************************************************
2873
2976
  *
@@ -2906,7 +3009,7 @@
2906
3009
  * subscriber
2907
3010
  ***************************************************************************/
2908
3011
  function gobj_find_subscribings(subscriber, event, kw, publisher) {
2909
- return _find_subscriptions(subscriber.dl_subscribings, publisher, event, kw, subscriber);
3012
+ return _find_subscriptions(subscriber.dl_subscribings, publisher, event, null, kw, subscriber);
2910
3013
  }
2911
3014
  /***************************************************************************
2912
3015
  * Return the sum of returns of gobj_send_event
@@ -2932,7 +3035,7 @@
2932
3035
  if (tracea) {
2933
3036
  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
3037
  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);
3038
+ if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw)) trace_json_masked(kw);
2936
3039
  }
2937
3040
  if (publisher.gclass.gmt.mt_publish_event) {
2938
3041
  let topublish = publisher.gclass.gmt.mt_publish_event(publisher, event, kw);
@@ -2957,11 +3060,12 @@
2957
3060
  let __global__ = kw_get_dict(publisher, subs, "__global__", null, 0);
2958
3061
  let __local__ = kw_get_dict(publisher, subs, "__local__", null, 0);
2959
3062
  let __filter__ = kw_get_dict_value(publisher, subs, "__filter__", null, 0);
2960
- let kw2publish = kw;
3063
+ let event_name = subs.renamed_event;
3064
+ if (empty_string(event_name)) event_name = event;
2961
3065
  let topublish = 1;
2962
- if (publisher.gclass.gmt.mt_publication_filter) topublish = publisher.gclass.gmt.mt_publication_filter(publisher, event, kw2publish, subscriber);
3066
+ if (publisher.gclass.gmt.mt_publication_filter) topublish = publisher.gclass.gmt.mt_publication_filter(publisher, event, kw, subscriber);
2963
3067
  else if (json_size(__filter__) > 0) {
2964
- topublish = kw_match_simple(kw2publish, __filter__) ? 1 : 0;
3068
+ topublish = kw_match_simple(kw, __filter__) ? 1 : 0;
2965
3069
  if (tracea) {
2966
3070
  trace_machine(sprintf("💜💜🔄%s publishing with filter, event '%s', subscriber'%s', publisher %s", topublish ? "👍" : "👎", event ? event : "", gobj_short_name(subscriber), gobj_short_name(publisher)));
2967
3071
  trace_json(__filter__);
@@ -2975,15 +3079,17 @@
2975
3079
  if (!gobj_has_event(subscriber, ev_.event_name, 0)) continue;
2976
3080
  }
2977
3081
  }
2978
- if (__local__) kw_pop(kw2publish, __local__);
3082
+ let kw2publish = kw;
3083
+ if (json_size(__local__) > 0 || json_size(__global__) > 0) kw2publish = Object.assign({}, kw);
3084
+ if (json_size(__local__) > 0) kw_pop(kw2publish, __local__);
2979
3085
  if (__config__) {}
2980
- if (__global__) json_object_update(kw2publish, __global__);
3086
+ if (json_size(__global__) > 0) json_object_update(kw2publish, json_deep_copy(__global__));
2981
3087
  if (tracea) {
2982
- if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event ? event : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
2983
- 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)));
2984
- if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json(kw2publish);
3088
+ if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event_name ? event_name : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
3089
+ 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)));
3090
+ if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json_masked(kw2publish);
2985
3091
  }
2986
- let ret_ = gobj_send_event(subscriber, event, kw2publish, publisher);
3092
+ let ret_ = gobj_send_event(subscriber, event_name, kw2publish, publisher);
2987
3093
  if (ret_ < 0 && subs_flag & subs_flag_t.__own_event__) {
2988
3094
  sent_count = -1;
2989
3095
  break;
@@ -3007,8 +3113,9 @@
3007
3113
  return -1;
3008
3114
  }
3009
3115
  if (is_commands_tracing(gobj) || is_machine_tracing(gobj, null) && !is_machine_not_tracing(src, null)) {
3010
- trace_machine(sprintf("🌀🌀 mach(%s%s), cmd: %s, src: %s", !gobj_is_running(gobj) ? "!!" : "", gobj_short_name(gobj), command, gobj_short_name(src)));
3011
- if (__trace_gobj_ev_kw__(gobj)) trace_json(kw);
3116
+ let command_shown = mask_secrets_inline(command);
3117
+ 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)));
3118
+ if (__trace_gobj_ev_kw__(gobj)) trace_json_masked(kw);
3012
3119
  }
3013
3120
  if (gobj.gclass.gmt.mt_command_parser) return gobj.gclass.gmt.mt_command_parser(gobj, command, kw, src);
3014
3121
  if (gobj.gclass.command_table) {
@@ -3224,6 +3331,268 @@
3224
3331
  emit_log_callback("json", jn, current_timestamp());
3225
3332
  }
3226
3333
  /************************************************************
3334
+ * The names of the secrets (is_secret_name()). The same lists
3335
+ * as the C kernel's helpers.c: a name that holds one of these
3336
+ * (any case) is a secret.
3337
+ ************************************************************/
3338
+ var SECRET_NAME_PARTS = [
3339
+ "passw",
3340
+ "pwd",
3341
+ "passphrase",
3342
+ "secret",
3343
+ "token",
3344
+ "jwt",
3345
+ "bearer",
3346
+ "authorization",
3347
+ "cookie",
3348
+ "credential",
3349
+ "salt"
3350
+ ];
3351
+ var SECRET_NAME_JOINED = [
3352
+ "apikey",
3353
+ "sessionid",
3354
+ "sessionkey",
3355
+ "authdata"
3356
+ ];
3357
+ var NOT_SECRET_SEGMENTS = /* @__PURE__ */ new Set([
3358
+ "endpoint",
3359
+ "url",
3360
+ "uri",
3361
+ "domain",
3362
+ "path",
3363
+ "file",
3364
+ "public",
3365
+ "pub",
3366
+ "count",
3367
+ "counts",
3368
+ "type",
3369
+ "name",
3370
+ "len",
3371
+ "length",
3372
+ "size",
3373
+ "max",
3374
+ "min",
3375
+ "ttl",
3376
+ "timeout",
3377
+ "expiry",
3378
+ "expires",
3379
+ "mode"
3380
+ ]);
3381
+ /************************************************************
3382
+ * TRUE if `name` is the name of a secret: a password, a token,
3383
+ * a key... Any case. As the C kernel's is_secret_name().
3384
+ ************************************************************/
3385
+ function is_secret_name(name) {
3386
+ if (typeof name !== "string" || name.length < 3) return false;
3387
+ let lower = name.toLowerCase();
3388
+ let hit = false;
3389
+ for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
3390
+ hit = true;
3391
+ break;
3392
+ }
3393
+ if (!hit) {
3394
+ let joined = lower.replace(/[_\-. \t]/g, "");
3395
+ for (const part of SECRET_NAME_JOINED) if (joined.indexOf(part) >= 0) {
3396
+ hit = true;
3397
+ break;
3398
+ }
3399
+ }
3400
+ if (!hit) hit = lower.indexOf("priv") >= 0 && lower.indexOf("key") >= 0;
3401
+ if (!hit) return false;
3402
+ for (const segment of lower.split(/[_\-. \t]/)) if (NOT_SECRET_SEGMENTS.has(segment)) return false;
3403
+ return true;
3404
+ }
3405
+ /************************************************************
3406
+ * Where an unquoted secret value that begins at `p` ends: at
3407
+ * the end, at the quote that closes an outer quoted value, or
3408
+ * before the next "word=" -- a plain word after a blank is still
3409
+ * part of it ("password=correct horse battery", "password=
3410
+ * hunter2"). As the C kernel's inline_secret_value_end().
3411
+ ************************************************************/
3412
+ function inline_secret_value_end(str, p, outer) {
3413
+ const blank = (c) => c === " " || c === " ";
3414
+ const closes_outer = (i) => outer && str[i] === outer && (i + 1 >= str.length || blank(str[i + 1]));
3415
+ let end = p;
3416
+ while (true) {
3417
+ while (end < str.length && !blank(str[end])) {
3418
+ if (closes_outer(end)) return end;
3419
+ end++;
3420
+ }
3421
+ if (end >= str.length) return end;
3422
+ let w = end;
3423
+ while (w < str.length && blank(str[w])) w++;
3424
+ if (w >= str.length || closes_outer(w)) return end;
3425
+ for (let e = w; e < str.length && !blank(str[e]) && !closes_outer(e); e++) if (str[e] === "=" && e > w) return end;
3426
+ end = w;
3427
+ }
3428
+ }
3429
+ /************************************************************
3430
+ * A text (a command line) with the value of every "name=value"
3431
+ * whose name is a secret's written as "********", quoted or not
3432
+ * (and the "value" of a write-attr whose attribute names a
3433
+ * secret). The rest as it is. Return null when there was nothing
3434
+ * to mask. As the C kernel's mask_secrets_inline().
3435
+ ************************************************************/
3436
+ var MASK_MAX_NAME = 128;
3437
+ var MASK_MAX_BYTES = 4194304;
3438
+ var MASK_TOO_LARGE = "<not shown: too large to mask>";
3439
+ function mask_secrets_inline(str) {
3440
+ if (typeof str !== "string" || str.indexOf("=") < 0) return null;
3441
+ if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3442
+ let value_is_secret = false;
3443
+ let re_attr = /(^|[ \t])attribute=["']?([^ \t"']*)/g;
3444
+ let m;
3445
+ while ((m = re_attr.exec(str)) !== null) if (is_secret_name(m[2].slice(-128))) value_is_secret = true;
3446
+ let parts = [];
3447
+ let copied = 0;
3448
+ let changed = false;
3449
+ let outer = "";
3450
+ let word = 0;
3451
+ let p = 0;
3452
+ const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " ";
3453
+ while (p < str.length) {
3454
+ let c = str[p];
3455
+ if (c === " " || c === " ") {
3456
+ p++;
3457
+ word = p;
3458
+ continue;
3459
+ }
3460
+ if (c === "\"" || c === "'") {
3461
+ if (!outer && (p === 0 || str[p - 1] === " " || str[p - 1] === " " || str[p - 1] === "=")) outer = c;
3462
+ else if (c === outer && is_blank_or_end(p + 1)) outer = "";
3463
+ }
3464
+ if (c !== "=") {
3465
+ p++;
3466
+ continue;
3467
+ }
3468
+ let name = str.substring(Math.max(word, p - MASK_MAX_NAME), p);
3469
+ word = p + 1;
3470
+ if (!(is_secret_name(name) || value_is_secret && name.toLowerCase() === "value")) {
3471
+ p++;
3472
+ continue;
3473
+ }
3474
+ p++;
3475
+ let quote = str[p] === "\"" || str[p] === "'" ? str[p] : "";
3476
+ let end;
3477
+ if (quote) {
3478
+ end = p + 1;
3479
+ while (end < str.length && str[end] !== quote) end++;
3480
+ if (str[end] === quote) end++;
3481
+ } else end = inline_secret_value_end(str, p, outer);
3482
+ if (end > p) {
3483
+ parts.push(str.substring(copied, p));
3484
+ parts.push("********");
3485
+ copied = end;
3486
+ changed = true;
3487
+ }
3488
+ p = end;
3489
+ word = p;
3490
+ }
3491
+ if (!changed) return null;
3492
+ parts.push(str.substring(copied));
3493
+ return parts.join("");
3494
+ }
3495
+ /************************************************************
3496
+ * A json as a log or a trace may show it: at any depth, the
3497
+ * value of a key with a secret's name is "********", whatever
3498
+ * its type (not an absent one, a null or an empty string); so is
3499
+ * the "value" of a dict whose "attribute" names a secret; and a
3500
+ * string is masked as mask_secrets_inline(). Return a masked
3501
+ * copy, or `jn` itself when there was nothing to mask. As the C
3502
+ * kernel's json_mask_secrets().
3503
+ *
3504
+ * Only JSON is walked: plain objects (their prototype is
3505
+ * Object.prototype or null) and arrays. Anything else -- a gobj,
3506
+ * a DOM node, a class instance, a typed array, a function -- is
3507
+ * passed as it is, never walked, as trace_json() always showed it
3508
+ * (a kw may carry a gobj: its parent and children make a cycle).
3509
+ * A plain object met twice is masked once, the same everywhere; a
3510
+ * cycle back to one is "<cycle>"; below MASK_MAX_DEPTH levels
3511
+ * (64, as in C) nothing is shown.
3512
+ * It never throws: a failure is the placeholder below.
3513
+ ************************************************************/
3514
+ var MASK_MAX_DEPTH = 64;
3515
+ var MASK_FAILED = "<not shown: the masking failed>";
3516
+ function is_plain_object(value) {
3517
+ if (value === null || typeof value !== "object") return false;
3518
+ let proto = Object.getPrototypeOf(value);
3519
+ return proto === Object.prototype || proto === null;
3520
+ }
3521
+ var MASK_IN_PROGRESS = Symbol("mask-in-progress");
3522
+ function _json_mask_secrets(jn, memo, depth, budget) {
3523
+ if (typeof jn === "string") {
3524
+ if (jn.length > budget.left) {
3525
+ budget.left = 0;
3526
+ return MASK_TOO_LARGE;
3527
+ }
3528
+ budget.left -= jn.length;
3529
+ let masked = mask_secrets_inline(jn);
3530
+ return masked !== null ? masked : jn;
3531
+ }
3532
+ let is_array = Array.isArray(jn);
3533
+ if (!is_array && !is_plain_object(jn)) return jn;
3534
+ if (budget.left <= 0) return MASK_TOO_LARGE;
3535
+ if (memo.has(jn)) {
3536
+ let seen = memo.get(jn);
3537
+ return seen === MASK_IN_PROGRESS ? "<cycle>" : seen;
3538
+ }
3539
+ if (depth >= MASK_MAX_DEPTH) return "<deeper not shown>";
3540
+ budget.left -= 1;
3541
+ memo.set(jn, MASK_IN_PROGRESS);
3542
+ let changed = false;
3543
+ let truncated = false;
3544
+ let result;
3545
+ if (is_array) {
3546
+ result = [];
3547
+ for (let i = 0; i < jn.length; i++) {
3548
+ if (budget.left <= 0) {
3549
+ truncated = true;
3550
+ break;
3551
+ }
3552
+ budget.left -= 1;
3553
+ let shown = _json_mask_secrets(jn[i], memo, depth + 1, budget);
3554
+ if (shown !== jn[i]) changed = true;
3555
+ result.push(shown);
3556
+ }
3557
+ if (truncated) result.push(MASK_TOO_LARGE);
3558
+ } else {
3559
+ result = Object.create(Object.getPrototypeOf(jn));
3560
+ let value_is_secret = typeof jn.attribute === "string" && is_secret_name(jn.attribute.slice(-128));
3561
+ for (const key in jn) {
3562
+ if (!Object.prototype.hasOwnProperty.call(jn, key)) continue;
3563
+ if (budget.left <= 0) {
3564
+ truncated = true;
3565
+ break;
3566
+ }
3567
+ budget.left -= 1 + key.length;
3568
+ let value = jn[key];
3569
+ let shown;
3570
+ if ((is_secret_name(key.slice(-128)) || value_is_secret && key === "value") && value !== void 0 && value !== null && value !== "") shown = "********";
3571
+ else shown = _json_mask_secrets(value, memo, depth + 1, budget);
3572
+ if (shown !== value) changed = true;
3573
+ result[key] = shown;
3574
+ }
3575
+ if (truncated) result["<more>"] = MASK_TOO_LARGE;
3576
+ }
3577
+ if (!changed && !truncated) result = jn;
3578
+ memo.set(jn, result);
3579
+ return result;
3580
+ }
3581
+ function json_mask_secrets(jn) {
3582
+ try {
3583
+ return _json_mask_secrets(jn, /* @__PURE__ */ new WeakMap(), 0, { left: MASK_MAX_BYTES });
3584
+ } catch (e) {
3585
+ return MASK_FAILED;
3586
+ }
3587
+ }
3588
+ /************************************************************
3589
+ * trace_json() of a json that can hold a credential: what
3590
+ * json_mask_secrets() masks is shown masked
3591
+ ************************************************************/
3592
+ function trace_json_masked(jn, msg) {
3593
+ trace_json(json_mask_secrets(jn), msg);
3594
+ }
3595
+ /************************************************************
3227
3596
  * Duplicate an object (new references)
3228
3597
  * using the modern structuredClone (Deep copy of a single object)
3229
3598
  ************************************************************/
@@ -3525,8 +3894,8 @@
3525
3894
  ***************************************************************************/
3526
3895
  function is_metadata_key(key) {
3527
3896
  if (!is_string(key)) return false;
3528
- let i;
3529
- for (i = 0; i < key.length; i++) {
3897
+ let i = 0;
3898
+ for (; i < key.length; i++) {
3530
3899
  if (key[i] !== "_") break;
3531
3900
  if (i > 2) break;
3532
3901
  }
@@ -3537,8 +3906,8 @@
3537
3906
  ***************************************************************************/
3538
3907
  function is_private_key(key) {
3539
3908
  if (!is_string(key)) return false;
3540
- let i;
3541
- for (i = 0; i < key.length; i++) {
3909
+ let i = 0;
3910
+ for (; i < key.length; i++) {
3542
3911
  if (key[i] !== "_") break;
3543
3912
  if (i > 2) break;
3544
3913
  }
@@ -3649,7 +4018,7 @@
3649
4018
  return v;
3650
4019
  } else if (required) {
3651
4020
  log_error(`path not found: '${path}'`);
3652
- trace_json(kw);
4021
+ trace_json_masked(kw);
3653
4022
  }
3654
4023
  return Boolean(default_value);
3655
4024
  }
@@ -3663,11 +4032,11 @@
3663
4032
  } else if (wild && b === null) value = false;
3664
4033
  else if (wild) {
3665
4034
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3666
- trace_json(kw);
4035
+ trace_json_masked(kw);
3667
4036
  return false;
3668
4037
  } else {
3669
4038
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json boolean: '${path}'`);
3670
- trace_json(kw);
4039
+ trace_json_masked(kw);
3671
4040
  return Boolean(default_value);
3672
4041
  }
3673
4042
  if (extract) kw_delete(gobj, kw, path);
@@ -3717,7 +4086,7 @@
3717
4086
  return v;
3718
4087
  } else if (required) {
3719
4088
  log_error(`path not found: '${path}'`);
3720
- trace_json(kw);
4089
+ trace_json_masked(kw);
3721
4090
  }
3722
4091
  return parseInt(default_value);
3723
4092
  }
@@ -3725,14 +4094,14 @@
3725
4094
  if (is_number(v)) value = Math.trunc(v);
3726
4095
  else if (!wild) {
3727
4096
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json integer: '${path}'`);
3728
- trace_json(kw);
4097
+ trace_json_masked(kw);
3729
4098
  return parseInt(default_value);
3730
4099
  } else if (is_boolean(v)) value = v ? 1 : 0;
3731
4100
  else if (is_string(v)) value = strtoll_base0(v);
3732
4101
  else if (v === null) value = 0;
3733
4102
  else {
3734
4103
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3735
- trace_json(kw);
4104
+ trace_json_masked(kw);
3736
4105
  return 0;
3737
4106
  }
3738
4107
  if (extract) kw_delete(gobj, kw, path);
@@ -3764,7 +4133,7 @@
3764
4133
  return v;
3765
4134
  } else if (required) {
3766
4135
  log_error(`path not found: '${path}'`);
3767
- trace_json(kw);
4136
+ trace_json_masked(kw);
3768
4137
  }
3769
4138
  return Number(default_value);
3770
4139
  }
@@ -3772,7 +4141,7 @@
3772
4141
  if (is_number(v)) value = v;
3773
4142
  else if (!wild) {
3774
4143
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json real: '${path}'`);
3775
- trace_json(kw);
4144
+ trace_json_masked(kw);
3776
4145
  return Number(default_value);
3777
4146
  } else if (is_boolean(v)) value = v ? 1 : 0;
3778
4147
  else if (is_string(v)) {
@@ -3781,7 +4150,7 @@
3781
4150
  } else if (v === null) value = 0;
3782
4151
  else {
3783
4152
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3784
- trace_json(kw);
4153
+ trace_json_masked(kw);
3785
4154
  return 0;
3786
4155
  }
3787
4156
  if (extract) kw_delete(gobj, kw, path);
@@ -3833,14 +4202,14 @@
3833
4202
  return default_value;
3834
4203
  } else if (required && !kw_path_holds_key(kw, path)) {
3835
4204
  log_error(`path not found: '${path}'`);
3836
- trace_json(kw);
4205
+ trace_json_masked(kw);
3837
4206
  }
3838
4207
  return default_value;
3839
4208
  }
3840
4209
  if (!is_string(v)) {
3841
4210
  if (v !== null) {
3842
4211
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json str: '${path}'`);
3843
- trace_json(kw);
4212
+ trace_json_masked(kw);
3844
4213
  }
3845
4214
  return default_value;
3846
4215
  }
@@ -3863,7 +4232,7 @@
3863
4232
  return v;
3864
4233
  } else if (required) {
3865
4234
  log_error(`path not found: '${path}'`);
3866
- trace_json(kw);
4235
+ trace_json_masked(kw);
3867
4236
  }
3868
4237
  return default_value;
3869
4238
  }
@@ -3890,14 +4259,14 @@
3890
4259
  }
3891
4260
  if (required) {
3892
4261
  log_error(`path not found: '${path}'`);
3893
- trace_json(kw);
4262
+ trace_json_masked(kw);
3894
4263
  }
3895
4264
  return default_value;
3896
4265
  }
3897
4266
  if (!is_object(v)) {
3898
4267
  if (required) {
3899
4268
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json dict: '${path}'`);
3900
- trace_json(kw);
4269
+ trace_json_masked(kw);
3901
4270
  }
3902
4271
  return default_value;
3903
4272
  }
@@ -3920,7 +4289,7 @@
3920
4289
  return v;
3921
4290
  } else if (required) {
3922
4291
  log_error(`path not found: '${path}'`);
3923
- trace_json(kw);
4292
+ trace_json_masked(kw);
3924
4293
  }
3925
4294
  return default_value;
3926
4295
  }
@@ -3948,14 +4317,14 @@
3948
4317
  }
3949
4318
  if (required) {
3950
4319
  log_error(`path not found: '${path}'`);
3951
- trace_json(kw);
4320
+ trace_json_masked(kw);
3952
4321
  }
3953
4322
  return default_value;
3954
4323
  }
3955
4324
  if (!is_array(v)) {
3956
4325
  if (required) {
3957
4326
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json list: '${path}'`);
3958
- trace_json(kw);
4327
+ trace_json_masked(kw);
3959
4328
  }
3960
4329
  return default_value;
3961
4330
  }
@@ -6710,7 +7079,8 @@
6710
7079
  window.console.error(`Not in session`);
6711
7080
  return -1;
6712
7081
  }
6713
- if (!kw) kw = {};
7082
+ kw = Object.assign({}, is_object(kw) ? kw : {});
7083
+ if (is_object(kw["__md_iev__"])) kw["__md_iev__"] = json_deep_copy(kw["__md_iev__"]);
6714
7084
  if (msg_iev_get_stack(gobj, kw, IEVENT_STACK_ID)) {} else {
6715
7085
  let jn_ievent_id = build_cli_ievent_request(gobj, gobj_name(src), kw_get_str(gobj, kw, "__service__", 0, 0));
6716
7086
  json_object_del(kw, "__service__");
@@ -6816,7 +7186,7 @@
6816
7186
  let hora = current_timestamp();
6817
7187
  try {
6818
7188
  log_debug("\n" + hora + " " + prefix + "\n");
6819
- trace_json(iev);
7189
+ trace_json_masked(iev);
6820
7190
  } catch (e) {
6821
7191
  log_debug("ERROR in trace_inter_event: " + e);
6822
7192
  }
@@ -7498,6 +7868,7 @@
7498
7868
  exports.is_object = is_object;
7499
7869
  exports.is_private_key = is_private_key;
7500
7870
  exports.is_pure_number = is_pure_number;
7871
+ exports.is_secret_name = is_secret_name;
7501
7872
  exports.is_string = is_string;
7502
7873
  exports.jdb_delete = jdb_delete;
7503
7874
  exports.jdb_get = jdb_get;
@@ -7513,6 +7884,7 @@
7513
7884
  exports.json_array_size = json_array_size;
7514
7885
  exports.json_deep_copy = json_deep_copy;
7515
7886
  exports.json_is_identical = json_is_identical;
7887
+ exports.json_mask_secrets = json_mask_secrets;
7516
7888
  exports.json_object_del = json_object_del;
7517
7889
  exports.json_object_get = json_object_get;
7518
7890
  exports.json_object_set = json_object_set;
@@ -7560,6 +7932,7 @@
7560
7932
  exports.log_error = log_error;
7561
7933
  exports.log_info = log_info;
7562
7934
  exports.log_warning = log_warning;
7935
+ exports.mask_secrets_inline = mask_secrets_inline;
7563
7936
  exports.msg_iev_get_msg_type = msg_iev_get_msg_type;
7564
7937
  exports.msg_iev_get_stack = msg_iev_get_stack;
7565
7938
  exports.msg_iev_push_stack = msg_iev_push_stack;
@@ -7593,6 +7966,7 @@
7593
7966
  exports.template_get_field_desc = template_get_field_desc;
7594
7967
  exports.timeTracker = timeTracker;
7595
7968
  exports.trace_json = trace_json;
7969
+ exports.trace_json_masked = trace_json_masked;
7596
7970
  exports.trace_level_t = trace_level_t;
7597
7971
  exports.trace_msg = trace_msg;
7598
7972
  exports.treedb_decoder_fkey = treedb_decoder_fkey;