@yuneta/gobj-ui 7.25.20 → 7.25.22
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 +13 -0
- package/dist/gobj-ui.cjs.js +125 -14
- package/dist/gobj-ui.es.js +125 -14
- package/package.json +2 -2
- package/src/c_yui_treedb_graph.js +94 -6
- package/src/c_yui_treedb_topics.js +88 -5
- package/src/treedb_json_drill.wiring.test.js +348 -0
package/README.md
CHANGED
|
@@ -593,6 +593,19 @@ Two layout facts the browser taught this component, both worth keeping:
|
|
|
593
593
|
- Output event: `EV_EXPAND_PATH {path, size}` (`EVF_OUTPUT_EVENT`) — the parent
|
|
594
594
|
must declare it in its own FSM (CHILD subscription model), and ANSWER it: the
|
|
595
595
|
stub shows "loading" until `EV_SUBTREE_LOADED` or `EV_SUBTREE_ERROR` arrives.
|
|
596
|
+
**Every** way the drill can fail is an answer: a request asked out of
|
|
597
|
+
session, or refused by the transport on its way out, never reaches the
|
|
598
|
+
backend, so no answer will ever come back for it -- the host answers it
|
|
599
|
+
itself (`C_YUI_TREEDB_TOPICS` / `C_YUI_TREEDB_GRAPH` since `7.25.21`: `i18n:
|
|
600
|
+
"no session"`, or the transport's refusal as `error`). So is a drill
|
|
601
|
+
already SENT when the session drops: a transport answers nothing in flight
|
|
602
|
+
on a close, so the host keeps the paths it asked and, on the disconnect
|
|
603
|
+
edge (`EV_TRANSPORT_STATE` or the shell's `EV_CONNECTION_STATE`, both
|
|
604
|
+
`{connected: false}`), answers each one `i18n: "the connection dropped"`
|
|
605
|
+
(since `7.25.22`); a failure that lands after that answer is dropped with
|
|
606
|
+
a warning, not shown twice. While a path is pending the viewer ignores
|
|
607
|
+
every click on it, so a drill left unanswered is a dead stub for the life
|
|
608
|
+
of the viewer.
|
|
596
609
|
A host that cannot read a subtree says so with the error, as
|
|
597
610
|
`C_YUI_TREEDB_TOPIC_WITH_FORM` does for its schema, cell and table viewers
|
|
598
611
|
since `7.25.19` (before, it declared nothing: *"Event NOT DEFINED"*):
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -23791,7 +23791,8 @@ var PRIVATE_DATA$6 = {
|
|
|
23791
23791
|
schema_gobj: null,
|
|
23792
23792
|
json_gobj: null,
|
|
23793
23793
|
json_win: null,
|
|
23794
|
-
json_modal: null
|
|
23794
|
+
json_modal: null,
|
|
23795
|
+
drills_in_flight: null
|
|
23795
23796
|
};
|
|
23796
23797
|
var __gclass__$6 = null;
|
|
23797
23798
|
/******************************
|
|
@@ -23804,6 +23805,7 @@ function mt_create$6(gobj) {
|
|
|
23804
23805
|
gobj.priv.form_writes = {};
|
|
23805
23806
|
gobj.priv.reread_on_session = false;
|
|
23806
23807
|
gobj.priv.conn_shell = null;
|
|
23808
|
+
gobj.priv.drills_in_flight = /* @__PURE__ */ new Set();
|
|
23807
23809
|
build_ui$6(gobj);
|
|
23808
23810
|
let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
|
|
23809
23811
|
if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
|
|
@@ -25102,6 +25104,7 @@ function close_json_viewer$1(gobj) {
|
|
|
25102
25104
|
priv.json_gobj = null;
|
|
25103
25105
|
priv.json_win = null;
|
|
25104
25106
|
priv.json_modal = null;
|
|
25107
|
+
priv.drills_in_flight.clear();
|
|
25105
25108
|
if (win && (0, _yuneta_gobj_js.is_gobj)(win)) try {
|
|
25106
25109
|
if ((0, _yuneta_gobj_js.gobj_is_running)(win)) (0, _yuneta_gobj_js.gobj_stop)(win);
|
|
25107
25110
|
(0, _yuneta_gobj_js.gobj_destroy)(win);
|
|
@@ -25136,6 +25139,27 @@ function request_schema_file(gobj) {
|
|
|
25136
25139
|
if (ret) (0, _yuneta_gobj_js.log_error)(ret);
|
|
25137
25140
|
}
|
|
25138
25141
|
/************************************************************
|
|
25142
|
+
* A drill (or the whole document) that will get no answer from the
|
|
25143
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
25144
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
25145
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
25146
|
+
************************************************************/
|
|
25147
|
+
function answer_print_tranger_refused$1(gobj, path, answer) {
|
|
25148
|
+
let jv = gobj.priv.json_gobj;
|
|
25149
|
+
if (!jv || !(0, _yuneta_gobj_js.is_gobj)(jv) || (0, _yuneta_gobj_js.gobj_is_destroying)(jv)) return;
|
|
25150
|
+
if (path) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
|
|
25151
|
+
else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t: i18next.t });
|
|
25152
|
+
}
|
|
25153
|
+
/************************************************************
|
|
25154
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
25155
|
+
* a command response or a number.
|
|
25156
|
+
************************************************************/
|
|
25157
|
+
function refusal_text$1(ret) {
|
|
25158
|
+
if (typeof ret === "string" && ret) return ret;
|
|
25159
|
+
if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
|
|
25160
|
+
return "print-tranger failed";
|
|
25161
|
+
}
|
|
25162
|
+
/************************************************************
|
|
25139
25163
|
* The whole tranger, for the raw-json viewer.
|
|
25140
25164
|
************************************************************/
|
|
25141
25165
|
function request_print_tranger$1(gobj, path) {
|
|
@@ -25143,11 +25167,12 @@ function request_print_tranger$1(gobj, path) {
|
|
|
25143
25167
|
let remote = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "gobj_remote_yuno");
|
|
25144
25168
|
if (!remote) {
|
|
25145
25169
|
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: No gobj_remote_yuno defined`);
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25170
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
|
|
25171
|
+
return;
|
|
25172
|
+
}
|
|
25173
|
+
if (!is_connected$1(gobj)) {
|
|
25174
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: print-tranger '${path || ""}' not asked: no session`);
|
|
25175
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
|
|
25151
25176
|
return;
|
|
25152
25177
|
}
|
|
25153
25178
|
let ret = (0, _yuneta_gobj_js.gobj_command)(remote, "print-tranger", {
|
|
@@ -25158,7 +25183,28 @@ function request_print_tranger$1(gobj, path) {
|
|
|
25158
25183
|
path: path || "",
|
|
25159
25184
|
__md_command__: { path: path || "" }
|
|
25160
25185
|
}, gobj);
|
|
25161
|
-
if (ret)
|
|
25186
|
+
if (ret) {
|
|
25187
|
+
(0, _yuneta_gobj_js.log_error)(ret);
|
|
25188
|
+
answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) });
|
|
25189
|
+
return;
|
|
25190
|
+
}
|
|
25191
|
+
if (path) priv.drills_in_flight.add(path);
|
|
25192
|
+
}
|
|
25193
|
+
/************************************************************
|
|
25194
|
+
* The session dropped: a drill already sent will never be answered
|
|
25195
|
+
* (the transport answers nothing in flight on a close), and its stub
|
|
25196
|
+
* would show "loading" for the life of the viewer. Answered here, so a
|
|
25197
|
+
* click once the session is back asks again. Idempotent: both edges of
|
|
25198
|
+
* one drop may arrive.
|
|
25199
|
+
************************************************************/
|
|
25200
|
+
function answer_drills_in_flight$1(gobj) {
|
|
25201
|
+
let priv = gobj.priv;
|
|
25202
|
+
let paths = Array.from(priv.drills_in_flight);
|
|
25203
|
+
priv.drills_in_flight.clear();
|
|
25204
|
+
paths.forEach((path) => {
|
|
25205
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: transport closed, the drill '${path}' of the raw json is lost`);
|
|
25206
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "the connection dropped" });
|
|
25207
|
+
});
|
|
25162
25208
|
}
|
|
25163
25209
|
/***************************************************************
|
|
25164
25210
|
* The last gate before a write leaves for the yuno. The per-topic
|
|
@@ -25200,6 +25246,12 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
25200
25246
|
let jv = gobj.priv.json_gobj;
|
|
25201
25247
|
if (!jv || !(0, _yuneta_gobj_js.is_gobj)(jv) || (0, _yuneta_gobj_js.gobj_is_destroying)(jv)) return 0;
|
|
25202
25248
|
let path = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "path", "", 0);
|
|
25249
|
+
if (path) {
|
|
25250
|
+
if (!gobj.priv.drills_in_flight.delete(path) && result < 0) {
|
|
25251
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: late failure of the drill '${path}' dropped, it was already answered: ${comment || ""}`);
|
|
25252
|
+
return 0;
|
|
25253
|
+
}
|
|
25254
|
+
} else gobj.priv.drills_in_flight.clear();
|
|
25203
25255
|
if (result < 0) {
|
|
25204
25256
|
if (path) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", {
|
|
25205
25257
|
path,
|
|
@@ -25458,6 +25510,7 @@ function ac_transport_state$2(gobj, event, kw, src) {
|
|
|
25458
25510
|
refresh_toolbar_buttons(gobj, connected);
|
|
25459
25511
|
if (!connected) {
|
|
25460
25512
|
gobj.priv.reread_on_session = true;
|
|
25513
|
+
answer_drills_in_flight$1(gobj);
|
|
25461
25514
|
abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
|
|
25462
25515
|
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
|
|
25463
25516
|
answer_form_write(gobj, get_gobj_formtable(gobj, w.topic_name), w.form_write, false);
|
|
@@ -25851,6 +25904,7 @@ function ac_json_closed$1(gobj, event, kw, src) {
|
|
|
25851
25904
|
priv.json_gobj = null;
|
|
25852
25905
|
priv.json_win = null;
|
|
25853
25906
|
priv.json_modal = null;
|
|
25907
|
+
priv.drills_in_flight.clear();
|
|
25854
25908
|
if (jv && (0, _yuneta_gobj_js.is_gobj)(jv)) try {
|
|
25855
25909
|
(0, _yuneta_gobj_js.gobj_stop)(jv);
|
|
25856
25910
|
(0, _yuneta_gobj_js.gobj_destroy)(jv);
|
|
@@ -67443,6 +67497,7 @@ var PRIVATE_DATA$3 = {
|
|
|
67443
67497
|
json_gobj: null,
|
|
67444
67498
|
json_win: null,
|
|
67445
67499
|
json_modal: null,
|
|
67500
|
+
drills_in_flight: null,
|
|
67446
67501
|
with_treedb_tables: false,
|
|
67447
67502
|
canvas_id: null,
|
|
67448
67503
|
operation_mode: null,
|
|
@@ -67462,6 +67517,7 @@ var __gclass__$3 = null;
|
|
|
67462
67517
|
***************************************************************/
|
|
67463
67518
|
function mt_create$3(gobj) {
|
|
67464
67519
|
let priv = gobj.priv;
|
|
67520
|
+
priv.drills_in_flight = /* @__PURE__ */ new Set();
|
|
67465
67521
|
let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
|
|
67466
67522
|
if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
|
|
67467
67523
|
(0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
|
|
@@ -67620,9 +67676,13 @@ function refresh_raw_json_button(gobj, connected) {
|
|
|
67620
67676
|
* "Raw JSON" button disables the moment the session drops and re-enables on
|
|
67621
67677
|
* reconnect. The library view must not subscribe to the C_IEVENT_CLI itself
|
|
67622
67678
|
* (that forwards the subscription upstream and breaks the session).
|
|
67679
|
+
* On the drop, the drills of the raw-json viewer still in flight are
|
|
67680
|
+
* answered: the transport answers nothing in flight on a close.
|
|
67623
67681
|
************************************************************/
|
|
67624
67682
|
function ac_transport_state$1(gobj, event, kw, src) {
|
|
67625
|
-
|
|
67683
|
+
let connected = !!(kw && kw.connected);
|
|
67684
|
+
refresh_raw_json_button(gobj, connected);
|
|
67685
|
+
if (!connected) answer_drills_in_flight(gobj);
|
|
67626
67686
|
return 0;
|
|
67627
67687
|
}
|
|
67628
67688
|
/************************************************************
|
|
@@ -68325,6 +68385,7 @@ function close_json_viewer(gobj) {
|
|
|
68325
68385
|
priv.json_gobj = null;
|
|
68326
68386
|
priv.json_win = null;
|
|
68327
68387
|
priv.json_modal = null;
|
|
68388
|
+
priv.drills_in_flight.clear();
|
|
68328
68389
|
if (win && (0, _yuneta_gobj_js.is_gobj)(win)) try {
|
|
68329
68390
|
if ((0, _yuneta_gobj_js.gobj_is_running)(win)) (0, _yuneta_gobj_js.gobj_stop)(win);
|
|
68330
68391
|
(0, _yuneta_gobj_js.gobj_destroy)(win);
|
|
@@ -68344,6 +68405,27 @@ function close_json_viewer(gobj) {
|
|
|
68344
68405
|
}
|
|
68345
68406
|
}
|
|
68346
68407
|
/************************************************************
|
|
68408
|
+
* A drill (or the whole document) that will get no answer from the
|
|
68409
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
68410
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
68411
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
68412
|
+
************************************************************/
|
|
68413
|
+
function answer_print_tranger_refused(gobj, path, answer) {
|
|
68414
|
+
let jv = gobj.priv.json_gobj;
|
|
68415
|
+
if (!jv || !(0, _yuneta_gobj_js.is_gobj)(jv) || (0, _yuneta_gobj_js.gobj_is_destroying)(jv)) return;
|
|
68416
|
+
if (path) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
|
|
68417
|
+
else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t: i18next.t });
|
|
68418
|
+
}
|
|
68419
|
+
/************************************************************
|
|
68420
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
68421
|
+
* a command response or a number.
|
|
68422
|
+
************************************************************/
|
|
68423
|
+
function refusal_text(ret) {
|
|
68424
|
+
if (typeof ret === "string" && ret) return ret;
|
|
68425
|
+
if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
|
|
68426
|
+
return "print-tranger failed";
|
|
68427
|
+
}
|
|
68428
|
+
/************************************************************
|
|
68347
68429
|
* Fetch the treedb's raw tranger (or one subtree when `path` is set) as
|
|
68348
68430
|
* bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
|
|
68349
68431
|
* small payload of `__collapsed__` stubs the viewer expands on demand.
|
|
@@ -68352,11 +68434,12 @@ function request_print_tranger(gobj, path) {
|
|
|
68352
68434
|
let priv = gobj.priv;
|
|
68353
68435
|
if (!priv.gobj_remote_yuno) {
|
|
68354
68436
|
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: No gobj_remote_yuno defined`);
|
|
68355
|
-
|
|
68356
|
-
|
|
68357
|
-
|
|
68358
|
-
|
|
68359
|
-
|
|
68437
|
+
answer_print_tranger_refused(gobj, path, { i18n: "no session" });
|
|
68438
|
+
return;
|
|
68439
|
+
}
|
|
68440
|
+
if (!is_connected(gobj)) {
|
|
68441
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: print-tranger '${path || ""}' not asked: no session`);
|
|
68442
|
+
answer_print_tranger_refused(gobj, path, { i18n: "no session" });
|
|
68360
68443
|
return;
|
|
68361
68444
|
}
|
|
68362
68445
|
let ret = (0, _yuneta_gobj_js.gobj_command)(priv.gobj_remote_yuno, "print-tranger", {
|
|
@@ -68367,7 +68450,28 @@ function request_print_tranger(gobj, path) {
|
|
|
68367
68450
|
path: path || "",
|
|
68368
68451
|
__md_command__: { path: path || "" }
|
|
68369
68452
|
}, gobj);
|
|
68370
|
-
if (ret)
|
|
68453
|
+
if (ret) {
|
|
68454
|
+
(0, _yuneta_gobj_js.log_error)(ret);
|
|
68455
|
+
answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) });
|
|
68456
|
+
return;
|
|
68457
|
+
}
|
|
68458
|
+
if (path) priv.drills_in_flight.add(path);
|
|
68459
|
+
}
|
|
68460
|
+
/************************************************************
|
|
68461
|
+
* The session dropped: a drill already sent will never be answered
|
|
68462
|
+
* (the transport answers nothing in flight on a close), and its stub
|
|
68463
|
+
* would show "loading" for the life of the viewer. Answered here, so a
|
|
68464
|
+
* click once the session is back asks again. Idempotent: both edges of
|
|
68465
|
+
* one drop may arrive.
|
|
68466
|
+
************************************************************/
|
|
68467
|
+
function answer_drills_in_flight(gobj) {
|
|
68468
|
+
let priv = gobj.priv;
|
|
68469
|
+
let paths = Array.from(priv.drills_in_flight);
|
|
68470
|
+
priv.drills_in_flight.clear();
|
|
68471
|
+
paths.forEach((path) => {
|
|
68472
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: transport closed, the drill '${path}' of the raw json is lost`);
|
|
68473
|
+
answer_print_tranger_refused(gobj, path, { i18n: "the connection dropped" });
|
|
68474
|
+
});
|
|
68371
68475
|
}
|
|
68372
68476
|
/************************************************************
|
|
68373
68477
|
* Populate layout dropdown from child's available layouts
|
|
@@ -68745,6 +68849,12 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
|
|
|
68745
68849
|
let jv = priv.json_gobj;
|
|
68746
68850
|
if (!jv || !(0, _yuneta_gobj_js.is_gobj)(jv) || (0, _yuneta_gobj_js.gobj_is_destroying)(jv)) return 0;
|
|
68747
68851
|
let path = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "path", "", 0);
|
|
68852
|
+
if (path) {
|
|
68853
|
+
if (!priv.drills_in_flight.delete(path) && result < 0) {
|
|
68854
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: late failure of the drill '${path}' dropped, it was already answered: ${comment || ""}`);
|
|
68855
|
+
return 0;
|
|
68856
|
+
}
|
|
68857
|
+
} else priv.drills_in_flight.clear();
|
|
68748
68858
|
if (result < 0) {
|
|
68749
68859
|
if (path) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", {
|
|
68750
68860
|
path,
|
|
@@ -68931,6 +69041,7 @@ function ac_json_closed(gobj, event, kw, src) {
|
|
|
68931
69041
|
priv.json_gobj = null;
|
|
68932
69042
|
priv.json_win = null;
|
|
68933
69043
|
priv.json_modal = null;
|
|
69044
|
+
priv.drills_in_flight.clear();
|
|
68934
69045
|
if (jv && (0, _yuneta_gobj_js.is_gobj)(jv)) try {
|
|
68935
69046
|
(0, _yuneta_gobj_js.gobj_stop)(jv);
|
|
68936
69047
|
(0, _yuneta_gobj_js.gobj_destroy)(jv);
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -23786,7 +23786,8 @@ var PRIVATE_DATA$6 = {
|
|
|
23786
23786
|
schema_gobj: null,
|
|
23787
23787
|
json_gobj: null,
|
|
23788
23788
|
json_win: null,
|
|
23789
|
-
json_modal: null
|
|
23789
|
+
json_modal: null,
|
|
23790
|
+
drills_in_flight: null
|
|
23790
23791
|
};
|
|
23791
23792
|
var __gclass__$6 = null;
|
|
23792
23793
|
/******************************
|
|
@@ -23799,6 +23800,7 @@ function mt_create$6(gobj) {
|
|
|
23799
23800
|
gobj.priv.form_writes = {};
|
|
23800
23801
|
gobj.priv.reread_on_session = false;
|
|
23801
23802
|
gobj.priv.conn_shell = null;
|
|
23803
|
+
gobj.priv.drills_in_flight = /* @__PURE__ */ new Set();
|
|
23802
23804
|
build_ui$6(gobj);
|
|
23803
23805
|
let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
|
|
23804
23806
|
if (!subscriber) subscriber = gobj_parent(gobj);
|
|
@@ -25097,6 +25099,7 @@ function close_json_viewer$1(gobj) {
|
|
|
25097
25099
|
priv.json_gobj = null;
|
|
25098
25100
|
priv.json_win = null;
|
|
25099
25101
|
priv.json_modal = null;
|
|
25102
|
+
priv.drills_in_flight.clear();
|
|
25100
25103
|
if (win && is_gobj(win)) try {
|
|
25101
25104
|
if (gobj_is_running(win)) gobj_stop(win);
|
|
25102
25105
|
gobj_destroy(win);
|
|
@@ -25131,6 +25134,27 @@ function request_schema_file(gobj) {
|
|
|
25131
25134
|
if (ret) log_error(ret);
|
|
25132
25135
|
}
|
|
25133
25136
|
/************************************************************
|
|
25137
|
+
* A drill (or the whole document) that will get no answer from the
|
|
25138
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
25139
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
25140
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
25141
|
+
************************************************************/
|
|
25142
|
+
function answer_print_tranger_refused$1(gobj, path, answer) {
|
|
25143
|
+
let jv = gobj.priv.json_gobj;
|
|
25144
|
+
if (!jv || !is_gobj(jv) || gobj_is_destroying(jv)) return;
|
|
25145
|
+
if (path) gobj_send_event(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
|
|
25146
|
+
else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t });
|
|
25147
|
+
}
|
|
25148
|
+
/************************************************************
|
|
25149
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
25150
|
+
* a command response or a number.
|
|
25151
|
+
************************************************************/
|
|
25152
|
+
function refusal_text$1(ret) {
|
|
25153
|
+
if (typeof ret === "string" && ret) return ret;
|
|
25154
|
+
if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
|
|
25155
|
+
return "print-tranger failed";
|
|
25156
|
+
}
|
|
25157
|
+
/************************************************************
|
|
25134
25158
|
* The whole tranger, for the raw-json viewer.
|
|
25135
25159
|
************************************************************/
|
|
25136
25160
|
function request_print_tranger$1(gobj, path) {
|
|
@@ -25138,11 +25162,12 @@ function request_print_tranger$1(gobj, path) {
|
|
|
25138
25162
|
let remote = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
|
|
25139
25163
|
if (!remote) {
|
|
25140
25164
|
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
}
|
|
25165
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
|
|
25166
|
+
return;
|
|
25167
|
+
}
|
|
25168
|
+
if (!is_connected$1(gobj)) {
|
|
25169
|
+
log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' not asked: no session`);
|
|
25170
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
|
|
25146
25171
|
return;
|
|
25147
25172
|
}
|
|
25148
25173
|
let ret = gobj_command(remote, "print-tranger", {
|
|
@@ -25153,7 +25178,28 @@ function request_print_tranger$1(gobj, path) {
|
|
|
25153
25178
|
path: path || "",
|
|
25154
25179
|
__md_command__: { path: path || "" }
|
|
25155
25180
|
}, gobj);
|
|
25156
|
-
if (ret)
|
|
25181
|
+
if (ret) {
|
|
25182
|
+
log_error(ret);
|
|
25183
|
+
answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) });
|
|
25184
|
+
return;
|
|
25185
|
+
}
|
|
25186
|
+
if (path) priv.drills_in_flight.add(path);
|
|
25187
|
+
}
|
|
25188
|
+
/************************************************************
|
|
25189
|
+
* The session dropped: a drill already sent will never be answered
|
|
25190
|
+
* (the transport answers nothing in flight on a close), and its stub
|
|
25191
|
+
* would show "loading" for the life of the viewer. Answered here, so a
|
|
25192
|
+
* click once the session is back asks again. Idempotent: both edges of
|
|
25193
|
+
* one drop may arrive.
|
|
25194
|
+
************************************************************/
|
|
25195
|
+
function answer_drills_in_flight$1(gobj) {
|
|
25196
|
+
let priv = gobj.priv;
|
|
25197
|
+
let paths = Array.from(priv.drills_in_flight);
|
|
25198
|
+
priv.drills_in_flight.clear();
|
|
25199
|
+
paths.forEach((path) => {
|
|
25200
|
+
log_warning(`${gobj_short_name(gobj)}: transport closed, the drill '${path}' of the raw json is lost`);
|
|
25201
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "the connection dropped" });
|
|
25202
|
+
});
|
|
25157
25203
|
}
|
|
25158
25204
|
/***************************************************************
|
|
25159
25205
|
* The last gate before a write leaves for the yuno. The per-topic
|
|
@@ -25195,6 +25241,12 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
25195
25241
|
let jv = gobj.priv.json_gobj;
|
|
25196
25242
|
if (!jv || !is_gobj(jv) || gobj_is_destroying(jv)) return 0;
|
|
25197
25243
|
let path = kw_get_str(gobj, kw_command, "path", "", 0);
|
|
25244
|
+
if (path) {
|
|
25245
|
+
if (!gobj.priv.drills_in_flight.delete(path) && result < 0) {
|
|
25246
|
+
log_warning(`${gobj_short_name(gobj)}: late failure of the drill '${path}' dropped, it was already answered: ${comment || ""}`);
|
|
25247
|
+
return 0;
|
|
25248
|
+
}
|
|
25249
|
+
} else gobj.priv.drills_in_flight.clear();
|
|
25198
25250
|
if (result < 0) {
|
|
25199
25251
|
if (path) gobj_send_event(jv, "EV_SUBTREE_ERROR", {
|
|
25200
25252
|
path,
|
|
@@ -25453,6 +25505,7 @@ function ac_transport_state$2(gobj, event, kw, src) {
|
|
|
25453
25505
|
refresh_toolbar_buttons(gobj, connected);
|
|
25454
25506
|
if (!connected) {
|
|
25455
25507
|
gobj.priv.reread_on_session = true;
|
|
25508
|
+
answer_drills_in_flight$1(gobj);
|
|
25456
25509
|
abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
|
|
25457
25510
|
log_warning(`${gobj_short_name(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
|
|
25458
25511
|
answer_form_write(gobj, get_gobj_formtable(gobj, w.topic_name), w.form_write, false);
|
|
@@ -25846,6 +25899,7 @@ function ac_json_closed$1(gobj, event, kw, src) {
|
|
|
25846
25899
|
priv.json_gobj = null;
|
|
25847
25900
|
priv.json_win = null;
|
|
25848
25901
|
priv.json_modal = null;
|
|
25902
|
+
priv.drills_in_flight.clear();
|
|
25849
25903
|
if (jv && is_gobj(jv)) try {
|
|
25850
25904
|
gobj_stop(jv);
|
|
25851
25905
|
gobj_destroy(jv);
|
|
@@ -67438,6 +67492,7 @@ var PRIVATE_DATA$3 = {
|
|
|
67438
67492
|
json_gobj: null,
|
|
67439
67493
|
json_win: null,
|
|
67440
67494
|
json_modal: null,
|
|
67495
|
+
drills_in_flight: null,
|
|
67441
67496
|
with_treedb_tables: false,
|
|
67442
67497
|
canvas_id: null,
|
|
67443
67498
|
operation_mode: null,
|
|
@@ -67457,6 +67512,7 @@ var __gclass__$3 = null;
|
|
|
67457
67512
|
***************************************************************/
|
|
67458
67513
|
function mt_create$3(gobj) {
|
|
67459
67514
|
let priv = gobj.priv;
|
|
67515
|
+
priv.drills_in_flight = /* @__PURE__ */ new Set();
|
|
67460
67516
|
let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
|
|
67461
67517
|
if (!subscriber) subscriber = gobj_parent(gobj);
|
|
67462
67518
|
gobj_subscribe_event(gobj, null, {}, subscriber);
|
|
@@ -67615,9 +67671,13 @@ function refresh_raw_json_button(gobj, connected) {
|
|
|
67615
67671
|
* "Raw JSON" button disables the moment the session drops and re-enables on
|
|
67616
67672
|
* reconnect. The library view must not subscribe to the C_IEVENT_CLI itself
|
|
67617
67673
|
* (that forwards the subscription upstream and breaks the session).
|
|
67674
|
+
* On the drop, the drills of the raw-json viewer still in flight are
|
|
67675
|
+
* answered: the transport answers nothing in flight on a close.
|
|
67618
67676
|
************************************************************/
|
|
67619
67677
|
function ac_transport_state$1(gobj, event, kw, src) {
|
|
67620
|
-
|
|
67678
|
+
let connected = !!(kw && kw.connected);
|
|
67679
|
+
refresh_raw_json_button(gobj, connected);
|
|
67680
|
+
if (!connected) answer_drills_in_flight(gobj);
|
|
67621
67681
|
return 0;
|
|
67622
67682
|
}
|
|
67623
67683
|
/************************************************************
|
|
@@ -68320,6 +68380,7 @@ function close_json_viewer(gobj) {
|
|
|
68320
68380
|
priv.json_gobj = null;
|
|
68321
68381
|
priv.json_win = null;
|
|
68322
68382
|
priv.json_modal = null;
|
|
68383
|
+
priv.drills_in_flight.clear();
|
|
68323
68384
|
if (win && is_gobj(win)) try {
|
|
68324
68385
|
if (gobj_is_running(win)) gobj_stop(win);
|
|
68325
68386
|
gobj_destroy(win);
|
|
@@ -68339,6 +68400,27 @@ function close_json_viewer(gobj) {
|
|
|
68339
68400
|
}
|
|
68340
68401
|
}
|
|
68341
68402
|
/************************************************************
|
|
68403
|
+
* A drill (or the whole document) that will get no answer from the
|
|
68404
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
68405
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
68406
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
68407
|
+
************************************************************/
|
|
68408
|
+
function answer_print_tranger_refused(gobj, path, answer) {
|
|
68409
|
+
let jv = gobj.priv.json_gobj;
|
|
68410
|
+
if (!jv || !is_gobj(jv) || gobj_is_destroying(jv)) return;
|
|
68411
|
+
if (path) gobj_send_event(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
|
|
68412
|
+
else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t });
|
|
68413
|
+
}
|
|
68414
|
+
/************************************************************
|
|
68415
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
68416
|
+
* a command response or a number.
|
|
68417
|
+
************************************************************/
|
|
68418
|
+
function refusal_text(ret) {
|
|
68419
|
+
if (typeof ret === "string" && ret) return ret;
|
|
68420
|
+
if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
|
|
68421
|
+
return "print-tranger failed";
|
|
68422
|
+
}
|
|
68423
|
+
/************************************************************
|
|
68342
68424
|
* Fetch the treedb's raw tranger (or one subtree when `path` is set) as
|
|
68343
68425
|
* bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
|
|
68344
68426
|
* small payload of `__collapsed__` stubs the viewer expands on demand.
|
|
@@ -68347,11 +68429,12 @@ function request_print_tranger(gobj, path) {
|
|
|
68347
68429
|
let priv = gobj.priv;
|
|
68348
68430
|
if (!priv.gobj_remote_yuno) {
|
|
68349
68431
|
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
68350
|
-
|
|
68351
|
-
|
|
68352
|
-
|
|
68353
|
-
|
|
68354
|
-
}
|
|
68432
|
+
answer_print_tranger_refused(gobj, path, { i18n: "no session" });
|
|
68433
|
+
return;
|
|
68434
|
+
}
|
|
68435
|
+
if (!is_connected(gobj)) {
|
|
68436
|
+
log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' not asked: no session`);
|
|
68437
|
+
answer_print_tranger_refused(gobj, path, { i18n: "no session" });
|
|
68355
68438
|
return;
|
|
68356
68439
|
}
|
|
68357
68440
|
let ret = gobj_command(priv.gobj_remote_yuno, "print-tranger", {
|
|
@@ -68362,7 +68445,28 @@ function request_print_tranger(gobj, path) {
|
|
|
68362
68445
|
path: path || "",
|
|
68363
68446
|
__md_command__: { path: path || "" }
|
|
68364
68447
|
}, gobj);
|
|
68365
|
-
if (ret)
|
|
68448
|
+
if (ret) {
|
|
68449
|
+
log_error(ret);
|
|
68450
|
+
answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) });
|
|
68451
|
+
return;
|
|
68452
|
+
}
|
|
68453
|
+
if (path) priv.drills_in_flight.add(path);
|
|
68454
|
+
}
|
|
68455
|
+
/************************************************************
|
|
68456
|
+
* The session dropped: a drill already sent will never be answered
|
|
68457
|
+
* (the transport answers nothing in flight on a close), and its stub
|
|
68458
|
+
* would show "loading" for the life of the viewer. Answered here, so a
|
|
68459
|
+
* click once the session is back asks again. Idempotent: both edges of
|
|
68460
|
+
* one drop may arrive.
|
|
68461
|
+
************************************************************/
|
|
68462
|
+
function answer_drills_in_flight(gobj) {
|
|
68463
|
+
let priv = gobj.priv;
|
|
68464
|
+
let paths = Array.from(priv.drills_in_flight);
|
|
68465
|
+
priv.drills_in_flight.clear();
|
|
68466
|
+
paths.forEach((path) => {
|
|
68467
|
+
log_warning(`${gobj_short_name(gobj)}: transport closed, the drill '${path}' of the raw json is lost`);
|
|
68468
|
+
answer_print_tranger_refused(gobj, path, { i18n: "the connection dropped" });
|
|
68469
|
+
});
|
|
68366
68470
|
}
|
|
68367
68471
|
/************************************************************
|
|
68368
68472
|
* Populate layout dropdown from child's available layouts
|
|
@@ -68740,6 +68844,12 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
|
|
|
68740
68844
|
let jv = priv.json_gobj;
|
|
68741
68845
|
if (!jv || !is_gobj(jv) || gobj_is_destroying(jv)) return 0;
|
|
68742
68846
|
let path = kw_get_str(gobj, kw_command, "path", "", 0);
|
|
68847
|
+
if (path) {
|
|
68848
|
+
if (!priv.drills_in_flight.delete(path) && result < 0) {
|
|
68849
|
+
log_warning(`${gobj_short_name(gobj)}: late failure of the drill '${path}' dropped, it was already answered: ${comment || ""}`);
|
|
68850
|
+
return 0;
|
|
68851
|
+
}
|
|
68852
|
+
} else priv.drills_in_flight.clear();
|
|
68743
68853
|
if (result < 0) {
|
|
68744
68854
|
if (path) gobj_send_event(jv, "EV_SUBTREE_ERROR", {
|
|
68745
68855
|
path,
|
|
@@ -68926,6 +69036,7 @@ function ac_json_closed(gobj, event, kw, src) {
|
|
|
68926
69036
|
priv.json_gobj = null;
|
|
68927
69037
|
priv.json_win = null;
|
|
68928
69038
|
priv.json_modal = null;
|
|
69039
|
+
priv.drills_in_flight.clear();
|
|
68929
69040
|
if (jv && is_gobj(jv)) try {
|
|
68930
69041
|
gobj_stop(jv);
|
|
68931
69042
|
gobj_destroy(jv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuneta/gobj-ui",
|
|
3
|
-
"version": "7.25.
|
|
3
|
+
"version": "7.25.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/gobj-ui.cjs.js",
|
|
6
6
|
"module": "dist/gobj-ui.es.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@rollup/plugin-terser": "^1.0.0",
|
|
32
32
|
"@vitest/coverage-v8": "^4.1.10",
|
|
33
|
-
"@yuneta/gobj-js": "^7.25.
|
|
33
|
+
"@yuneta/gobj-js": "^7.25.8",
|
|
34
34
|
"vite": "^8.2.1",
|
|
35
35
|
"vitest": "^4.1.10"
|
|
36
36
|
},
|
|
@@ -183,6 +183,7 @@ let PRIVATE_DATA = {
|
|
|
183
183
|
json_gobj: null, /* C_YUI_JSON viewer of the raw tranger (or null) */
|
|
184
184
|
json_win: null, /* C_YUI_WINDOW hosting it, desktop (or null) */
|
|
185
185
|
json_modal: null, /* shell modal hosting it, mobile (or null) */
|
|
186
|
+
drills_in_flight: null, /* Set of the viewer's drilled paths sent, not answered */
|
|
186
187
|
with_treedb_tables: false,
|
|
187
188
|
canvas_id: null,
|
|
188
189
|
operation_mode: null,
|
|
@@ -214,6 +215,10 @@ function mt_create(gobj)
|
|
|
214
215
|
{
|
|
215
216
|
let priv = gobj.priv;
|
|
216
217
|
|
|
218
|
+
/* The drills of the raw-json viewer sent and not yet answered:
|
|
219
|
+
* answered on the disconnect edge (answer_drills_in_flight). */
|
|
220
|
+
priv.drills_in_flight = new Set();
|
|
221
|
+
|
|
217
222
|
/*
|
|
218
223
|
* CHILD subscription model
|
|
219
224
|
*/
|
|
@@ -474,10 +479,16 @@ function refresh_raw_json_button(gobj, connected)
|
|
|
474
479
|
* "Raw JSON" button disables the moment the session drops and re-enables on
|
|
475
480
|
* reconnect. The library view must not subscribe to the C_IEVENT_CLI itself
|
|
476
481
|
* (that forwards the subscription upstream and breaks the session).
|
|
482
|
+
* On the drop, the drills of the raw-json viewer still in flight are
|
|
483
|
+
* answered: the transport answers nothing in flight on a close.
|
|
477
484
|
************************************************************/
|
|
478
485
|
function ac_transport_state(gobj, event, kw, src)
|
|
479
486
|
{
|
|
480
|
-
|
|
487
|
+
let connected = !!(kw && kw.connected);
|
|
488
|
+
refresh_raw_json_button(gobj, connected);
|
|
489
|
+
if(!connected) {
|
|
490
|
+
answer_drills_in_flight(gobj);
|
|
491
|
+
}
|
|
481
492
|
return 0;
|
|
482
493
|
}
|
|
483
494
|
|
|
@@ -1270,6 +1281,7 @@ function close_json_viewer(gobj)
|
|
|
1270
1281
|
priv.json_gobj = null;
|
|
1271
1282
|
priv.json_win = null;
|
|
1272
1283
|
priv.json_modal = null;
|
|
1284
|
+
priv.drills_in_flight.clear();
|
|
1273
1285
|
|
|
1274
1286
|
if(win && is_gobj(win)) {
|
|
1275
1287
|
try {
|
|
@@ -1312,6 +1324,42 @@ function close_json_viewer(gobj)
|
|
|
1312
1324
|
}
|
|
1313
1325
|
}
|
|
1314
1326
|
|
|
1327
|
+
/************************************************************
|
|
1328
|
+
* A drill (or the whole document) that will get no answer from the
|
|
1329
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
1330
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
1331
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
1332
|
+
************************************************************/
|
|
1333
|
+
function answer_print_tranger_refused(gobj, path, answer)
|
|
1334
|
+
{
|
|
1335
|
+
let jv = gobj.priv.json_gobj;
|
|
1336
|
+
if(!jv || !is_gobj(jv) || gobj_is_destroying(jv)) {
|
|
1337
|
+
return; /* no viewer to answer: the refusal is already logged */
|
|
1338
|
+
}
|
|
1339
|
+
if(path) {
|
|
1340
|
+
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
1341
|
+
Object.assign({path: path}, answer), gobj);
|
|
1342
|
+
} else {
|
|
1343
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
1344
|
+
answer.i18n || answer.error, {t: t});
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/************************************************************
|
|
1349
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
1350
|
+
* a command response or a number.
|
|
1351
|
+
************************************************************/
|
|
1352
|
+
function refusal_text(ret)
|
|
1353
|
+
{
|
|
1354
|
+
if(typeof ret === "string" && ret) {
|
|
1355
|
+
return ret;
|
|
1356
|
+
}
|
|
1357
|
+
if(ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) {
|
|
1358
|
+
return ret.comment;
|
|
1359
|
+
}
|
|
1360
|
+
return "print-tranger failed";
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1315
1363
|
/************************************************************
|
|
1316
1364
|
* Fetch the treedb's raw tranger (or one subtree when `path` is set) as
|
|
1317
1365
|
* bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
|
|
@@ -1322,11 +1370,13 @@ function request_print_tranger(gobj, path)
|
|
|
1322
1370
|
let priv = gobj.priv;
|
|
1323
1371
|
if(!priv.gobj_remote_yuno) {
|
|
1324
1372
|
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
}
|
|
1373
|
+
answer_print_tranger_refused(gobj, path, {i18n: "no session"});
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
if(!is_connected(gobj)) {
|
|
1377
|
+
log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' ` +
|
|
1378
|
+
`not asked: no session`);
|
|
1379
|
+
answer_print_tranger_refused(gobj, path, {i18n: "no session"});
|
|
1330
1380
|
return;
|
|
1331
1381
|
}
|
|
1332
1382
|
let ret = gobj_command(priv.gobj_remote_yuno, "print-tranger",
|
|
@@ -1342,9 +1392,33 @@ function request_print_tranger(gobj, path)
|
|
|
1342
1392
|
}, gobj);
|
|
1343
1393
|
if(ret) {
|
|
1344
1394
|
log_error(ret);
|
|
1395
|
+
answer_print_tranger_refused(gobj, path, {error: refusal_text(ret)});
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
if(path) {
|
|
1399
|
+
priv.drills_in_flight.add(path);
|
|
1345
1400
|
}
|
|
1346
1401
|
}
|
|
1347
1402
|
|
|
1403
|
+
/************************************************************
|
|
1404
|
+
* The session dropped: a drill already sent will never be answered
|
|
1405
|
+
* (the transport answers nothing in flight on a close), and its stub
|
|
1406
|
+
* would show "loading" for the life of the viewer. Answered here, so a
|
|
1407
|
+
* click once the session is back asks again. Idempotent: both edges of
|
|
1408
|
+
* one drop may arrive.
|
|
1409
|
+
************************************************************/
|
|
1410
|
+
function answer_drills_in_flight(gobj)
|
|
1411
|
+
{
|
|
1412
|
+
let priv = gobj.priv;
|
|
1413
|
+
let paths = Array.from(priv.drills_in_flight);
|
|
1414
|
+
priv.drills_in_flight.clear();
|
|
1415
|
+
paths.forEach((path) => {
|
|
1416
|
+
log_warning(`${gobj_short_name(gobj)}: transport closed, the drill ` +
|
|
1417
|
+
`'${path}' of the raw json is lost`);
|
|
1418
|
+
answer_print_tranger_refused(gobj, path, {i18n: "the connection dropped"});
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1348
1422
|
/************************************************************
|
|
1349
1423
|
* Populate layout dropdown from child's available layouts
|
|
1350
1424
|
************************************************************/
|
|
@@ -2061,6 +2135,19 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
2061
2135
|
return 0; /* viewer closed before its answer landed: benign */
|
|
2062
2136
|
}
|
|
2063
2137
|
let path = kw_get_str(gobj, kw_command, "path", "", 0);
|
|
2138
|
+
if(path) {
|
|
2139
|
+
let in_flight = priv.drills_in_flight.delete(path);
|
|
2140
|
+
if(!in_flight && result < 0) {
|
|
2141
|
+
/* Already answered (the drop answered it, or the viewer
|
|
2142
|
+
* was refilled): a second answer would log it twice. */
|
|
2143
|
+
log_warning(`${gobj_short_name(gobj)}: late failure of the drill ` +
|
|
2144
|
+
`'${path}' dropped, it was already answered: ${comment || ""}`);
|
|
2145
|
+
return 0;
|
|
2146
|
+
}
|
|
2147
|
+
} else {
|
|
2148
|
+
/* A whole document replaces every stub the drills were for. */
|
|
2149
|
+
priv.drills_in_flight.clear();
|
|
2150
|
+
}
|
|
2064
2151
|
if(result < 0) {
|
|
2065
2152
|
if(path) {
|
|
2066
2153
|
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
@@ -2372,6 +2459,7 @@ function ac_json_closed(gobj, event, kw, src)
|
|
|
2372
2459
|
priv.json_gobj = null;
|
|
2373
2460
|
priv.json_win = null;
|
|
2374
2461
|
priv.json_modal = null;
|
|
2462
|
+
priv.drills_in_flight.clear();
|
|
2375
2463
|
if(jv && is_gobj(jv)) {
|
|
2376
2464
|
try {
|
|
2377
2465
|
/* STOP before destroy — the viewer was STARTED in open_json_viewer
|
|
@@ -137,6 +137,7 @@ let PRIVATE_DATA = {
|
|
|
137
137
|
json_gobj: null, /* C_YUI_JSON raw-tranger viewer (or null) */
|
|
138
138
|
json_win: null, /* C_YUI_WINDOW hosting it, desktop (or null) */
|
|
139
139
|
json_modal: null, /* shell modal hosting it, mobile (or null) */
|
|
140
|
+
drills_in_flight: null, /* Set of the viewer's drilled paths sent, not answered */
|
|
140
141
|
};
|
|
141
142
|
|
|
142
143
|
let __gclass__ = null;
|
|
@@ -166,6 +167,9 @@ function mt_create(gobj)
|
|
|
166
167
|
* (reread_open_topics), never out of session. */
|
|
167
168
|
gobj.priv.reread_on_session = false;
|
|
168
169
|
gobj.priv.conn_shell = null; /* the shell whose EV_CONNECTION_STATE we hear */
|
|
170
|
+
/* The drills of the raw-json viewer sent and not yet answered:
|
|
171
|
+
* answered on the disconnect edge (answer_drills_in_flight). */
|
|
172
|
+
gobj.priv.drills_in_flight = new Set();
|
|
169
173
|
|
|
170
174
|
build_ui(gobj);
|
|
171
175
|
|
|
@@ -1887,6 +1891,7 @@ function close_json_viewer(gobj)
|
|
|
1887
1891
|
priv.json_gobj = null;
|
|
1888
1892
|
priv.json_win = null;
|
|
1889
1893
|
priv.json_modal = null;
|
|
1894
|
+
priv.drills_in_flight.clear();
|
|
1890
1895
|
|
|
1891
1896
|
if(win && is_gobj(win)) {
|
|
1892
1897
|
try {
|
|
@@ -1943,6 +1948,42 @@ function request_schema_file(gobj)
|
|
|
1943
1948
|
}
|
|
1944
1949
|
}
|
|
1945
1950
|
|
|
1951
|
+
/************************************************************
|
|
1952
|
+
* A drill (or the whole document) that will get no answer from the
|
|
1953
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
1954
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
1955
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
1956
|
+
************************************************************/
|
|
1957
|
+
function answer_print_tranger_refused(gobj, path, answer)
|
|
1958
|
+
{
|
|
1959
|
+
let jv = gobj.priv.json_gobj;
|
|
1960
|
+
if(!jv || !is_gobj(jv) || gobj_is_destroying(jv)) {
|
|
1961
|
+
return; /* no viewer to answer: the refusal is already logged */
|
|
1962
|
+
}
|
|
1963
|
+
if(path) {
|
|
1964
|
+
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
1965
|
+
Object.assign({path: path}, answer), gobj);
|
|
1966
|
+
} else {
|
|
1967
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
1968
|
+
answer.i18n || answer.error, {t: t});
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
/************************************************************
|
|
1973
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
1974
|
+
* a command response or a number.
|
|
1975
|
+
************************************************************/
|
|
1976
|
+
function refusal_text(ret)
|
|
1977
|
+
{
|
|
1978
|
+
if(typeof ret === "string" && ret) {
|
|
1979
|
+
return ret;
|
|
1980
|
+
}
|
|
1981
|
+
if(ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) {
|
|
1982
|
+
return ret.comment;
|
|
1983
|
+
}
|
|
1984
|
+
return "print-tranger failed";
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1946
1987
|
/************************************************************
|
|
1947
1988
|
* The whole tranger, for the raw-json viewer.
|
|
1948
1989
|
************************************************************/
|
|
@@ -1952,11 +1993,13 @@ function request_print_tranger(gobj, path)
|
|
|
1952
1993
|
let remote = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
|
|
1953
1994
|
if(!remote) {
|
|
1954
1995
|
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
}
|
|
1996
|
+
answer_print_tranger_refused(gobj, path, {i18n: "no session"});
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
if(!is_connected(gobj)) {
|
|
2000
|
+
log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' ` +
|
|
2001
|
+
`not asked: no session`);
|
|
2002
|
+
answer_print_tranger_refused(gobj, path, {i18n: "no session"});
|
|
1960
2003
|
return;
|
|
1961
2004
|
}
|
|
1962
2005
|
let ret = gobj_command(remote, "print-tranger",
|
|
@@ -1972,9 +2015,33 @@ function request_print_tranger(gobj, path)
|
|
|
1972
2015
|
}, gobj);
|
|
1973
2016
|
if(ret) {
|
|
1974
2017
|
log_error(ret);
|
|
2018
|
+
answer_print_tranger_refused(gobj, path, {error: refusal_text(ret)});
|
|
2019
|
+
return;
|
|
2020
|
+
}
|
|
2021
|
+
if(path) {
|
|
2022
|
+
priv.drills_in_flight.add(path);
|
|
1975
2023
|
}
|
|
1976
2024
|
}
|
|
1977
2025
|
|
|
2026
|
+
/************************************************************
|
|
2027
|
+
* The session dropped: a drill already sent will never be answered
|
|
2028
|
+
* (the transport answers nothing in flight on a close), and its stub
|
|
2029
|
+
* would show "loading" for the life of the viewer. Answered here, so a
|
|
2030
|
+
* click once the session is back asks again. Idempotent: both edges of
|
|
2031
|
+
* one drop may arrive.
|
|
2032
|
+
************************************************************/
|
|
2033
|
+
function answer_drills_in_flight(gobj)
|
|
2034
|
+
{
|
|
2035
|
+
let priv = gobj.priv;
|
|
2036
|
+
let paths = Array.from(priv.drills_in_flight);
|
|
2037
|
+
priv.drills_in_flight.clear();
|
|
2038
|
+
paths.forEach((path) => {
|
|
2039
|
+
log_warning(`${gobj_short_name(gobj)}: transport closed, the drill ` +
|
|
2040
|
+
`'${path}' of the raw json is lost`);
|
|
2041
|
+
answer_print_tranger_refused(gobj, path, {i18n: "the connection dropped"});
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
|
|
1978
2045
|
|
|
1979
2046
|
|
|
1980
2047
|
|
|
@@ -2041,6 +2108,19 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
2041
2108
|
return 0; /* viewer closed before its answer landed: benign */
|
|
2042
2109
|
}
|
|
2043
2110
|
let path = kw_get_str(gobj, kw_command, "path", "", 0);
|
|
2111
|
+
if(path) {
|
|
2112
|
+
let in_flight = gobj.priv.drills_in_flight.delete(path);
|
|
2113
|
+
if(!in_flight && result < 0) {
|
|
2114
|
+
/* Already answered (the drop answered it, or the viewer
|
|
2115
|
+
* was refilled): a second answer would log it twice. */
|
|
2116
|
+
log_warning(`${gobj_short_name(gobj)}: late failure of the drill ` +
|
|
2117
|
+
`'${path}' dropped, it was already answered: ${comment || ""}`);
|
|
2118
|
+
return 0;
|
|
2119
|
+
}
|
|
2120
|
+
} else {
|
|
2121
|
+
/* A whole document replaces every stub the drills were for. */
|
|
2122
|
+
gobj.priv.drills_in_flight.clear();
|
|
2123
|
+
}
|
|
2044
2124
|
if(result < 0) {
|
|
2045
2125
|
if(path) {
|
|
2046
2126
|
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
@@ -2513,12 +2593,14 @@ function ac_transport_state(gobj, event, kw, src)
|
|
|
2513
2593
|
* the backend: its form stayed open and busy for ever. Answered
|
|
2514
2594
|
* refused: the form stays open on what
|
|
2515
2595
|
* was typed, and the person saves again when the session is back.
|
|
2596
|
+
* The same for a drill of the raw-json viewer: its stub is answered.
|
|
2516
2597
|
*/
|
|
2517
2598
|
if(!connected) {
|
|
2518
2599
|
/* What happens in the store while the session is down reaches
|
|
2519
2600
|
* no table: its node events are published to nobody. Every open
|
|
2520
2601
|
* table is read again when the session is back. */
|
|
2521
2602
|
gobj.priv.reread_on_session = true;
|
|
2603
|
+
answer_drills_in_flight(gobj);
|
|
2522
2604
|
abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
|
|
2523
2605
|
log_warning(`${gobj_short_name(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
|
|
2524
2606
|
answer_form_write(
|
|
@@ -3086,6 +3168,7 @@ function ac_json_closed(gobj, event, kw, src)
|
|
|
3086
3168
|
priv.json_gobj = null;
|
|
3087
3169
|
priv.json_win = null;
|
|
3088
3170
|
priv.json_modal = null;
|
|
3171
|
+
priv.drills_in_flight.clear();
|
|
3089
3172
|
if(jv && is_gobj(jv)) {
|
|
3090
3173
|
try {
|
|
3091
3174
|
/* STOP before destroy — the viewer was STARTED in open_json_viewer
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* treedb_json_drill.wiring.test.js
|
|
3
|
+
*
|
|
4
|
+
* The raw-tranger viewer of C_YUI_TREEDB_TOPICS and of
|
|
5
|
+
* C_YUI_TREEDB_GRAPH drills into a `__collapsed__` stub by asking
|
|
6
|
+
* its host (EV_EXPAND_PATH), and the stub shows "loading" until the
|
|
7
|
+
* host answers EV_SUBTREE_LOADED or EV_SUBTREE_ERROR. While the
|
|
8
|
+
* path is pending, C_YUI_JSON ignores every further click on it.
|
|
9
|
+
*
|
|
10
|
+
* Before gobj-ui 7.25.21 the host answered only when it had no
|
|
11
|
+
* transport at all. A drill asked out of session, or refused by the
|
|
12
|
+
* transport on the way out, was logged and never answered: the stub
|
|
13
|
+
* stayed on "loading" for the life of the viewer, and no click --
|
|
14
|
+
* not even after the reconnect -- asked again.
|
|
15
|
+
*
|
|
16
|
+
* Before gobj-ui 7.25.22 a drill already SENT when the session
|
|
17
|
+
* dropped was never answered either: the transport answers nothing
|
|
18
|
+
* in flight on a close, and neither host remembered what it had
|
|
19
|
+
* asked. Each host now keeps its drills in flight and answers them
|
|
20
|
+
* on the disconnect edge.
|
|
21
|
+
*
|
|
22
|
+
* Driven through the real hosts and the real viewer on a document
|
|
23
|
+
* double, with a fake transport whose state is the one the hosts
|
|
24
|
+
* read and which refuses on demand.
|
|
25
|
+
*
|
|
26
|
+
* Copyright (c) 2026, ArtGins.
|
|
27
|
+
* All Rights Reserved.
|
|
28
|
+
***********************************************************************/
|
|
29
|
+
import {describe, test, expect, beforeAll, beforeEach} from "vitest";
|
|
30
|
+
import {install_dom_double} from "../test/dom_double.js";
|
|
31
|
+
|
|
32
|
+
install_dom_double();
|
|
33
|
+
|
|
34
|
+
if(!globalThis.ResizeObserver) {
|
|
35
|
+
globalThis.ResizeObserver = class {
|
|
36
|
+
observe() {}
|
|
37
|
+
unobserve() {}
|
|
38
|
+
disconnect() {}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
SDATA, SDATA_END, data_type_t,
|
|
44
|
+
gclass_create,
|
|
45
|
+
gobj_start_up, gobj_create_yuno, gobj_create, gobj_create_service,
|
|
46
|
+
gobj_create_pure_child,
|
|
47
|
+
gobj_start, gobj_send_event,
|
|
48
|
+
gobj_read_pointer_attr, gobj_subscribe_event,
|
|
49
|
+
gobj_change_state, gobj_current_state,
|
|
50
|
+
set_log_callback,
|
|
51
|
+
} = await import("@yuneta/gobj-js");
|
|
52
|
+
|
|
53
|
+
const logged = [];
|
|
54
|
+
const commands = []; /* what the fake transport was asked */
|
|
55
|
+
let refuse_in_session = false;
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* The transport. Out of session it refuses, as gui_agent's routing
|
|
59
|
+
* adapter (C_AGENT_TREEDB_LINK) does; in session it refuses only when
|
|
60
|
+
* the test says so (a transport that cannot route the command).
|
|
61
|
+
*/
|
|
62
|
+
function remote_command_parser(gobj, command, kw, src)
|
|
63
|
+
{
|
|
64
|
+
commands.push({command, kw});
|
|
65
|
+
if(gobj_current_state(gobj) !== "ST_SESSION") {
|
|
66
|
+
return `cannot route '${command}' -- not in session`;
|
|
67
|
+
}
|
|
68
|
+
if(refuse_in_session) {
|
|
69
|
+
return `cannot route '${command}'`;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let yuno = null;
|
|
75
|
+
|
|
76
|
+
beforeAll(async () => {
|
|
77
|
+
const i18next = (await import("i18next")).default;
|
|
78
|
+
await i18next.init({lng: "en", resources: {}});
|
|
79
|
+
gobj_start_up(null, null, null, null, null, null, null);
|
|
80
|
+
set_log_callback((level, msg) => {
|
|
81
|
+
logged.push({level: String(level), msg: String(msg)});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
gclass_create("C_TEST_HOST", [], [["ST_IDLE", []]], {}, 0, [SDATA_END()], {}, 0, 0, 0, 0);
|
|
85
|
+
gclass_create(
|
|
86
|
+
"C_TEST_VIEW_HOST",
|
|
87
|
+
[["EV_RECORD_WRITTEN", 0], ["EV_TOPIC_SELECTED", 0], ["EV_OPERATION_MODE_CHANGED", 0]],
|
|
88
|
+
[["ST_IDLE", [
|
|
89
|
+
["EV_RECORD_WRITTEN", () => 0, null],
|
|
90
|
+
["EV_TOPIC_SELECTED", () => 0, null],
|
|
91
|
+
["EV_OPERATION_MODE_CHANGED", () => 0, null]
|
|
92
|
+
]]],
|
|
93
|
+
{}, 0, [SDATA_END()], {}, 0, 0, 0, 0
|
|
94
|
+
);
|
|
95
|
+
gclass_create(
|
|
96
|
+
"C_TEST_REMOTE",
|
|
97
|
+
[],
|
|
98
|
+
[["ST_SESSION", []], ["ST_DISCONNECTED", []]],
|
|
99
|
+
{mt_command_parser: remote_command_parser},
|
|
100
|
+
0, [SDATA_END()], {}, 0, 0, 0, 0
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
/* The graph's engine, as a silent double: this test is about the
|
|
104
|
+
* viewer, not about the graph. */
|
|
105
|
+
const ENGINE_EVENTS = [
|
|
106
|
+
"EV_DESCS", "EV_CLEAR_DATA", "EV_LOAD_DATA", "EV_NODE_CREATED",
|
|
107
|
+
"EV_NODE_UPDATED", "EV_NODE_DELETED", "EV_GRAPHS_WRITE_REFUSED",
|
|
108
|
+
"EV_SHOW", "EV_HIDE", "EV_RESIZE", "EV_THEME", "EV_LANGUAGE_CHANGED",
|
|
109
|
+
"EV_SET_OPERATION_MODE", "EV_SET_LAYOUT"
|
|
110
|
+
];
|
|
111
|
+
gclass_create(
|
|
112
|
+
"C_G6_NODES_TREE",
|
|
113
|
+
ENGINE_EVENTS.map((e) => [e, 0]).concat([["EV_UPDATE_NODE", 2]]),
|
|
114
|
+
[["ST_IDLE", ENGINE_EVENTS.map((e) => [e, () => 0, null])]],
|
|
115
|
+
{
|
|
116
|
+
mt_create: (gobj) => {
|
|
117
|
+
const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
|
|
118
|
+
if(subscriber) {
|
|
119
|
+
gobj_subscribe_event(gobj, null, {}, subscriber);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
0,
|
|
124
|
+
[
|
|
125
|
+
SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, ""),
|
|
126
|
+
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, ""),
|
|
127
|
+
SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, ""),
|
|
128
|
+
SDATA(data_type_t.DTP_STRING, "treedb_name", 0, "", ""),
|
|
129
|
+
SDATA(data_type_t.DTP_JSON, "layout_names", 0, "[]", ""),
|
|
130
|
+
SDATA(data_type_t.DTP_STRING, "layout", 0, "", ""),
|
|
131
|
+
SDATA_END()
|
|
132
|
+
],
|
|
133
|
+
{}, 0, 0, 0, 0
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const {register_c_yui_json} = await import("./c_yui_json.js");
|
|
137
|
+
const {register_c_yui_treedb_topics} = await import("./c_yui_treedb_topics.js");
|
|
138
|
+
const {register_c_yui_treedb_graph} = await import("./c_yui_treedb_graph.js");
|
|
139
|
+
register_c_yui_json();
|
|
140
|
+
register_c_yui_treedb_topics();
|
|
141
|
+
register_c_yui_treedb_graph();
|
|
142
|
+
|
|
143
|
+
yuno = gobj_create_yuno("json_drill_yuno", "C_TEST_HOST", {});
|
|
144
|
+
gobj_start(yuno);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
beforeEach(() => {
|
|
148
|
+
logged.length = 0;
|
|
149
|
+
commands.length = 0;
|
|
150
|
+
refuse_in_session = false;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
* A host of each kind, with its viewer open on a tranger that carries
|
|
155
|
+
* one stub. The viewer is the one the host created: it publishes
|
|
156
|
+
* EV_EXPAND_PATH to the host, and the host answers it.
|
|
157
|
+
*/
|
|
158
|
+
function build(name, gclass)
|
|
159
|
+
{
|
|
160
|
+
const remote = gobj_create_service(`${name}_remote`, "C_TEST_REMOTE", {}, yuno);
|
|
161
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
162
|
+
const view_host = gobj_create(`${name}_view_host`, "C_TEST_VIEW_HOST", {}, yuno);
|
|
163
|
+
const host = gobj_create_service(`${name}_host`, gclass, {
|
|
164
|
+
gobj_remote_yuno: remote,
|
|
165
|
+
treedb_name: "treedb_test",
|
|
166
|
+
subscriber: view_host
|
|
167
|
+
}, view_host);
|
|
168
|
+
if(gclass === "C_YUI_TREEDB_TOPICS") {
|
|
169
|
+
gobj_start(host);
|
|
170
|
+
}
|
|
171
|
+
const viewer = gobj_create_pure_child(`${name}_jv`, "C_YUI_JSON", {
|
|
172
|
+
subscriber: host,
|
|
173
|
+
json_data: {topics: {__collapsed__: {size: 3, path: "topics"}}}
|
|
174
|
+
}, host);
|
|
175
|
+
gobj_start(viewer);
|
|
176
|
+
host.priv.json_gobj = viewer;
|
|
177
|
+
logged.length = 0;
|
|
178
|
+
commands.length = 0;
|
|
179
|
+
return {remote, host, viewer};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function click_stub(viewer)
|
|
183
|
+
{
|
|
184
|
+
gobj_send_event(viewer, "EV_EXPAND_COLLAPSED", {path: "topics", size: 3}, viewer);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function drills()
|
|
188
|
+
{
|
|
189
|
+
return commands.filter((c) => c.command === "print-tranger").map((c) => c.kw.path);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
for(const gclass of ["C_YUI_TREEDB_TOPICS", "C_YUI_TREEDB_GRAPH"]) {
|
|
193
|
+
describe(`${gclass}: a drill that cannot go out is answered`, () => {
|
|
194
|
+
|
|
195
|
+
test("out of session: the stub says 'no session', and a click after the reconnect asks", () => {
|
|
196
|
+
const {remote, viewer} = build(`${gclass}_a`, gclass);
|
|
197
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
198
|
+
|
|
199
|
+
click_stub(viewer);
|
|
200
|
+
|
|
201
|
+
expect(drills()).toEqual([]); /* not even tried */
|
|
202
|
+
expect(viewer.priv.pending.has("topics")).toBe(false);
|
|
203
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
204
|
+
{error: "no session", is_key: true});
|
|
205
|
+
|
|
206
|
+
/* The reconnect: the same stub asks again. */
|
|
207
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
208
|
+
click_stub(viewer);
|
|
209
|
+
expect(drills()).toEqual(["topics"]);
|
|
210
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("refused by the transport: the stub says why, and can be clicked again", () => {
|
|
214
|
+
const {viewer} = build(`${gclass}_b`, gclass);
|
|
215
|
+
refuse_in_session = true;
|
|
216
|
+
|
|
217
|
+
click_stub(viewer);
|
|
218
|
+
|
|
219
|
+
expect(drills()).toEqual(["topics"]);
|
|
220
|
+
expect(viewer.priv.pending.has("topics")).toBe(false);
|
|
221
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
222
|
+
{error: "cannot route 'print-tranger'", is_key: false});
|
|
223
|
+
|
|
224
|
+
refuse_in_session = false;
|
|
225
|
+
click_stub(viewer);
|
|
226
|
+
expect(drills()).toEqual(["topics", "topics"]);
|
|
227
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("in session and sent: the stub waits for its answer", () => {
|
|
231
|
+
const {viewer} = build(`${gclass}_c`, gclass);
|
|
232
|
+
click_stub(viewer);
|
|
233
|
+
expect(drills()).toEqual(["topics"]);
|
|
234
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
235
|
+
expect(logged.filter((l) => l.level === "error")).toEqual([]);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe(`${gclass}: a drill in flight when the session drops is answered`, () => {
|
|
240
|
+
|
|
241
|
+
test("the drop answers the stub, and a click after the reconnect asks again", () => {
|
|
242
|
+
const {remote, host, viewer} = build(`${gclass}_d`, gclass);
|
|
243
|
+
click_stub(viewer);
|
|
244
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
245
|
+
|
|
246
|
+
/* The session drops before the answer: the transport never
|
|
247
|
+
* answers a command in flight. */
|
|
248
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
249
|
+
gobj_send_event(host, "EV_TRANSPORT_STATE", {connected: false}, host);
|
|
250
|
+
|
|
251
|
+
expect(viewer.priv.pending.has("topics")).toBe(false);
|
|
252
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
253
|
+
{error: "the connection dropped", is_key: true});
|
|
254
|
+
|
|
255
|
+
/* The same edge again answers nothing more. */
|
|
256
|
+
logged.length = 0;
|
|
257
|
+
gobj_send_event(host, "EV_TRANSPORT_STATE", {connected: false}, host);
|
|
258
|
+
expect(logged.filter((l) => l.level === "error")).toEqual([]);
|
|
259
|
+
|
|
260
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
261
|
+
gobj_send_event(host, "EV_TRANSPORT_STATE", {connected: true}, host);
|
|
262
|
+
click_stub(viewer);
|
|
263
|
+
expect(drills()).toEqual(["topics", "topics"]);
|
|
264
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("a failure that lands after the drop answered is not answered twice", () => {
|
|
268
|
+
/* gui_agent's routing adapter answers every request in flight
|
|
269
|
+
* as failed on the close, and that answer can land after the
|
|
270
|
+
* edge that already answered the stub. */
|
|
271
|
+
const {remote, host, viewer} = build(`${gclass}_h`, gclass);
|
|
272
|
+
click_stub(viewer);
|
|
273
|
+
const request = commands.find((c) => c.command === "print-tranger");
|
|
274
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
275
|
+
gobj_send_event(host, "EV_TRANSPORT_STATE", {connected: false}, host);
|
|
276
|
+
|
|
277
|
+
logged.length = 0;
|
|
278
|
+
gobj_send_event(host, "EV_MT_COMMAND_ANSWER", {
|
|
279
|
+
result: -1, comment: "the connection dropped", data: null,
|
|
280
|
+
__md_iev__: {
|
|
281
|
+
command_stack: [{command: "print-tranger", kw: request.kw.__md_command__}]
|
|
282
|
+
}
|
|
283
|
+
}, remote);
|
|
284
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
285
|
+
{error: "the connection dropped", is_key: true});
|
|
286
|
+
expect(logged.filter((l) => l.level === "error")).toEqual([]);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("a drill already answered is not answered again by a drop", () => {
|
|
290
|
+
const {remote, host, viewer} = build(`${gclass}_e`, gclass);
|
|
291
|
+
click_stub(viewer);
|
|
292
|
+
const request = commands.find((c) => c.command === "print-tranger");
|
|
293
|
+
gobj_send_event(host, "EV_MT_COMMAND_ANSWER", {
|
|
294
|
+
result: 0, comment: "", data: {a: 1, b: 2, c: 3},
|
|
295
|
+
__md_iev__: {
|
|
296
|
+
command_stack: [{command: "print-tranger", kw: request.kw.__md_command__}]
|
|
297
|
+
}
|
|
298
|
+
}, remote);
|
|
299
|
+
expect(viewer.priv.pending.has("topics")).toBe(false);
|
|
300
|
+
expect(viewer.priv.errors.has("topics")).toBe(false);
|
|
301
|
+
|
|
302
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
303
|
+
gobj_send_event(host, "EV_TRANSPORT_STATE", {connected: false}, host);
|
|
304
|
+
expect(viewer.priv.errors.has("topics")).toBe(false);
|
|
305
|
+
expect(logged.filter((l) => l.level === "error")).toEqual([]);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test("a drill that failed is not answered again by a drop", () => {
|
|
309
|
+
const {remote, host, viewer} = build(`${gclass}_f`, gclass);
|
|
310
|
+
click_stub(viewer);
|
|
311
|
+
const request = commands.find((c) => c.command === "print-tranger");
|
|
312
|
+
gobj_send_event(host, "EV_MT_COMMAND_ANSWER", {
|
|
313
|
+
result: -1, comment: "no such path", data: null,
|
|
314
|
+
__md_iev__: {
|
|
315
|
+
command_stack: [{command: "print-tranger", kw: request.kw.__md_command__}]
|
|
316
|
+
}
|
|
317
|
+
}, remote);
|
|
318
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
319
|
+
{error: "no such path", is_key: false});
|
|
320
|
+
|
|
321
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
322
|
+
gobj_send_event(host, "EV_TRANSPORT_STATE", {connected: false}, host);
|
|
323
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
324
|
+
{error: "no such path", is_key: false});
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/*
|
|
330
|
+
* wattyzer and yunovatios hear the edge from the SHELL
|
|
331
|
+
* (EV_CONNECTION_STATE), not from a host that forwards the transport.
|
|
332
|
+
*/
|
|
333
|
+
describe("C_YUI_TREEDB_TOPICS: the shell's 'down' answers a drill in flight", () => {
|
|
334
|
+
test("EV_CONNECTION_STATE down answers the stub once, with its edge twin", () => {
|
|
335
|
+
const {remote, host, viewer} = build("topics_g", "C_YUI_TREEDB_TOPICS");
|
|
336
|
+
click_stub(viewer);
|
|
337
|
+
|
|
338
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
339
|
+
gobj_send_event(host, "EV_CONNECTION_STATE", {connected: false}, host);
|
|
340
|
+
expect(viewer.priv.pending.has("topics")).toBe(false);
|
|
341
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
342
|
+
{error: "the connection dropped", is_key: true});
|
|
343
|
+
|
|
344
|
+
const errors_after_first = logged.filter((l) => l.level === "error").length;
|
|
345
|
+
gobj_send_event(host, "EV_TRANSPORT_STATE", {connected: false}, host);
|
|
346
|
+
expect(logged.filter((l) => l.level === "error").length).toBe(errors_after_first);
|
|
347
|
+
});
|
|
348
|
+
});
|