@yuneta/gobj-ui 7.25.20 → 7.25.21
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 +7 -0
- package/dist/gobj-ui.cjs.js +62 -12
- package/dist/gobj-ui.es.js +62 -12
- package/package.json +2 -2
- package/src/c_yui_treedb_graph.js +44 -5
- package/src/c_yui_treedb_topics.js +44 -5
- package/src/treedb_json_drill.wiring.test.js +232 -0
package/README.md
CHANGED
|
@@ -593,6 +593,13 @@ 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`). While a path is
|
|
601
|
+
pending the viewer ignores every click on it, so a drill left unanswered
|
|
602
|
+
is a dead stub for the life of the viewer.
|
|
596
603
|
A host that cannot read a subtree says so with the error, as
|
|
597
604
|
`C_YUI_TREEDB_TOPIC_WITH_FORM` does for its schema, cell and table viewers
|
|
598
605
|
since `7.25.19` (before, it declared nothing: *"Event NOT DEFINED"*):
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -25136,6 +25136,27 @@ function request_schema_file(gobj) {
|
|
|
25136
25136
|
if (ret) (0, _yuneta_gobj_js.log_error)(ret);
|
|
25137
25137
|
}
|
|
25138
25138
|
/************************************************************
|
|
25139
|
+
* A drill (or the whole document) that will get no answer from the
|
|
25140
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
25141
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
25142
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
25143
|
+
************************************************************/
|
|
25144
|
+
function answer_print_tranger_refused$1(gobj, path, answer) {
|
|
25145
|
+
let jv = gobj.priv.json_gobj;
|
|
25146
|
+
if (!jv || !(0, _yuneta_gobj_js.is_gobj)(jv) || (0, _yuneta_gobj_js.gobj_is_destroying)(jv)) return;
|
|
25147
|
+
if (path) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
|
|
25148
|
+
else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t: i18next.t });
|
|
25149
|
+
}
|
|
25150
|
+
/************************************************************
|
|
25151
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
25152
|
+
* a command response or a number.
|
|
25153
|
+
************************************************************/
|
|
25154
|
+
function refusal_text$1(ret) {
|
|
25155
|
+
if (typeof ret === "string" && ret) return ret;
|
|
25156
|
+
if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
|
|
25157
|
+
return "print-tranger failed";
|
|
25158
|
+
}
|
|
25159
|
+
/************************************************************
|
|
25139
25160
|
* The whole tranger, for the raw-json viewer.
|
|
25140
25161
|
************************************************************/
|
|
25141
25162
|
function request_print_tranger$1(gobj, path) {
|
|
@@ -25143,11 +25164,12 @@ function request_print_tranger$1(gobj, path) {
|
|
|
25143
25164
|
let remote = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "gobj_remote_yuno");
|
|
25144
25165
|
if (!remote) {
|
|
25145
25166
|
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: No gobj_remote_yuno defined`);
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25167
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
|
|
25168
|
+
return;
|
|
25169
|
+
}
|
|
25170
|
+
if (!is_connected$1(gobj)) {
|
|
25171
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: print-tranger '${path || ""}' not asked: no session`);
|
|
25172
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
|
|
25151
25173
|
return;
|
|
25152
25174
|
}
|
|
25153
25175
|
let ret = (0, _yuneta_gobj_js.gobj_command)(remote, "print-tranger", {
|
|
@@ -25158,7 +25180,10 @@ function request_print_tranger$1(gobj, path) {
|
|
|
25158
25180
|
path: path || "",
|
|
25159
25181
|
__md_command__: { path: path || "" }
|
|
25160
25182
|
}, gobj);
|
|
25161
|
-
if (ret)
|
|
25183
|
+
if (ret) {
|
|
25184
|
+
(0, _yuneta_gobj_js.log_error)(ret);
|
|
25185
|
+
answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) });
|
|
25186
|
+
}
|
|
25162
25187
|
}
|
|
25163
25188
|
/***************************************************************
|
|
25164
25189
|
* The last gate before a write leaves for the yuno. The per-topic
|
|
@@ -68344,6 +68369,27 @@ function close_json_viewer(gobj) {
|
|
|
68344
68369
|
}
|
|
68345
68370
|
}
|
|
68346
68371
|
/************************************************************
|
|
68372
|
+
* A drill (or the whole document) that will get no answer from the
|
|
68373
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
68374
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
68375
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
68376
|
+
************************************************************/
|
|
68377
|
+
function answer_print_tranger_refused(gobj, path, answer) {
|
|
68378
|
+
let jv = gobj.priv.json_gobj;
|
|
68379
|
+
if (!jv || !(0, _yuneta_gobj_js.is_gobj)(jv) || (0, _yuneta_gobj_js.gobj_is_destroying)(jv)) return;
|
|
68380
|
+
if (path) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
|
|
68381
|
+
else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t: i18next.t });
|
|
68382
|
+
}
|
|
68383
|
+
/************************************************************
|
|
68384
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
68385
|
+
* a command response or a number.
|
|
68386
|
+
************************************************************/
|
|
68387
|
+
function refusal_text(ret) {
|
|
68388
|
+
if (typeof ret === "string" && ret) return ret;
|
|
68389
|
+
if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
|
|
68390
|
+
return "print-tranger failed";
|
|
68391
|
+
}
|
|
68392
|
+
/************************************************************
|
|
68347
68393
|
* Fetch the treedb's raw tranger (or one subtree when `path` is set) as
|
|
68348
68394
|
* bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
|
|
68349
68395
|
* small payload of `__collapsed__` stubs the viewer expands on demand.
|
|
@@ -68352,11 +68398,12 @@ function request_print_tranger(gobj, path) {
|
|
|
68352
68398
|
let priv = gobj.priv;
|
|
68353
68399
|
if (!priv.gobj_remote_yuno) {
|
|
68354
68400
|
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: No gobj_remote_yuno defined`);
|
|
68355
|
-
|
|
68356
|
-
|
|
68357
|
-
|
|
68358
|
-
|
|
68359
|
-
|
|
68401
|
+
answer_print_tranger_refused(gobj, path, { i18n: "no session" });
|
|
68402
|
+
return;
|
|
68403
|
+
}
|
|
68404
|
+
if (!is_connected(gobj)) {
|
|
68405
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: print-tranger '${path || ""}' not asked: no session`);
|
|
68406
|
+
answer_print_tranger_refused(gobj, path, { i18n: "no session" });
|
|
68360
68407
|
return;
|
|
68361
68408
|
}
|
|
68362
68409
|
let ret = (0, _yuneta_gobj_js.gobj_command)(priv.gobj_remote_yuno, "print-tranger", {
|
|
@@ -68367,7 +68414,10 @@ function request_print_tranger(gobj, path) {
|
|
|
68367
68414
|
path: path || "",
|
|
68368
68415
|
__md_command__: { path: path || "" }
|
|
68369
68416
|
}, gobj);
|
|
68370
|
-
if (ret)
|
|
68417
|
+
if (ret) {
|
|
68418
|
+
(0, _yuneta_gobj_js.log_error)(ret);
|
|
68419
|
+
answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) });
|
|
68420
|
+
}
|
|
68371
68421
|
}
|
|
68372
68422
|
/************************************************************
|
|
68373
68423
|
* Populate layout dropdown from child's available layouts
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -25131,6 +25131,27 @@ function request_schema_file(gobj) {
|
|
|
25131
25131
|
if (ret) log_error(ret);
|
|
25132
25132
|
}
|
|
25133
25133
|
/************************************************************
|
|
25134
|
+
* A drill (or the whole document) that will get no answer from the
|
|
25135
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
25136
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
25137
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
25138
|
+
************************************************************/
|
|
25139
|
+
function answer_print_tranger_refused$1(gobj, path, answer) {
|
|
25140
|
+
let jv = gobj.priv.json_gobj;
|
|
25141
|
+
if (!jv || !is_gobj(jv) || gobj_is_destroying(jv)) return;
|
|
25142
|
+
if (path) gobj_send_event(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
|
|
25143
|
+
else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t });
|
|
25144
|
+
}
|
|
25145
|
+
/************************************************************
|
|
25146
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
25147
|
+
* a command response or a number.
|
|
25148
|
+
************************************************************/
|
|
25149
|
+
function refusal_text$1(ret) {
|
|
25150
|
+
if (typeof ret === "string" && ret) return ret;
|
|
25151
|
+
if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
|
|
25152
|
+
return "print-tranger failed";
|
|
25153
|
+
}
|
|
25154
|
+
/************************************************************
|
|
25134
25155
|
* The whole tranger, for the raw-json viewer.
|
|
25135
25156
|
************************************************************/
|
|
25136
25157
|
function request_print_tranger$1(gobj, path) {
|
|
@@ -25138,11 +25159,12 @@ function request_print_tranger$1(gobj, path) {
|
|
|
25138
25159
|
let remote = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
|
|
25139
25160
|
if (!remote) {
|
|
25140
25161
|
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
}
|
|
25162
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
|
|
25163
|
+
return;
|
|
25164
|
+
}
|
|
25165
|
+
if (!is_connected$1(gobj)) {
|
|
25166
|
+
log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' not asked: no session`);
|
|
25167
|
+
answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
|
|
25146
25168
|
return;
|
|
25147
25169
|
}
|
|
25148
25170
|
let ret = gobj_command(remote, "print-tranger", {
|
|
@@ -25153,7 +25175,10 @@ function request_print_tranger$1(gobj, path) {
|
|
|
25153
25175
|
path: path || "",
|
|
25154
25176
|
__md_command__: { path: path || "" }
|
|
25155
25177
|
}, gobj);
|
|
25156
|
-
if (ret)
|
|
25178
|
+
if (ret) {
|
|
25179
|
+
log_error(ret);
|
|
25180
|
+
answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) });
|
|
25181
|
+
}
|
|
25157
25182
|
}
|
|
25158
25183
|
/***************************************************************
|
|
25159
25184
|
* The last gate before a write leaves for the yuno. The per-topic
|
|
@@ -68339,6 +68364,27 @@ function close_json_viewer(gobj) {
|
|
|
68339
68364
|
}
|
|
68340
68365
|
}
|
|
68341
68366
|
/************************************************************
|
|
68367
|
+
* A drill (or the whole document) that will get no answer from the
|
|
68368
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
68369
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
68370
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
68371
|
+
************************************************************/
|
|
68372
|
+
function answer_print_tranger_refused(gobj, path, answer) {
|
|
68373
|
+
let jv = gobj.priv.json_gobj;
|
|
68374
|
+
if (!jv || !is_gobj(jv) || gobj_is_destroying(jv)) return;
|
|
68375
|
+
if (path) gobj_send_event(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
|
|
68376
|
+
else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t });
|
|
68377
|
+
}
|
|
68378
|
+
/************************************************************
|
|
68379
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
68380
|
+
* a command response or a number.
|
|
68381
|
+
************************************************************/
|
|
68382
|
+
function refusal_text(ret) {
|
|
68383
|
+
if (typeof ret === "string" && ret) return ret;
|
|
68384
|
+
if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
|
|
68385
|
+
return "print-tranger failed";
|
|
68386
|
+
}
|
|
68387
|
+
/************************************************************
|
|
68342
68388
|
* Fetch the treedb's raw tranger (or one subtree when `path` is set) as
|
|
68343
68389
|
* bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
|
|
68344
68390
|
* small payload of `__collapsed__` stubs the viewer expands on demand.
|
|
@@ -68347,11 +68393,12 @@ function request_print_tranger(gobj, path) {
|
|
|
68347
68393
|
let priv = gobj.priv;
|
|
68348
68394
|
if (!priv.gobj_remote_yuno) {
|
|
68349
68395
|
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
68350
|
-
|
|
68351
|
-
|
|
68352
|
-
|
|
68353
|
-
|
|
68354
|
-
}
|
|
68396
|
+
answer_print_tranger_refused(gobj, path, { i18n: "no session" });
|
|
68397
|
+
return;
|
|
68398
|
+
}
|
|
68399
|
+
if (!is_connected(gobj)) {
|
|
68400
|
+
log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' not asked: no session`);
|
|
68401
|
+
answer_print_tranger_refused(gobj, path, { i18n: "no session" });
|
|
68355
68402
|
return;
|
|
68356
68403
|
}
|
|
68357
68404
|
let ret = gobj_command(priv.gobj_remote_yuno, "print-tranger", {
|
|
@@ -68362,7 +68409,10 @@ function request_print_tranger(gobj, path) {
|
|
|
68362
68409
|
path: path || "",
|
|
68363
68410
|
__md_command__: { path: path || "" }
|
|
68364
68411
|
}, gobj);
|
|
68365
|
-
if (ret)
|
|
68412
|
+
if (ret) {
|
|
68413
|
+
log_error(ret);
|
|
68414
|
+
answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) });
|
|
68415
|
+
}
|
|
68366
68416
|
}
|
|
68367
68417
|
/************************************************************
|
|
68368
68418
|
* Populate layout dropdown from child's available layouts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuneta/gobj-ui",
|
|
3
|
-
"version": "7.25.
|
|
3
|
+
"version": "7.25.21",
|
|
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.7",
|
|
34
34
|
"vite": "^8.2.1",
|
|
35
35
|
"vitest": "^4.1.10"
|
|
36
36
|
},
|
|
@@ -1312,6 +1312,42 @@ function close_json_viewer(gobj)
|
|
|
1312
1312
|
}
|
|
1313
1313
|
}
|
|
1314
1314
|
|
|
1315
|
+
/************************************************************
|
|
1316
|
+
* A drill (or the whole document) that will get no answer from the
|
|
1317
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
1318
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
1319
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
1320
|
+
************************************************************/
|
|
1321
|
+
function answer_print_tranger_refused(gobj, path, answer)
|
|
1322
|
+
{
|
|
1323
|
+
let jv = gobj.priv.json_gobj;
|
|
1324
|
+
if(!jv || !is_gobj(jv) || gobj_is_destroying(jv)) {
|
|
1325
|
+
return; /* no viewer to answer: the refusal is already logged */
|
|
1326
|
+
}
|
|
1327
|
+
if(path) {
|
|
1328
|
+
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
1329
|
+
Object.assign({path: path}, answer), gobj);
|
|
1330
|
+
} else {
|
|
1331
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
1332
|
+
answer.i18n || answer.error, {t: t});
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
/************************************************************
|
|
1337
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
1338
|
+
* a command response or a number.
|
|
1339
|
+
************************************************************/
|
|
1340
|
+
function refusal_text(ret)
|
|
1341
|
+
{
|
|
1342
|
+
if(typeof ret === "string" && ret) {
|
|
1343
|
+
return ret;
|
|
1344
|
+
}
|
|
1345
|
+
if(ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) {
|
|
1346
|
+
return ret.comment;
|
|
1347
|
+
}
|
|
1348
|
+
return "print-tranger failed";
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1315
1351
|
/************************************************************
|
|
1316
1352
|
* Fetch the treedb's raw tranger (or one subtree when `path` is set) as
|
|
1317
1353
|
* bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
|
|
@@ -1322,11 +1358,13 @@ function request_print_tranger(gobj, path)
|
|
|
1322
1358
|
let priv = gobj.priv;
|
|
1323
1359
|
if(!priv.gobj_remote_yuno) {
|
|
1324
1360
|
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
}
|
|
1361
|
+
answer_print_tranger_refused(gobj, path, {i18n: "no session"});
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
if(!is_connected(gobj)) {
|
|
1365
|
+
log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' ` +
|
|
1366
|
+
`not asked: no session`);
|
|
1367
|
+
answer_print_tranger_refused(gobj, path, {i18n: "no session"});
|
|
1330
1368
|
return;
|
|
1331
1369
|
}
|
|
1332
1370
|
let ret = gobj_command(priv.gobj_remote_yuno, "print-tranger",
|
|
@@ -1342,6 +1380,7 @@ function request_print_tranger(gobj, path)
|
|
|
1342
1380
|
}, gobj);
|
|
1343
1381
|
if(ret) {
|
|
1344
1382
|
log_error(ret);
|
|
1383
|
+
answer_print_tranger_refused(gobj, path, {error: refusal_text(ret)});
|
|
1345
1384
|
}
|
|
1346
1385
|
}
|
|
1347
1386
|
|
|
@@ -1943,6 +1943,42 @@ function request_schema_file(gobj)
|
|
|
1943
1943
|
}
|
|
1944
1944
|
}
|
|
1945
1945
|
|
|
1946
|
+
/************************************************************
|
|
1947
|
+
* A drill (or the whole document) that will get no answer from the
|
|
1948
|
+
* backend is answered HERE, or its stub shows "loading" for the life
|
|
1949
|
+
* of the viewer: C_YUI_JSON ignores every click on a pending path.
|
|
1950
|
+
* `answer` is {i18n: <key>} or {error: <text>}.
|
|
1951
|
+
************************************************************/
|
|
1952
|
+
function answer_print_tranger_refused(gobj, path, answer)
|
|
1953
|
+
{
|
|
1954
|
+
let jv = gobj.priv.json_gobj;
|
|
1955
|
+
if(!jv || !is_gobj(jv) || gobj_is_destroying(jv)) {
|
|
1956
|
+
return; /* no viewer to answer: the refusal is already logged */
|
|
1957
|
+
}
|
|
1958
|
+
if(path) {
|
|
1959
|
+
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
1960
|
+
Object.assign({path: path}, answer), gobj);
|
|
1961
|
+
} else {
|
|
1962
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
1963
|
+
answer.i18n || answer.error, {t: t});
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
/************************************************************
|
|
1968
|
+
* The text of a transport's refusal: gobj_command() answers a string,
|
|
1969
|
+
* a command response or a number.
|
|
1970
|
+
************************************************************/
|
|
1971
|
+
function refusal_text(ret)
|
|
1972
|
+
{
|
|
1973
|
+
if(typeof ret === "string" && ret) {
|
|
1974
|
+
return ret;
|
|
1975
|
+
}
|
|
1976
|
+
if(ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) {
|
|
1977
|
+
return ret.comment;
|
|
1978
|
+
}
|
|
1979
|
+
return "print-tranger failed";
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1946
1982
|
/************************************************************
|
|
1947
1983
|
* The whole tranger, for the raw-json viewer.
|
|
1948
1984
|
************************************************************/
|
|
@@ -1952,11 +1988,13 @@ function request_print_tranger(gobj, path)
|
|
|
1952
1988
|
let remote = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
|
|
1953
1989
|
if(!remote) {
|
|
1954
1990
|
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
}
|
|
1991
|
+
answer_print_tranger_refused(gobj, path, {i18n: "no session"});
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
if(!is_connected(gobj)) {
|
|
1995
|
+
log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' ` +
|
|
1996
|
+
`not asked: no session`);
|
|
1997
|
+
answer_print_tranger_refused(gobj, path, {i18n: "no session"});
|
|
1960
1998
|
return;
|
|
1961
1999
|
}
|
|
1962
2000
|
let ret = gobj_command(remote, "print-tranger",
|
|
@@ -1972,6 +2010,7 @@ function request_print_tranger(gobj, path)
|
|
|
1972
2010
|
}, gobj);
|
|
1973
2011
|
if(ret) {
|
|
1974
2012
|
log_error(ret);
|
|
2013
|
+
answer_print_tranger_refused(gobj, path, {error: refusal_text(ret)});
|
|
1975
2014
|
}
|
|
1976
2015
|
}
|
|
1977
2016
|
|
|
@@ -0,0 +1,232 @@
|
|
|
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
|
+
* Driven through the real hosts and the real viewer on a document
|
|
17
|
+
* double, with a fake transport whose state is the one the hosts
|
|
18
|
+
* read and which refuses on demand.
|
|
19
|
+
*
|
|
20
|
+
* Copyright (c) 2026, ArtGins.
|
|
21
|
+
* All Rights Reserved.
|
|
22
|
+
***********************************************************************/
|
|
23
|
+
import {describe, test, expect, beforeAll, beforeEach} from "vitest";
|
|
24
|
+
import {install_dom_double} from "../test/dom_double.js";
|
|
25
|
+
|
|
26
|
+
install_dom_double();
|
|
27
|
+
|
|
28
|
+
if(!globalThis.ResizeObserver) {
|
|
29
|
+
globalThis.ResizeObserver = class {
|
|
30
|
+
observe() {}
|
|
31
|
+
unobserve() {}
|
|
32
|
+
disconnect() {}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
SDATA, SDATA_END, data_type_t,
|
|
38
|
+
gclass_create,
|
|
39
|
+
gobj_start_up, gobj_create_yuno, gobj_create, gobj_create_service,
|
|
40
|
+
gobj_create_pure_child,
|
|
41
|
+
gobj_start, gobj_send_event,
|
|
42
|
+
gobj_read_pointer_attr, gobj_subscribe_event,
|
|
43
|
+
gobj_change_state, gobj_current_state,
|
|
44
|
+
set_log_callback,
|
|
45
|
+
} = await import("@yuneta/gobj-js");
|
|
46
|
+
|
|
47
|
+
const logged = [];
|
|
48
|
+
const commands = []; /* what the fake transport was asked */
|
|
49
|
+
let refuse_in_session = false;
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* The transport. Out of session it refuses, as gui_agent's routing
|
|
53
|
+
* adapter (C_AGENT_TREEDB_LINK) does; in session it refuses only when
|
|
54
|
+
* the test says so (a transport that cannot route the command).
|
|
55
|
+
*/
|
|
56
|
+
function remote_command_parser(gobj, command, kw, src)
|
|
57
|
+
{
|
|
58
|
+
commands.push({command, kw});
|
|
59
|
+
if(gobj_current_state(gobj) !== "ST_SESSION") {
|
|
60
|
+
return `cannot route '${command}' -- not in session`;
|
|
61
|
+
}
|
|
62
|
+
if(refuse_in_session) {
|
|
63
|
+
return `cannot route '${command}'`;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let yuno = null;
|
|
69
|
+
|
|
70
|
+
beforeAll(async () => {
|
|
71
|
+
const i18next = (await import("i18next")).default;
|
|
72
|
+
await i18next.init({lng: "en", resources: {}});
|
|
73
|
+
gobj_start_up(null, null, null, null, null, null, null);
|
|
74
|
+
set_log_callback((level, msg) => {
|
|
75
|
+
logged.push({level: String(level), msg: String(msg)});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
gclass_create("C_TEST_HOST", [], [["ST_IDLE", []]], {}, 0, [SDATA_END()], {}, 0, 0, 0, 0);
|
|
79
|
+
gclass_create(
|
|
80
|
+
"C_TEST_VIEW_HOST",
|
|
81
|
+
[["EV_RECORD_WRITTEN", 0], ["EV_TOPIC_SELECTED", 0], ["EV_OPERATION_MODE_CHANGED", 0]],
|
|
82
|
+
[["ST_IDLE", [
|
|
83
|
+
["EV_RECORD_WRITTEN", () => 0, null],
|
|
84
|
+
["EV_TOPIC_SELECTED", () => 0, null],
|
|
85
|
+
["EV_OPERATION_MODE_CHANGED", () => 0, null]
|
|
86
|
+
]]],
|
|
87
|
+
{}, 0, [SDATA_END()], {}, 0, 0, 0, 0
|
|
88
|
+
);
|
|
89
|
+
gclass_create(
|
|
90
|
+
"C_TEST_REMOTE",
|
|
91
|
+
[],
|
|
92
|
+
[["ST_SESSION", []], ["ST_DISCONNECTED", []]],
|
|
93
|
+
{mt_command_parser: remote_command_parser},
|
|
94
|
+
0, [SDATA_END()], {}, 0, 0, 0, 0
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
/* The graph's engine, as a silent double: this test is about the
|
|
98
|
+
* viewer, not about the graph. */
|
|
99
|
+
const ENGINE_EVENTS = [
|
|
100
|
+
"EV_DESCS", "EV_CLEAR_DATA", "EV_LOAD_DATA", "EV_NODE_CREATED",
|
|
101
|
+
"EV_NODE_UPDATED", "EV_NODE_DELETED", "EV_GRAPHS_WRITE_REFUSED",
|
|
102
|
+
"EV_SHOW", "EV_HIDE", "EV_RESIZE", "EV_THEME", "EV_LANGUAGE_CHANGED",
|
|
103
|
+
"EV_SET_OPERATION_MODE", "EV_SET_LAYOUT"
|
|
104
|
+
];
|
|
105
|
+
gclass_create(
|
|
106
|
+
"C_G6_NODES_TREE",
|
|
107
|
+
ENGINE_EVENTS.map((e) => [e, 0]).concat([["EV_UPDATE_NODE", 2]]),
|
|
108
|
+
[["ST_IDLE", ENGINE_EVENTS.map((e) => [e, () => 0, null])]],
|
|
109
|
+
{
|
|
110
|
+
mt_create: (gobj) => {
|
|
111
|
+
const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
|
|
112
|
+
if(subscriber) {
|
|
113
|
+
gobj_subscribe_event(gobj, null, {}, subscriber);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
0,
|
|
118
|
+
[
|
|
119
|
+
SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, ""),
|
|
120
|
+
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, ""),
|
|
121
|
+
SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, ""),
|
|
122
|
+
SDATA(data_type_t.DTP_STRING, "treedb_name", 0, "", ""),
|
|
123
|
+
SDATA(data_type_t.DTP_JSON, "layout_names", 0, "[]", ""),
|
|
124
|
+
SDATA(data_type_t.DTP_STRING, "layout", 0, "", ""),
|
|
125
|
+
SDATA_END()
|
|
126
|
+
],
|
|
127
|
+
{}, 0, 0, 0, 0
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const {register_c_yui_json} = await import("./c_yui_json.js");
|
|
131
|
+
const {register_c_yui_treedb_topics} = await import("./c_yui_treedb_topics.js");
|
|
132
|
+
const {register_c_yui_treedb_graph} = await import("./c_yui_treedb_graph.js");
|
|
133
|
+
register_c_yui_json();
|
|
134
|
+
register_c_yui_treedb_topics();
|
|
135
|
+
register_c_yui_treedb_graph();
|
|
136
|
+
|
|
137
|
+
yuno = gobj_create_yuno("json_drill_yuno", "C_TEST_HOST", {});
|
|
138
|
+
gobj_start(yuno);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
beforeEach(() => {
|
|
142
|
+
logged.length = 0;
|
|
143
|
+
commands.length = 0;
|
|
144
|
+
refuse_in_session = false;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
/*
|
|
148
|
+
* A host of each kind, with its viewer open on a tranger that carries
|
|
149
|
+
* one stub. The viewer is the one the host created: it publishes
|
|
150
|
+
* EV_EXPAND_PATH to the host, and the host answers it.
|
|
151
|
+
*/
|
|
152
|
+
function build(name, gclass)
|
|
153
|
+
{
|
|
154
|
+
const remote = gobj_create_service(`${name}_remote`, "C_TEST_REMOTE", {}, yuno);
|
|
155
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
156
|
+
const view_host = gobj_create(`${name}_view_host`, "C_TEST_VIEW_HOST", {}, yuno);
|
|
157
|
+
const host = gobj_create_service(`${name}_host`, gclass, {
|
|
158
|
+
gobj_remote_yuno: remote,
|
|
159
|
+
treedb_name: "treedb_test",
|
|
160
|
+
subscriber: view_host
|
|
161
|
+
}, view_host);
|
|
162
|
+
if(gclass === "C_YUI_TREEDB_TOPICS") {
|
|
163
|
+
gobj_start(host);
|
|
164
|
+
}
|
|
165
|
+
const viewer = gobj_create_pure_child(`${name}_jv`, "C_YUI_JSON", {
|
|
166
|
+
subscriber: host,
|
|
167
|
+
json_data: {topics: {__collapsed__: {size: 3, path: "topics"}}}
|
|
168
|
+
}, host);
|
|
169
|
+
gobj_start(viewer);
|
|
170
|
+
host.priv.json_gobj = viewer;
|
|
171
|
+
logged.length = 0;
|
|
172
|
+
commands.length = 0;
|
|
173
|
+
return {remote, host, viewer};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function click_stub(viewer)
|
|
177
|
+
{
|
|
178
|
+
gobj_send_event(viewer, "EV_EXPAND_COLLAPSED", {path: "topics", size: 3}, viewer);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function drills()
|
|
182
|
+
{
|
|
183
|
+
return commands.filter((c) => c.command === "print-tranger").map((c) => c.kw.path);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for(const gclass of ["C_YUI_TREEDB_TOPICS", "C_YUI_TREEDB_GRAPH"]) {
|
|
187
|
+
describe(`${gclass}: a drill that cannot go out is answered`, () => {
|
|
188
|
+
|
|
189
|
+
test("out of session: the stub says 'no session', and a click after the reconnect asks", () => {
|
|
190
|
+
const {remote, viewer} = build(`${gclass}_a`, gclass);
|
|
191
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
192
|
+
|
|
193
|
+
click_stub(viewer);
|
|
194
|
+
|
|
195
|
+
expect(drills()).toEqual([]); /* not even tried */
|
|
196
|
+
expect(viewer.priv.pending.has("topics")).toBe(false);
|
|
197
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
198
|
+
{error: "no session", is_key: true});
|
|
199
|
+
|
|
200
|
+
/* The reconnect: the same stub asks again. */
|
|
201
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
202
|
+
click_stub(viewer);
|
|
203
|
+
expect(drills()).toEqual(["topics"]);
|
|
204
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("refused by the transport: the stub says why, and can be clicked again", () => {
|
|
208
|
+
const {viewer} = build(`${gclass}_b`, gclass);
|
|
209
|
+
refuse_in_session = true;
|
|
210
|
+
|
|
211
|
+
click_stub(viewer);
|
|
212
|
+
|
|
213
|
+
expect(drills()).toEqual(["topics"]);
|
|
214
|
+
expect(viewer.priv.pending.has("topics")).toBe(false);
|
|
215
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
216
|
+
{error: "cannot route 'print-tranger'", is_key: false});
|
|
217
|
+
|
|
218
|
+
refuse_in_session = false;
|
|
219
|
+
click_stub(viewer);
|
|
220
|
+
expect(drills()).toEqual(["topics", "topics"]);
|
|
221
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("in session and sent: the stub waits for its answer", () => {
|
|
225
|
+
const {viewer} = build(`${gclass}_c`, gclass);
|
|
226
|
+
click_stub(viewer);
|
|
227
|
+
expect(drills()).toEqual(["topics"]);
|
|
228
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
229
|
+
expect(logged.filter((l) => l.level === "error")).toEqual([]);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|