@yuneta/gobj-ui 7.23.31 → 7.23.32

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.
@@ -64092,6 +64092,86 @@ function register_c_yui_period() {
64092
64092
  return create_gclass(GCLASS_NAME);
64093
64093
  }
64094
64094
  //#endregion
64095
+ //#region src/dev_machine_trace.js
64096
+ /***********************************************************************
64097
+ * dev_machine_trace.js
64098
+ *
64099
+ * Reading a MACHINE trace line back.
64100
+ *
64101
+ * The developer window mirrors the runtime's log, and the
64102
+ * `machine` trace is most of what lands there once it is on.
64103
+ * Those lines arrive as text, so to the window they were just
64104
+ * "a debug log" -- which is why its `Periodic` filter could
64105
+ * not touch them: the filter works on a signature, and every
64106
+ * mirrored log carried the same one (`log:debug`).
64107
+ *
64108
+ * This gives a machine line back its EVENT, which is the only
64109
+ * thing that says whether it is noise. Both formats gobj-js
64110
+ * emits (`gobj_set_trace_machine_format`):
64111
+ *
64112
+ * 0 verbose "🔄 mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ac: …"
64113
+ * "<- mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ret: 0"
64114
+ * 1 compact "🔄 EV_TIMEOUT C_TIMER^t ST_IDLE from C_TIMER^t"
64115
+ *
64116
+ * Copyright (c) 2026, ArtGins.
64117
+ * All Rights Reserved.
64118
+ ***********************************************************************/
64119
+ var MACHINE_MARK_RE = /\bmach\(/;
64120
+ var EVENT_FIELD_RE = /\bev:\s*([A-Za-z0-9_]+)/;
64121
+ var EVENT_HEAD_RE = /^\s*\S{0,4}\s*(EV_[A-Za-z0-9_]+)(?=\s|$)/;
64122
+ var PERIODIC_EVENT_RE = /PERIODIC|TIMEOUT|HEARTBEAT|PING|POLL/i;
64123
+ /************************************************************
64124
+ * The event of a machine trace line, or "" when the line is
64125
+ * not one.
64126
+ *
64127
+ * "" is also the answer for a machine line that carries no
64128
+ * event -- a state change ("new st(…), prev st(…)") -- and
64129
+ * that is right: what it reports is not repetitive.
64130
+ ************************************************************/
64131
+ function machine_event_of(text) {
64132
+ if (typeof text !== "string" || text.length === 0) return "";
64133
+ if (MACHINE_MARK_RE.test(text)) {
64134
+ let m = EVENT_FIELD_RE.exec(text);
64135
+ return m ? m[1] : "";
64136
+ }
64137
+ let m = EVENT_HEAD_RE.exec(text);
64138
+ return m ? m[1] : "";
64139
+ }
64140
+ /************************************************************
64141
+ * Is this event one of the ones that arrive by themselves,
64142
+ * for ever, and say nothing about what the app is doing?
64143
+ ************************************************************/
64144
+ function is_periodic_event(event_name) {
64145
+ if (!event_name) return false;
64146
+ return PERIODIC_EVENT_RE.test(event_name);
64147
+ }
64148
+ /************************************************************
64149
+ * The signature of a mirrored log line: what the window
64150
+ * counts it under and what its filters match on.
64151
+ *
64152
+ * A machine line is signed by its EVENT, so a hundred
64153
+ * EV_TIMEOUTs are one recurring thing rather than a hundred
64154
+ * anonymous debug lines. Everything else is signed by its
64155
+ * level, as before.
64156
+ ************************************************************/
64157
+ function log_signature(level, text) {
64158
+ let event = machine_event_of(text);
64159
+ if (event) return "mach:" + event;
64160
+ return "log:" + (level || "debug");
64161
+ }
64162
+ /************************************************************
64163
+ * May the periodic filter hide this log line?
64164
+ *
64165
+ * Only a machine transition, and only below warning level.
64166
+ * An ERROR that names EV_TIMEOUT -- "Event NOT DEFINED in
64167
+ * state", which is exactly the loud one the framework wants
64168
+ * seen -- must never disappear because of the word in it.
64169
+ ************************************************************/
64170
+ function log_is_periodic(level, text) {
64171
+ if (level === "error" || level === "warning") return false;
64172
+ return is_periodic_event(machine_event_of(text));
64173
+ }
64174
+ //#endregion
64095
64175
  //#region src/yui_dev.js
64096
64176
  /***********************************************************************
64097
64177
  * ui_dev.js
@@ -64228,8 +64308,9 @@ function dev_view() {
64228
64308
  let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("dev_view_mode", "detailed", false);
64229
64309
  return v === "compact" || v === "name" || v === "full" ? v : "detailed";
64230
64310
  }
64311
+ var HIDE_PERIODIC_DEFAULT = 1;
64231
64312
  function dev_hide_periodic() {
64232
- return dev_num("dev_hide_periodic", 0) ? true : false;
64313
+ return dev_num("dev_hide_periodic", HIDE_PERIODIC_DEFAULT) ? true : false;
64233
64314
  }
64234
64315
  function dev_route() {
64235
64316
  let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("dev_output_route", "both", false);
@@ -64305,7 +64386,10 @@ function build_filter_ctx() {
64305
64386
  };
64306
64387
  }
64307
64388
  function entry_hidden(e, ctx) {
64308
- if (e.kind === "log") return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
64389
+ if (e.kind === "log") {
64390
+ if (ctx.hide_periodic && e.periodic) return true;
64391
+ return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
64392
+ }
64309
64393
  if (e.dir === 1 && !ctx.out) return true;
64310
64394
  if (e.dir === 2 && !ctx.inc) return true;
64311
64395
  if (e.dir === 3 && !ctx.err) return true;
@@ -65006,7 +65090,8 @@ function info_log(level, msg, hora) {
65006
65090
  dir: 0,
65007
65091
  size: 0,
65008
65092
  ts: traffic_now(),
65009
- sig: "log:" + lvl,
65093
+ sig: log_signature(lvl, text),
65094
+ periodic: log_is_periodic(lvl, text),
65010
65095
  hay: (lvl + " " + text).toLowerCase(),
65011
65096
  $node: null
65012
65097
  };
@@ -65339,12 +65424,12 @@ function build_control_bar() {
65339
65424
  class: "YDEV_CHIP",
65340
65425
  "data-toggle": "periodic",
65341
65426
  type: "button",
65342
- title: "Hide recurring / periodic events (polls, heartbeats)"
65427
+ title: "Hide the timers: EV_TIMEOUT / PERIODIC / HEARTBEAT / PING transitions, and recurring traffic. On by default"
65343
65428
  },
65344
65429
  "⊘ Periodic",
65345
65430
  { click: (ev) => {
65346
65431
  ev.stopPropagation();
65347
- toggle_pref("dev_hide_periodic", 0);
65432
+ toggle_pref("dev_hide_periodic", HIDE_PERIODIC_DEFAULT);
65348
65433
  } }
65349
65434
  ];
65350
65435
  let search = [
@@ -64087,6 +64087,86 @@ function register_c_yui_period() {
64087
64087
  return create_gclass(GCLASS_NAME);
64088
64088
  }
64089
64089
  //#endregion
64090
+ //#region src/dev_machine_trace.js
64091
+ /***********************************************************************
64092
+ * dev_machine_trace.js
64093
+ *
64094
+ * Reading a MACHINE trace line back.
64095
+ *
64096
+ * The developer window mirrors the runtime's log, and the
64097
+ * `machine` trace is most of what lands there once it is on.
64098
+ * Those lines arrive as text, so to the window they were just
64099
+ * "a debug log" -- which is why its `Periodic` filter could
64100
+ * not touch them: the filter works on a signature, and every
64101
+ * mirrored log carried the same one (`log:debug`).
64102
+ *
64103
+ * This gives a machine line back its EVENT, which is the only
64104
+ * thing that says whether it is noise. Both formats gobj-js
64105
+ * emits (`gobj_set_trace_machine_format`):
64106
+ *
64107
+ * 0 verbose "🔄 mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ac: …"
64108
+ * "<- mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ret: 0"
64109
+ * 1 compact "🔄 EV_TIMEOUT C_TIMER^t ST_IDLE from C_TIMER^t"
64110
+ *
64111
+ * Copyright (c) 2026, ArtGins.
64112
+ * All Rights Reserved.
64113
+ ***********************************************************************/
64114
+ var MACHINE_MARK_RE = /\bmach\(/;
64115
+ var EVENT_FIELD_RE = /\bev:\s*([A-Za-z0-9_]+)/;
64116
+ var EVENT_HEAD_RE = /^\s*\S{0,4}\s*(EV_[A-Za-z0-9_]+)(?=\s|$)/;
64117
+ var PERIODIC_EVENT_RE = /PERIODIC|TIMEOUT|HEARTBEAT|PING|POLL/i;
64118
+ /************************************************************
64119
+ * The event of a machine trace line, or "" when the line is
64120
+ * not one.
64121
+ *
64122
+ * "" is also the answer for a machine line that carries no
64123
+ * event -- a state change ("new st(…), prev st(…)") -- and
64124
+ * that is right: what it reports is not repetitive.
64125
+ ************************************************************/
64126
+ function machine_event_of(text) {
64127
+ if (typeof text !== "string" || text.length === 0) return "";
64128
+ if (MACHINE_MARK_RE.test(text)) {
64129
+ let m = EVENT_FIELD_RE.exec(text);
64130
+ return m ? m[1] : "";
64131
+ }
64132
+ let m = EVENT_HEAD_RE.exec(text);
64133
+ return m ? m[1] : "";
64134
+ }
64135
+ /************************************************************
64136
+ * Is this event one of the ones that arrive by themselves,
64137
+ * for ever, and say nothing about what the app is doing?
64138
+ ************************************************************/
64139
+ function is_periodic_event(event_name) {
64140
+ if (!event_name) return false;
64141
+ return PERIODIC_EVENT_RE.test(event_name);
64142
+ }
64143
+ /************************************************************
64144
+ * The signature of a mirrored log line: what the window
64145
+ * counts it under and what its filters match on.
64146
+ *
64147
+ * A machine line is signed by its EVENT, so a hundred
64148
+ * EV_TIMEOUTs are one recurring thing rather than a hundred
64149
+ * anonymous debug lines. Everything else is signed by its
64150
+ * level, as before.
64151
+ ************************************************************/
64152
+ function log_signature(level, text) {
64153
+ let event = machine_event_of(text);
64154
+ if (event) return "mach:" + event;
64155
+ return "log:" + (level || "debug");
64156
+ }
64157
+ /************************************************************
64158
+ * May the periodic filter hide this log line?
64159
+ *
64160
+ * Only a machine transition, and only below warning level.
64161
+ * An ERROR that names EV_TIMEOUT -- "Event NOT DEFINED in
64162
+ * state", which is exactly the loud one the framework wants
64163
+ * seen -- must never disappear because of the word in it.
64164
+ ************************************************************/
64165
+ function log_is_periodic(level, text) {
64166
+ if (level === "error" || level === "warning") return false;
64167
+ return is_periodic_event(machine_event_of(text));
64168
+ }
64169
+ //#endregion
64090
64170
  //#region src/yui_dev.js
64091
64171
  /***********************************************************************
64092
64172
  * ui_dev.js
@@ -64223,8 +64303,9 @@ function dev_view() {
64223
64303
  let v = kw_get_local_storage_value("dev_view_mode", "detailed", false);
64224
64304
  return v === "compact" || v === "name" || v === "full" ? v : "detailed";
64225
64305
  }
64306
+ var HIDE_PERIODIC_DEFAULT = 1;
64226
64307
  function dev_hide_periodic() {
64227
- return dev_num("dev_hide_periodic", 0) ? true : false;
64308
+ return dev_num("dev_hide_periodic", HIDE_PERIODIC_DEFAULT) ? true : false;
64228
64309
  }
64229
64310
  function dev_route() {
64230
64311
  let v = kw_get_local_storage_value("dev_output_route", "both", false);
@@ -64300,7 +64381,10 @@ function build_filter_ctx() {
64300
64381
  };
64301
64382
  }
64302
64383
  function entry_hidden(e, ctx) {
64303
- if (e.kind === "log") return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
64384
+ if (e.kind === "log") {
64385
+ if (ctx.hide_periodic && e.periodic) return true;
64386
+ return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
64387
+ }
64304
64388
  if (e.dir === 1 && !ctx.out) return true;
64305
64389
  if (e.dir === 2 && !ctx.inc) return true;
64306
64390
  if (e.dir === 3 && !ctx.err) return true;
@@ -65001,7 +65085,8 @@ function info_log(level, msg, hora) {
65001
65085
  dir: 0,
65002
65086
  size: 0,
65003
65087
  ts: traffic_now(),
65004
- sig: "log:" + lvl,
65088
+ sig: log_signature(lvl, text),
65089
+ periodic: log_is_periodic(lvl, text),
65005
65090
  hay: (lvl + " " + text).toLowerCase(),
65006
65091
  $node: null
65007
65092
  };
@@ -65334,12 +65419,12 @@ function build_control_bar() {
65334
65419
  class: "YDEV_CHIP",
65335
65420
  "data-toggle": "periodic",
65336
65421
  type: "button",
65337
- title: "Hide recurring / periodic events (polls, heartbeats)"
65422
+ title: "Hide the timers: EV_TIMEOUT / PERIODIC / HEARTBEAT / PING transitions, and recurring traffic. On by default"
65338
65423
  },
65339
65424
  "⊘ Periodic",
65340
65425
  { click: (ev) => {
65341
65426
  ev.stopPropagation();
65342
- toggle_pref("dev_hide_periodic", 0);
65427
+ toggle_pref("dev_hide_periodic", HIDE_PERIODIC_DEFAULT);
65343
65428
  } }
65344
65429
  ];
65345
65430
  let search = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.31",
3
+ "version": "7.23.32",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -0,0 +1,116 @@
1
+ /***********************************************************************
2
+ * dev_machine_trace.js
3
+ *
4
+ * Reading a MACHINE trace line back.
5
+ *
6
+ * The developer window mirrors the runtime's log, and the
7
+ * `machine` trace is most of what lands there once it is on.
8
+ * Those lines arrive as text, so to the window they were just
9
+ * "a debug log" -- which is why its `Periodic` filter could
10
+ * not touch them: the filter works on a signature, and every
11
+ * mirrored log carried the same one (`log:debug`).
12
+ *
13
+ * This gives a machine line back its EVENT, which is the only
14
+ * thing that says whether it is noise. Both formats gobj-js
15
+ * emits (`gobj_set_trace_machine_format`):
16
+ *
17
+ * 0 verbose "🔄 mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ac: …"
18
+ * "<- mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ret: 0"
19
+ * 1 compact "🔄 EV_TIMEOUT C_TIMER^t ST_IDLE from C_TIMER^t"
20
+ *
21
+ * Copyright (c) 2026, ArtGins.
22
+ * All Rights Reserved.
23
+ ***********************************************************************/
24
+
25
+
26
+ /* The verbose format names itself; the compact one does not. */
27
+ const MACHINE_MARK_RE = /\bmach\(/;
28
+
29
+ /* Verbose: the event is the `ev:` field. */
30
+ const EVENT_FIELD_RE = /\bev:\s*([A-Za-z0-9_]+)/;
31
+
32
+ /* Compact: the event is the first token, behind the emoji. Anchored,
33
+ * so a line that merely MENTIONS an event ("subscribed to EV_X") is
34
+ * not mistaken for a transition. */
35
+ const EVENT_HEAD_RE = /^\s*\S{0,4}\s*(EV_[A-Za-z0-9_]+)(?=\s|$)/;
36
+
37
+ /* What "recurring" means by name. The count-based half of the filter
38
+ * lives in the window, which is the only thing that can count. */
39
+ const PERIODIC_EVENT_RE = /PERIODIC|TIMEOUT|HEARTBEAT|PING|POLL/i;
40
+
41
+
42
+ /************************************************************
43
+ * The event of a machine trace line, or "" when the line is
44
+ * not one.
45
+ *
46
+ * "" is also the answer for a machine line that carries no
47
+ * event -- a state change ("new st(…), prev st(…)") -- and
48
+ * that is right: what it reports is not repetitive.
49
+ ************************************************************/
50
+ function machine_event_of(text)
51
+ {
52
+ if(typeof text !== "string" || text.length === 0) {
53
+ return "";
54
+ }
55
+
56
+ if(MACHINE_MARK_RE.test(text)) {
57
+ let m = EVENT_FIELD_RE.exec(text);
58
+ return m? m[1]: "";
59
+ }
60
+
61
+ let m = EVENT_HEAD_RE.exec(text);
62
+ return m? m[1]: "";
63
+ }
64
+
65
+ /************************************************************
66
+ * Is this event one of the ones that arrive by themselves,
67
+ * for ever, and say nothing about what the app is doing?
68
+ ************************************************************/
69
+ function is_periodic_event(event_name)
70
+ {
71
+ if(!event_name) {
72
+ return false;
73
+ }
74
+ return PERIODIC_EVENT_RE.test(event_name);
75
+ }
76
+
77
+ /************************************************************
78
+ * The signature of a mirrored log line: what the window
79
+ * counts it under and what its filters match on.
80
+ *
81
+ * A machine line is signed by its EVENT, so a hundred
82
+ * EV_TIMEOUTs are one recurring thing rather than a hundred
83
+ * anonymous debug lines. Everything else is signed by its
84
+ * level, as before.
85
+ ************************************************************/
86
+ function log_signature(level, text)
87
+ {
88
+ let event = machine_event_of(text);
89
+ if(event) {
90
+ return "mach:" + event;
91
+ }
92
+ return "log:" + (level || "debug");
93
+ }
94
+
95
+ /************************************************************
96
+ * May the periodic filter hide this log line?
97
+ *
98
+ * Only a machine transition, and only below warning level.
99
+ * An ERROR that names EV_TIMEOUT -- "Event NOT DEFINED in
100
+ * state", which is exactly the loud one the framework wants
101
+ * seen -- must never disappear because of the word in it.
102
+ ************************************************************/
103
+ function log_is_periodic(level, text)
104
+ {
105
+ if(level === "error" || level === "warning") {
106
+ return false;
107
+ }
108
+ return is_periodic_event(machine_event_of(text));
109
+ }
110
+
111
+ export {
112
+ machine_event_of,
113
+ is_periodic_event,
114
+ log_signature,
115
+ log_is_periodic,
116
+ };
@@ -0,0 +1,98 @@
1
+ import {describe, it, expect} from "vitest";
2
+ import {
3
+ machine_event_of,
4
+ is_periodic_event,
5
+ log_signature,
6
+ log_is_periodic,
7
+ } from "./dev_machine_trace.js";
8
+
9
+ /* Real lines, copied from the developer window. */
10
+ const VERBOSE_IN =
11
+ "🔄 mach(C_TIMER^demo_yuno), st: ST_IDLE, ev: EV_TIMEOUT, ac: fi(), from(C_TIMER^demo_yuno)";
12
+ const VERBOSE_RET =
13
+ "<- mach(C_YUNO^demo_yuno), st: ST_IDLE, ev: EV_TIMEOUT_PERIODIC, ret: 0";
14
+ const VERBOSE_WORK =
15
+ "🔄 mach(C_YUI_SHELL^shell), st: ST_IDLE, ev: EV_ROUTE_CHANGED, ac: ac_route(), from(C_YUI_NAV^nav)";
16
+ const COMPACT =
17
+ "🔄 EV_TIMEOUT C_TIMER^demo_yuno ST_IDLE from C_TIMER^demo_yuno";
18
+ const STATE_CHANGE =
19
+ "🔀🔀 mach(C_TCP^conn), new st(ST_CONNECTED), prev st(ST_WAIT_CONNECTED)";
20
+ const NOT_DEFINED =
21
+ "📛 mach(C_X^x), st: ST_IDLE, ev: EV_TIMEOUT, 📛📛ERROR Event NOT DEFINED in state📛📛, from(C_TIMER^t)";
22
+
23
+ describe("machine_event_of", () => {
24
+ it("reads the event out of the verbose format, both lines of it", () => {
25
+ expect(machine_event_of(VERBOSE_IN)).toBe("EV_TIMEOUT");
26
+ expect(machine_event_of(VERBOSE_RET)).toBe("EV_TIMEOUT_PERIODIC");
27
+ expect(machine_event_of(VERBOSE_WORK)).toBe("EV_ROUTE_CHANGED");
28
+ });
29
+
30
+ it("reads it out of the compact format, where it leads the line", () => {
31
+ expect(machine_event_of(COMPACT)).toBe("EV_TIMEOUT");
32
+ });
33
+
34
+ it("a state change carries no event, and says so", () => {
35
+ expect(machine_event_of(STATE_CHANGE)).toBe("");
36
+ });
37
+
38
+ it("a line that merely MENTIONS an event is not a transition", () => {
39
+ expect(machine_event_of("subscribed to EV_TIMEOUT of C_TIMER^t")).toBe("");
40
+ expect(machine_event_of("cannot send EV_ON_OPEN")).toBe("");
41
+ });
42
+
43
+ it("is safe on anything that is not a line", () => {
44
+ expect(machine_event_of("")).toBe("");
45
+ expect(machine_event_of(null)).toBe("");
46
+ expect(machine_event_of(undefined)).toBe("");
47
+ expect(machine_event_of(42)).toBe("");
48
+ });
49
+ });
50
+
51
+ describe("is_periodic_event", () => {
52
+ it("names the events that arrive by themselves for ever", () => {
53
+ expect(is_periodic_event("EV_TIMEOUT")).toBe(true);
54
+ expect(is_periodic_event("EV_TIMEOUT_PERIODIC")).toBe(true);
55
+ expect(is_periodic_event("EV_HEARTBEAT")).toBe(true);
56
+ expect(is_periodic_event("EV_PING")).toBe(true);
57
+ });
58
+
59
+ it("leaves the ones a user caused alone", () => {
60
+ expect(is_periodic_event("EV_ROUTE_CHANGED")).toBe(false);
61
+ expect(is_periodic_event("EV_ON_MESSAGE")).toBe(false);
62
+ expect(is_periodic_event("")).toBe(false);
63
+ expect(is_periodic_event(null)).toBe(false);
64
+ });
65
+ });
66
+
67
+ describe("log_signature", () => {
68
+ it("signs a machine line by its EVENT, so a hundred of them are one thing", () => {
69
+ expect(log_signature("debug", VERBOSE_IN)).toBe("mach:EV_TIMEOUT");
70
+ expect(log_signature("debug", VERBOSE_RET)).toBe("mach:EV_TIMEOUT_PERIODIC");
71
+ expect(log_signature("debug", COMPACT)).toBe("mach:EV_TIMEOUT");
72
+ });
73
+
74
+ it("signs everything else by its level, as before", () => {
75
+ expect(log_signature("error", "connect failed")).toBe("log:error");
76
+ expect(log_signature("debug", "hello")).toBe("log:debug");
77
+ expect(log_signature("", "hello")).toBe("log:debug");
78
+ });
79
+ });
80
+
81
+ describe("log_is_periodic", () => {
82
+ it("hides the timer traffic the filter is for", () => {
83
+ expect(log_is_periodic("debug", VERBOSE_IN)).toBe(true);
84
+ expect(log_is_periodic("debug", VERBOSE_RET)).toBe(true);
85
+ expect(log_is_periodic("debug", COMPACT)).toBe(true);
86
+ });
87
+
88
+ it("NEVER hides an error or a warning that names a timeout", () => {
89
+ expect(log_is_periodic("error", NOT_DEFINED)).toBe(false);
90
+ expect(log_is_periodic("warning", VERBOSE_RET)).toBe(false);
91
+ expect(log_is_periodic("error", "connection timeout")).toBe(false);
92
+ });
93
+
94
+ it("leaves a transition somebody caused alone", () => {
95
+ expect(log_is_periodic("debug", VERBOSE_WORK)).toBe(false);
96
+ expect(log_is_periodic("debug", "just a log line")).toBe(false);
97
+ });
98
+ });
package/src/yui_dev.js CHANGED
@@ -21,6 +21,8 @@ import {
21
21
  gobj_set_trace_machine_format,
22
22
  } from "@yuneta/gobj-js";
23
23
 
24
+ import {log_signature, log_is_periodic} from "./dev_machine_trace.js";
25
+
24
26
  import i18next from 'i18next';
25
27
 
26
28
  /***********************************************************************
@@ -164,9 +166,20 @@ function dev_view()
164
166
  return (v === "compact" || v === "name" || v === "full") ? v : "detailed";
165
167
  }
166
168
 
169
+ /* ON by default. The machine trace is the reason this window
170
+ * exists, and a yuno with one timer emits two lines a second for
171
+ * ever: leaving the filter off meant the first thing anybody saw
172
+ * after enabling Automata was a wall of EV_TIMEOUT with their own
173
+ * transitions somewhere inside it. The chip turns it back off.
174
+ *
175
+ * A CONSTANT, and not the literal in two places: the reader and the
176
+ * toggle must agree on what "unset" means, or the first click on the
177
+ * chip writes the value it already had and appears to do nothing. */
178
+ const HIDE_PERIODIC_DEFAULT = 1;
179
+
167
180
  function dev_hide_periodic()
168
181
  {
169
- return dev_num("dev_hide_periodic", 0) ? true : false;
182
+ return dev_num("dev_hide_periodic", HIDE_PERIODIC_DEFAULT) ? true : false;
170
183
  }
171
184
 
172
185
  /* Where dev output (traffic + all logs + automata) is routed:
@@ -300,7 +313,21 @@ function entry_hidden(e, ctx)
300
313
  {
301
314
  if(e.kind === "log") {
302
315
  /* Mirrored console logs respect the search box only — not the
303
- * in/out/err/periodic traffic filters. */
316
+ * in/out/err traffic filters, which are about DIRECTION and a
317
+ * log has none.
318
+ *
319
+ * The PERIODIC filter does reach them, and it has to: once
320
+ * Automata is on, the machine trace is what floods this
321
+ * window, and every one of those lines arrives here as a log.
322
+ * It applies to a machine TRANSITION only, and never above
323
+ * debug level — see log_is_periodic(). By NAME and not by
324
+ * count, unlike the traffic half below: a busy FSM crosses
325
+ * the recurrence threshold on almost every event within
326
+ * seconds, so counting here would empty the window of the
327
+ * very transitions somebody turned Automata on to read. */
328
+ if(ctx.hide_periodic && e.periodic) {
329
+ return true;
330
+ }
304
331
  return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
305
332
  }
306
333
  if(e.dir === 1 && !ctx.out) {
@@ -1031,10 +1058,16 @@ function info_log(level, msg, hora)
1031
1058
  } else {
1032
1059
  text = is_string(msg) ? msg : String(msg);
1033
1060
  }
1061
+ /* A machine line is signed by its EVENT, not by "log:debug":
1062
+ * that is what makes a hundred EV_TIMEOUTs ONE recurring
1063
+ * thing the filter and the counter can see, instead of a
1064
+ * hundred anonymous debug lines they cannot tell apart. */
1034
1065
  let entry = {
1035
1066
  kind: "log", level: lvl, text: text,
1036
1067
  dir: 0, size: 0, ts: traffic_now(),
1037
- sig: "log:" + lvl, hay: (lvl + " " + text).toLowerCase(), $node: null,
1068
+ sig: log_signature(lvl, text),
1069
+ periodic: log_is_periodic(lvl, text),
1070
+ hay: (lvl + " " + text).toLowerCase(), $node: null,
1038
1071
  };
1039
1072
  TRAFFIC_LOG.push(entry);
1040
1073
  if(TRAFFIC_LOG.length > TRAFFIC_MAX) {
@@ -1384,11 +1417,12 @@ function build_control_bar()
1384
1417
 
1385
1418
  let periodic_chip = ['button', {
1386
1419
  class: 'YDEV_CHIP', 'data-toggle': 'periodic', type: 'button',
1387
- title: 'Hide recurring / periodic events (polls, heartbeats)',
1420
+ title: 'Hide the timers: EV_TIMEOUT / PERIODIC / HEARTBEAT / PING ' +
1421
+ 'transitions, and recurring traffic. On by default',
1388
1422
  }, '⊘ Periodic', {
1389
1423
  click: (ev) => {
1390
1424
  ev.stopPropagation();
1391
- toggle_pref('dev_hide_periodic', 0);
1425
+ toggle_pref('dev_hide_periodic', HIDE_PERIODIC_DEFAULT);
1392
1426
  }
1393
1427
  }];
1394
1428