@yuneta/gobj-ui 7.23.31 → 7.23.33
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/dist/gobj-ui.cjs.js +101 -5
- package/dist/gobj-ui.es.js +101 -5
- package/package.json +1 -1
- package/src/dev_machine_trace.js +116 -0
- package/src/dev_machine_trace.test.js +98 -0
- package/src/yui_dev.js +88 -6
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -32,6 +32,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
32
32
|
}) : target, mod));
|
|
33
33
|
//#endregion
|
|
34
34
|
let _yuneta_gobj_js = require("@yuneta/gobj-js");
|
|
35
|
+
_yuneta_gobj_js = __toESM(_yuneta_gobj_js, 1);
|
|
35
36
|
let i18next = require("i18next");
|
|
36
37
|
i18next = __toESM(i18next, 1);
|
|
37
38
|
let tom_select = require("tom-select");
|
|
@@ -64092,6 +64093,86 @@ function register_c_yui_period() {
|
|
|
64092
64093
|
return create_gclass(GCLASS_NAME);
|
|
64093
64094
|
}
|
|
64094
64095
|
//#endregion
|
|
64096
|
+
//#region src/dev_machine_trace.js
|
|
64097
|
+
/***********************************************************************
|
|
64098
|
+
* dev_machine_trace.js
|
|
64099
|
+
*
|
|
64100
|
+
* Reading a MACHINE trace line back.
|
|
64101
|
+
*
|
|
64102
|
+
* The developer window mirrors the runtime's log, and the
|
|
64103
|
+
* `machine` trace is most of what lands there once it is on.
|
|
64104
|
+
* Those lines arrive as text, so to the window they were just
|
|
64105
|
+
* "a debug log" -- which is why its `Periodic` filter could
|
|
64106
|
+
* not touch them: the filter works on a signature, and every
|
|
64107
|
+
* mirrored log carried the same one (`log:debug`).
|
|
64108
|
+
*
|
|
64109
|
+
* This gives a machine line back its EVENT, which is the only
|
|
64110
|
+
* thing that says whether it is noise. Both formats gobj-js
|
|
64111
|
+
* emits (`gobj_set_trace_machine_format`):
|
|
64112
|
+
*
|
|
64113
|
+
* 0 verbose "🔄 mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ac: …"
|
|
64114
|
+
* "<- mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ret: 0"
|
|
64115
|
+
* 1 compact "🔄 EV_TIMEOUT C_TIMER^t ST_IDLE from C_TIMER^t"
|
|
64116
|
+
*
|
|
64117
|
+
* Copyright (c) 2026, ArtGins.
|
|
64118
|
+
* All Rights Reserved.
|
|
64119
|
+
***********************************************************************/
|
|
64120
|
+
var MACHINE_MARK_RE = /\bmach\(/;
|
|
64121
|
+
var EVENT_FIELD_RE = /\bev:\s*([A-Za-z0-9_]+)/;
|
|
64122
|
+
var EVENT_HEAD_RE = /^\s*\S{0,4}\s*(EV_[A-Za-z0-9_]+)(?=\s|$)/;
|
|
64123
|
+
var PERIODIC_EVENT_RE = /PERIODIC|TIMEOUT|HEARTBEAT|PING|POLL/i;
|
|
64124
|
+
/************************************************************
|
|
64125
|
+
* The event of a machine trace line, or "" when the line is
|
|
64126
|
+
* not one.
|
|
64127
|
+
*
|
|
64128
|
+
* "" is also the answer for a machine line that carries no
|
|
64129
|
+
* event -- a state change ("new st(…), prev st(…)") -- and
|
|
64130
|
+
* that is right: what it reports is not repetitive.
|
|
64131
|
+
************************************************************/
|
|
64132
|
+
function machine_event_of(text) {
|
|
64133
|
+
if (typeof text !== "string" || text.length === 0) return "";
|
|
64134
|
+
if (MACHINE_MARK_RE.test(text)) {
|
|
64135
|
+
let m = EVENT_FIELD_RE.exec(text);
|
|
64136
|
+
return m ? m[1] : "";
|
|
64137
|
+
}
|
|
64138
|
+
let m = EVENT_HEAD_RE.exec(text);
|
|
64139
|
+
return m ? m[1] : "";
|
|
64140
|
+
}
|
|
64141
|
+
/************************************************************
|
|
64142
|
+
* Is this event one of the ones that arrive by themselves,
|
|
64143
|
+
* for ever, and say nothing about what the app is doing?
|
|
64144
|
+
************************************************************/
|
|
64145
|
+
function is_periodic_event(event_name) {
|
|
64146
|
+
if (!event_name) return false;
|
|
64147
|
+
return PERIODIC_EVENT_RE.test(event_name);
|
|
64148
|
+
}
|
|
64149
|
+
/************************************************************
|
|
64150
|
+
* The signature of a mirrored log line: what the window
|
|
64151
|
+
* counts it under and what its filters match on.
|
|
64152
|
+
*
|
|
64153
|
+
* A machine line is signed by its EVENT, so a hundred
|
|
64154
|
+
* EV_TIMEOUTs are one recurring thing rather than a hundred
|
|
64155
|
+
* anonymous debug lines. Everything else is signed by its
|
|
64156
|
+
* level, as before.
|
|
64157
|
+
************************************************************/
|
|
64158
|
+
function log_signature(level, text) {
|
|
64159
|
+
let event = machine_event_of(text);
|
|
64160
|
+
if (event) return "mach:" + event;
|
|
64161
|
+
return "log:" + (level || "debug");
|
|
64162
|
+
}
|
|
64163
|
+
/************************************************************
|
|
64164
|
+
* May the periodic filter hide this log line?
|
|
64165
|
+
*
|
|
64166
|
+
* Only a machine transition, and only below warning level.
|
|
64167
|
+
* An ERROR that names EV_TIMEOUT -- "Event NOT DEFINED in
|
|
64168
|
+
* state", which is exactly the loud one the framework wants
|
|
64169
|
+
* seen -- must never disappear because of the word in it.
|
|
64170
|
+
************************************************************/
|
|
64171
|
+
function log_is_periodic(level, text) {
|
|
64172
|
+
if (level === "error" || level === "warning") return false;
|
|
64173
|
+
return is_periodic_event(machine_event_of(text));
|
|
64174
|
+
}
|
|
64175
|
+
//#endregion
|
|
64095
64176
|
//#region src/yui_dev.js
|
|
64096
64177
|
/***********************************************************************
|
|
64097
64178
|
* ui_dev.js
|
|
@@ -64228,8 +64309,9 @@ function dev_view() {
|
|
|
64228
64309
|
let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("dev_view_mode", "detailed", false);
|
|
64229
64310
|
return v === "compact" || v === "name" || v === "full" ? v : "detailed";
|
|
64230
64311
|
}
|
|
64312
|
+
var HIDE_PERIODIC_DEFAULT = 1;
|
|
64231
64313
|
function dev_hide_periodic() {
|
|
64232
|
-
return dev_num("dev_hide_periodic",
|
|
64314
|
+
return dev_num("dev_hide_periodic", HIDE_PERIODIC_DEFAULT) ? true : false;
|
|
64233
64315
|
}
|
|
64234
64316
|
function dev_route() {
|
|
64235
64317
|
let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("dev_output_route", "both", false);
|
|
@@ -64237,7 +64319,16 @@ function dev_route() {
|
|
|
64237
64319
|
}
|
|
64238
64320
|
function apply_console_route() {
|
|
64239
64321
|
(0, _yuneta_gobj_js.set_console_log_enabled)(dev_route() !== "window");
|
|
64322
|
+
if (typeof _yuneta_gobj_js.set_console_log_filter === "function") _yuneta_gobj_js.set_console_log_filter((level, msg) => {
|
|
64323
|
+
if (!dev_hide_periodic()) return true;
|
|
64324
|
+
return !log_is_periodic(level, (0, _yuneta_gobj_js.is_string)(msg) ? msg : String(msg));
|
|
64325
|
+
});
|
|
64326
|
+
else if (!__console_filter_warned__) {
|
|
64327
|
+
__console_filter_warned__ = true;
|
|
64328
|
+
console.warn("yui_dev: gobj-js has no set_console_log_filter (needs >= 7.13.6): the Periodic filter applies to this window only, not to the console");
|
|
64329
|
+
}
|
|
64240
64330
|
}
|
|
64331
|
+
var __console_filter_warned__ = false;
|
|
64241
64332
|
function console_traffic(title, jn, direction, size) {
|
|
64242
64333
|
let event = jn && jn.event ? String(jn.event) : "(no event)";
|
|
64243
64334
|
let kw = jn && jn.kw && typeof jn.kw === "object" ? jn.kw : jn;
|
|
@@ -64267,6 +64358,7 @@ function set_output_route(v) {
|
|
|
64267
64358
|
function toggle_pref(key, def) {
|
|
64268
64359
|
let v = dev_num(key, def) ? 0 : 1;
|
|
64269
64360
|
(0, _yuneta_gobj_js.kw_set_local_storage_value)(key, v);
|
|
64361
|
+
apply_console_route();
|
|
64270
64362
|
rerender_all();
|
|
64271
64363
|
refresh_dev_chrome();
|
|
64272
64364
|
}
|
|
@@ -64305,7 +64397,10 @@ function build_filter_ctx() {
|
|
|
64305
64397
|
};
|
|
64306
64398
|
}
|
|
64307
64399
|
function entry_hidden(e, ctx) {
|
|
64308
|
-
if (e.kind === "log")
|
|
64400
|
+
if (e.kind === "log") {
|
|
64401
|
+
if (ctx.hide_periodic && e.periodic) return true;
|
|
64402
|
+
return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
|
|
64403
|
+
}
|
|
64309
64404
|
if (e.dir === 1 && !ctx.out) return true;
|
|
64310
64405
|
if (e.dir === 2 && !ctx.inc) return true;
|
|
64311
64406
|
if (e.dir === 3 && !ctx.err) return true;
|
|
@@ -65006,7 +65101,8 @@ function info_log(level, msg, hora) {
|
|
|
65006
65101
|
dir: 0,
|
|
65007
65102
|
size: 0,
|
|
65008
65103
|
ts: traffic_now(),
|
|
65009
|
-
sig:
|
|
65104
|
+
sig: log_signature(lvl, text),
|
|
65105
|
+
periodic: log_is_periodic(lvl, text),
|
|
65010
65106
|
hay: (lvl + " " + text).toLowerCase(),
|
|
65011
65107
|
$node: null
|
|
65012
65108
|
};
|
|
@@ -65339,12 +65435,12 @@ function build_control_bar() {
|
|
|
65339
65435
|
class: "YDEV_CHIP",
|
|
65340
65436
|
"data-toggle": "periodic",
|
|
65341
65437
|
type: "button",
|
|
65342
|
-
title: "Hide
|
|
65438
|
+
title: "Hide the timers: EV_TIMEOUT / PERIODIC / HEARTBEAT / PING transitions, and recurring traffic. On by default"
|
|
65343
65439
|
},
|
|
65344
65440
|
"⊘ Periodic",
|
|
65345
65441
|
{ click: (ev) => {
|
|
65346
65442
|
ev.stopPropagation();
|
|
65347
|
-
toggle_pref("dev_hide_periodic",
|
|
65443
|
+
toggle_pref("dev_hide_periodic", HIDE_PERIODIC_DEFAULT);
|
|
65348
65444
|
} }
|
|
65349
65445
|
];
|
|
65350
65446
|
let search = [
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as gobj_js from "@yuneta/gobj-js";
|
|
1
2
|
import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, duplicate_objects, empty_string, escapeHtml, event_flag_t, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_bottom_gobj, gobj_change_state, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_post_event, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_start_tree, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, gobj_yuno_name, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_get_ids, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_console_log_enabled, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
|
|
2
3
|
import i18next, { t } from "i18next";
|
|
3
4
|
import TomSelect from "tom-select";
|
|
@@ -64087,6 +64088,86 @@ function register_c_yui_period() {
|
|
|
64087
64088
|
return create_gclass(GCLASS_NAME);
|
|
64088
64089
|
}
|
|
64089
64090
|
//#endregion
|
|
64091
|
+
//#region src/dev_machine_trace.js
|
|
64092
|
+
/***********************************************************************
|
|
64093
|
+
* dev_machine_trace.js
|
|
64094
|
+
*
|
|
64095
|
+
* Reading a MACHINE trace line back.
|
|
64096
|
+
*
|
|
64097
|
+
* The developer window mirrors the runtime's log, and the
|
|
64098
|
+
* `machine` trace is most of what lands there once it is on.
|
|
64099
|
+
* Those lines arrive as text, so to the window they were just
|
|
64100
|
+
* "a debug log" -- which is why its `Periodic` filter could
|
|
64101
|
+
* not touch them: the filter works on a signature, and every
|
|
64102
|
+
* mirrored log carried the same one (`log:debug`).
|
|
64103
|
+
*
|
|
64104
|
+
* This gives a machine line back its EVENT, which is the only
|
|
64105
|
+
* thing that says whether it is noise. Both formats gobj-js
|
|
64106
|
+
* emits (`gobj_set_trace_machine_format`):
|
|
64107
|
+
*
|
|
64108
|
+
* 0 verbose "🔄 mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ac: …"
|
|
64109
|
+
* "<- mach(C_TIMER^t), st: ST_IDLE, ev: EV_TIMEOUT, ret: 0"
|
|
64110
|
+
* 1 compact "🔄 EV_TIMEOUT C_TIMER^t ST_IDLE from C_TIMER^t"
|
|
64111
|
+
*
|
|
64112
|
+
* Copyright (c) 2026, ArtGins.
|
|
64113
|
+
* All Rights Reserved.
|
|
64114
|
+
***********************************************************************/
|
|
64115
|
+
var MACHINE_MARK_RE = /\bmach\(/;
|
|
64116
|
+
var EVENT_FIELD_RE = /\bev:\s*([A-Za-z0-9_]+)/;
|
|
64117
|
+
var EVENT_HEAD_RE = /^\s*\S{0,4}\s*(EV_[A-Za-z0-9_]+)(?=\s|$)/;
|
|
64118
|
+
var PERIODIC_EVENT_RE = /PERIODIC|TIMEOUT|HEARTBEAT|PING|POLL/i;
|
|
64119
|
+
/************************************************************
|
|
64120
|
+
* The event of a machine trace line, or "" when the line is
|
|
64121
|
+
* not one.
|
|
64122
|
+
*
|
|
64123
|
+
* "" is also the answer for a machine line that carries no
|
|
64124
|
+
* event -- a state change ("new st(…), prev st(…)") -- and
|
|
64125
|
+
* that is right: what it reports is not repetitive.
|
|
64126
|
+
************************************************************/
|
|
64127
|
+
function machine_event_of(text) {
|
|
64128
|
+
if (typeof text !== "string" || text.length === 0) return "";
|
|
64129
|
+
if (MACHINE_MARK_RE.test(text)) {
|
|
64130
|
+
let m = EVENT_FIELD_RE.exec(text);
|
|
64131
|
+
return m ? m[1] : "";
|
|
64132
|
+
}
|
|
64133
|
+
let m = EVENT_HEAD_RE.exec(text);
|
|
64134
|
+
return m ? m[1] : "";
|
|
64135
|
+
}
|
|
64136
|
+
/************************************************************
|
|
64137
|
+
* Is this event one of the ones that arrive by themselves,
|
|
64138
|
+
* for ever, and say nothing about what the app is doing?
|
|
64139
|
+
************************************************************/
|
|
64140
|
+
function is_periodic_event(event_name) {
|
|
64141
|
+
if (!event_name) return false;
|
|
64142
|
+
return PERIODIC_EVENT_RE.test(event_name);
|
|
64143
|
+
}
|
|
64144
|
+
/************************************************************
|
|
64145
|
+
* The signature of a mirrored log line: what the window
|
|
64146
|
+
* counts it under and what its filters match on.
|
|
64147
|
+
*
|
|
64148
|
+
* A machine line is signed by its EVENT, so a hundred
|
|
64149
|
+
* EV_TIMEOUTs are one recurring thing rather than a hundred
|
|
64150
|
+
* anonymous debug lines. Everything else is signed by its
|
|
64151
|
+
* level, as before.
|
|
64152
|
+
************************************************************/
|
|
64153
|
+
function log_signature(level, text) {
|
|
64154
|
+
let event = machine_event_of(text);
|
|
64155
|
+
if (event) return "mach:" + event;
|
|
64156
|
+
return "log:" + (level || "debug");
|
|
64157
|
+
}
|
|
64158
|
+
/************************************************************
|
|
64159
|
+
* May the periodic filter hide this log line?
|
|
64160
|
+
*
|
|
64161
|
+
* Only a machine transition, and only below warning level.
|
|
64162
|
+
* An ERROR that names EV_TIMEOUT -- "Event NOT DEFINED in
|
|
64163
|
+
* state", which is exactly the loud one the framework wants
|
|
64164
|
+
* seen -- must never disappear because of the word in it.
|
|
64165
|
+
************************************************************/
|
|
64166
|
+
function log_is_periodic(level, text) {
|
|
64167
|
+
if (level === "error" || level === "warning") return false;
|
|
64168
|
+
return is_periodic_event(machine_event_of(text));
|
|
64169
|
+
}
|
|
64170
|
+
//#endregion
|
|
64090
64171
|
//#region src/yui_dev.js
|
|
64091
64172
|
/***********************************************************************
|
|
64092
64173
|
* ui_dev.js
|
|
@@ -64223,8 +64304,9 @@ function dev_view() {
|
|
|
64223
64304
|
let v = kw_get_local_storage_value("dev_view_mode", "detailed", false);
|
|
64224
64305
|
return v === "compact" || v === "name" || v === "full" ? v : "detailed";
|
|
64225
64306
|
}
|
|
64307
|
+
var HIDE_PERIODIC_DEFAULT = 1;
|
|
64226
64308
|
function dev_hide_periodic() {
|
|
64227
|
-
return dev_num("dev_hide_periodic",
|
|
64309
|
+
return dev_num("dev_hide_periodic", HIDE_PERIODIC_DEFAULT) ? true : false;
|
|
64228
64310
|
}
|
|
64229
64311
|
function dev_route() {
|
|
64230
64312
|
let v = kw_get_local_storage_value("dev_output_route", "both", false);
|
|
@@ -64232,7 +64314,16 @@ function dev_route() {
|
|
|
64232
64314
|
}
|
|
64233
64315
|
function apply_console_route() {
|
|
64234
64316
|
set_console_log_enabled(dev_route() !== "window");
|
|
64317
|
+
if (typeof gobj_js.set_console_log_filter === "function") gobj_js.set_console_log_filter((level, msg) => {
|
|
64318
|
+
if (!dev_hide_periodic()) return true;
|
|
64319
|
+
return !log_is_periodic(level, is_string(msg) ? msg : String(msg));
|
|
64320
|
+
});
|
|
64321
|
+
else if (!__console_filter_warned__) {
|
|
64322
|
+
__console_filter_warned__ = true;
|
|
64323
|
+
console.warn("yui_dev: gobj-js has no set_console_log_filter (needs >= 7.13.6): the Periodic filter applies to this window only, not to the console");
|
|
64324
|
+
}
|
|
64235
64325
|
}
|
|
64326
|
+
var __console_filter_warned__ = false;
|
|
64236
64327
|
function console_traffic(title, jn, direction, size) {
|
|
64237
64328
|
let event = jn && jn.event ? String(jn.event) : "(no event)";
|
|
64238
64329
|
let kw = jn && jn.kw && typeof jn.kw === "object" ? jn.kw : jn;
|
|
@@ -64262,6 +64353,7 @@ function set_output_route(v) {
|
|
|
64262
64353
|
function toggle_pref(key, def) {
|
|
64263
64354
|
let v = dev_num(key, def) ? 0 : 1;
|
|
64264
64355
|
kw_set_local_storage_value(key, v);
|
|
64356
|
+
apply_console_route();
|
|
64265
64357
|
rerender_all();
|
|
64266
64358
|
refresh_dev_chrome();
|
|
64267
64359
|
}
|
|
@@ -64300,7 +64392,10 @@ function build_filter_ctx() {
|
|
|
64300
64392
|
};
|
|
64301
64393
|
}
|
|
64302
64394
|
function entry_hidden(e, ctx) {
|
|
64303
|
-
if (e.kind === "log")
|
|
64395
|
+
if (e.kind === "log") {
|
|
64396
|
+
if (ctx.hide_periodic && e.periodic) return true;
|
|
64397
|
+
return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
|
|
64398
|
+
}
|
|
64304
64399
|
if (e.dir === 1 && !ctx.out) return true;
|
|
64305
64400
|
if (e.dir === 2 && !ctx.inc) return true;
|
|
64306
64401
|
if (e.dir === 3 && !ctx.err) return true;
|
|
@@ -65001,7 +65096,8 @@ function info_log(level, msg, hora) {
|
|
|
65001
65096
|
dir: 0,
|
|
65002
65097
|
size: 0,
|
|
65003
65098
|
ts: traffic_now(),
|
|
65004
|
-
sig:
|
|
65099
|
+
sig: log_signature(lvl, text),
|
|
65100
|
+
periodic: log_is_periodic(lvl, text),
|
|
65005
65101
|
hay: (lvl + " " + text).toLowerCase(),
|
|
65006
65102
|
$node: null
|
|
65007
65103
|
};
|
|
@@ -65334,12 +65430,12 @@ function build_control_bar() {
|
|
|
65334
65430
|
class: "YDEV_CHIP",
|
|
65335
65431
|
"data-toggle": "periodic",
|
|
65336
65432
|
type: "button",
|
|
65337
|
-
title: "Hide
|
|
65433
|
+
title: "Hide the timers: EV_TIMEOUT / PERIODIC / HEARTBEAT / PING transitions, and recurring traffic. On by default"
|
|
65338
65434
|
},
|
|
65339
65435
|
"⊘ Periodic",
|
|
65340
65436
|
{ click: (ev) => {
|
|
65341
65437
|
ev.stopPropagation();
|
|
65342
|
-
toggle_pref("dev_hide_periodic",
|
|
65438
|
+
toggle_pref("dev_hide_periodic", HIDE_PERIODIC_DEFAULT);
|
|
65343
65439
|
} }
|
|
65344
65440
|
];
|
|
65345
65441
|
let search = [
|
package/package.json
CHANGED
|
@@ -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,16 @@ import {
|
|
|
21
21
|
gobj_set_trace_machine_format,
|
|
22
22
|
} from "@yuneta/gobj-js";
|
|
23
23
|
|
|
24
|
+
/* A NAMESPACE import, deliberately, for one optional API.
|
|
25
|
+
*
|
|
26
|
+
* `set_console_log_filter` arrived in gobj-js 7.13.6, and a NAMED import of
|
|
27
|
+
* an export the installed copy does not have is a hard module-load error --
|
|
28
|
+
* it would take the whole app down instead of degrading. Read off the
|
|
29
|
+
* namespace, a missing export is plain `undefined` and can be tested for. */
|
|
30
|
+
import * as gobj_js from "@yuneta/gobj-js";
|
|
31
|
+
|
|
32
|
+
import {log_signature, log_is_periodic} from "./dev_machine_trace.js";
|
|
33
|
+
|
|
24
34
|
import i18next from 'i18next';
|
|
25
35
|
|
|
26
36
|
/***********************************************************************
|
|
@@ -164,9 +174,20 @@ function dev_view()
|
|
|
164
174
|
return (v === "compact" || v === "name" || v === "full") ? v : "detailed";
|
|
165
175
|
}
|
|
166
176
|
|
|
177
|
+
/* ON by default. The machine trace is the reason this window
|
|
178
|
+
* exists, and a yuno with one timer emits two lines a second for
|
|
179
|
+
* ever: leaving the filter off meant the first thing anybody saw
|
|
180
|
+
* after enabling Automata was a wall of EV_TIMEOUT with their own
|
|
181
|
+
* transitions somewhere inside it. The chip turns it back off.
|
|
182
|
+
*
|
|
183
|
+
* A CONSTANT, and not the literal in two places: the reader and the
|
|
184
|
+
* toggle must agree on what "unset" means, or the first click on the
|
|
185
|
+
* chip writes the value it already had and appears to do nothing. */
|
|
186
|
+
const HIDE_PERIODIC_DEFAULT = 1;
|
|
187
|
+
|
|
167
188
|
function dev_hide_periodic()
|
|
168
189
|
{
|
|
169
|
-
return dev_num("dev_hide_periodic",
|
|
190
|
+
return dev_num("dev_hide_periodic", HIDE_PERIODIC_DEFAULT) ? true : false;
|
|
170
191
|
}
|
|
171
192
|
|
|
172
193
|
/* Where dev output (traffic + all logs + automata) is routed:
|
|
@@ -182,12 +203,46 @@ function dev_route()
|
|
|
182
203
|
}
|
|
183
204
|
|
|
184
205
|
/* Push the current route's console decision down to gobj-js. Called from
|
|
185
|
-
* apply_dev_traces (startup)
|
|
206
|
+
* apply_dev_traces (startup), whenever the Output selector changes, and
|
|
207
|
+
* whenever a filter that the console also obeys is toggled. */
|
|
186
208
|
function apply_console_route()
|
|
187
209
|
{
|
|
188
210
|
set_console_log_enabled(dev_route() !== "window");
|
|
211
|
+
|
|
212
|
+
/* ONE rule, TWO sinks.
|
|
213
|
+
*
|
|
214
|
+
* The window's Periodic filter used to end at the window, so with the
|
|
215
|
+
* Output on "Both" the timers were gone from the panel and still
|
|
216
|
+
* arriving in the browser console, two lines a second — the same flood,
|
|
217
|
+
* one pane over. It cannot be fixed from this side alone: gobj-js writes
|
|
218
|
+
* the console line BEFORE it calls the log sink, so nothing here can
|
|
219
|
+
* un-print it. It hands out a say instead (`set_console_log_filter`,
|
|
220
|
+
* gobj-js >= 7.13.6), and this is the same predicate `entry_hidden()`
|
|
221
|
+
* uses, so the two panes cannot disagree about what is noise.
|
|
222
|
+
*
|
|
223
|
+
* Feature-detected rather than required: against an older gobj-js the
|
|
224
|
+
* window still filters and the console still floods, exactly as before,
|
|
225
|
+
* and the reason is logged once instead of failing. */
|
|
226
|
+
if(typeof gobj_js.set_console_log_filter === "function") {
|
|
227
|
+
gobj_js.set_console_log_filter((level, msg) => {
|
|
228
|
+
if(!dev_hide_periodic()) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
return !log_is_periodic(level, is_string(msg) ? msg : String(msg));
|
|
232
|
+
});
|
|
233
|
+
} else if(!__console_filter_warned__) {
|
|
234
|
+
__console_filter_warned__ = true;
|
|
235
|
+
console.warn(
|
|
236
|
+
"yui_dev: gobj-js has no set_console_log_filter (needs >= 7.13.6): " +
|
|
237
|
+
"the Periodic filter applies to this window only, not to the console"
|
|
238
|
+
);
|
|
239
|
+
}
|
|
189
240
|
}
|
|
190
241
|
|
|
242
|
+
/* The warning above is worth saying, and worth saying ONCE: this runs on
|
|
243
|
+
* every Output change and every filter toggle. */
|
|
244
|
+
let __console_filter_warned__ = false;
|
|
245
|
+
|
|
191
246
|
/* Emit one inter-event traffic line to the browser console (routes
|
|
192
247
|
* "console" and "both"). gobj-js only knows about framework logs, not this
|
|
193
248
|
* traffic feed, so the console mirror is produced here. */
|
|
@@ -240,6 +295,12 @@ function toggle_pref(key, def)
|
|
|
240
295
|
{
|
|
241
296
|
let v = dev_num(key, def) ? 0 : 1;
|
|
242
297
|
kw_set_local_storage_value(key, v);
|
|
298
|
+
/* The console filter closes over dev_hide_periodic(), which reads the
|
|
299
|
+
* stored value on each call, so it follows this on its own. Re-pushed
|
|
300
|
+
* anyway: a filter installed before this module reached apply_dev_traces
|
|
301
|
+
* is the difference between the console obeying the chip and ignoring
|
|
302
|
+
* it, and one assignment is cheaper than that class of bug. */
|
|
303
|
+
apply_console_route();
|
|
243
304
|
rerender_all();
|
|
244
305
|
refresh_dev_chrome();
|
|
245
306
|
}
|
|
@@ -300,7 +361,21 @@ function entry_hidden(e, ctx)
|
|
|
300
361
|
{
|
|
301
362
|
if(e.kind === "log") {
|
|
302
363
|
/* Mirrored console logs respect the search box only — not the
|
|
303
|
-
* in/out/err
|
|
364
|
+
* in/out/err traffic filters, which are about DIRECTION and a
|
|
365
|
+
* log has none.
|
|
366
|
+
*
|
|
367
|
+
* The PERIODIC filter does reach them, and it has to: once
|
|
368
|
+
* Automata is on, the machine trace is what floods this
|
|
369
|
+
* window, and every one of those lines arrives here as a log.
|
|
370
|
+
* It applies to a machine TRANSITION only, and never above
|
|
371
|
+
* debug level — see log_is_periodic(). By NAME and not by
|
|
372
|
+
* count, unlike the traffic half below: a busy FSM crosses
|
|
373
|
+
* the recurrence threshold on almost every event within
|
|
374
|
+
* seconds, so counting here would empty the window of the
|
|
375
|
+
* very transitions somebody turned Automata on to read. */
|
|
376
|
+
if(ctx.hide_periodic && e.periodic) {
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
304
379
|
return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
|
|
305
380
|
}
|
|
306
381
|
if(e.dir === 1 && !ctx.out) {
|
|
@@ -1031,10 +1106,16 @@ function info_log(level, msg, hora)
|
|
|
1031
1106
|
} else {
|
|
1032
1107
|
text = is_string(msg) ? msg : String(msg);
|
|
1033
1108
|
}
|
|
1109
|
+
/* A machine line is signed by its EVENT, not by "log:debug":
|
|
1110
|
+
* that is what makes a hundred EV_TIMEOUTs ONE recurring
|
|
1111
|
+
* thing the filter and the counter can see, instead of a
|
|
1112
|
+
* hundred anonymous debug lines they cannot tell apart. */
|
|
1034
1113
|
let entry = {
|
|
1035
1114
|
kind: "log", level: lvl, text: text,
|
|
1036
1115
|
dir: 0, size: 0, ts: traffic_now(),
|
|
1037
|
-
sig:
|
|
1116
|
+
sig: log_signature(lvl, text),
|
|
1117
|
+
periodic: log_is_periodic(lvl, text),
|
|
1118
|
+
hay: (lvl + " " + text).toLowerCase(), $node: null,
|
|
1038
1119
|
};
|
|
1039
1120
|
TRAFFIC_LOG.push(entry);
|
|
1040
1121
|
if(TRAFFIC_LOG.length > TRAFFIC_MAX) {
|
|
@@ -1384,11 +1465,12 @@ function build_control_bar()
|
|
|
1384
1465
|
|
|
1385
1466
|
let periodic_chip = ['button', {
|
|
1386
1467
|
class: 'YDEV_CHIP', 'data-toggle': 'periodic', type: 'button',
|
|
1387
|
-
title: 'Hide
|
|
1468
|
+
title: 'Hide the timers: EV_TIMEOUT / PERIODIC / HEARTBEAT / PING ' +
|
|
1469
|
+
'transitions, and recurring traffic. On by default',
|
|
1388
1470
|
}, '⊘ Periodic', {
|
|
1389
1471
|
click: (ev) => {
|
|
1390
1472
|
ev.stopPropagation();
|
|
1391
|
-
toggle_pref('dev_hide_periodic',
|
|
1473
|
+
toggle_pref('dev_hide_periodic', HIDE_PERIODIC_DEFAULT);
|
|
1392
1474
|
}
|
|
1393
1475
|
}];
|
|
1394
1476
|
|