@yuneta/gobj-js 7.16.6 → 7.22.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.
package/README.md CHANGED
@@ -662,9 +662,27 @@ The trace arrives as `debug`, so **`set_log_callback()` is how you read it** —
662
662
  that is how a dev panel shows the machine inside the app, and how
663
663
  `doc.yuneta.io/navigation` feeds the panel under each of its demos.
664
664
 
665
- > The yuno attrs `tracing` / `trace_timer` / `trace_creation` /
666
- > `trace_start_stop` predate this and still work (gobj-ui's dev panel writes
667
- > them); they fold in as one more source of bits.
665
+ **The yuno persists the levels a user sets**, as a C yuno does: `C_YUNO` has the
666
+ trace commands of the C kernel (`set-global-trace`, `set-global-no-trace`,
667
+ `set-gclass-trace`, `set-gclass-no-trace` and their `get-` forms) and saves
668
+ each scope whole in `trace_levels` / `no_trace_levels`. At start up a saved
669
+ scope **replaces** what `main.js` set before creating the yuno:
670
+
671
+ ```javascript
672
+ gobj_set_global_no_trace("timer_periodic", true); // main.js default
673
+ let yuno = gobj_create_yuno("app_yuno", "C_YUNO", {...}); // restores the saved scopes
674
+ gobj_command(yuno, "set-global-no-trace", {level: "timer_periodic", set: 0}, yuno);
675
+ // no_trace_levels.__global_no_trace__ = [] -- stays off after a reload
676
+ ```
677
+
678
+ The traffic of the websocket is `C_IEVENT_CLI`'s own level `ievents`; its lines
679
+ go to the yuno attr `trace_ievent_callback`, or to the console.
680
+
681
+ > The old yuno attrs `tracing`, `trace_timer`, `trace_inter_event`,
682
+ > `trace_creation`, `trace_start_stop`, `trace_subscriptions`, `trace_i18n` and
683
+ > `no_poll` are gone (7.22.0). A `gobj_create_yuno()` that still passes them
684
+ > logs *"GClass Attribute NOT FOUND"* for each: remove them, and turn traces on
685
+ > with the commands above.
668
686
 
669
687
  ### i18n: `refresh_language` and the `data-i18n-*` family
670
688
 
@@ -194,7 +194,7 @@ function build_stats_response(gobj, result, comment, schema, data) {
194
194
  *
195
195
  * Licence: MIT (http://www.opensource.org/licenses/mit-license)
196
196
  * Copyright (c) 2014,2024 Niyamaka.
197
- * Copyright (c) 2025, ArtGins.
197
+ * Copyright (c) 2025-2026, ArtGins.
198
198
  *********************************************************************************/
199
199
  /**************************************************************************
200
200
  * Constants
@@ -429,6 +429,7 @@ var trace_level_t = Object.freeze({
429
429
  TRACE_URING_TIME: 536870912,
430
430
  TRACE_COMMANDS: 1073741824
431
431
  });
432
+ var TRACE_USER_LEVEL = 65535;
432
433
  var TRACE_GLOBAL_LEVEL1 = 268369920;
433
434
  var s_global_trace_level = [
434
435
  [
@@ -510,17 +511,28 @@ var s_global_trace_level = [
510
511
  var __global_trace_level__ = 0;
511
512
  var __global_trace_no_level__ = 0;
512
513
  var __deep_trace__ = 0;
513
- function level2bit(level) {
514
+ function level2bit(level, s_user_trace_level) {
514
515
  for (const [name, bit] of s_global_trace_level) if (name === level) return bit;
516
+ if (Array.isArray(s_user_trace_level)) {
517
+ for (let i = 0; i < s_user_trace_level.length && i < 16; i++) if (s_user_trace_level[i] && s_user_trace_level[i][0] === level) return 1 << i;
518
+ }
515
519
  return 0;
516
520
  }
517
- function trace_bitmask(level, who) {
518
- if (empty_string(level)) return TRACE_GLOBAL_LEVEL1;
521
+ function bit2level(bitmask, s_user_trace_level) {
522
+ let levels = [];
523
+ for (const [name, bit] of s_global_trace_level) if (bitmask & bit) levels.push(name);
524
+ if (Array.isArray(s_user_trace_level)) {
525
+ for (let i = 0; i < s_user_trace_level.length && i < 16; i++) if (s_user_trace_level[i] && bitmask & 1 << i) levels.push(s_user_trace_level[i][0]);
526
+ }
527
+ return levels;
528
+ }
529
+ function trace_bitmask(level, who, s_user_trace_level) {
530
+ if (empty_string(level)) return s_user_trace_level === void 0 ? TRACE_GLOBAL_LEVEL1 : TRACE_USER_LEVEL;
519
531
  if (/^[0-9]+$/.test(String(level))) {
520
532
  const n = Number(level);
521
533
  if (n) return n;
522
534
  }
523
- const bit = level2bit(String(level));
535
+ const bit = level2bit(String(level), s_user_trace_level);
524
536
  if (!bit) {
525
537
  log_error(`${who}: trace level NOT FOUND: ${level}`);
526
538
  return 0;
@@ -549,32 +561,30 @@ function gobj_trace_no_level(gobj) {
549
561
  }
550
562
  return bitmask;
551
563
  }
552
- function legacy_yuno_trace_bits() {
553
- if (!__yuno__ || !__yuno__.jn_attrs) return 0;
554
- let bits = 0;
555
- if (gobj_has_attr(__yuno__, "tracing")) {
556
- const tracing = Number(gobj_read_attr(__yuno__, "tracing")) || 0;
557
- if (tracing >= 1) bits |= trace_level_t.TRACE_MACHINE;
558
- if (tracing >= 2) bits |= trace_level_t.TRACE_EV_KW;
559
- }
560
- if (gobj_has_attr(__yuno__, "trace_timer") && gobj_read_attr(__yuno__, "trace_timer")) bits |= trace_level_t.TRACE_TIMER;
561
- if (gobj_has_attr(__yuno__, "trace_creation") && gobj_read_attr(__yuno__, "trace_creation")) bits |= trace_level_t.TRACE_CREATE_DELETE;
562
- if (gobj_has_attr(__yuno__, "trace_start_stop") && gobj_read_attr(__yuno__, "trace_start_stop")) bits |= trace_level_t.TRACE_START_STOP;
563
- return bits;
564
- }
565
564
  function __trace_gobj_create_delete__(gobj) {
566
- return (gobj_trace_level(gobj) | legacy_yuno_trace_bits()) & trace_level_t.TRACE_CREATE_DELETE;
565
+ return gobj_trace_level(gobj) & trace_level_t.TRACE_CREATE_DELETE;
566
+ }
567
+ function __trace_gobj_subscriptions__(gobj) {
568
+ return gobj_trace_level(gobj) & trace_level_t.TRACE_SUBSCRIPTIONS;
567
569
  }
568
570
  function __trace_gobj_start_stop__(gobj) {
569
- return (gobj_trace_level(gobj) | legacy_yuno_trace_bits()) & trace_level_t.TRACE_START_STOP;
571
+ return gobj_trace_level(gobj) & trace_level_t.TRACE_START_STOP;
570
572
  }
571
573
  function __trace_gobj_ev_kw__(gobj) {
572
- return (gobj_trace_level(gobj) | legacy_yuno_trace_bits()) & trace_level_t.TRACE_EV_KW;
574
+ return gobj_trace_level(gobj) & trace_level_t.TRACE_EV_KW;
573
575
  }
574
576
  function __trace_gobj_states__(gobj) {
575
577
  return gobj_trace_level(gobj) & trace_level_t.TRACE_STATES;
576
578
  }
577
579
  /************************************************************
580
+ * Must trace a command? — the C kernel's is_commands_tracing()
581
+ ************************************************************/
582
+ function is_commands_tracing(gobj) {
583
+ if (__deep_trace__ > 1) return true;
584
+ if (!gobj) return false;
585
+ return gobj_trace_level(gobj) & trace_level_t.TRACE_COMMANDS ? true : false;
586
+ }
587
+ /************************************************************
578
588
  * Must trace? — the C kernel's is_machine_tracing(), including
579
589
  * the two event-specific levels: `timer` and `timer_periodic`
580
590
  * light up only for their own event.
@@ -582,7 +592,7 @@ function __trace_gobj_states__(gobj) {
582
592
  function is_machine_tracing(gobj, event) {
583
593
  if (__deep_trace__ > 1) return true;
584
594
  if (!gobj) return false;
585
- const level = gobj_trace_level(gobj) | legacy_yuno_trace_bits();
595
+ const level = gobj_trace_level(gobj);
586
596
  return level & trace_level_t.TRACE_MACHINE || level & trace_level_t.TRACE_TIMER_PERIODIC && event === "EV_TIMEOUT_PERIODIC" || level & trace_level_t.TRACE_TIMER && event === "EV_TIMEOUT" ? true : false;
587
597
  }
588
598
  /************************************************************
@@ -615,6 +625,49 @@ function gobj_set_global_no_trace(level, set) {
615
625
  function gobj_global_trace_level() {
616
626
  return __global_trace_level__;
617
627
  }
628
+ function gobj_global_trace_no_level() {
629
+ return __global_trace_no_level__;
630
+ }
631
+ function gobj_set_global_trace2(bitmask, set) {
632
+ if (set) __global_trace_level__ = (__global_trace_level__ | bitmask) >>> 0;
633
+ else __global_trace_level__ = (__global_trace_level__ & ~bitmask) >>> 0;
634
+ return 0;
635
+ }
636
+ function gobj_set_global_no_trace2(bitmask, set) {
637
+ if (set) __global_trace_no_level__ = (__global_trace_no_level__ | bitmask) >>> 0;
638
+ else __global_trace_no_level__ = (__global_trace_no_level__ & ~bitmask) >>> 0;
639
+ return 0;
640
+ }
641
+ function gobj_get_global_trace_level() {
642
+ return bit2level(__global_trace_level__, null);
643
+ }
644
+ function gobj_get_global_trace_no_level() {
645
+ return bit2level(__global_trace_no_level__, null);
646
+ }
647
+ function gobj_get_gclass_trace_level(gclass) {
648
+ const gclass_ = is_string(gclass) ? gclass_find_by_name(gclass) : gclass;
649
+ if (!gclass_) {
650
+ log_error(`gobj_get_gclass_trace_level: gclass NOT FOUND: ${gclass}`);
651
+ return [];
652
+ }
653
+ return bit2level(gclass_.trace_level | __global_trace_level__, gclass_.s_user_trace_level);
654
+ }
655
+ function gobj_get_gclass_trace_level2(gclass) {
656
+ const gclass_ = is_string(gclass) ? gclass_find_by_name(gclass) : gclass;
657
+ if (!gclass_) {
658
+ log_error(`gobj_get_gclass_trace_level2: gclass NOT FOUND: ${gclass}`);
659
+ return [];
660
+ }
661
+ return bit2level(gclass_.trace_level, gclass_.s_user_trace_level);
662
+ }
663
+ function gobj_get_gclass_trace_no_level(gclass) {
664
+ const gclass_ = is_string(gclass) ? gclass_find_by_name(gclass) : gclass;
665
+ if (!gclass_) {
666
+ log_error(`gobj_get_gclass_trace_no_level: gclass NOT FOUND: ${gclass}`);
667
+ return [];
668
+ }
669
+ return bit2level(gclass_.no_trace_level, gclass_.s_user_trace_level);
670
+ }
618
671
  function gobj_set_deep_trace(value) {
619
672
  __deep_trace__ = Number(value) || 0;
620
673
  return 0;
@@ -625,7 +678,7 @@ function gobj_set_gclass_trace(gclass, level, set) {
625
678
  log_error(`gobj_set_gclass_trace: gclass NOT FOUND: ${gclass}`);
626
679
  return -1;
627
680
  }
628
- const bitmask = trace_bitmask(level, "gobj_set_gclass_trace");
681
+ const bitmask = level === null ? 4294967295 : trace_bitmask(level, "gobj_set_gclass_trace", gclass_.s_user_trace_level || null);
629
682
  if (!bitmask) return -1;
630
683
  if (set) gclass_.trace_level |= bitmask;
631
684
  else gclass_.trace_level &= ~bitmask;
@@ -636,7 +689,7 @@ function gobj_set_gobj_trace(gobj, level, set) {
636
689
  log_error(`gobj_set_gobj_trace: gobj NULL`);
637
690
  return -1;
638
691
  }
639
- const bitmask = trace_bitmask(level, "gobj_set_gobj_trace");
692
+ const bitmask = trace_bitmask(level, "gobj_set_gobj_trace", gobj.gclass && gobj.gclass.s_user_trace_level || null);
640
693
  if (!bitmask) return -1;
641
694
  if (set) gobj.trace_level |= bitmask;
642
695
  else gobj.trace_level &= ~bitmask;
@@ -648,7 +701,7 @@ function gobj_set_gclass_no_trace(gclass, level, set) {
648
701
  log_error(`gobj_set_gclass_no_trace: gclass NOT FOUND: ${gclass}`);
649
702
  return -1;
650
703
  }
651
- const bitmask = trace_bitmask(level, "gobj_set_gclass_no_trace");
704
+ const bitmask = level === null ? 4294967295 : trace_bitmask(level, "gobj_set_gclass_no_trace", gclass_.s_user_trace_level || null);
652
705
  if (!bitmask) return -1;
653
706
  if (set) gclass_.no_trace_level |= bitmask;
654
707
  else gclass_.no_trace_level &= ~bitmask;
@@ -659,7 +712,7 @@ function gobj_set_gobj_no_trace(gobj, level, set) {
659
712
  log_error(`gobj_set_gobj_no_trace: gobj NULL`);
660
713
  return -1;
661
714
  }
662
- const bitmask = trace_bitmask(level, "gobj_set_gobj_no_trace");
715
+ const bitmask = trace_bitmask(level, "gobj_set_gobj_no_trace", gobj.gclass && gobj.gclass.s_user_trace_level || null);
663
716
  if (!bitmask) return -1;
664
717
  if (set) gobj.no_trace_level |= bitmask;
665
718
  else gobj.no_trace_level &= ~bitmask;
@@ -2663,7 +2716,7 @@ function _delete_subscription(gobj, subs, force, not_inform) {
2663
2716
  if (kw_get_int(gobj, subs, "subs_flag", null, kw_flag_t.KW_REQUIRED) & subs_flag_t.__hard_subscription__ ? 1 : 0) {
2664
2717
  if (!force) return -1;
2665
2718
  }
2666
- if (__yuno__ && gobj_read_bool_attr(__yuno__, "trace_subscriptions")) {
2719
+ if (__trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher)) {
2667
2720
  trace_machine(sprintf("💜💜👎 unsubscribing event '%s': publisher %s, subscriber'%s'", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2668
2721
  trace_json(subs);
2669
2722
  }
@@ -2734,7 +2787,7 @@ function gobj_subscribe_event(publisher, event, kw, subscriber) {
2734
2787
  }
2735
2788
  json_array_append(publisher.dl_subscriptions, subs);
2736
2789
  json_array_append(subscriber.dl_subscribings, subs);
2737
- if (__yuno__ && gobj_read_bool_attr(__yuno__, "trace_subscriptions")) {
2790
+ if (__trace_gobj_subscriptions__(subscriber) || __trace_gobj_subscriptions__(publisher)) {
2738
2791
  trace_machine(sprintf("💜💜👍 subscribing event '%s', publisher '%s', subscriber %s", event ? event : "", gobj_short_name(publisher), gobj_short_name(subscriber)));
2739
2792
  if (kw) trace_json(kw);
2740
2793
  }
@@ -2872,7 +2925,7 @@ function gobj_publish_event(publisher, event, kw) {
2872
2925
  return -1;
2873
2926
  }
2874
2927
  }
2875
- let tracea = __yuno__ && gobj_read_bool_attr(__yuno__, "trace_subscriptions");
2928
+ let tracea = (__trace_gobj_subscriptions__(publisher) || is_machine_tracing(publisher, event)) && !is_machine_not_tracing(publisher, event);
2876
2929
  if (tracea) {
2877
2930
  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));
2878
2931
  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 : "", ""));
@@ -2950,10 +3003,9 @@ function gobj_command(gobj, command, kw, src) {
2950
3003
  log_error("gobj NULL or DESTROYED");
2951
3004
  return -1;
2952
3005
  }
2953
- let tracea = __yuno__ && gobj_read_integer_attr(__yuno__, "tracing");
2954
- if (tracea) {
3006
+ if (is_commands_tracing(gobj) || is_machine_tracing(gobj, null) && !is_machine_not_tracing(src, null)) {
2955
3007
  trace_machine(sprintf("🌀🌀 mach(%s%s), cmd: %s, src: %s", !gobj_is_running(gobj) ? "!!" : "", gobj_short_name(gobj), command, gobj_short_name(src)));
2956
- if (tracea > 1) trace_json(kw);
3008
+ if (__trace_gobj_ev_kw__(gobj)) trace_json(kw);
2957
3009
  }
2958
3010
  if (gobj.gclass.gmt.mt_command_parser) return gobj.gclass.gmt.mt_command_parser(gobj, command, kw, src);
2959
3011
  if (gobj.gclass.command_table) {
@@ -4021,10 +4073,18 @@ Being `kw` a:
4021
4073
  - list of strings [s,...]
4022
4074
  - list of dicts [{},...]
4023
4075
  - dict of dicts {id:{},...}
4024
- return a **NEW** list of incref (clone) kw filtering the rows by `jn_filter` (where),
4025
- and matching the ids.
4026
- If match_fn is 0 then kw_match_simple is used.
4027
- NOTE Using JSON_INCREF/JSON_DECREF HACK
4076
+ return a NEW list holding the rows that match `ids` and the
4077
+ `jn_filter` (where). If match_fn is 0 then kw_match_simple is used.
4078
+
4079
+ WARNING the LIST is new; its elements are THE SAME objects, not
4080
+ copies. The C twin increfs each one and says "clone", which this
4081
+ comment repeated for years: javascript has no refcount, so a write
4082
+ through a collected row writes into `kw`. Copy what you mean to
4083
+ modify.
4084
+
4085
+ Returns null -- not an empty list -- for a `kw` that is neither a
4086
+ list nor a dict, so a caller that reads `.length` off the answer
4087
+ must check it first (kwid_find_one_record does).
4028
4088
  *************************************************************/
4029
4089
  function kwid_collect(gobj, kw, ids, jn_filter, match_fn) {
4030
4090
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
@@ -4053,12 +4113,17 @@ function kwid_collect(gobj, kw, ids, jn_filter, match_fn) {
4053
4113
  }
4054
4114
  /*************************************************************
4055
4115
  Utility for databases.
4056
- Return a new dict from a "dict of records" or "list of records"
4057
- WARNING the "id" of a dict's record is hardcorded to their key.
4116
+ Return a dict from a "dict of records" or "list of records".
4117
+ WARNING the "id" of a dict's record is hardcoded to its key.
4058
4118
  Convention:
4059
- - all arrays are list of records (dicts) with "id" field as primary key
4119
+ - all arrays are lists of records (dicts) with "id" as primary key
4060
4120
  - delimiter is '`' and '.'
4061
4121
  If path is empty then use kw
4122
+
4123
+ WARNING "new" is the C twin's word, where it means a new REFERENCE:
4124
+ handed a dict, both return THAT dict and not a copy of it. Only the
4125
+ list case builds something new, and even then the records inside it
4126
+ are the same objects.
4062
4127
  *************************************************************/
4063
4128
  function kwid_new_dict(gobj, kw, path) {
4064
4129
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
@@ -4067,20 +4132,59 @@ function kwid_new_dict(gobj, kw, path) {
4067
4132
  if (is_object(kw)) new_dict = kw;
4068
4133
  else if (is_array(kw)) for (let i = 0; i < kw.length; i++) {
4069
4134
  let kv = kw[i];
4070
- let id = kw_get_str(gobj, kv, "id", "", 0);
4135
+ let id = kw_get_str(gobj, kv, "id", "", kw_flag_t.KW_REQUIRED);
4071
4136
  if (!empty_string(id)) new_dict[id] = kv;
4072
4137
  }
4073
4138
  else log_error(`${gobj_short_name(gobj)} kwid_new_dict: data type unknown`);
4074
4139
  return new_dict;
4075
4140
  }
4076
4141
  /*************************************************************
4142
+ Utility for databases.
4143
+ Return a list from a "dict of records" or "list of records".
4144
+ The NORMALIZING function of this family: whatever shape the data
4145
+ arrives in, what comes back is the shape a table indexed and
4146
+ sorted by `id` wants.
4147
+ Convention:
4148
+ - all arrays are lists of records (dicts) with "id" as primary key
4149
+ - delimiter is '`' and '.'
4150
+ If path is empty then use kw
4151
+
4152
+ WARNING handed a DICT it writes each record's key into the record
4153
+ as its `id`, OVERWRITING one that differs -- in the source records,
4154
+ which it does not copy. That is the C twin's behaviour
4155
+ (`json_object_set_new(v, "id", ...)` in kwid.c) and it is the point:
4156
+ a dict keyed by id whose records disagree with their own key is
4157
+ exactly what this is for. Handed a LIST it returns that same list,
4158
+ untouched, as C returns a new reference to it.
4159
+ *************************************************************/
4160
+ function kwid_new_list(gobj, kw, path) {
4161
+ if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
4162
+ if (!empty_string(path)) kw = kw_find_path(gobj, kw, path);
4163
+ if (is_array(kw)) return kw;
4164
+ if (is_object(kw)) {
4165
+ let new_list = [];
4166
+ for (let id of Object.keys(kw)) {
4167
+ let record = kw[id];
4168
+ if (!is_object(record)) {
4169
+ log_error(`${gobj_short_name(gobj)} kwid_new_list: not a record: '${id}'`);
4170
+ continue;
4171
+ }
4172
+ record["id"] = id;
4173
+ new_list.push(record);
4174
+ }
4175
+ return new_list;
4176
+ }
4177
+ log_error(`${gobj_short_name(gobj)} kwid_new_list: wrong type for list`);
4178
+ return [];
4179
+ }
4180
+ /*************************************************************
4077
4181
  * Utility for databases. See kwid_collect parameters
4078
4182
  *************************************************************/
4079
4183
  function kwid_find_one_record(gobj, kw, ids, jn_filter, match_fn) {
4080
4184
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
4081
4185
  let list = kwid_collect(gobj, kw, ids, jn_filter, match_fn);
4082
- if (list.length > 0) return list[0];
4083
- else return null;
4186
+ if (!list || list.length === 0) return null;
4187
+ return list[0];
4084
4188
  }
4085
4189
  /*************************************************************
4086
4190
  Utility for databases.
@@ -5985,7 +6089,7 @@ function clear_timeout(gobj) {
5985
6089
  *
5986
6090
  * The default yuno in js
5987
6091
  *
5988
- * Copyright (c) 2025, ArtGins.
6092
+ * Copyright (c) 2025-2026, ArtGins.
5989
6093
  * All Rights Reserved. ****************************************************************************/
5990
6094
  /***************************************************************
5991
6095
  * Constants
@@ -6008,15 +6112,9 @@ var attrs_table$1 = [
6008
6112
  SDATA(data_type_t.DTP_STRING, "yuno_version", 0, "", "Yuno version (APP_VERSION)"),
6009
6113
  SDATA(data_type_t.DTP_STRING, "yuneta_version", 0, YUNETA_VERSION, "Yuneta version"),
6010
6114
  SDATA(data_type_t.DTP_LIST, "required_services", 0, "[]", "Required services"),
6011
- SDATA(data_type_t.DTP_INTEGER, "tracing", 0, 0, "Tracing level"),
6012
- SDATA(data_type_t.DTP_BOOLEAN, "trace_timer", 0, 0, "Trace timers"),
6013
- SDATA(data_type_t.DTP_BOOLEAN, "trace_inter_event", 0, 0, "Trace traffic"),
6014
- SDATA(data_type_t.DTP_POINTER, "trace_ievent_callback", 0, null, "Trace traffic callback"),
6015
- SDATA(data_type_t.DTP_BOOLEAN, "trace_creation", 0, 0, "Trace creation"),
6016
- SDATA(data_type_t.DTP_BOOLEAN, "trace_start_stop", 0, 0, "Trace start/stop"),
6017
- SDATA(data_type_t.DTP_BOOLEAN, "trace_subscriptions", 0, 0, "Trace subscription"),
6018
- SDATA(data_type_t.DTP_BOOLEAN, "trace_i18n", 0, 0, "Trace i18n"),
6019
- SDATA(data_type_t.DTP_BOOLEAN, "no_poll", 0, 0, "no poll"),
6115
+ SDATA(data_type_t.DTP_DICT, "trace_levels", sdata_flag_t.SDF_PERSIST, "{}", "Trace levels"),
6116
+ SDATA(data_type_t.DTP_DICT, "no_trace_levels", sdata_flag_t.SDF_PERSIST, "{}", "No trace levels"),
6117
+ SDATA(data_type_t.DTP_POINTER, "trace_ievent_callback", 0, null, "Where the traffic trace goes (C_IEVENT_CLI levels ievents/ievents2); console when null"),
6020
6118
  SDATA(data_type_t.DTP_BOOLEAN, "developer", 0, false, "Developer mode enabled"),
6021
6119
  SDATA(data_type_t.DTP_INTEGER, "periodic_timeout", 0, "1000", "Timeout periodic, in miliseconds."),
6022
6120
  SDATA_END()
@@ -6035,6 +6133,8 @@ function mt_create$1(gobj) {
6035
6133
  gobj_write_str_attr(gobj, "node_uuid", node_uuid());
6036
6134
  gobj.priv.gobj_timer = gobj_create_pure_child(gobj_name(gobj), "C_TIMER", {}, gobj);
6037
6135
  priv.periodic_timeout = gobj_read_attr(gobj, "periodic_timeout");
6136
+ set_user_gclass_traces(gobj);
6137
+ set_user_gclass_no_traces(gobj);
6038
6138
  }
6039
6139
  /***************************************************************
6040
6140
  * Framework Method: Writing
@@ -6079,8 +6179,135 @@ function mt_destroy$1(gobj) {}
6079
6179
  * Local Methods
6080
6180
  ***************************/
6081
6181
  /***************************************************************
6182
+ * Restore the trace levels the user persisted.
6082
6183
  *
6184
+ * A saved scope REPLACES what is in force: main() sets its defaults
6185
+ * (gobj_set_global_no_trace("timer_periodic"), a gclass it wants quiet)
6186
+ * before the yuno is created, and a default the user turned off must
6187
+ * stay off. A scope is saved whole, empty included (save_global_trace());
6188
+ * a scope never saved keeps main()'s. The C kernel's C_YUNO does the same.
6189
+ ***************************************************************/
6190
+ function set_user_gclass_traces(gobj) {
6191
+ let jn_trace_levels = gobj_read_attr(gobj, "trace_levels");
6192
+ if (!is_object(jn_trace_levels)) return 0;
6193
+ let jn_global = jn_trace_levels["__global_trace__"];
6194
+ if (is_array(jn_global)) {
6195
+ gobj_set_global_trace2(4294967295, false);
6196
+ for (let level of jn_global) gobj_set_global_trace(level, true);
6197
+ }
6198
+ for (const [name, jn_levels] of Object.entries(jn_trace_levels)) {
6199
+ let gclass = gclass_find_by_name(name);
6200
+ if (!gclass) continue;
6201
+ if (!is_array(jn_levels)) {
6202
+ log_error(`${gobj_name(gobj)}: trace_levels of ${name} MUST be a list`);
6203
+ continue;
6204
+ }
6205
+ gobj_set_gclass_trace(gclass, null, false);
6206
+ for (let level of jn_levels) gobj_set_gclass_trace(gclass, level, true);
6207
+ }
6208
+ return 0;
6209
+ }
6210
+ /***************************************************************
6211
+ * Restore the no-trace levels, see set_user_gclass_traces()
6212
+ ***************************************************************/
6213
+ function set_user_gclass_no_traces(gobj) {
6214
+ let jn_no_trace_levels = gobj_read_attr(gobj, "no_trace_levels");
6215
+ if (!is_object(jn_no_trace_levels)) return 0;
6216
+ let jn_global = jn_no_trace_levels["__global_no_trace__"];
6217
+ if (is_array(jn_global)) {
6218
+ gobj_set_global_no_trace2(4294967295, false);
6219
+ for (let level of jn_global) gobj_set_global_no_trace(level, true);
6220
+ }
6221
+ for (const [name, jn_levels] of Object.entries(jn_no_trace_levels)) {
6222
+ let gclass = gclass_find_by_name(name);
6223
+ if (!gclass) continue;
6224
+ if (!is_array(jn_levels)) {
6225
+ log_error(`${gobj_name(gobj)}: no_trace_levels of ${name} MUST be a list`);
6226
+ continue;
6227
+ }
6228
+ gobj_set_gclass_no_trace(gclass, null, false);
6229
+ for (let level of jn_levels) gobj_set_gclass_no_trace(gclass, level, true);
6230
+ }
6231
+ return 0;
6232
+ }
6233
+ /***************************************************************
6234
+ * Save a scope WHOLE, from the levels in force (see
6235
+ * set_user_gclass_traces()): a saved scope replaces main()'s, so it
6236
+ * has to carry everything that must be in force.
6237
+ ***************************************************************/
6238
+ function save_trace_scope(gobj, attr, scope, levels) {
6239
+ let jn_levels = gobj_read_attr(gobj, attr);
6240
+ if (!is_object(jn_levels)) jn_levels = {};
6241
+ jn_levels[scope] = levels;
6242
+ gobj_write_attr(gobj, attr, jn_levels);
6243
+ return gobj_save_persistent_attrs(gobj, attr);
6244
+ }
6245
+ /***************************************************************
6246
+ * "set" as the C command reads it: TRUE / set / 1, FALSE / reset / 0
6083
6247
  ***************************************************************/
6248
+ function parse_set(value) {
6249
+ if (value === true || value === false) return value;
6250
+ let v = String(value === void 0 || value === null ? "" : value).toLowerCase();
6251
+ if (v === "true" || v === "set") return true;
6252
+ if (v === "false" || v === "reset") return false;
6253
+ if (v === "" || isNaN(Number(v))) return null;
6254
+ return Number(v) ? true : false;
6255
+ }
6256
+ function yuno_prefix() {
6257
+ return `${gobj_yuno_role()}^${gobj_yuno_name()}`;
6258
+ }
6259
+ /***************************
6260
+ * Commands
6261
+ ***************************/
6262
+ /***************************************************************
6263
+ * The trace commands of the C kernel's C_YUNO, same names and same
6264
+ * parameters (`level`, `set`, `gclass_name` / `gclass`). Setting a
6265
+ * level saves its scope.
6266
+ ***************************************************************/
6267
+ function mt_command_parser(gobj, command, kw, src) {
6268
+ kw = kw || {};
6269
+ switch (command) {
6270
+ case "get-global-trace": return build_command_response(gobj, 0, null, null, gobj_get_global_trace_level());
6271
+ case "get-global-no-trace": return build_command_response(gobj, 0, null, null, gobj_get_global_trace_no_level());
6272
+ case "set-global-trace":
6273
+ case "set-global-no-trace": {
6274
+ let no = command === "set-global-no-trace";
6275
+ let level = kw.level;
6276
+ let set = parse_set(kw.set);
6277
+ if (!level) return build_command_response(gobj, -1, `${yuno_prefix()}: what level?`, null, null);
6278
+ if (set === null) return build_command_response(gobj, -1, `${yuno_prefix()}: bitmask set or re-set?`, null, null);
6279
+ if ((no ? gobj_set_global_no_trace(level, set) : gobj_set_global_trace(level, set)) < 0) return build_command_response(gobj, -1, `${yuno_prefix()}: global trace level not found: ${level}`, null, null);
6280
+ if (no) {
6281
+ save_trace_scope(gobj, "no_trace_levels", "__global_no_trace__", gobj_get_global_trace_no_level());
6282
+ return build_command_response(gobj, 0, null, null, gobj_get_global_trace_no_level());
6283
+ }
6284
+ save_trace_scope(gobj, "trace_levels", "__global_trace__", gobj_get_global_trace_level());
6285
+ return build_command_response(gobj, 0, null, null, gobj_get_global_trace_level());
6286
+ }
6287
+ case "get-gclass-trace":
6288
+ case "get-gclass-no-trace":
6289
+ case "set-gclass-trace":
6290
+ case "set-gclass-no-trace": {
6291
+ let no = command.indexOf("no-trace") >= 0;
6292
+ let gclass_name = kw.gclass_name || kw.gclass || "";
6293
+ let gclass = gclass_find_by_name(gclass_name);
6294
+ if (!gclass) return build_command_response(gobj, -1, `${yuno_prefix()}: what gclass is '${gclass_name}'?`, null, null);
6295
+ if (command.indexOf("get-") === 0) return build_command_response(gobj, 0, null, null, no ? gobj_get_gclass_trace_no_level(gclass) : gobj_get_gclass_trace_level(gclass));
6296
+ let level = kw.level;
6297
+ let set = parse_set(kw.set);
6298
+ if (!level) return build_command_response(gobj, -1, `${yuno_prefix()}: what level?`, null, null);
6299
+ if (set === null) return build_command_response(gobj, -1, `${yuno_prefix()}: bitmask set or re-set?`, null, null);
6300
+ if ((no ? gobj_set_gclass_no_trace(gclass, level, set) : gobj_set_gclass_trace(gclass, level, set)) < 0) return build_command_response(gobj, -1, `${yuno_prefix()}: trace level of ${gclass_name} not found: ${level}`, null, null);
6301
+ if (no) {
6302
+ save_trace_scope(gobj, "no_trace_levels", gclass_name, gobj_get_gclass_trace_no_level(gclass));
6303
+ return build_command_response(gobj, 0, null, null, gobj_get_gclass_trace_no_level(gclass));
6304
+ }
6305
+ save_trace_scope(gobj, "trace_levels", gclass_name, gobj_get_gclass_trace_level2(gclass));
6306
+ return build_command_response(gobj, 0, null, null, gobj_get_gclass_trace_level(gclass));
6307
+ }
6308
+ default: return build_command_response(gobj, -1, `${yuno_prefix()}: command not available: ${command}`, null, null);
6309
+ }
6310
+ }
6084
6311
  /***************************
6085
6312
  * Actions
6086
6313
  ***************************/
@@ -6101,7 +6328,8 @@ var gmt$1 = {
6101
6328
  mt_stop: mt_stop$1,
6102
6329
  mt_destroy: mt_destroy$1,
6103
6330
  mt_play,
6104
- mt_pause
6331
+ mt_pause,
6332
+ mt_command_parser
6105
6333
  };
6106
6334
  /***************************************************************
6107
6335
  * Create the GClass
@@ -6137,7 +6365,7 @@ function register_c_yuno() {
6137
6365
  *
6138
6366
  * Licence: MIT (http://www.opensource.org/licenses/mit-license)
6139
6367
  * Copyright (c) 2014,2024 Niyamaka.
6140
- * Copyright (c) 2025, ArtGins.
6368
+ * Copyright (c) 2025-2026, ArtGins.
6141
6369
  **************************************************************************/
6142
6370
  /***************************************************************
6143
6371
  * Constants
@@ -6167,6 +6395,14 @@ var attrs_table = [
6167
6395
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, 0, "subscriber of output-events. If null then subscriber is the parent"),
6168
6396
  SDATA_END()
6169
6397
  ];
6398
+ var s_user_trace_level = [
6399
+ ["ievents", "Trace inter-events with metadata of kw"],
6400
+ ["ievents2", "Trace inter-events with full kw"],
6401
+ ["identity-card", "Trace identity_card messages"]
6402
+ ];
6403
+ function is_traffic_tracing(gobj) {
6404
+ return gobj_trace_level(gobj) & 3 ? true : false;
6405
+ }
6170
6406
  var PRIVATE_DATA = {
6171
6407
  url: null,
6172
6408
  remote_yuno_name: null,
@@ -6429,7 +6665,7 @@ function iev_create(event, kw) {
6429
6665
  function send_iev(gobj, iev) {
6430
6666
  let priv = gobj.priv;
6431
6667
  let msg = JSON.stringify(iev);
6432
- if (gobj_read_bool_attr(gobj_yuno(), "trace_inter_event")) {
6668
+ if (is_traffic_tracing(gobj)) {
6433
6669
  let url = gobj_read_str_attr(gobj, "url");
6434
6670
  let prefix = gobj_name(gobj_yuno()) + " ==> " + url;
6435
6671
  let trace_ievent_callback = gobj_read_attr(gobj_yuno(), "trace_ievent_callback");
@@ -6723,7 +6959,7 @@ function ac_identity_card_ack(gobj, event, kw, src) {
6723
6959
  function ac_on_message(gobj, event, kw, src) {
6724
6960
  let url = gobj.priv.url;
6725
6961
  let iev_msg = iev_create_from_json(gobj, kw.data);
6726
- if (gobj_read_bool_attr(gobj_yuno(), "trace_inter_event")) {
6962
+ if (is_traffic_tracing(gobj)) {
6727
6963
  let prefix = gobj_name(gobj_yuno()) + " <== " + url;
6728
6964
  let size = kw.data.length;
6729
6965
  let trace_ievent_callback = gobj_read_attr(gobj_yuno(), "trace_ievent_callback");
@@ -6896,7 +7132,7 @@ function create_gclass(gclass_name) {
6896
7132
  ["EV_ON_OPEN_ERROR", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
6897
7133
  ["EV_TIMEOUT", 0],
6898
7134
  [null, 0]
6899
- ], states, gmt, 0, attrs_table, PRIVATE_DATA, 0, 0, 0, gclass_flag_t.gcflag_no_check_output_events);
7135
+ ], states, gmt, 0, attrs_table, PRIVATE_DATA, 0, 0, s_user_trace_level, gclass_flag_t.gcflag_no_check_output_events);
6900
7136
  if (!__gclass__) return -1;
6901
7137
  return 0;
6902
7138
  }
@@ -6988,7 +7224,13 @@ exports.gobj_find_subscriptions = gobj_find_subscriptions;
6988
7224
  exports.gobj_flag_t = gobj_flag_t;
6989
7225
  exports.gobj_full_name = gobj_full_name;
6990
7226
  exports.gobj_gclass_name = gobj_gclass_name;
7227
+ exports.gobj_get_gclass_trace_level = gobj_get_gclass_trace_level;
7228
+ exports.gobj_get_gclass_trace_level2 = gobj_get_gclass_trace_level2;
7229
+ exports.gobj_get_gclass_trace_no_level = gobj_get_gclass_trace_no_level;
7230
+ exports.gobj_get_global_trace_level = gobj_get_global_trace_level;
7231
+ exports.gobj_get_global_trace_no_level = gobj_get_global_trace_no_level;
6991
7232
  exports.gobj_global_trace_level = gobj_global_trace_level;
7233
+ exports.gobj_global_trace_no_level = gobj_global_trace_no_level;
6992
7234
  exports.gobj_has_attr = gobj_has_attr;
6993
7235
  exports.gobj_has_event = gobj_has_event;
6994
7236
  exports.gobj_has_output_event = gobj_has_output_event;
@@ -7030,7 +7272,9 @@ exports.gobj_set_deep_trace = gobj_set_deep_trace;
7030
7272
  exports.gobj_set_gclass_no_trace = gobj_set_gclass_no_trace;
7031
7273
  exports.gobj_set_gclass_trace = gobj_set_gclass_trace;
7032
7274
  exports.gobj_set_global_no_trace = gobj_set_global_no_trace;
7275
+ exports.gobj_set_global_no_trace2 = gobj_set_global_no_trace2;
7033
7276
  exports.gobj_set_global_trace = gobj_set_global_trace;
7277
+ exports.gobj_set_global_trace2 = gobj_set_global_trace2;
7034
7278
  exports.gobj_set_gobj_no_trace = gobj_set_gobj_no_trace;
7035
7279
  exports.gobj_set_gobj_trace = gobj_set_gobj_trace;
7036
7280
  exports.gobj_set_trace_machine_format = gobj_set_trace_machine_format;
@@ -7128,6 +7372,7 @@ exports.kwid_find_one_record = kwid_find_one_record;
7128
7372
  exports.kwid_get_ids = kwid_get_ids;
7129
7373
  exports.kwid_match_id = kwid_match_id;
7130
7374
  exports.kwid_new_dict = kwid_new_dict;
7375
+ exports.kwid_new_list = kwid_new_list;
7131
7376
  exports.list2options = list2options;
7132
7377
  exports.load_json_file = load_json_file;
7133
7378
  exports.log_debug = log_debug;